mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 01:27:34 -05:00
#90 [WIP] Resolves issue with MPT-1327 decoder which has apparently been broken for a long time.
This commit is contained in:
@@ -20,11 +20,15 @@ package channel.metadata;
|
|||||||
|
|
||||||
import channel.state.State;
|
import channel.state.State;
|
||||||
import module.decode.DecoderType;
|
import module.decode.DecoderType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import sample.Broadcaster;
|
import sample.Broadcaster;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
|
|
||||||
public class MutableMetadata extends Metadata implements Listener<AttributeChangeRequest>
|
public class MutableMetadata extends Metadata implements Listener<AttributeChangeRequest>
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(MutableMetadata.class);
|
||||||
|
|
||||||
private Broadcaster<MutableMetadataChangeEvent> mMetadataChangeEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<MutableMetadataChangeEvent> mMetadataChangeEventBroadcaster = new Broadcaster<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,7 +142,7 @@ public class MutableMetadata extends Metadata implements Listener<AttributeChang
|
|||||||
broadcast(Attribute.SECONDARY_ADDRESS_TO);
|
broadcast(Attribute.SECONDARY_ADDRESS_TO);
|
||||||
break;
|
break;
|
||||||
case CHANNEL_STATE:
|
case CHANNEL_STATE:
|
||||||
mState = (State)request.getValue();
|
mState = (State) request.getValue();
|
||||||
broadcast(Attribute.CHANNEL_STATE);
|
broadcast(Attribute.CHANNEL_STATE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -121,8 +121,6 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
{
|
{
|
||||||
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
|
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
|
||||||
|
|
||||||
mMutableMetadata.resetTemporalAttributes();
|
|
||||||
|
|
||||||
mState = State.IDLE;
|
mState = State.IDLE;
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
||||||
}
|
}
|
||||||
@@ -475,6 +473,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
*/
|
*/
|
||||||
public void configureAsTrafficChannel(TrafficChannelManager manager, CallEvent callEvent)
|
public void configureAsTrafficChannel(TrafficChannelManager manager, CallEvent callEvent)
|
||||||
{
|
{
|
||||||
|
mLog.debug("Configuring for traffic channel: " + callEvent.toCSV());
|
||||||
mTrafficChannelEndListener = manager;
|
mTrafficChannelEndListener = manager;
|
||||||
|
|
||||||
mTrafficChannelCallEvent = callEvent;
|
mTrafficChannelCallEvent = callEvent;
|
||||||
@@ -487,30 +486,32 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL, channel));
|
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL, channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long frequency = mTrafficChannelCallEvent.getFrequency();
|
||||||
|
|
||||||
|
if(frequency > 0)
|
||||||
|
{
|
||||||
|
mMutableMetadata.receive(new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY, frequency));
|
||||||
|
}
|
||||||
|
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<DecoderType>(Attribute.PRIMARY_DECODER_TYPE,
|
mMutableMetadata.receive(new AttributeChangeRequest<DecoderType>(Attribute.PRIMARY_DECODER_TYPE,
|
||||||
mTrafficChannelCallEvent.getDecoderType()));
|
mTrafficChannelCallEvent.getDecoderType()));
|
||||||
|
|
||||||
String details = mTrafficChannelCallEvent.getDetails();
|
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_LABEL_1, "TRAFFIC"));
|
||||||
|
|
||||||
String from = mTrafficChannelCallEvent.getFromID();
|
String from = mTrafficChannelCallEvent.getFromID();
|
||||||
|
|
||||||
if(from != null)
|
if(from != null)
|
||||||
{
|
{
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_FROM, from));
|
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_FROM, from,
|
||||||
|
mTrafficChannelCallEvent.getFromIDAlias()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String to = mTrafficChannelCallEvent.getToID();
|
String to = mTrafficChannelCallEvent.getToID();
|
||||||
|
|
||||||
if(to != null)
|
if(to != null)
|
||||||
{
|
{
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_TO, to));
|
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_TO, to,
|
||||||
}
|
mTrafficChannelCallEvent.getToIDAlias()));
|
||||||
|
|
||||||
long frequency = mTrafficChannelCallEvent.getFrequency();
|
|
||||||
|
|
||||||
if(frequency > 0)
|
|
||||||
{
|
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY, frequency));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rebroadcast the allocation event so that the internal decoder states
|
/* Rebroadcast the allocation event so that the internal decoder states
|
||||||
|
|||||||
@@ -194,8 +194,7 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
|
|
||||||
if(processingChain == null)
|
if(processingChain == null)
|
||||||
{
|
{
|
||||||
processingChain = new ProcessingChain(channel.getName(),
|
processingChain = new ProcessingChain(channel.getName(), channel.getChannelType());
|
||||||
channel.getChannelType());
|
|
||||||
|
|
||||||
/* Register global listeners */
|
/* Register global listeners */
|
||||||
for(Listener<AudioPacket> listener : mAudioPacketListeners)
|
for(Listener<AudioPacket> listener : mAudioPacketListeners)
|
||||||
|
|||||||
@@ -1,155 +1,157 @@
|
|||||||
package controller.channel.map;
|
package controller.channel.map;
|
||||||
|
|
||||||
|
import controller.channel.map.ChannelMapEvent.Event;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sample.Broadcaster;
|
||||||
|
import sample.Listener;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.AbstractListModel;
|
|
||||||
|
|
||||||
import controller.channel.map.ChannelMapEvent.Event;
|
|
||||||
import sample.Broadcaster;
|
|
||||||
import sample.Listener;
|
|
||||||
|
|
||||||
public class ChannelMapModel extends AbstractListModel<ChannelMap>
|
public class ChannelMapModel extends AbstractListModel<ChannelMap>
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(ChannelMapModel.class);
|
||||||
|
|
||||||
private List<ChannelMap> mChannelMaps = new ArrayList<>();
|
private List<ChannelMap> mChannelMaps = new ArrayList<>();
|
||||||
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
|
||||||
|
|
||||||
public ChannelMapModel()
|
public ChannelMapModel()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an unmodifiable list of channel maps currently in the model
|
* Returns an unmodifiable list of channel maps currently in the model
|
||||||
*/
|
*/
|
||||||
public List<ChannelMap> getChannelMaps()
|
public List<ChannelMap> getChannelMaps()
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableList( mChannelMaps );
|
return Collections.unmodifiableList(mChannelMaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the channel map with a matching name or null
|
* Returns the channel map with a matching name or null
|
||||||
*/
|
*/
|
||||||
public ChannelMap getChannelMap( String name )
|
public ChannelMap getChannelMap(String name)
|
||||||
{
|
{
|
||||||
for( ChannelMap channelMap: mChannelMaps )
|
for(ChannelMap channelMap : mChannelMaps)
|
||||||
{
|
{
|
||||||
if( channelMap.getName().equalsIgnoreCase( name ) )
|
if(channelMap.getName().equalsIgnoreCase(name))
|
||||||
{
|
{
|
||||||
return channelMap;
|
return channelMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener to receive notifications when channel map updates occur
|
* Adds a listener to receive notifications when channel map updates occur
|
||||||
*/
|
*/
|
||||||
public void addListener( Listener<ChannelMapEvent> listener )
|
public void addListener(Listener<ChannelMapEvent> listener)
|
||||||
{
|
{
|
||||||
mEventBroadcaster.addListener( listener );
|
mEventBroadcaster.addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the channel map update notification listener
|
* Removes the channel map update notification listener
|
||||||
*/
|
*/
|
||||||
public void removeListener( Listener<ChannelMapEvent> listener )
|
public void removeListener(Listener<ChannelMapEvent> listener)
|
||||||
{
|
{
|
||||||
mEventBroadcaster.removeListener( listener );
|
mEventBroadcaster.removeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcasts a channel map change.
|
* Broadcasts a channel map change.
|
||||||
*
|
*
|
||||||
* Note: use the add/remove methods to add or remove channel maps from this
|
* Note: use the add/remove methods to add or remove channel maps from this
|
||||||
* model. When using those methods, an add or delete event will automatically
|
* model. When using those methods, an add or delete event will automatically
|
||||||
* be generated.
|
* be generated.
|
||||||
*/
|
*/
|
||||||
public void broadcast( ChannelMapEvent event )
|
public void broadcast(ChannelMapEvent event)
|
||||||
{
|
{
|
||||||
if( event.getEvent() == ChannelMapEvent.Event.CHANGE ||
|
if(event.getEvent() == ChannelMapEvent.Event.CHANGE ||
|
||||||
event.getEvent() == ChannelMapEvent.Event.RENAME )
|
event.getEvent() == ChannelMapEvent.Event.RENAME)
|
||||||
{
|
{
|
||||||
int index = mChannelMaps.indexOf( event.getChannelMap() );
|
int index = mChannelMaps.indexOf(event.getChannelMap());
|
||||||
|
|
||||||
if( index >= 0 )
|
if(index >= 0)
|
||||||
{
|
{
|
||||||
fireContentsChanged( this, index, index );
|
fireContentsChanged(this, index, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mEventBroadcaster.broadcast( event );
|
mEventBroadcaster.broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a list of channel maps to this model
|
* Adds a list of channel maps to this model
|
||||||
*/
|
*/
|
||||||
public void addChannelMaps( List<ChannelMap> channelMaps )
|
public void addChannelMaps(List<ChannelMap> channelMaps)
|
||||||
{
|
{
|
||||||
for( ChannelMap channelMap: channelMaps )
|
for(ChannelMap channelMap : channelMaps)
|
||||||
{
|
{
|
||||||
addChannelMap( channelMap );
|
addChannelMap(channelMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the channel map to this model
|
* Adds the channel map to this model
|
||||||
*/
|
*/
|
||||||
public void addChannelMap( ChannelMap channelMap )
|
public void addChannelMap(ChannelMap channelMap)
|
||||||
{
|
{
|
||||||
if( !mChannelMaps.contains( channelMap ) )
|
if(!mChannelMaps.contains(channelMap))
|
||||||
{
|
{
|
||||||
mChannelMaps.add( channelMap );
|
mChannelMaps.add(channelMap);
|
||||||
|
|
||||||
int index = mChannelMaps.indexOf( channelMap );
|
int index = mChannelMaps.indexOf(channelMap);
|
||||||
|
|
||||||
fireIntervalAdded( this, index, index );
|
fireIntervalAdded(this, index, index);
|
||||||
|
|
||||||
broadcast( new ChannelMapEvent( channelMap, Event.ADD ) );
|
broadcast(new ChannelMapEvent(channelMap, Event.ADD));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the channel map from this model
|
* Removes the channel map from this model
|
||||||
*/
|
*/
|
||||||
public void removeChannelMap( ChannelMap channelMap )
|
public void removeChannelMap(ChannelMap channelMap)
|
||||||
{
|
{
|
||||||
if( mChannelMaps.contains( channelMap ) )
|
if(mChannelMaps.contains(channelMap))
|
||||||
{
|
{
|
||||||
int index = mChannelMaps.indexOf( channelMap );
|
int index = mChannelMaps.indexOf(channelMap);
|
||||||
|
|
||||||
mChannelMaps.remove( channelMap );
|
mChannelMaps.remove(channelMap);
|
||||||
|
|
||||||
fireIntervalRemoved( this, index, index );
|
fireIntervalRemoved(this, index, index);
|
||||||
|
|
||||||
broadcast( new ChannelMapEvent( channelMap, Event.DELETE ) );
|
broadcast(new ChannelMapEvent(channelMap, Event.DELETE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Size/Number of channel maps in this model
|
* Size/Number of channel maps in this model
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getSize()
|
public int getSize()
|
||||||
{
|
{
|
||||||
return mChannelMaps.size();
|
return mChannelMaps.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the channel map at the specified index
|
* Returns the channel map at the specified index
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelMap getElementAt( int index )
|
public ChannelMap getElementAt(int index)
|
||||||
{
|
{
|
||||||
if( index <= mChannelMaps.size() )
|
if(index <= mChannelMaps.size())
|
||||||
{
|
{
|
||||||
return mChannelMaps.get( index );
|
return mChannelMaps.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -51,25 +51,25 @@ public class FilterViewer extends Application
|
|||||||
// .stopBandRipple( 0.027 )
|
// .stopBandRipple( 0.027 )
|
||||||
// .build();
|
// .build();
|
||||||
|
|
||||||
FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
|
// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
|
||||||
.sampleRate( 8000 )
|
// .sampleRate( 8000 )
|
||||||
.stopBandCutoff( 250 )
|
// .stopBandCutoff( 250 )
|
||||||
.stopBandAmplitude( 0.0 )
|
// .stopBandAmplitude( 0.0 )
|
||||||
.stopBandRipple( 0.01 )
|
// .stopBandRipple( 0.01 )
|
||||||
.passBandStart( 300 )
|
// .passBandStart( 300 )
|
||||||
.passBandAmplitude( 1.0 )
|
// .passBandAmplitude( 1.0 )
|
||||||
.passBandRipple( 0.01 )
|
// .passBandRipple( 0.01 )
|
||||||
.build();
|
// .build();
|
||||||
|
|
||||||
// FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
|
FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
|
||||||
// .sampleRate( 48000 )
|
.sampleRate( 48000 )
|
||||||
// .stopFrequency1( 150 )
|
.stopFrequency1( 200 )
|
||||||
// .passFrequencyBegin( 200 )
|
.passFrequencyBegin( 400 )
|
||||||
// .passFrequencyEnd( 3950 )
|
.passFrequencyEnd( 3200 )
|
||||||
// .stopFrequency2( 4000 )
|
.stopFrequency2( 3400 )
|
||||||
// .stopRipple( 0.0005 )
|
.stopRipple( 0.0003 )
|
||||||
// .passRipple( 0.003)
|
.passRipple( 0.008)
|
||||||
// .build();
|
.build();
|
||||||
|
|
||||||
float[] taps = null;
|
float[] taps = null;
|
||||||
|
|
||||||
|
|||||||
@@ -17,21 +17,22 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.fsk;
|
package dsp.fsk;
|
||||||
|
|
||||||
|
import buffer.BooleanAveragingBuffer;
|
||||||
|
import dsp.symbol.SymbolEvent;
|
||||||
|
import dsp.symbol.SymbolEvent.Shift;
|
||||||
import instrument.Instrumentable;
|
import instrument.Instrumentable;
|
||||||
import instrument.tap.Tap;
|
import instrument.tap.Tap;
|
||||||
import instrument.tap.TapGroup;
|
import instrument.tap.TapGroup;
|
||||||
import instrument.tap.stream.SymbolEventTap;
|
import instrument.tap.stream.SymbolEventTap;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sample.Listener;
|
||||||
|
import sample.real.RealBuffer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import sample.Listener;
|
|
||||||
import sample.real.RealBuffer;
|
|
||||||
import buffer.BooleanAveragingBuffer;
|
|
||||||
import dsp.symbol.SymbolEvent;
|
|
||||||
import dsp.symbol.SymbolEvent.Shift;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binary Frequency Shift Keying (FSK) decoder. Implements a BFSK correlation
|
* Binary Frequency Shift Keying (FSK) decoder. Implements a BFSK correlation
|
||||||
* decoder. Provides normal or inverted decoded output. Requires the symbol
|
* decoder. Provides normal or inverted decoded output. Requires the symbol
|
||||||
@@ -54,243 +55,248 @@ import dsp.symbol.SymbolEvent.Shift;
|
|||||||
*/
|
*/
|
||||||
public class FSK2Decoder implements Instrumentable, Listener<RealBuffer>
|
public class FSK2Decoder implements Instrumentable, Listener<RealBuffer>
|
||||||
{
|
{
|
||||||
public enum Output{ NORMAL, INVERTED };
|
private final static Logger mLog = LoggerFactory.getLogger(FSK2Decoder.class);
|
||||||
|
|
||||||
/* Instrumentation taps */
|
public enum Output
|
||||||
private static final String INSTRUMENT_DECISION =
|
{
|
||||||
"Tap Point: FSK2 Symbol Decision";
|
NORMAL, INVERTED
|
||||||
private List<TapGroup> mAvailableTaps;
|
}
|
||||||
private ArrayList<SymbolEventTap> mTaps = new ArrayList<SymbolEventTap>();
|
|
||||||
|
|
||||||
private Listener<Boolean> mListener;
|
/* Instrumentation taps */
|
||||||
private BooleanAveragingBuffer mDelayBuffer;
|
private static final String INSTRUMENT_DECISION =
|
||||||
private BooleanAveragingBuffer mLowPassFilter;
|
"Tap Point: FSK2 Symbol Decision";
|
||||||
private Slicer mSlicer;
|
private List<TapGroup> mAvailableTaps;
|
||||||
private boolean mNormalOutput;
|
private ArrayList<SymbolEventTap> mTaps = new ArrayList<SymbolEventTap>();
|
||||||
private int mSamplesPerSymbol;
|
|
||||||
private int mSymbolRate;
|
|
||||||
|
|
||||||
public FSK2Decoder( int sampleRate, int symbolRate, Output output )
|
private Listener<Boolean> mListener;
|
||||||
{
|
private BooleanAveragingBuffer mDelayBuffer;
|
||||||
/* Ensure we're using an integral symbolRate of the sampleRate */
|
private BooleanAveragingBuffer mLowPassFilter;
|
||||||
assert( sampleRate % symbolRate == 0 );
|
private Slicer mSlicer;
|
||||||
|
private boolean mNormalOutput;
|
||||||
|
private int mSamplesPerSymbol;
|
||||||
|
private int mSymbolRate;
|
||||||
|
|
||||||
mSamplesPerSymbol = (int)( sampleRate / symbolRate );
|
public FSK2Decoder(int sampleRate, int symbolRate, Output output)
|
||||||
mNormalOutput = ( output == Output.NORMAL );
|
{
|
||||||
mSymbolRate = symbolRate;
|
/* Ensure we're using an integral symbolRate of the sampleRate */
|
||||||
|
assert (sampleRate % symbolRate == 0);
|
||||||
|
|
||||||
mDelayBuffer = new BooleanAveragingBuffer( mSamplesPerSymbol );
|
mSamplesPerSymbol = (int) (sampleRate / symbolRate);
|
||||||
mLowPassFilter = new BooleanAveragingBuffer( mSamplesPerSymbol );
|
mNormalOutput = (output == Output.NORMAL);
|
||||||
mSlicer = new Slicer( mSamplesPerSymbol );
|
mSymbolRate = symbolRate;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
mDelayBuffer = new BooleanAveragingBuffer(mSamplesPerSymbol);
|
||||||
* Disposes of all references to prepare for garbage collection
|
mLowPassFilter = new BooleanAveragingBuffer(mSamplesPerSymbol);
|
||||||
*/
|
mSlicer = new Slicer(mSamplesPerSymbol);
|
||||||
public void dispose()
|
}
|
||||||
{
|
|
||||||
mListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary sample input
|
* Disposes of all references to prepare for garbage collection
|
||||||
*/
|
*/
|
||||||
@Override
|
public void dispose()
|
||||||
public void receive( RealBuffer buffer )
|
{
|
||||||
{
|
mListener = null;
|
||||||
for( float sample: buffer.getSamples() )
|
}
|
||||||
{
|
|
||||||
|
/**
|
||||||
|
* 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
|
/* Square the sample. Greater than zero is a 1 (true) and less than
|
||||||
* zero is a 0 (false) */
|
* zero is a 0 (false) */
|
||||||
boolean bitSample = ( sample >= 0.0f );
|
boolean bitSample = (sample >= 0.0f);
|
||||||
|
|
||||||
/* Feed the delay buffer and fetch the one-baud delayed sample */
|
/* Feed the delay buffer and fetch the one-baud delayed sample */
|
||||||
boolean delayedBitSample = mDelayBuffer.get( bitSample );
|
boolean delayedBitSample = mDelayBuffer.get(bitSample);
|
||||||
|
|
||||||
/* Correlation: xor current bit with delayed bit */
|
/* Correlation: xor current bit with delayed bit */
|
||||||
boolean softBit = bitSample ^ delayedBitSample;
|
boolean softBit = bitSample ^ delayedBitSample;
|
||||||
|
|
||||||
/* Low pass filter to smooth the correlated values */
|
/* Low pass filter to smooth the correlated values */
|
||||||
boolean filteredSoftBit = mLowPassFilter.getAverage( softBit );
|
boolean filteredSoftBit = mLowPassFilter.getAverage(softBit);
|
||||||
|
|
||||||
/* Send the filtered correlated bit to the slicer */
|
/* Send the filtered correlated bit to the slicer */
|
||||||
mSlicer.receive( filteredSoftBit );
|
mSlicer.receive(filteredSoftBit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a listener to receive the decoded FSK bits
|
* Registers a listener to receive the decoded FSK bits
|
||||||
*/
|
*/
|
||||||
public void setListener( Listener<Boolean> listener )
|
public void setListener(Listener<Boolean> listener)
|
||||||
{
|
{
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the listener
|
* Removes the listener
|
||||||
*/
|
*/
|
||||||
public void removeListener( Listener<Boolean> listener )
|
public void removeListener(Listener<Boolean> listener)
|
||||||
{
|
{
|
||||||
mListener = null;
|
mListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symbol slicer with auto-aligning baud timing
|
* Symbol slicer with auto-aligning baud timing
|
||||||
*/
|
*/
|
||||||
public class Slicer
|
public class Slicer
|
||||||
{
|
{
|
||||||
private BitSet mBitSet = new BitSet();
|
private BitSet mBitSet = new BitSet();
|
||||||
private int mSymbolLength;
|
private int mSymbolLength;
|
||||||
private int mDecisionThreshold;
|
private int mDecisionThreshold;
|
||||||
private int mSampleCounter;
|
private int mSampleCounter;
|
||||||
|
|
||||||
public Slicer( int samplesPerSymbol )
|
public Slicer(int samplesPerSymbol)
|
||||||
{
|
{
|
||||||
mSymbolLength = samplesPerSymbol;
|
mSymbolLength = samplesPerSymbol;
|
||||||
|
|
||||||
mDecisionThreshold = (int)( mSymbolLength / 2 );
|
mDecisionThreshold = (int) (mSymbolLength / 2);
|
||||||
|
|
||||||
/* Adjust for an odd number of samples per baud */
|
/* Adjust for an odd number of samples per baud */
|
||||||
if( mSymbolLength % 2 == 1 )
|
if(mSymbolLength % 2 == 1)
|
||||||
{
|
{
|
||||||
mDecisionThreshold++;
|
mDecisionThreshold++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void receive( boolean softBit )
|
public void receive(boolean softBit)
|
||||||
{
|
{
|
||||||
if( mSampleCounter >= 0 )
|
if(mSampleCounter >= 0)
|
||||||
{
|
{
|
||||||
if( softBit )
|
if(softBit)
|
||||||
{
|
{
|
||||||
mBitSet.set( mSampleCounter );
|
mBitSet.set(mSampleCounter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mBitSet.clear( mSampleCounter );
|
mBitSet.clear(mSampleCounter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mSampleCounter++;
|
mSampleCounter++;
|
||||||
|
|
||||||
if( mSampleCounter >= mSymbolLength )
|
if(mSampleCounter >= mSymbolLength)
|
||||||
{
|
{
|
||||||
boolean decision = mBitSet.cardinality() >= mDecisionThreshold;
|
boolean decision = mBitSet.cardinality() >= mDecisionThreshold;
|
||||||
|
|
||||||
send( decision );
|
send(decision);
|
||||||
|
|
||||||
/* Shift timing left if the left bit in the bitset is opposite
|
/* Shift timing left if the left bit in the bitset is opposite
|
||||||
* the decision and the right bit is the same */
|
* the decision and the right bit is the same */
|
||||||
if( ( mBitSet.get( 0 ) ^ decision ) &&
|
if((mBitSet.get(0) ^ decision) &&
|
||||||
( !( mBitSet.get( mSymbolLength - 1 ) ^ decision ) ) )
|
(!(mBitSet.get(mSymbolLength - 1) ^ decision)))
|
||||||
{
|
{
|
||||||
sendTapEvent( mBitSet, Shift.LEFT, decision );
|
sendTapEvent(mBitSet, Shift.LEFT, decision);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
mSampleCounter--;
|
mSampleCounter--;
|
||||||
}
|
}
|
||||||
/* Shift timing right if the left bit is the same as the
|
/* Shift timing right if the left bit is the same as the
|
||||||
* decision and the right bit is opposite */
|
* decision and the right bit is opposite */
|
||||||
else if( ( !( mBitSet.get( 0 ) ^ decision ) ) &&
|
else if((!(mBitSet.get(0) ^ decision)) &&
|
||||||
( mBitSet.get( mSymbolLength - 1 ) ^ decision ) )
|
(mBitSet.get(mSymbolLength - 1) ^ decision))
|
||||||
{
|
{
|
||||||
sendTapEvent( mBitSet, Shift.RIGHT, decision );
|
sendTapEvent(mBitSet, Shift.RIGHT, decision);
|
||||||
|
|
||||||
/* Last bit from previous symbol to pre-fill next symbol */
|
/* Last bit from previous symbol to pre-fill next symbol */
|
||||||
boolean previousSoftBit = mBitSet.get( mSymbolLength - 1 );
|
boolean previousSoftBit = mBitSet.get(mSymbolLength - 1);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
if( previousSoftBit )
|
if(previousSoftBit)
|
||||||
{
|
{
|
||||||
mBitSet.set( 0 );
|
mBitSet.set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSampleCounter++;
|
mSampleCounter++;
|
||||||
}
|
}
|
||||||
/* No shift */
|
/* No shift */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sendTapEvent( mBitSet, Shift.NONE, decision );
|
sendTapEvent(mBitSet, Shift.NONE, decision);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the bit decision to the listener
|
* Sends the bit decision to the listener
|
||||||
*/
|
*/
|
||||||
private void send( boolean decision )
|
private void send(boolean decision)
|
||||||
{
|
{
|
||||||
if( mListener != null )
|
if(mListener != null)
|
||||||
{
|
{
|
||||||
mListener.receive( mNormalOutput ? decision : !decision );
|
mListener.receive(mNormalOutput ? decision : !decision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset()
|
private void reset()
|
||||||
{
|
{
|
||||||
mBitSet.clear();
|
mBitSet.clear();
|
||||||
mSampleCounter = 0;
|
mSampleCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends instrumentation tap event to all registered listeners
|
* Sends instrumentation tap event to all registered listeners
|
||||||
*/
|
*/
|
||||||
private void sendTapEvent( BitSet bitset, Shift shift, boolean decision )
|
private void sendTapEvent(BitSet bitset, Shift shift, boolean decision)
|
||||||
{
|
{
|
||||||
for( SymbolEventTap tap: mTaps )
|
for(SymbolEventTap tap : mTaps)
|
||||||
{
|
{
|
||||||
SymbolEvent event =
|
SymbolEvent event =
|
||||||
new SymbolEvent( bitset.get( 0, mSymbolLength ),
|
new SymbolEvent(bitset.get(0, mSymbolLength),
|
||||||
mSymbolLength,
|
mSymbolLength,
|
||||||
decision,
|
decision,
|
||||||
shift );
|
shift);
|
||||||
|
|
||||||
tap.receive( event );
|
tap.receive(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get instrumentation taps
|
* Get instrumentation taps
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<TapGroup> getTapGroups()
|
public List<TapGroup> getTapGroups()
|
||||||
{
|
{
|
||||||
if( mAvailableTaps == null )
|
if(mAvailableTaps == null)
|
||||||
{
|
{
|
||||||
mAvailableTaps = new ArrayList<TapGroup>();
|
mAvailableTaps = new ArrayList<TapGroup>();
|
||||||
|
|
||||||
TapGroup tapGroup = new TapGroup( "FSK2 Decoder" );
|
TapGroup tapGroup = new TapGroup("FSK2 Decoder");
|
||||||
tapGroup.add( new SymbolEventTap( INSTRUMENT_DECISION, 0, .025f ) );
|
tapGroup.add(new SymbolEventTap(INSTRUMENT_DECISION, 0, .025f));
|
||||||
|
|
||||||
mAvailableTaps.add( tapGroup );
|
mAvailableTaps.add(tapGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mAvailableTaps;
|
return mAvailableTaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add instrumentation tap
|
* Add instrumentation tap
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void registerTap( Tap tap )
|
public void registerTap(Tap tap)
|
||||||
{
|
{
|
||||||
if( tap instanceof SymbolEventTap && !mTaps.contains( tap ) )
|
if(tap instanceof SymbolEventTap && !mTaps.contains(tap))
|
||||||
{
|
{
|
||||||
mTaps.add( (SymbolEventTap)tap );
|
mTaps.add((SymbolEventTap) tap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instrumentation tap
|
* Remove instrumentation tap
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void unregisterTap( Tap tap )
|
public void unregisterTap(Tap tap)
|
||||||
{
|
{
|
||||||
mTaps.remove( tap );
|
mTaps.remove(tap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
|||||||
if(mBroadcastStatusPanel == null)
|
if(mBroadcastStatusPanel == null)
|
||||||
{
|
{
|
||||||
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
|
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
|
||||||
mBroadcastStatusPanel.setPreferredSize(new Dimension(880, 100));
|
mBroadcastStatusPanel.setPreferredSize(new Dimension(880, 70));
|
||||||
mBroadcastStatusPanel.getTable().setEnabled(false);
|
mBroadcastStatusPanel.getTable().setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import controller.channel.ChannelModel;
|
|||||||
import controller.channel.ChannelProcessingManager;
|
import controller.channel.ChannelProcessingManager;
|
||||||
import controller.channel.map.ChannelMap;
|
import controller.channel.map.ChannelMap;
|
||||||
import controller.channel.map.ChannelMapModel;
|
import controller.channel.map.ChannelMapModel;
|
||||||
|
import dsp.filter.FilterFactory;
|
||||||
|
import dsp.filter.fir.FIRFilterSpecification;
|
||||||
import filter.AllPassFilter;
|
import filter.AllPassFilter;
|
||||||
import filter.FilterSet;
|
import filter.FilterSet;
|
||||||
import filter.IFilter;
|
import filter.IFilter;
|
||||||
@@ -89,12 +91,34 @@ import module.decode.tait.Tait1200DecoderState;
|
|||||||
import module.demodulate.am.AMDemodulatorModule;
|
import module.demodulate.am.AMDemodulatorModule;
|
||||||
import module.demodulate.audio.DemodulatedAudioFilterModule;
|
import module.demodulate.audio.DemodulatedAudioFilterModule;
|
||||||
import module.demodulate.fm.FMDemodulatorModule;
|
import module.demodulate.fm.FMDemodulatorModule;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DecoderFactory
|
public class DecoderFactory
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(DecoderFactory.class);
|
||||||
|
|
||||||
|
private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.bandPassBuilder()
|
||||||
|
.sampleRate(48000).stopFrequency1(200).passFrequencyBegin(400).passFrequencyEnd(3200)
|
||||||
|
.stopFrequency2(3400).stopRipple(0.0003).passRipple(0.008).build();
|
||||||
|
|
||||||
|
private static float[] MPT1327_BANDPASS_FILTER;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MPT1327_BANDPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Couldn't design MPT1327 bandpass filter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final boolean NO_REMOVE_DC = false;
|
public static final boolean NO_REMOVE_DC = false;
|
||||||
public static final boolean REMOVE_DC = true;
|
public static final boolean REMOVE_DC = true;
|
||||||
|
|
||||||
@@ -197,7 +221,7 @@ public class DecoderFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
||||||
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
|
modules.add(new DemodulatedAudioFilterModule(MPT1327_BANDPASS_FILTER, 2.0f));
|
||||||
modules.add(new AudioModule(metadata));
|
modules.add(new AudioModule(metadata));
|
||||||
break;
|
break;
|
||||||
case PASSPORT:
|
case PASSPORT:
|
||||||
|
|||||||
@@ -1,322 +1,290 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* 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/>
|
||||||
*
|
*
|
||||||
* 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 module.decode.mpt1327;
|
package module.decode.mpt1327;
|
||||||
|
|
||||||
|
import alias.AliasList;
|
||||||
|
import bits.MessageFramer;
|
||||||
|
import bits.SyncPattern;
|
||||||
|
import dsp.filter.Filters;
|
||||||
|
import dsp.filter.dc.DCRemovalFilterLyons;
|
||||||
|
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
|
||||||
|
import dsp.filter.halfband.real.HalfBandFilter_RB_RB;
|
||||||
|
import dsp.fsk.FSK2Decoder;
|
||||||
|
import dsp.fsk.FSK2Decoder.Output;
|
||||||
import instrument.Instrumentable;
|
import instrument.Instrumentable;
|
||||||
import instrument.tap.Tap;
|
import instrument.tap.Tap;
|
||||||
import instrument.tap.TapGroup;
|
import instrument.tap.TapGroup;
|
||||||
import instrument.tap.stream.BinaryTap;
|
import instrument.tap.stream.BinaryTap;
|
||||||
import instrument.tap.stream.FloatBufferTap;
|
import instrument.tap.stream.FloatBufferTap;
|
||||||
import instrument.tap.stream.FloatTap;
|
import instrument.tap.stream.FloatTap;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import module.decode.Decoder;
|
import module.decode.Decoder;
|
||||||
import module.decode.DecoderType;
|
import module.decode.DecoderType;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sample.Broadcaster;
|
import sample.Broadcaster;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
import sample.real.IFilteredRealBufferListener;
|
import sample.real.IFilteredRealBufferListener;
|
||||||
import sample.real.RealBuffer;
|
import sample.real.RealBuffer;
|
||||||
import alias.AliasList;
|
|
||||||
import bits.MessageFramer;
|
import java.util.ArrayList;
|
||||||
import bits.SyncPattern;
|
import java.util.List;
|
||||||
import dsp.filter.FilterFactory;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import dsp.filter.Filters;
|
|
||||||
import dsp.filter.Window.WindowType;
|
|
||||||
import dsp.filter.dc.DCRemovalFilterLyons;
|
|
||||||
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
|
|
||||||
import dsp.filter.halfband.real.HalfBandFilter_RB_RB;
|
|
||||||
import dsp.fsk.FSK2Decoder;
|
|
||||||
import dsp.fsk.FSK2Decoder.Output;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
|
* MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
|
||||||
* complex or floating point samples and output fully framed MPT1327 control and
|
* complex or floating point samples and output fully framed MPT1327 control and
|
||||||
* traffic messages.
|
* traffic messages.
|
||||||
*/
|
*/
|
||||||
public class MPT1327Decoder extends Decoder
|
public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable
|
||||||
implements IFilteredRealBufferListener, Instrumentable
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( MPT1327Decoder.class );
|
private final static Logger mLog = LoggerFactory.getLogger(MPT1327Decoder.class);
|
||||||
|
|
||||||
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
|
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
|
||||||
private static final int sDECIMATED_SAMPLE_RATE = 24000;
|
private static final int sDECIMATED_SAMPLE_RATE = 24000;
|
||||||
|
|
||||||
/* Baud or Symbol Rate */
|
/* Baud or Symbol Rate */
|
||||||
private static final int sSYMBOL_RATE = 1200;
|
private static final int sSYMBOL_RATE = 1200;
|
||||||
|
|
||||||
/* Message length -- longest possible message is:
|
/* Message length -- longest possible message is:
|
||||||
* 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */
|
* 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */
|
||||||
private static final int sMESSAGE_LENGTH = 350;
|
private static final int sMESSAGE_LENGTH = 350;
|
||||||
|
|
||||||
/* Instrumentation Taps */
|
/* Instrumentation Taps */
|
||||||
private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS =
|
private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS =
|
||||||
"Tap Point: Half-band Decimation Filter > < Low Pass Filter";
|
"Tap Point: Half-band Decimation Filter > < Low Pass Filter";
|
||||||
private static final String INSTRUMENT_LOW_PASS_TO_DECODER =
|
private static final String INSTRUMENT_LOW_PASS_TO_DECODER =
|
||||||
"Tap Point: Low Pass Filter > < Decoder";
|
"Tap Point: Low Pass Filter > < Decoder";
|
||||||
private static final String INSTRUMENT_DECODER_TO_FRAMER =
|
private static final String INSTRUMENT_DECODER_TO_FRAMER =
|
||||||
"Tap Point: Decoder > < Sync Detect/Message Framer";
|
"Tap Point: Decoder > < Sync Detect/Message Framer";
|
||||||
private List<TapGroup> mAvailableTaps;
|
private List<TapGroup> mAvailableTaps;
|
||||||
|
|
||||||
private HalfBandFilter_RB_RB mDecimationFilter;
|
private HalfBandFilter_RB_RB mDecimationFilter;
|
||||||
private RealFIRFilter_RB_RB mLowPassFilter;
|
private RealFIRFilter_RB_RB mLowPassFilter;
|
||||||
private DCRemovalFilterLyons mDCRemovalFilter;
|
private DCRemovalFilterLyons mDCRemovalFilter;
|
||||||
private FSK2Decoder mFSKDecoder;
|
private FSK2Decoder mFSKDecoder;
|
||||||
private Broadcaster<Boolean> mSymbolBroadcaster;
|
private Broadcaster<Boolean> mSymbolBroadcaster;
|
||||||
private MessageFramer mControlMessageFramer;
|
private MessageFramer mControlMessageFramer;
|
||||||
private MessageFramer mTrafficMessageFramer;
|
private MessageFramer mTrafficMessageFramer;
|
||||||
private MPT1327MessageProcessor mMessageProcessor;
|
private MPT1327MessageProcessor mMessageProcessor;
|
||||||
|
|
||||||
public MPT1327Decoder( AliasList aliasList, Sync sync )
|
public MPT1327Decoder(AliasList aliasList, Sync sync)
|
||||||
{
|
{
|
||||||
|
/* Decimation filter - 48000 / 2 = 24000 output */
|
||||||
|
mDecimationFilter = new HalfBandFilter_RB_RB(Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true);
|
||||||
|
|
||||||
/* Decimation filter - 48000 / 2 = 24000 output */
|
/**
|
||||||
mDecimationFilter = new HalfBandFilter_RB_RB(
|
* Normal: 2FSK Decoder with inverted output
|
||||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true );
|
* French: 2FSK Decoder with normal output
|
||||||
|
*/
|
||||||
|
if(sync == Sync.NORMAL)
|
||||||
|
{
|
||||||
|
mFSKDecoder = new FSK2Decoder(24000, 1200, Output.INVERTED);
|
||||||
|
}
|
||||||
|
else if(sync == Sync.FRENCH)
|
||||||
|
{
|
||||||
|
mFSKDecoder = new FSK2Decoder(24000, 1200, Output.NORMAL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("MPT1327 Decoder - unrecognized Sync type");
|
||||||
|
}
|
||||||
|
|
||||||
/* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */
|
mDecimationFilter.setListener(mFSKDecoder);
|
||||||
mLowPassFilter = new RealFIRFilter_RB_RB(
|
|
||||||
FilterFactory.getLowPass( 24000, //sampleRate,
|
|
||||||
2000, //passFrequency,
|
|
||||||
4000, //stopFrequency,
|
|
||||||
60, //attenuation,
|
|
||||||
WindowType.HANNING, //windowType,
|
|
||||||
true ) //forceOddLength
|
|
||||||
, 2.0f ); //Gain
|
|
||||||
mDecimationFilter.setListener( mLowPassFilter );
|
|
||||||
|
|
||||||
/**
|
mSymbolBroadcaster = new Broadcaster<Boolean>();
|
||||||
* Normal: 2FSK Decoder with inverted output
|
mFSKDecoder.setListener(mSymbolBroadcaster);
|
||||||
* French: 2FSK Decoder with normal output
|
|
||||||
*/
|
|
||||||
if( sync == Sync.NORMAL )
|
|
||||||
{
|
|
||||||
mFSKDecoder = new FSK2Decoder( 24000, 1200, Output.INVERTED );
|
|
||||||
}
|
|
||||||
else if( sync == Sync.FRENCH )
|
|
||||||
{
|
|
||||||
mFSKDecoder = new FSK2Decoder( 24000, 1200, Output.NORMAL );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException( "MPT1327 Decoder - unrecognized Sync type" );
|
|
||||||
}
|
|
||||||
mLowPassFilter.setListener( mFSKDecoder );
|
|
||||||
|
|
||||||
mSymbolBroadcaster = new Broadcaster<Boolean>();
|
|
||||||
mFSKDecoder.setListener( mSymbolBroadcaster );
|
|
||||||
|
|
||||||
/* Message framer for control channel messages */
|
/* Message framer for control channel messages */
|
||||||
mControlMessageFramer =
|
mControlMessageFramer = new MessageFramer(sync.getControlSyncPattern().getPattern(), sMESSAGE_LENGTH);
|
||||||
new MessageFramer( sync.getControlSyncPattern().getPattern(),
|
mSymbolBroadcaster.addListener(mControlMessageFramer);
|
||||||
sMESSAGE_LENGTH );
|
|
||||||
mSymbolBroadcaster.addListener( mControlMessageFramer );
|
|
||||||
|
|
||||||
/* Message framer for traffic channel massages */
|
/* Message framer for traffic channel massages */
|
||||||
mTrafficMessageFramer =
|
mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), sMESSAGE_LENGTH);
|
||||||
new MessageFramer( sync.getTrafficSyncPattern().getPattern(),
|
mSymbolBroadcaster.addListener(mTrafficMessageFramer);
|
||||||
sMESSAGE_LENGTH );
|
|
||||||
mSymbolBroadcaster.addListener( mTrafficMessageFramer );
|
|
||||||
|
|
||||||
/* Fully decoded and framed messages processor */
|
/* Fully decoded and framed messages processor */
|
||||||
mMessageProcessor = new MPT1327MessageProcessor( aliasList );
|
mMessageProcessor = new MPT1327MessageProcessor(aliasList);
|
||||||
mMessageProcessor.setMessageListener( this );
|
mMessageProcessor.setMessageListener(this);
|
||||||
mControlMessageFramer.addMessageListener( mMessageProcessor );
|
mControlMessageFramer.addMessageListener(mMessageProcessor);
|
||||||
mTrafficMessageFramer.addMessageListener( mMessageProcessor );
|
mTrafficMessageFramer.addMessageListener(mMessageProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Listener<RealBuffer> getFilteredRealBufferListener()
|
public Listener<RealBuffer> getFilteredRealBufferListener()
|
||||||
{
|
{
|
||||||
return mDecimationFilter;
|
return mDecimationFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecoderType getDecoderType()
|
public DecoderType getDecoderType()
|
||||||
{
|
{
|
||||||
return DecoderType.MPT1327;
|
return DecoderType.MPT1327;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup method
|
* Cleanup method
|
||||||
*/
|
*/
|
||||||
public void dispose()
|
public void dispose()
|
||||||
{
|
{
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
||||||
mSymbolBroadcaster.dispose();
|
mSymbolBroadcaster.dispose();
|
||||||
|
|
||||||
mControlMessageFramer.dispose();
|
mControlMessageFramer.dispose();
|
||||||
|
|
||||||
if( mDCRemovalFilter != null )
|
if(mDCRemovalFilter != null)
|
||||||
{
|
{
|
||||||
mDCRemovalFilter.dispose();
|
mDCRemovalFilter.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFSKDecoder.dispose();
|
mFSKDecoder.dispose();
|
||||||
|
|
||||||
mDecimationFilter.dispose();
|
mDecimationFilter.dispose();
|
||||||
|
|
||||||
mLowPassFilter.dispose();
|
mLowPassFilter.dispose();
|
||||||
|
|
||||||
mMessageProcessor.dispose();
|
mMessageProcessor.dispose();
|
||||||
|
|
||||||
mTrafficMessageFramer.dispose();
|
mTrafficMessageFramer.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Instrumentation Taps */
|
/* Instrumentation Taps */
|
||||||
@Override
|
@Override
|
||||||
public List<TapGroup> getTapGroups()
|
public List<TapGroup> getTapGroups()
|
||||||
{
|
{
|
||||||
if( mAvailableTaps == null )
|
if(mAvailableTaps == null)
|
||||||
{
|
{
|
||||||
mAvailableTaps = new ArrayList<TapGroup>();
|
mAvailableTaps = new ArrayList<TapGroup>();
|
||||||
|
|
||||||
TapGroup group = new TapGroup( "MPT-1327 Decoder" );
|
TapGroup group = new TapGroup("MPT-1327 Decoder");
|
||||||
|
|
||||||
group.add(
|
group.add(new FloatTap(INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f));
|
||||||
new FloatTap( INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f ) );
|
group.add(new FloatTap(INSTRUMENT_LOW_PASS_TO_DECODER, 0, 0.5f));
|
||||||
group.add(
|
group.add(new BinaryTap(INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f));
|
||||||
new FloatTap( INSTRUMENT_LOW_PASS_TO_DECODER, 0, 0.5f ) );
|
|
||||||
group.add(
|
|
||||||
new BinaryTap( INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f ) );
|
|
||||||
|
|
||||||
mAvailableTaps.add( group );
|
mAvailableTaps.add(group);
|
||||||
|
|
||||||
/* Add the taps from the FSK decoder */
|
/* Add the taps from the FSK decoder */
|
||||||
mAvailableTaps.addAll( mFSKDecoder.getTapGroups() );
|
mAvailableTaps.addAll(mFSKDecoder.getTapGroups());
|
||||||
}
|
}
|
||||||
|
|
||||||
return mAvailableTaps;
|
return mAvailableTaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerTap( Tap tap )
|
public void registerTap(Tap tap)
|
||||||
{
|
{
|
||||||
/* Send request to decoder */
|
/* Send request to decoder */
|
||||||
mFSKDecoder.registerTap( tap );
|
mFSKDecoder.registerTap(tap);
|
||||||
|
|
||||||
switch( tap.getName() )
|
switch(tap.getName())
|
||||||
{
|
{
|
||||||
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
||||||
FloatBufferTap hb1Tap = (FloatBufferTap)tap;
|
FloatBufferTap hb1Tap = (FloatBufferTap) tap;
|
||||||
mDecimationFilter.setListener( hb1Tap );
|
mDecimationFilter.setListener(hb1Tap);
|
||||||
hb1Tap.setListener( mLowPassFilter );
|
hb1Tap.setListener(mLowPassFilter);
|
||||||
break;
|
break;
|
||||||
case INSTRUMENT_LOW_PASS_TO_DECODER:
|
case INSTRUMENT_LOW_PASS_TO_DECODER:
|
||||||
FloatBufferTap lowTap = (FloatBufferTap)tap;
|
FloatBufferTap lowTap = (FloatBufferTap) tap;
|
||||||
mLowPassFilter.setListener( lowTap );
|
mLowPassFilter.setListener(lowTap);
|
||||||
lowTap.setListener( mFSKDecoder );
|
lowTap.setListener(mFSKDecoder);
|
||||||
break;
|
break;
|
||||||
case INSTRUMENT_DECODER_TO_FRAMER:
|
case INSTRUMENT_DECODER_TO_FRAMER:
|
||||||
BinaryTap decoderTap = (BinaryTap)tap;
|
BinaryTap decoderTap = (BinaryTap) tap;
|
||||||
mFSKDecoder.setListener( decoderTap );
|
mFSKDecoder.setListener(decoderTap);
|
||||||
decoderTap.setListener( mSymbolBroadcaster );
|
decoderTap.setListener(mSymbolBroadcaster);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unregisterTap( Tap tap )
|
public void unregisterTap(Tap tap)
|
||||||
{
|
{
|
||||||
mFSKDecoder.unregisterTap( tap );
|
mFSKDecoder.unregisterTap(tap);
|
||||||
|
|
||||||
switch( tap.getName() )
|
switch(tap.getName())
|
||||||
{
|
{
|
||||||
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
||||||
mDecimationFilter.setListener( mLowPassFilter );
|
mDecimationFilter.setListener(mLowPassFilter);
|
||||||
break;
|
break;
|
||||||
case INSTRUMENT_LOW_PASS_TO_DECODER:
|
case INSTRUMENT_LOW_PASS_TO_DECODER:
|
||||||
mLowPassFilter.setListener( mFSKDecoder );
|
mLowPassFilter.setListener(mFSKDecoder);
|
||||||
break;
|
break;
|
||||||
case INSTRUMENT_DECODER_TO_FRAMER:
|
case INSTRUMENT_DECODER_TO_FRAMER:
|
||||||
mFSKDecoder.setListener( mSymbolBroadcaster );
|
mFSKDecoder.setListener(mSymbolBroadcaster);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Sync
|
public enum Sync
|
||||||
{
|
{
|
||||||
NORMAL( "Normal",
|
NORMAL("Normal", SyncPattern.MPT1327_CONTROL, SyncPattern.MPT1327_TRAFFIC),
|
||||||
SyncPattern.MPT1327_CONTROL,
|
FRENCH("French", SyncPattern.MPT1327_CONTROL_FRENCH, SyncPattern.MPT1327_TRAFFIC_FRENCH);
|
||||||
SyncPattern.MPT1327_TRAFFIC ),
|
|
||||||
FRENCH( "French",
|
|
||||||
SyncPattern.MPT1327_CONTROL_FRENCH,
|
|
||||||
SyncPattern.MPT1327_TRAFFIC_FRENCH );
|
|
||||||
|
|
||||||
private String mLabel;
|
private String mLabel;
|
||||||
private SyncPattern mControlSyncPattern;
|
private SyncPattern mControlSyncPattern;
|
||||||
private SyncPattern mTrafficSyncPattern;
|
private SyncPattern mTrafficSyncPattern;
|
||||||
|
|
||||||
private Sync( String label,
|
private Sync(String label, SyncPattern controlPattern, SyncPattern trafficPattern)
|
||||||
SyncPattern controlPattern,
|
{
|
||||||
SyncPattern trafficPattern )
|
mLabel = label;
|
||||||
{
|
mControlSyncPattern = controlPattern;
|
||||||
mLabel = label;
|
mTrafficSyncPattern = trafficPattern;
|
||||||
mControlSyncPattern = controlPattern;
|
}
|
||||||
mTrafficSyncPattern = trafficPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLabel()
|
public String getLabel()
|
||||||
{
|
{
|
||||||
return mLabel;
|
return mLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncPattern getControlSyncPattern()
|
public SyncPattern getControlSyncPattern()
|
||||||
{
|
{
|
||||||
return mControlSyncPattern;
|
return mControlSyncPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncPattern getTrafficSyncPattern()
|
public SyncPattern getTrafficSyncPattern()
|
||||||
{
|
{
|
||||||
return mTrafficSyncPattern;
|
return mTrafficSyncPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return getLabel();
|
return getLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
mControlMessageFramer.reset();
|
mControlMessageFramer.reset();
|
||||||
mTrafficMessageFramer.reset();
|
mTrafficMessageFramer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start( ScheduledExecutorService executor )
|
public void start(ScheduledExecutorService executor)
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
}
|
||||||
|
|
||||||
}
|
@Override
|
||||||
|
public void stop()
|
||||||
@Override
|
{
|
||||||
public void stop()
|
// TODO Auto-generated method stub
|
||||||
{
|
}
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +1,118 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* 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/>
|
||||||
*
|
*
|
||||||
* 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 module.demodulate.audio;
|
package module.demodulate.audio;
|
||||||
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
import module.Module;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import sample.Listener;
|
|
||||||
import sample.real.IFilteredRealBufferProvider;
|
|
||||||
import sample.real.IUnFilteredRealBufferListener;
|
|
||||||
import sample.real.RealBuffer;
|
|
||||||
import dsp.filter.FilterFactory;
|
import dsp.filter.FilterFactory;
|
||||||
import dsp.filter.Window.WindowType;
|
import dsp.filter.Window.WindowType;
|
||||||
import dsp.filter.dc.DCRemovalFilter_RealBuffer;
|
import dsp.filter.dc.DCRemovalFilter_RealBuffer;
|
||||||
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
|
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
|
||||||
|
import module.Module;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sample.Listener;
|
||||||
|
import sample.real.IFilteredRealBufferProvider;
|
||||||
|
import sample.real.IUnFilteredRealBufferListener;
|
||||||
|
import sample.real.RealBuffer;
|
||||||
|
|
||||||
public class DemodulatedAudioFilterModule extends Module
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
implements IUnFilteredRealBufferListener, IFilteredRealBufferProvider
|
|
||||||
|
public class DemodulatedAudioFilterModule extends Module implements IUnFilteredRealBufferListener, IFilteredRealBufferProvider
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( DemodulatedAudioFilterModule.class );
|
private final static Logger mLog = LoggerFactory.getLogger(DemodulatedAudioFilterModule.class);
|
||||||
|
|
||||||
private DCRemovalFilter_RealBuffer mDCFilter = new DCRemovalFilter_RealBuffer();
|
private DCRemovalFilter_RealBuffer mDCFilter;
|
||||||
private RealFIRFilter_RB_RB mBandPassFilter;
|
private RealFIRFilter_RB_RB mBandPassFilter;
|
||||||
/**
|
|
||||||
* Filters demodulated audio and removes any DC component from the buffer
|
|
||||||
*
|
|
||||||
* @param pass - audio pass band pass frequency
|
|
||||||
* @param stop - audio pass band stop frequency
|
|
||||||
*/
|
|
||||||
public DemodulatedAudioFilterModule( int pass, int stop )
|
|
||||||
{
|
|
||||||
assert( stop > pass );
|
|
||||||
|
|
||||||
//TODO: change this to a band pass filter
|
/**
|
||||||
mBandPassFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( 48000, pass, stop, 60,
|
* Filters demodulated audio and removes any DC component from the buffer
|
||||||
WindowType.HANNING, true ), 1.0f );
|
*
|
||||||
|
* @param pass - audio pass band pass frequency
|
||||||
|
* @param stop - audio pass band stop frequency
|
||||||
|
*/
|
||||||
|
public DemodulatedAudioFilterModule(int pass, int stop)
|
||||||
|
{
|
||||||
|
assert (stop > pass);
|
||||||
|
|
||||||
mDCFilter.setListener( mBandPassFilter );
|
mDCFilter = new DCRemovalFilter_RealBuffer();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
//TODO: change this to a band pass filter
|
||||||
public void dispose()
|
mBandPassFilter = new RealFIRFilter_RB_RB(FilterFactory.getLowPass(48000, pass, stop, 60,
|
||||||
{
|
WindowType.HANNING, true), 1.0f);
|
||||||
mBandPassFilter.dispose();
|
|
||||||
mBandPassFilter = null;
|
|
||||||
|
|
||||||
mDCFilter.dispose();
|
mDCFilter.setListener(mBandPassFilter);
|
||||||
mDCFilter = null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public void reset()
|
* Constructs a demodulated audio filter using the filter taps and gain provided
|
||||||
{
|
*/
|
||||||
}
|
public DemodulatedAudioFilterModule(float[] taps, float gain)
|
||||||
|
{
|
||||||
|
mBandPassFilter = new RealFIRFilter_RB_RB(taps, gain);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFilteredRealBufferListener( Listener<RealBuffer> listener )
|
public void dispose()
|
||||||
{
|
{
|
||||||
mBandPassFilter.setListener( listener );
|
mBandPassFilter.dispose();
|
||||||
}
|
mBandPassFilter = null;
|
||||||
|
|
||||||
@Override
|
mDCFilter.dispose();
|
||||||
public void removeFilteredRealBufferListener()
|
mDCFilter = null;
|
||||||
{
|
}
|
||||||
mBandPassFilter.removeListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start( ScheduledExecutorService executor )
|
public void reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop()
|
public void setFilteredRealBufferListener(Listener<RealBuffer> listener)
|
||||||
{
|
{
|
||||||
}
|
mBandPassFilter.setListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Listener<RealBuffer> getUnFilteredRealBufferListener()
|
public void removeFilteredRealBufferListener()
|
||||||
{
|
{
|
||||||
return mDCFilter;
|
mBandPassFilter.removeListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(ScheduledExecutorService executor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Listener<RealBuffer> getUnFilteredRealBufferListener()
|
||||||
|
{
|
||||||
|
if(mDCFilter != null)
|
||||||
|
{
|
||||||
|
return mDCFilter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return mBandPassFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,96 +17,92 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package module.demodulate.fm;
|
package module.demodulate.fm;
|
||||||
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import dsp.filter.FilterFactory;
|
||||||
|
import dsp.filter.Window.WindowType;
|
||||||
|
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
||||||
|
import dsp.fm.FMDemodulator_CB;
|
||||||
import module.Module;
|
import module.Module;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
import sample.complex.ComplexBuffer;
|
import sample.complex.ComplexBuffer;
|
||||||
import sample.complex.IComplexBufferListener;
|
import sample.complex.IComplexBufferListener;
|
||||||
import sample.real.IUnFilteredRealBufferProvider;
|
import sample.real.IUnFilteredRealBufferProvider;
|
||||||
import sample.real.RealBuffer;
|
import sample.real.RealBuffer;
|
||||||
import dsp.filter.FilterFactory;
|
|
||||||
import dsp.filter.Window.WindowType;
|
|
||||||
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
|
||||||
import dsp.fm.FMDemodulator_CB;
|
|
||||||
|
|
||||||
public class FMDemodulatorModule extends Module
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
implements IComplexBufferListener, IUnFilteredRealBufferProvider
|
|
||||||
|
public class FMDemodulatorModule extends Module implements IComplexBufferListener, IUnFilteredRealBufferProvider
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( FMDemodulatorModule.class );
|
private final static Logger mLog = LoggerFactory.getLogger(FMDemodulatorModule.class);
|
||||||
|
|
||||||
private static final int SAMPLE_RATE = 48000;
|
private static final int SAMPLE_RATE = 48000;
|
||||||
|
|
||||||
private ComplexFIRFilter_CB_CB mIQFilter;
|
private ComplexFIRFilter_CB_CB mIQFilter;
|
||||||
private FMDemodulator_CB mDemodulator;
|
private FMDemodulator_CB mDemodulator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FM Demodulator with I/Q filter. Demodulated output is unfiltered and
|
* FM Demodulator with I/Q filter. Demodulated output is unfiltered and
|
||||||
* may contain a DC component.
|
* may contain a DC component.
|
||||||
*
|
*
|
||||||
* @param pass - pass frequency for IQ filtering prior to demodulation. This
|
* @param pass - pass frequency for IQ filtering prior to demodulation. This
|
||||||
* frequency should be half of the signal bandwidth since the filter will
|
* frequency should be half of the signal bandwidth since the filter will
|
||||||
* be applied against each of the inphase and quadrature signals and the
|
* be applied against each of the inphase and quadrature signals and the
|
||||||
* combined pass bandwidth will be twice this value.
|
* combined pass bandwidth will be twice this value.
|
||||||
*
|
* @param stop - stop frequency for IQ filtering prior to demodulation.
|
||||||
* @param stop - stop frequency for IQ filtering prior to demodulation.
|
*/
|
||||||
*/
|
public FMDemodulatorModule(int pass, int stop)
|
||||||
public FMDemodulatorModule( int pass, int stop )
|
{
|
||||||
{
|
assert (stop > pass);
|
||||||
assert( stop > pass );
|
|
||||||
|
|
||||||
mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass(
|
mIQFilter = new ComplexFIRFilter_CB_CB(FilterFactory.getLowPass(
|
||||||
SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true ), 1.0f );
|
SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true), 1.0f);
|
||||||
|
|
||||||
mDemodulator = new FMDemodulator_CB( 1.0f );
|
mDemodulator = new FMDemodulator_CB(1.0f);
|
||||||
mIQFilter.setListener( mDemodulator );
|
mIQFilter.setListener(mDemodulator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Listener<ComplexBuffer> getComplexBufferListener()
|
public Listener<ComplexBuffer> getComplexBufferListener()
|
||||||
{
|
{
|
||||||
return mIQFilter;
|
return mIQFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose()
|
public void dispose()
|
||||||
{
|
{
|
||||||
mIQFilter.dispose();
|
mIQFilter.dispose();
|
||||||
mIQFilter = null;
|
mIQFilter = null;
|
||||||
|
|
||||||
mDemodulator.dispose();
|
mDemodulator.dispose();
|
||||||
mDemodulator = null;
|
mDemodulator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
mDemodulator.reset();
|
mDemodulator.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUnFilteredRealBufferListener( Listener<RealBuffer> listener )
|
public void setUnFilteredRealBufferListener(Listener<RealBuffer> listener)
|
||||||
{
|
{
|
||||||
mDemodulator.setListener( listener );
|
mDemodulator.setListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUnFilteredRealBufferListener()
|
public void removeUnFilteredRealBufferListener()
|
||||||
{
|
{
|
||||||
mDemodulator.removeListener();
|
mDemodulator.removeListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start( ScheduledExecutorService executor )
|
public void start(ScheduledExecutorService executor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,9 +166,11 @@ public class PlaylistManager implements ChannelEventListener
|
|||||||
|
|
||||||
mAliasModel.addAliases(playlist.getAliases());
|
mAliasModel.addAliases(playlist.getAliases());
|
||||||
mBroadcastModel.addBroadcastConfigurations(playlist.getBroadcastConfigurations());
|
mBroadcastModel.addBroadcastConfigurations(playlist.getBroadcastConfigurations());
|
||||||
mChannelModel.addChannels(playlist.getChannels());
|
|
||||||
mChannelMapModel.addChannelMaps(playlist.getChannelMaps());
|
mChannelMapModel.addChannelMaps(playlist.getChannelMaps());
|
||||||
|
|
||||||
|
//Channel model has to be loaded last since it will auto-start channels that are enabled
|
||||||
|
mChannelModel.addChannels(playlist.getChannels());
|
||||||
|
|
||||||
mPlaylistLoading = false;
|
mPlaylistLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user