#90 [WIP] Resolves issue with MPT-1327 decoder which has apparently been broken for a long time.

This commit is contained in:
Denny
2017-01-21 11:05:30 -05:00
parent 6b67a74055
commit 26837b8a68
13 changed files with 1499 additions and 1459 deletions

View File

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

View File

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

View File

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

View File

@@ -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 Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
public ChannelMapModel() private List<ChannelMap> mChannelMaps = new ArrayList<>();
{ private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
}
/** public ChannelMapModel()
* Returns an unmodifiable list of channel maps currently in the model {
*/ }
public List<ChannelMap> getChannelMaps()
{
return Collections.unmodifiableList( mChannelMaps );
}
/** /**
* Returns the channel map with a matching name or null * Returns an unmodifiable list of channel maps currently in the model
*/ */
public ChannelMap getChannelMap( String name ) public List<ChannelMap> getChannelMaps()
{ {
for( ChannelMap channelMap: mChannelMaps ) return Collections.unmodifiableList(mChannelMaps);
{ }
if( channelMap.getName().equalsIgnoreCase( name ) )
{
return channelMap;
}
}
return null;
}
/** /**
* Adds a listener to receive notifications when channel map updates occur * Returns the channel map with a matching name or null
*/ */
public void addListener( Listener<ChannelMapEvent> listener ) public ChannelMap getChannelMap(String name)
{ {
mEventBroadcaster.addListener( listener ); for(ChannelMap channelMap : mChannelMaps)
} {
if(channelMap.getName().equalsIgnoreCase(name))
{
return channelMap;
}
}
/** return null;
* Removes the channel map update notification listener }
*/
public void removeListener( Listener<ChannelMapEvent> listener )
{
mEventBroadcaster.removeListener( listener );
}
/** /**
* Broadcasts a channel map change. * Adds a listener to receive notifications when channel map updates occur
* */
* Note: use the add/remove methods to add or remove channel maps from this public void addListener(Listener<ChannelMapEvent> listener)
* model. When using those methods, an add or delete event will automatically {
* be generated. mEventBroadcaster.addListener(listener);
*/ }
public void broadcast( ChannelMapEvent event )
{
if( event.getEvent() == ChannelMapEvent.Event.CHANGE ||
event.getEvent() == ChannelMapEvent.Event.RENAME )
{
int index = mChannelMaps.indexOf( event.getChannelMap() );
if( index >= 0 )
{
fireContentsChanged( this, index, index );
}
}
mEventBroadcaster.broadcast( event );
}
/**
* Adds a list of channel maps to this model
*/
public void addChannelMaps( List<ChannelMap> channelMaps )
{
for( ChannelMap channelMap: channelMaps )
{
addChannelMap( channelMap );
}
}
/** /**
* Adds the channel map to this model * Removes the channel map update notification listener
*/ */
public void addChannelMap( ChannelMap channelMap ) public void removeListener(Listener<ChannelMapEvent> listener)
{ {
if( !mChannelMaps.contains( channelMap ) ) mEventBroadcaster.removeListener(listener);
{ }
mChannelMaps.add( channelMap );
int index = mChannelMaps.indexOf( channelMap );
fireIntervalAdded( this, index, index );
broadcast( new ChannelMapEvent( channelMap, Event.ADD ) );
}
}
/** /**
* Removes the channel map from this model * Broadcasts a channel map change.
*/ *
public void removeChannelMap( ChannelMap channelMap ) * 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
if( mChannelMaps.contains( channelMap ) ) * be generated.
{ */
int index = mChannelMaps.indexOf( channelMap ); public void broadcast(ChannelMapEvent event)
{
mChannelMaps.remove( channelMap ); if(event.getEvent() == ChannelMapEvent.Event.CHANGE ||
event.getEvent() == ChannelMapEvent.Event.RENAME)
fireIntervalRemoved( this, index, index ); {
int index = mChannelMaps.indexOf(event.getChannelMap());
broadcast( new ChannelMapEvent( channelMap, Event.DELETE ) ); if(index >= 0)
} {
} fireContentsChanged(this, index, index);
}
}
/** mEventBroadcaster.broadcast(event);
* Size/Number of channel maps in this model }
*/
@Override
public int getSize()
{
return mChannelMaps.size();
}
/** /**
* Returns the channel map at the specified index * Adds a list of channel maps to this model
*/ */
@Override public void addChannelMaps(List<ChannelMap> channelMaps)
public ChannelMap getElementAt( int index ) {
{ for(ChannelMap channelMap : channelMaps)
if( index <= mChannelMaps.size() ) {
{ addChannelMap(channelMap);
return mChannelMaps.get( index ); }
} }
return null; /**
} * Adds the channel map to this model
*/
public void addChannelMap(ChannelMap channelMap)
{
if(!mChannelMaps.contains(channelMap))
{
mChannelMaps.add(channelMap);
int index = mChannelMaps.indexOf(channelMap);
fireIntervalAdded(this, index, index);
broadcast(new ChannelMapEvent(channelMap, Event.ADD));
}
}
/**
* Removes the channel map from this model
*/
public void removeChannelMap(ChannelMap channelMap)
{
if(mChannelMaps.contains(channelMap))
{
int index = mChannelMaps.indexOf(channelMap);
mChannelMaps.remove(channelMap);
fireIntervalRemoved(this, index, index);
broadcast(new ChannelMapEvent(channelMap, Event.DELETE));
}
}
/**
* Size/Number of channel maps in this model
*/
@Override
public int getSize()
{
return mChannelMaps.size();
}
/**
* Returns the channel map at the specified index
*/
@Override
public ChannelMap getElementAt(int index)
{
if(index <= mChannelMaps.size())
{
return mChannelMaps.get(index);
}
return null;
}
} }

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,296 +1,302 @@
/******************************************************************************* /*******************************************************************************
* SDR Trunk * SDR Trunk
* Copyright (C) 2014 Dennis Sheirer * Copyright (C) 2014 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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> * along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/ ******************************************************************************/
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
* rate to be an integer multiple of the sample rate. * rate to be an integer multiple of the sample rate.
* *
* Implements a correlation decoder that correlates a modulated FSK signal upon * Implements a correlation decoder that correlates a modulated FSK signal upon
* against a one-baud delayed version of itself, converting all float samples to * 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 * binary (0 or 1), and uses XOR to produce the correlation value. Low pass
* filtering smoothes the correlated output. * filtering smoothes the correlated output.
* *
* Automatically aligns to symbol timing during symbol transitions (0 to 1, or * 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 * 1 to 0) by inspecting the samples at the symbol edges and advancing or
* retarding symbol window to maintain continuous symbol alignment. * retarding symbol window to maintain continuous symbol alignment.
* *
* Use a DC-removal filter prior to this decoder to ensure samples don't have * Use a DC-removal filter prior to this decoder to ensure samples don't have
* a DC component. * a DC component.
* *
* Implements instrumentable interface, so that slice events can be received * Implements instrumentable interface, so that slice events can be received
* externally to analyze decoder performance. * externally to analyze decoder performance.
*/ */
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;
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;
}
/** /* Instrumentation taps */
* Primary sample input private static final String INSTRUMENT_DECISION =
*/ "Tap Point: FSK2 Symbol Decision";
@Override private List<TapGroup> mAvailableTaps;
public void receive( RealBuffer buffer ) private ArrayList<SymbolEventTap> mTaps = new ArrayList<SymbolEventTap>();
{
for( float sample: buffer.getSamples() ) 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 /* 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();
}
}
}
/**
* 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;
}
/** reset();
* 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 * Sends the bit decision to the listener
*/ */
@Override 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() 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);
} }
} }

View File

@@ -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);
} }

View File

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

View File

@@ -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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> * 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 */
private static final int sDECIMATED_SAMPLE_RATE = 24000;
/* Baud or Symbol Rate */
private static final int sSYMBOL_RATE = 1200;
/* Message length -- longest possible message is: /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
* 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */ private static final int sDECIMATED_SAMPLE_RATE = 24000;
/* Baud or Symbol Rate */
private static final int sSYMBOL_RATE = 1200;
/* Message length -- longest possible message is:
* 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
*/
/* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */ if(sync == Sync.NORMAL)
mLowPassFilter = new RealFIRFilter_RB_RB( {
FilterFactory.getLowPass( 24000, //sampleRate, mFSKDecoder = new FSK2Decoder(24000, 1200, Output.INVERTED);
2000, //passFrequency, }
4000, //stopFrequency, else if(sync == Sync.FRENCH)
60, //attenuation, {
WindowType.HANNING, //windowType, mFSKDecoder = new FSK2Decoder(24000, 1200, Output.NORMAL);
true ) //forceOddLength }
, 2.0f ); //Gain else
mDecimationFilter.setListener( mLowPassFilter ); {
throw new IllegalArgumentException("MPT1327 Decoder - unrecognized Sync type");
}
/** mDecimationFilter.setListener(mFSKDecoder);
* Normal: 2FSK Decoder with inverted output
* French: 2FSK Decoder with normal output mSymbolBroadcaster = new Broadcaster<Boolean>();
*/ mFSKDecoder.setListener(mSymbolBroadcaster);
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
public Listener<RealBuffer> getFilteredRealBufferListener()
{
return mDecimationFilter;
}
@Override @Override
public DecoderType getDecoderType() public Listener<RealBuffer> getFilteredRealBufferListener()
{ {
return DecoderType.MPT1327; return mDecimationFilter;
} }
/**
* Cleanup method
*/
public void dispose()
{
super.dispose();
mSymbolBroadcaster.dispose(); @Override
public DecoderType getDecoderType()
mControlMessageFramer.dispose(); {
return DecoderType.MPT1327;
}
if( mDCRemovalFilter != null ) /**
{ * Cleanup method
mDCRemovalFilter.dispose(); */
} public void dispose()
{
super.dispose();
mFSKDecoder.dispose(); mSymbolBroadcaster.dispose();
mDecimationFilter.dispose(); mControlMessageFramer.dispose();
mLowPassFilter.dispose(); if(mDCRemovalFilter != null)
{
mMessageProcessor.dispose(); mDCRemovalFilter.dispose();
}
mTrafficMessageFramer.dispose(); mFSKDecoder.dispose();
}
/* Instrumentation Taps */ mDecimationFilter.dispose();
@Override
mLowPassFilter.dispose();
mMessageProcessor.dispose();
mTrafficMessageFramer.dispose();
}
/* Instrumentation Taps */
@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(
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(
new BinaryTap( INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f ) );
mAvailableTaps.add( group ); group.add(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(new BinaryTap(INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f));
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 SyncPattern mControlSyncPattern;
private SyncPattern mTrafficSyncPattern;
private Sync( String label, private String mLabel;
SyncPattern controlPattern, private SyncPattern mControlSyncPattern;
SyncPattern trafficPattern ) private SyncPattern mTrafficSyncPattern;
{
mLabel = label;
mControlSyncPattern = controlPattern;
mTrafficSyncPattern = trafficPattern;
}
public String getLabel()
{
return mLabel;
}
public SyncPattern getControlSyncPattern()
{
return mControlSyncPattern;
}
public SyncPattern getTrafficSyncPattern()
{
return mTrafficSyncPattern;
}
public String toString()
{
return getLabel();
}
}
@Override private Sync(String label, SyncPattern controlPattern, SyncPattern trafficPattern)
public void reset() {
{ mLabel = label;
mControlMessageFramer.reset(); mControlSyncPattern = controlPattern;
mTrafficMessageFramer.reset(); mTrafficSyncPattern = trafficPattern;
} }
@Override public String getLabel()
public void start( ScheduledExecutorService executor ) {
{ return mLabel;
// TODO Auto-generated method stub }
}
@Override public SyncPattern getControlSyncPattern()
public void stop() {
{ return mControlSyncPattern;
// TODO Auto-generated method stub }
} public SyncPattern getTrafficSyncPattern()
{
return mTrafficSyncPattern;
}
public String toString()
{
return getLabel();
}
}
@Override
public void reset()
{
mControlMessageFramer.reset();
mTrafficMessageFramer.reset();
}
@Override
public void start(ScheduledExecutorService executor)
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
} }

View File

@@ -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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> * 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,
WindowType.HANNING, true ), 1.0f );
mDCFilter.setListener( mBandPassFilter );
}
@Override /**
public void dispose() * Filters demodulated audio and removes any DC component from the buffer
{ *
mBandPassFilter.dispose(); * @param pass - audio pass band pass frequency
mBandPassFilter = null; * @param stop - audio pass band stop frequency
*/
mDCFilter.dispose(); public DemodulatedAudioFilterModule(int pass, int stop)
mDCFilter = null; {
} assert (stop > pass);
@Override mDCFilter = new DCRemovalFilter_RealBuffer();
public void reset()
{
}
@Override //TODO: change this to a band pass filter
public void setFilteredRealBufferListener( Listener<RealBuffer> listener ) mBandPassFilter = new RealFIRFilter_RB_RB(FilterFactory.getLowPass(48000, pass, stop, 60,
{ WindowType.HANNING, true), 1.0f);
mBandPassFilter.setListener( listener );
}
@Override mDCFilter.setListener(mBandPassFilter);
public void removeFilteredRealBufferListener() }
{
mBandPassFilter.removeListener();
}
@Override /**
public void start( ScheduledExecutorService executor ) * 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 stop() public void dispose()
{ {
} mBandPassFilter.dispose();
mBandPassFilter = null;
@Override mDCFilter.dispose();
public Listener<RealBuffer> getUnFilteredRealBufferListener() mDCFilter = null;
{ }
return mDCFilter;
} @Override
public void reset()
{
}
@Override
public void setFilteredRealBufferListener(Listener<RealBuffer> listener)
{
mBandPassFilter.setListener(listener);
}
@Override
public void removeFilteredRealBufferListener()
{
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;
}
}
} }

View File

@@ -1,112 +1,108 @@
/******************************************************************************* /*******************************************************************************
* SDR Trunk * SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer * Copyright (C) 2014-2016 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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/> * along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/ ******************************************************************************/
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 ComplexFIRFilter_CB_CB mIQFilter;
private FMDemodulator_CB mDemodulator;
/**
* FM Demodulator with I/Q filter. Demodulated output is unfiltered and
* may contain a DC component.
*
* @param pass - pass frequency for IQ filtering prior to demodulation. This
* frequency should be half of the signal bandwidth since the filter will
* be applied against each of the inphase and quadrature signals and the
* combined pass bandwidth will be twice this value.
*
* @param stop - stop frequency for IQ filtering prior to demodulation.
*/
public FMDemodulatorModule( int pass, int stop )
{
assert( stop > pass );
mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( private static final int SAMPLE_RATE = 48000;
SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true ), 1.0f );
mDemodulator = new FMDemodulator_CB( 1.0f );
mIQFilter.setListener( mDemodulator );
}
@Override private ComplexFIRFilter_CB_CB mIQFilter;
public Listener<ComplexBuffer> getComplexBufferListener() private FMDemodulator_CB mDemodulator;
{
return mIQFilter;
}
@Override /**
public void dispose() * FM Demodulator with I/Q filter. Demodulated output is unfiltered and
{ * may contain a DC component.
mIQFilter.dispose(); *
mIQFilter = null; * @param pass - pass frequency for IQ filtering prior to demodulation. This
* frequency should be half of the signal bandwidth since the filter will
mDemodulator.dispose(); * be applied against each of the inphase and quadrature signals and the
mDemodulator = null; * combined pass bandwidth will be twice this value.
} * @param stop - stop frequency for IQ filtering prior to demodulation.
*/
public FMDemodulatorModule(int pass, int stop)
{
assert (stop > pass);
@Override mIQFilter = new ComplexFIRFilter_CB_CB(FilterFactory.getLowPass(
public void reset() SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true), 1.0f);
{
mDemodulator.reset();
}
@Override mDemodulator = new FMDemodulator_CB(1.0f);
public void setUnFilteredRealBufferListener( Listener<RealBuffer> listener ) mIQFilter.setListener(mDemodulator);
{ }
mDemodulator.setListener( listener );
}
@Override @Override
public void removeUnFilteredRealBufferListener() public Listener<ComplexBuffer> getComplexBufferListener()
{ {
mDemodulator.removeListener(); return mIQFilter;
} }
@Override @Override
public void start( ScheduledExecutorService executor ) public void dispose()
{ {
} mIQFilter.dispose();
mIQFilter = null;
@Override mDemodulator.dispose();
public void stop() mDemodulator = null;
{ }
}
@Override
public void reset()
{
mDemodulator.reset();
}
@Override
public void setUnFilteredRealBufferListener(Listener<RealBuffer> listener)
{
mDemodulator.setListener(listener);
}
@Override
public void removeUnFilteredRealBufferListener()
{
mDemodulator.removeListener();
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
} }

View File

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