diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java
deleted file mode 100644
index f1cdf38f..00000000
--- a/src/audio/AudioModule.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package audio;
-
-import audio.squelch.ISquelchStateListener;
-import audio.squelch.SquelchState;
-import channel.metadata.Metadata;
-import dsp.filter.design.FilterDesignException;
-import dsp.filter.fir.FIRFilterSpecification;
-import dsp.filter.fir.remez.RemezFIRFilterDesigner;
-import dsp.filter.polyphase.PolyphaseFIRDecimatingFilter_RB;
-import module.Module;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sample.Listener;
-import sample.real.IFilteredRealBufferListener;
-import sample.real.RealBuffer;
-
-import java.util.concurrent.ScheduledExecutorService;
-
-/**
- * Provides packaging of demodulated audio sample buffers into audio packets for
- * broadcast to registered audio packet listeners. Includes audio packet
- * metadata in constructed audio packets.
- *
- * Incorporates audio squelch state listener to control if audio packets are
- * broadcast or ignored.
- */
-public class AudioModule extends Module implements IAudioPacketProvider, IFilteredRealBufferListener,
- ISquelchStateListener, Listener
-{
- protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
-
- private static float[] mDecimationCoefficients;
-
- static
- {
- //Band-pass filter to both decimate (48kHz to 8kHz) and block 0 - 300 Hz (LTR signalling).
- FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
- .sampleRate(48000)
- .stopFrequency1(150)
- .passFrequencyBegin(300)
- .passFrequencyEnd(3500)
- .stopFrequency2(4000)
- .stopRipple(0.0004)
- .passRipple(0.008)
- .build();
-
- RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
-
- try
- {
- if(!designer.isValid())
- {
- throw new FilterDesignException("Couldn't design the audio decimation filter");
- }
-
- mDecimationCoefficients = designer.getImpulseResponse();
- }
- catch(FilterDesignException e)
- {
- mLog.debug("Error designing filter", e);
- }
- }
-
- private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
- private SquelchState mSquelchState = SquelchState.SQUELCH;
- private Listener mAudioPacketListener;
- private PolyphaseFIRDecimatingFilter_RB mAudioDecimationFilter;
- private Metadata mMetadata;
-
- public AudioModule(Metadata metadata)
- {
- mMetadata = metadata;
-
- mAudioDecimationFilter = new PolyphaseFIRDecimatingFilter_RB(mDecimationCoefficients, 6, 2.0f);
- mAudioDecimationFilter.setListener(new Listener()
- {
- @Override
- public void receive(RealBuffer realBuffer)
- {
- if(mAudioPacketListener != null)
- {
- AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mMetadata.copyOf());
- mAudioPacketListener.receive(packet);
- }
- }
- });
- }
-
- @Override
- public void dispose()
- {
- mSquelchStateListener = null;
- mAudioPacketListener = null;
- }
-
- @Override
- public void reset()
- {
- }
-
- @Override
- public void start()
- {
- /* No start operations provided */
- }
-
- @Override
- public void stop()
- {
- /* Issue an end audio packet in case a recorder is still rolling */
- if(mAudioPacketListener != null)
- {
- mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
- }
- }
-
- /**
- * Processes demodulated audio samples into audio packets with current audio
- * metadata and sends to the registered listener
- */
- @Override
- public void receive(RealBuffer buffer)
- {
- if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
- {
- mAudioDecimationFilter.receive(buffer);
- }
- }
-
- @Override
- public Listener getFilteredRealBufferListener()
- {
- return this;
- }
-
- @Override
- public void setAudioPacketListener(Listener listener)
- {
- mAudioPacketListener = listener;
- }
-
- @Override
- public void removeAudioPacketListener()
- {
- mAudioPacketListener = null;
- }
-
- @Override
- public Listener getSquelchStateListener()
- {
- return mSquelchStateListener;
- }
-
- /**
- * Wrapper for squelch state listener
- */
- public class SquelchStateListener implements Listener
- {
- @Override
- public void receive(SquelchState state)
- {
- if(state == SquelchState.SQUELCH && mAudioPacketListener != null)
- {
- mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
- }
-
- mSquelchState = state;
- }
- }
-}
diff --git a/src/audio/broadcast/StreamManager.java b/src/audio/broadcast/StreamManager.java
deleted file mode 100644
index 8e84ccdd..00000000
--- a/src/audio/broadcast/StreamManager.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package audio.broadcast;
-
-import audio.AudioPacket;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import record.AudioRecorder;
-import sample.Listener;
-import util.ThreadPool;
-import util.TimeStamp;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class StreamManager implements Listener
-{
- private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
- private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
-
- private static AtomicInteger sNextRecordingNumber = new AtomicInteger();
-
- private Listener mAudioRecordingListener;
- private BroadcastFormat mBroadcastFormat;
- private Path mTempDirectory;
- private Map mStreamRecorders = new HashMap<>();
- private Runnable mRecorderMonitor;
- private ScheduledFuture> mRecorderMonitorFuture;
- private AtomicBoolean mRunning = new AtomicBoolean();
-
- /**
- * Stream manager processes all incoming audio packets and reassembles individual audio streams, converts audio
- * to desired output format and persists each stream to disc. Each recording is capped at a maximum length to
- * ensure that recordings don't run too long before they are streamed out and to ensure that inactive recordings
- * are closed in a timely fashion.
- *
- * Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion
- *
- * @param listener to receive completed audio recordings
- * @param tempDirectory where to store temporary audio recordings
- */
- public StreamManager(Listener listener, BroadcastFormat broadcastFormat, Path tempDirectory)
- {
- assert (tempDirectory != null && Files.isDirectory(tempDirectory));
-
- mAudioRecordingListener = listener;
- mBroadcastFormat = broadcastFormat;
- mTempDirectory = tempDirectory;
- }
-
- /**
- * Starts the stream manager. Schedules a processor to monitor for inactive temporary stream recorders
- * every 2 seconds.
- */
- public void start()
- {
- if(mRunning.compareAndSet(false, true))
- {
- if(mRecorderMonitor == null)
- {
- mRecorderMonitor = new RecorderMonitor();
- }
-
- mRecorderMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mRecorderMonitor,
- 0,2, TimeUnit.SECONDS);
- }
- }
-
- /**
- * Stops the stream manager. Stops the inactive temporary stream recorder monitoring thread.
- */
- public void stop()
- {
- if(mRunning.compareAndSet(true, false))
- {
- if(mRecorderMonitorFuture != null)
- {
- mRecorderMonitorFuture.cancel(true);
- }
-
- synchronized(mStreamRecorders)
- {
- List streamKeys = new ArrayList<>(mStreamRecorders.keySet());
-
- for(Integer streamKey : streamKeys)
- {
- removeRecorder(streamKey);
- }
- }
- }
- }
-
- @Override
- public void receive(AudioPacket audioPacket)
- {
- if(mRunning.get() && audioPacket.hasMetadata())
- {
- synchronized(mStreamRecorders)
- {
- int channelMetadataID = audioPacket.getMetadata().getMetadataID();
-
- AudioPacket.Type type = audioPacket.getType();
-
- if(type == AudioPacket.Type.AUDIO)
- {
- if(mStreamRecorders.containsKey(channelMetadataID))
- {
- AudioRecorder recorder = mStreamRecorders.get(channelMetadataID);
-
- if(recorder != null)
- {
- recorder.receive(audioPacket);
- }
- }
- else
- {
- AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
- mBroadcastFormat);
- recorder.start();
- recorder.receive(audioPacket);
- mStreamRecorders.put(channelMetadataID, recorder);
- }
- }
- else if(type == AudioPacket.Type.END)
- {
- removeRecorder(channelMetadataID);
- }
- else
- {
- mLog.info("Unrecognized Audio Packet Type: " + type);
- }
- }
- }
- }
-
- /**
- * Removes the recorder associated with the source channel ID.
- *
- * Note: this method invocation is not thread safe and must be invoked by a thread safe mechanism that protects the
- * mStreamRecorders map.
- *
- * @param sourceChannelID identifying the recorder
- */
- private void removeRecorder(Integer sourceChannelID)
- {
- if(mStreamRecorders.containsKey(sourceChannelID))
- {
- AudioRecorder recorder = mStreamRecorders.remove(sourceChannelID);
-
- recorder.close(new Listener()
- {
- @Override
- public void receive(AudioRecorder audioRecorder)
- {
- AudioRecording audioRecording =
- new AudioRecording(audioRecorder.getPath(), audioRecorder.getMetadata(),
- audioRecorder.getTimeRecordingStart(), audioRecorder.getRecordingLength());
-
- if(mAudioRecordingListener != null)
- {
- mAudioRecordingListener.receive(audioRecording);
- }
- }
- });
-
- }
- }
-
- /**
- * Creates a temporary streaming recording file path
- */
- private Path getTemporaryRecordingPath()
- {
- StringBuilder sb = new StringBuilder();
- sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
- sb.append(sNextRecordingNumber.incrementAndGet()).append("_");
- sb.append(TimeStamp.getLongTimeStamp("_"));
- sb.append(mBroadcastFormat.getFileExtension());
-
- Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString());
-
- return temporaryRecordingPath;
- }
-
- /**
- * Monitors recorders to ensure they don't exceed the maximum life-span allowed for a recording.
- */
- public class RecorderMonitor implements Runnable
- {
- @Override
- public void run()
- {
- synchronized(mStreamRecorders)
- {
- long now = System.currentTimeMillis();
-
- mStreamRecorders.entrySet().stream()
- .filter(entry -> entry.getValue().getTimeRecordingStart() + MAXIMUM_RECORDER_LIFESPAN_MILLIS < now)
- .forEach(entry ->
- {
- mLog.info("cycling recorder - max temporary streaming recording time limit reached [" +
- entry.getValue().getPath().toString() + "]");
-
- removeRecorder(entry.getKey());
- });
- }
- }
- }
-}
diff --git a/src/buffer/FloatCircularBuffer.java b/src/buffer/FloatCircularBuffer.java
deleted file mode 100644
index 4f6d600a..00000000
--- a/src/buffer/FloatCircularBuffer.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package buffer;
-
-import java.util.Arrays;
-
-/**
- * Circular sample buffer - allocates a buffer and stores samples in a circular
- * fashion, overwriting older samples with newly arrived samples.
- *
- * Can be used as a delay-type buffer, to delay samples by the 'size' amount
- */
-public class FloatCircularBuffer
-{
- float[] mBuffer;
- int mBufferPointer = 0;
-
- public FloatCircularBuffer(int size)
- {
- mBuffer = new float[size];
- }
-
- public int getSize()
- {
- return mBuffer.length;
- }
-
- public float[] getBuffer()
- {
- return mBuffer;
- }
-
- /**
- * Puts the new value into the buffer and returns the oldest buffer value
- * that it replaced
- *
- * @param newValue
- * @return
- */
- public float putAndGet(float newValue)
- {
- float oldestSample = mBuffer[mBufferPointer];
-
- put(newValue);
-
- return oldestSample;
- }
-
- /**
- * Adds the new value to this buffer.
- */
- public void put(float newValue)
- {
- mBuffer[mBufferPointer] = newValue;
-
- mBufferPointer++;
-
- if(mBufferPointer >= mBuffer.length)
- {
- mBufferPointer = 0;
- }
- }
-
- /**
- * Returns the sample at the specified index, where index 0 is the oldest sample and index (length - 1) is the
- * newest sample.
- * @param index in range of 0 to length-1
- * @return value at indexed position
- */
-
- public float get(int index)
- {
- if(index < mBuffer.length)
- {
- int pointer = mBufferPointer + index;
-
- if(pointer >= mBuffer.length)
- {
- pointer -= mBuffer.length;
- }
-
- return mBuffer[pointer];
- }
- else
- {
- throw new ArrayIndexOutOfBoundsException("Index [" + index + "] is not valid for the buffer size of [" +
- mBuffer.length + "]");
- }
- }
-
- /**
- * Returns the contents of the circular buffer unwrapped where index 0 is the oldest sample and index length-1
- * is the newest sample.
- *
- * @return array of samples
- */
- public float[] get()
- {
- float[] samples = new float[mBuffer.length];
-
- System.arraycopy(mBuffer, mBufferPointer, samples, 0, mBuffer.length - mBufferPointer);
-
- if(mBufferPointer != 0)
- {
- System.arraycopy(mBuffer, 0, samples, mBuffer.length - mBufferPointer, mBufferPointer);
- }
-
- return samples;
- }
-}
diff --git a/src/buffer/ReverseFloatCircularBuffer.java b/src/buffer/ReverseFloatCircularBuffer.java
deleted file mode 100644
index 3c3adb40..00000000
--- a/src/buffer/ReverseFloatCircularBuffer.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package buffer;
-
-/**
- * Circular sample buffer - allocates a buffer and stores samples in a circular
- * fashion, overwriting older samples with newly arrived samples.
- */
-public class ReverseFloatCircularBuffer
-{
- private float[] mBuffer;
- private int mBufferPointer = 0;
-
- public ReverseFloatCircularBuffer(int size)
- {
- mBuffer = new float[size];
- mBufferPointer = mBuffer.length - 1;
- }
-
- /**
- * Size of the buffer
- * @return
- */
- public int getSize()
- {
- return mBuffer.length;
- }
-
- public float[] getBuffer()
- {
- return mBuffer;
- }
-
- /**
- * Adds the new value to this buffer.
- */
- public void put(float newValue)
- {
- mBuffer[mBufferPointer--] = newValue;
-
- if(mBufferPointer < 0)
- {
- mBufferPointer = mBuffer.length - 1;
- }
- }
-
- /**
- * Returns the sample at the specified index, where index 0 is the newest sample and index (length - 1) is the
- * oldest sample.
- * @param index in range of 0 to length-1
- * @return value at indexed position
- */
-
- public float get(int index)
- {
- if(index < mBuffer.length)
- {
- int pointer = mBufferPointer + 1 + index;
-
- if(pointer >= mBuffer.length)
- {
- pointer -= mBuffer.length;
- }
-
- return mBuffer[pointer];
- }
- else
- {
- throw new ArrayIndexOutOfBoundsException("Index [" + index + "] is not valid for the buffer size of [" +
- mBuffer.length + "]");
- }
- }
-}
diff --git a/src/channel/state/AlwaysUnsquelchedDecoderState.java b/src/channel/state/AlwaysUnsquelchedDecoderState.java
deleted file mode 100644
index b2a67cc9..00000000
--- a/src/channel/state/AlwaysUnsquelchedDecoderState.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014,2015 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package channel.state;
-
-import channel.metadata.Attribute;
-import channel.metadata.AttributeChangeRequest;
-import channel.state.DecoderStateEvent.Event;
-import gui.SDRTrunk;
-import message.Message;
-import module.decode.DecoderType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.concurrent.ScheduledExecutorService;
-
-/**
- * Basic decoder channel state - provides the minimum channel state functionality
- * to support an always un-squelched audio decoder.
- */
-public class AlwaysUnsquelchedDecoderState extends DecoderState
-{
- private final static Logger mLog = LoggerFactory.getLogger(AlwaysUnsquelchedDecoderState.class);
-
- private DecoderType mDecoderType;
- private String mChannelName;
-
- public AlwaysUnsquelchedDecoderState(DecoderType decoderType, String channelName)
- {
- super(null);
-
- mDecoderType = decoderType;
- mChannelName = channelName;
- }
-
- @Override
- public void reset()
- {
- }
-
- @Override
- public String getActivitySummary()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append("Activity Summary\n");
- sb.append("\tDecoder:\t");
- sb.append(mDecoderType);
- sb.append("\n\n");
-
- return sb.toString();
- }
-
- @Override
- public void receive(Message t)
- {
- /* Not implemented */
- }
-
- @Override
- public void receiveDecoderStateEvent(DecoderStateEvent event)
- {
- if(event.getEvent() == Event.RESET)
- {
- //Each time we're reset, set the PRIMARY TO attribute back to the channel name, otherwise we won't have
- //a primary ID for any audio produced by this state.
- broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, mChannelName));
- }
- }
-
- @Override
- public DecoderType getDecoderType()
- {
- return mDecoderType;
- }
-
- @Override
- public void start()
- {
- broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, mChannelName));
- broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE));
- }
-}
diff --git a/src/channel/state/ChannelState.java b/src/channel/state/ChannelState.java
deleted file mode 100644
index 3bb4d00b..00000000
--- a/src/channel/state/ChannelState.java
+++ /dev/null
@@ -1,639 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package channel.state;
-
-import audio.squelch.ISquelchStateProvider;
-import audio.squelch.SquelchState;
-import source.heartbeat.Heartbeat;
-import source.heartbeat.IHeartbeatListener;
-import channel.metadata.Attribute;
-import channel.metadata.AttributeChangeRequest;
-import channel.metadata.IAttributeChangeRequestListener;
-import channel.metadata.MutableMetadata;
-import channel.state.DecoderStateEvent.Event;
-import channel.traffic.TrafficChannelManager;
-import controller.channel.Channel.ChannelType;
-import module.Module;
-import module.decode.DecoderType;
-import module.decode.config.DecodeConfiguration;
-import module.decode.event.CallEvent;
-import module.decode.event.ICallEventProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sample.Listener;
-import sample.real.IOverflowListener;
-import source.SourceEvent;
-import source.ISourceEventListener;
-
-import java.util.concurrent.ScheduledExecutorService;
-
-import static source.SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
-
-public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
- IDecoderStateEventProvider, ISourceEventListener, ISquelchStateProvider, IAttributeChangeRequestListener,
- IOverflowListener, IHeartbeatListener
-{
- private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
-
- public static final long FADE_TIMEOUT_DELAY = 1200;
- public static final long RESET_TIMEOUT_DELAY = 2000;
-
- private MutableMetadata mMutableMetadata = new MutableMetadata();
- private State mState = State.IDLE;
- private Listener mCallEventListener;
- private Listener mDecoderStateListener;
- private Listener mSquelchStateListener;
- private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver();
- private HeartbeatReceiver mHeartbeatReceiver = new HeartbeatReceiver();
- private ChannelType mChannelType;
- private TrafficChannelManager mTrafficChannelEndListener;
- private CallEvent mTrafficChannelCallEvent;
- private SourceChangeListener mSourceChangeListener;
-
- private boolean mSquelchLocked = false;
- private boolean mSelected = false;
- private boolean mSourceOverflow = false;
-
- private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
- private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
- private long mFadeTimeout;
- private long mEndTimeout;
-
-
- /**
- * Channel state tracks the overall state of all processing modules and decoders configured for the channel and
- * provides squelch control and decoder state reset events.
- *
- * Uses a state enumeration that defines allowable channel state transitions in order to track a call or data decode
- * event from start to finish. Uses a timer to monitor for inactivity and to provide a FADE period that indicates
- * to the user that the activity has stopped while continuing to provide details about the call, before the state is
- * reset to IDLE.
- *
- * State Descriptions:
- * IDLE: Normal state. No voice or data call activity
- * CALL/DATA/ENCRYPTED/CONTROL: Decoding states.
- * FADE: The phase after a voice or data call when either an explicit call end has been received, or when no new
- * signalling updates have been received, and the fade timer has expired. This phase allows for gui updates to
- * signal to the user that the call is ended, while continuing to display the call details for the user
- * TEARDOWN: Indicates a traffic channel that will be torn down for reuse.
- */
- public ChannelState(ChannelType channelType)
- {
- mChannelType = channelType;
- }
-
-
- /**
- * Metadata for this channel containing channel details, primary and secondary entities for call events and any
- * decoded messages. This metadata is primarily intended to be used by graphical components to convey the
- * details of the current channel state and to provide metadata backing any decoded audio packets produced by
- * any audio streams for this channel.
- */
- public MutableMetadata getMutableMetadata()
- {
- return mMutableMetadata;
- }
-
- /**
- * Resets this channel state and prepares it for reuse.
- */
- @Override
- public void reset()
- {
- broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
-
- mState = State.IDLE;
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState));
- mSourceOverflow = false;
- }
-
- @Override
- public void start()
- {
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState));
-
- if(mTrafficChannelEndListener != null)
- {
- setState(State.CALL);
- }
- }
-
- @Override
- public void stop()
- {
- mMutableMetadata.resetAllAttributes();
-
- mTrafficChannelEndListener = null;
-
- if(mTrafficChannelCallEvent != null)
- {
- mTrafficChannelCallEvent.end();
- broadcast(mTrafficChannelCallEvent);
- }
-
- mTrafficChannelCallEvent = null;
-
- mSquelchLocked = false;
- }
-
- public void dispose()
- {
- mCallEventListener = null;
- mDecoderStateListener = null;
- mSquelchStateListener = null;
- }
-
- @Override
- public Listener getSourceEventListener()
- {
- if(mSourceChangeListener == null)
- {
- mSourceChangeListener = new SourceChangeListener();
- }
-
- return mSourceChangeListener;
- }
-
- private boolean isStandardChannel()
- {
- return mChannelType == ChannelType.STANDARD;
- }
-
- private boolean isTrafficChannel()
- {
- return mChannelType == ChannelType.TRAFFIC;
- }
-
- public void setStandardChannelTimeout(long milliseconds)
- {
- mStandardChannelFadeTimeout = milliseconds;
-
- if(mChannelType == ChannelType.STANDARD)
- {
- mFadeTimeout = mStandardChannelFadeTimeout;
- }
- }
-
- public void setTrafficChannelTimeout(long milliseconds)
- {
- mTrafficChannelFadeTimeout = milliseconds;
-
- if(mChannelType == ChannelType.TRAFFIC)
- {
- mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
- }
- }
-
- public void setSelected(boolean selected)
- {
- mSelected = selected;
- }
-
- public boolean isSelected()
- {
- return mSelected;
- }
-
- public State getState()
- {
- return mState;
- }
-
- /**
- * Updates the fade timeout threshold to the current time plus delay
- */
- private void updateFadeTimeout()
- {
- if(isTrafficChannel())
- {
- mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
- }
- else
- {
- mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
- }
-
- }
-
- /**
- * Updates the reset timeout threshold to the current time plus delay
- */
- private void updateResetTimeout()
- {
- if(isTrafficChannel())
- {
- mEndTimeout = System.currentTimeMillis();
- }
- else
- {
- mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
- }
- }
-
- /**
- * Broadcasts the squelch state to the registered listener
- */
- protected void broadcast(SquelchState state)
- {
- if(mSquelchStateListener != null && !mSquelchLocked)
- {
- mSquelchStateListener.receive(state);
- }
- }
-
- /**
- * Sets the squelch state listener
- */
- @Override
- public void setSquelchStateListener(Listener listener)
- {
- mSquelchStateListener = listener;
- }
-
- /**
- * Removes the squelch state listener
- */
- @Override
- public void removeSquelchStateListener()
- {
- mSquelchStateListener = null;
- }
-
- /**
- * Sets the channel state to the specified state, or updates the timeout values so that the state monitor will not
- * change state. Broadcasts a squelch event when the state changes and the audio squelch state should change. Also
- * broadcasts changed attribute and decoder state events so that external processes can maintain sync with this
- * channel state.
- */
- protected void setState(State state)
- {
- if(state == mState)
- {
- if(State.CALL_STATES.contains(state))
- {
- updateFadeTimeout();
- }
- }
- else if(mState.canChangeTo(state))
- {
- switch(state)
- {
- case CONTROL:
- //Don't allow traffic channels to be control channels, otherwise they can't transition to teardown
- if(isStandardChannel())
- {
- broadcast(SquelchState.SQUELCH);
- updateFadeTimeout();
- mState = state;
- }
- break;
- case DATA:
- case ENCRYPTED:
- broadcast(SquelchState.SQUELCH);
- updateFadeTimeout();
- mState = state;
- break;
- case CALL:
- broadcast(SquelchState.UNSQUELCH);
- updateFadeTimeout();
- mState = state;
- break;
- case FADE:
- processFadeState();
- break;
- case IDLE:
- processIdleState();
- break;
- case TEARDOWN:
- processTeardownState();
- break;
- case RESET:
- mState = State.IDLE;
- break;
- default:
- break;
- }
-
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE,
- mSourceOverflow ? State.OVERFLOW : mState));
- }
- }
-
- /**
- * Receiver inner class that implements the IHeartbeatListener interface to receive heartbeat messages.
- */
- @Override
- public Listener getHeartbeatListener()
- {
- return mHeartbeatReceiver;
- }
-
- /**
- * This method is invoked if the source buffer provider goes into overflow state. Since this is an external state,
- * we use the mSourceOverflow variable to override the internal state reported to external listeners.
- * @param overflow true to indicate an overflow state
- */
- @Override
- public void sourceOverflow(boolean overflow)
- {
- mSourceOverflow = overflow;
-
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE,
- mSourceOverflow ? State.OVERFLOW : mState));
- }
-
- /**
- * Sets the state and processes related actions
- */
- private void processFadeState()
- {
- updateResetTimeout();
- mState = State.FADE;
-
- broadcast(SquelchState.SQUELCH);
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState));
- }
-
- private void processIdleState()
- {
- broadcast(SquelchState.SQUELCH);
-
- if(mState == State.FADE)
- {
- getMutableMetadata().resetTemporalAttributes();
- broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
- }
-
- mState = State.IDLE;
- getMutableMetadata().receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState));
- }
-
- private void processTeardownState()
- {
- broadcast(SquelchState.SQUELCH);
-
- getMutableMetadata().resetTemporalAttributes();
-
- mState = State.TEARDOWN;
-
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState));
-
- if(mTrafficChannelEndListener != null)
- {
- mTrafficChannelEndListener.callEnd(mTrafficChannelCallEvent.getChannel());
- }
- }
-
- /**
- * Broadcasts the call event to the registered listener
- */
- protected void broadcast(CallEvent event)
- {
- if(mCallEventListener != null)
- {
- mCallEventListener.receive(event);
- }
- }
-
- @Override
- public void addCallEventListener(Listener listener)
- {
- mCallEventListener = listener;
- }
-
- @Override
- public void removeCallEventListener(Listener listener)
- {
- mCallEventListener = null;
- }
-
-
- //NEW:
- @Override
- public Listener getAttributeChangeRequestListener()
- {
- return mMutableMetadata;
- }
-
- /**
- * Broadcasts a channel state event to any registered listeners
- */
- protected void broadcast(DecoderStateEvent event)
- {
- if(mDecoderStateListener != null)
- {
- mDecoderStateListener.receive(event);
- }
- }
-
- /**
- * Adds a decoder state event listener
- */
- @Override
- public void setDecoderStateListener(Listener listener)
- {
- mDecoderStateListener = listener;
- }
-
- /**
- * Removes the decoder state event listener
- */
- @Override
- public void removeDecoderStateListener()
- {
- mDecoderStateListener = null;
- }
-
- @Override
- public Listener getDecoderStateListener()
- {
- return mDecoderStateEventReceiver;
- }
-
- /**
- * Registers a listener to be notified when a traffic channel call event is
- * completed, so that the listener can perform call tear-down
- */
- public void configureAsTrafficChannel(TrafficChannelManager manager, CallEvent callEvent)
- {
- mTrafficChannelEndListener = manager;
-
- mTrafficChannelCallEvent = callEvent;
-
- /* Broadcast the call event details as metadata for the audio manager */
- String channel = mTrafficChannelCallEvent.getChannel();
-
- if(channel != null)
- {
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, channel));
- }
-
- long frequency = mTrafficChannelCallEvent.getFrequency();
-
- if(frequency > 0)
- {
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, frequency));
- }
-
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.PRIMARY_DECODER_TYPE,
- mTrafficChannelCallEvent.getDecoderType()));
-
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_CONFIGURATION_NAME, "TRAFFIC"));
-
- String from = mTrafficChannelCallEvent.getFromID();
-
- if(from != null)
- {
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_FROM, from,
- mTrafficChannelCallEvent.getFromIDAlias()));
- }
-
- String to = mTrafficChannelCallEvent.getToID();
-
- if(to != null)
- {
- mMutableMetadata.receive(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, to,
- mTrafficChannelCallEvent.getToIDAlias()));
- }
-
- /* Rebroadcast the allocation event so that the internal decoder states
- * can self-configure with the call event details */
- broadcast(mTrafficChannelCallEvent);
- }
-
- /**
- * Listener to receive frequency change events and rebroadcast them to the decoder states
- */
- public class SourceChangeListener implements Listener
- {
- @Override
- public void receive(SourceEvent sourceEvent)
- {
- if(sourceEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
- {
- long frequency = sourceEvent.getValue().longValue();
-
- broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
- }
- }
- }
-
- /**
- * DecoderStateEvent receiver wrapper
- */
- public class DecoderStateEventReceiver implements Listener
- {
- @Override
- public void receive(DecoderStateEvent event)
- {
- if(event.getSource() != this)
- {
- switch(event.getEvent())
- {
- case ALWAYS_UNSQUELCH:
- broadcast(SquelchState.UNSQUELCH);
- mSquelchLocked = true;
- break;
- case CHANGE_CALL_TIMEOUT:
- if(event instanceof ChangeChannelTimeoutEvent)
- {
- ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent) event;
-
- if(timeout.getChannelType() == ChannelType.STANDARD)
- {
- setStandardChannelTimeout(timeout.getCallTimeout());
- }
- else
- {
- setTrafficChannelTimeout(timeout.getCallTimeout());
- }
- }
- case CONTINUATION:
- case DECODE:
- case START:
- if(State.CALL_STATES.contains(event.getState()))
- {
- setState(event.getState());
- }
- break;
- case END:
- if(isTrafficChannel())
- {
- setState(State.TEARDOWN);
- }
- else
- {
- setState(State.FADE);
- }
- break;
- case RESET:
- /* Channel State does not respond to reset events */
- break;
- default:
- break;
- }
- }
- }
- }
-
- /**
- * Processes periodic heartbeats received from the processing chain to perform state monitoring and cleanup
- * functions.
- *
- * Monitors decoder state events to automatically transition the channel state to IDLE (standard channel) or to
- * TEARDOWN (traffic channel) when decoding stops or the monitored channel returns to a no signal state.
- *
- * Provides a FADE transition state to allow for momentary decoding dropouts and to allow the user access to call
- * details for a fade period upon call end.
- */
- public class HeartbeatReceiver implements Listener
- {
- @Override
- public void receive(Heartbeat heartbeat)
- {
- try
- {
- if(State.CALL_STATES.contains(mState) && mFadeTimeout <= System.currentTimeMillis())
- {
- processFadeState();
- }
- else if(mState == State.FADE && mEndTimeout <= System.currentTimeMillis())
- {
- if(isTrafficChannel())
- {
- processTeardownState();
- }
- else
- {
- processIdleState();
- }
- }
- }
- catch(Throwable e)
- {
- mLog.error("An error occurred while state monitor was running " +
- "- state [" + getState() +
- "] current [" + System.currentTimeMillis() +
- "] mResetTimeout [" + mEndTimeout +
- "] mFadeTimeout [" + mFadeTimeout +
- "]", e);
- }
- }
- }
-}
diff --git a/src/channel/state/DecoderState.java b/src/channel/state/DecoderState.java
deleted file mode 100644
index 707f0241..00000000
--- a/src/channel/state/DecoderState.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package channel.state;
-
-import alias.AliasList;
-import channel.metadata.AttributeChangeRequest;
-import channel.metadata.IAttributeChangeRequestProvider;
-import message.IMessageListener;
-import message.Message;
-import module.Module;
-import module.decode.DecoderType;
-import module.decode.event.ActivitySummaryProvider;
-import module.decode.event.CallEvent;
-import module.decode.event.ICallEventProvider;
-import sample.Broadcaster;
-import sample.Listener;
-
-/**
- * Channel state monitors the stream of decoded messages produced by the
- * decoder and broadcasts call events as they occur within the decoded message activity.
- *
- * Provides access to a textual activity summary of events observed.
- */
-public abstract class DecoderState extends Module implements ActivitySummaryProvider, Listener,
- IAttributeChangeRequestProvider, ICallEventProvider, IDecoderStateEventListener,
- IDecoderStateEventProvider, IMessageListener
-{
- protected String DIVIDER1 = "======================================================\n";
- protected String DIVIDER2 = "------------------------------------------------------\n";
-
- /* This has to be a broadcaster in order for references to persist */
- private Broadcaster mCallEventBroadcaster = new Broadcaster<>();
- private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>();
- private Listener mDecoderStateListener;
-
- private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener();
-
- protected CallEvent mCurrentCallEvent;
-
- private AliasList mAliasList;
-
- public DecoderState(AliasList aliasList)
- {
- mAliasList = aliasList;
- }
-
- public abstract DecoderType getDecoderType();
-
- public AliasList getAliasList()
- {
- return mAliasList;
- }
-
- public boolean hasAliasList()
- {
- return mAliasList != null;
- }
-
- /**
- * Provides subclass reference to the call event broadcaster
- */
- protected Broadcaster getCallEventBroadcaster()
- {
- return mCallEventBroadcaster;
- }
-
- /**
- * Reset the decoder state to prepare for processing a different sample
- * source
- */
- public abstract void reset();
-
- /**
- * Allow the decoder to perform any setup actions
- */
- public void init()
- {
-
- }
-
- @Override
- public void start()
- {
- }
-
- @Override
- public void stop()
- {
- }
-
- /**
- * Implements the IDecoderStateEventListener interface to receive state
- * reset events.
- */
- public abstract void receiveDecoderStateEvent(DecoderStateEvent event);
-
- /**
- * Disposes any resources or pointers held by this instance to prepare for
- * garbage collection
- */
- public void dispose()
- {
- mCallEventBroadcaster.dispose();
- mCallEventBroadcaster = null;
- mDecoderStateListener = null;
-
- mAliasList = null;
- }
-
- /**
- * Activity Summary - textual summary of activity observed by the channel state.
- */
- public abstract String getActivitySummary();
-
- /**
- * Broadcasts a call event to any registered listeners
- */
- protected void broadcast(CallEvent event)
- {
- mCallEventBroadcaster.broadcast(event);
- }
-
- /**
- * Adds a call event listener
- */
- @Override
- public void addCallEventListener(Listener listener)
- {
- mCallEventBroadcaster.addListener(listener);
- }
-
- /**
- * Removes the call event listener
- */
- @Override
- public void removeCallEventListener(Listener listener)
- {
- mCallEventBroadcaster.removeListener(listener);
- }
-
- @Override
- public Listener getDecoderStateListener()
- {
- return mDecoderStateEventListener;
- }
-
- private class DecoderStateEventListener implements Listener
- {
- @Override
- public void receive(DecoderStateEvent event)
- {
- receiveDecoderStateEvent(event);
- }
- }
-
- /**
- * Broadcasts a channel state event to any registered listeners
- */
- protected void broadcast(DecoderStateEvent event)
- {
- if(mDecoderStateListener != null)
- {
- mDecoderStateListener.receive(event);
- }
- }
-
- /**
- * Adds a decoder state event listener
- */
- @Override
- public void setDecoderStateListener(Listener listener)
- {
- mDecoderStateListener = listener;
- }
-
- /**
- * Removes the decoder state event listener
- */
- @Override
- public void removeDecoderStateListener()
- {
- mDecoderStateListener = null;
- }
-
- @Override
- public Listener getMessageListener()
- {
- return this;
- }
-
- /**
- * Broadcasts the attribute change request to the registered listener
- */
- protected void broadcast(AttributeChangeRequest> request)
- {
- mAttributeChangeRequestBroadcaster.broadcast(request);
- }
-
- /**
- * Sets the listener to receive attribute change requests from this decoder state
- */
- @Override
- public void setAttributeChangeRequestListener(Listener listener)
- {
- mAttributeChangeRequestBroadcaster.addListener(listener);
- }
-
- /**
- * Removes any listener from receiving attribute change requests
- */
- @Override
- public void removeAttributeChangeRequestListener(Listener listener)
- {
- mAttributeChangeRequestBroadcaster.removeListener(listener);
- }
-
- /**
- * Broadaster so that Attribute monitors can broadcast attribute change requests
- */
- protected Listener getAttributeChangeRequestListener()
- {
- return mAttributeChangeRequestBroadcaster;
- }
-}
diff --git a/src/channel/traffic/TrafficChannelManager.java b/src/channel/traffic/TrafficChannelManager.java
deleted file mode 100644
index f01bc492..00000000
--- a/src/channel/traffic/TrafficChannelManager.java
+++ /dev/null
@@ -1,395 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package channel.traffic;
-
-import channel.state.DecoderStateEvent;
-import channel.state.IDecoderStateEventListener;
-import controller.channel.Channel;
-import controller.channel.Channel.ChannelType;
-import controller.channel.ChannelEvent;
-import controller.channel.ChannelEvent.Event;
-import controller.channel.ChannelModel;
-import controller.channel.TrafficChannelEvent;
-import module.Module;
-import module.decode.config.DecodeConfiguration;
-import module.decode.event.CallEvent;
-import module.decode.event.CallEvent.CallEventType;
-import module.decode.event.ICallEventProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import record.config.RecordConfiguration;
-import sample.Listener;
-import source.config.SourceConfigTuner;
-import source.tuner.channel.TunerChannel;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-
-public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener
-{
- private final static Logger mLog = LoggerFactory.getLogger(TrafficChannelManager.class);
- public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED";
- public static final String NO_TUNER_AVAILABLE = "NO TUNER AVAILABLE";
- public static final String UNKNOWN_FREQUENCY = "UNKNOWN FREQUENCY";
-
- private int mTrafficChannelPoolMaximumSize = DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT;
- private List mTrafficChannelPool = new ArrayList();
- private Map mTrafficChannelsInUse = new ConcurrentHashMap();
-
- private DecoderStateEventListener mEventListener = new DecoderStateEventListener();
- private Listener mCallEventListener;
-
- private ChannelModel mChannelModel;
- private DecodeConfiguration mDecodeConfiguration;
- private RecordConfiguration mRecordConfiguration;
- private String mSystem;
- private String mSite;
- private String mAliasListName;
-
- /**
- * Monitors call events and allocates traffic decoder channels in response
- * to traffic channel allocation call events. Manages a pool of reusable
- * traffic channel allocations.
- *
- * @param channelModel containing channels currently in use
- * @param decodeConfiguration - decoder configuration to use for each
- * traffic channel allocation.
- * @param recordConfiguration - recording options for each traffic channel
- * @param system label to use to describe the system
- * @param site label to use to describe the site
- * @param aliasListName designated for the channel
- * @param trafficChannelPoolSize - maximum number of allocated traffic channels
- * in the pool
- */
- public TrafficChannelManager(ChannelModel channelModel,
- DecodeConfiguration decodeConfiguration,
- RecordConfiguration recordConfiguration,
- String system,
- String site,
- String aliasListName,
- int trafficChannelPoolSize)
- {
- mChannelModel = channelModel;
- mDecodeConfiguration = decodeConfiguration;
- mRecordConfiguration = recordConfiguration;
- mSystem = system;
- mSite = site;
- mAliasListName = aliasListName;
- mTrafficChannelPoolMaximumSize = trafficChannelPoolSize;
- }
-
- @Override
- public void dispose()
- {
- for(Channel trafficChannel : mTrafficChannelPool)
- {
- mChannelModel.broadcast(new ChannelEvent(trafficChannel, Event.REQUEST_DISABLE));
- }
-
- mTrafficChannelPool.clear();
-
- mTrafficChannelsInUse.clear();
-
- mCallEventListener = null;
- mDecodeConfiguration = null;
- }
-
- /**
- * Provides a channel by either reusing an existing channel or constructing
- * a new one, limited by the total number of constructed channels allowed.
- *
- * Note: you must enforce thread safety on the mTrafficChannelsInUse
- * external to this method.
- *
- * @param channelNumber
- * @param tunerChannel
- * @return
- */
- private Channel getChannel(String channelNumber, TunerChannel tunerChannel)
- {
- Channel channel = null;
-
- if(!mTrafficChannelsInUse.containsKey(channelNumber))
- {
- for(Channel configuredChannel : mTrafficChannelPool)
- {
- if(!configuredChannel.getEnabled())
- {
- channel = configuredChannel;
- break;
- }
- }
-
- if(channel == null && mTrafficChannelPool.size() < mTrafficChannelPoolMaximumSize)
- {
- channel = new Channel("Traffic", ChannelType.TRAFFIC);
-
- channel.setDecodeConfiguration(mDecodeConfiguration);
-
- channel.setRecordConfiguration(mRecordConfiguration);
-
- channel.setAliasListName(mAliasListName);
-
- mChannelModel.addChannel(channel);
-
- mTrafficChannelPool.add(channel);
- }
-
- /* If we have a configured channel, update metadata */
- if(channel != null)
- {
- channel.setSourceConfiguration(new SourceConfigTuner(tunerChannel));
- channel.setSystem(mSystem);
- channel.setSite(mSite);
- channel.setName(channelNumber);
-
- mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_CONFIGURATION_CHANGE));
- }
- }
-
- return channel;
- }
-
- /**
- * Processes the event and creates a traffic channel is resources are
- * available
- */
- private void process(TrafficChannelAllocationEvent event)
- {
- CallEvent callEvent = event.getCallEvent();
-
- /* Check for duplicate events and suppress */
- synchronized(mTrafficChannelsInUse)
- {
- if(mTrafficChannelsInUse.containsKey(callEvent.getChannel()))
- {
- return;
- }
-
- long frequency = callEvent.getFrequency();
-
- if(frequency > 0)
- {
- Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(frequency,
- mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
-
- if(channel != null)
- {
- TrafficChannelEvent trafficChannelEvent =
- new TrafficChannelEvent(this, channel, Event.REQUEST_ENABLE, callEvent);
-
- //Request to enable the channel
- mChannelModel.broadcast(trafficChannelEvent);
-
- if(channel.getEnabled())
- {
- mTrafficChannelsInUse.put(callEvent.getChannel(), channel);
- }
- else
- {
- callEvent.setCallEventType(CallEventType.CALL_DETECT);
-
- String details = callEvent.getDetails();
-
- if(details == null || details.isEmpty())
- {
- callEvent.setDetails(CHANNEL_START_REJECTED);
- }
- else if(!details.contains(CHANNEL_START_REJECTED))
- {
- callEvent.setDetails(new StringBuilder(CHANNEL_START_REJECTED).append(" : ")
- .append(callEvent.getDetails()).toString());
- }
- }
- }
- else
- {
- callEvent.setCallEventType(CallEventType.CALL_DETECT);
-
- String details = callEvent.getDetails();
-
- if(details == null || details.isEmpty())
- {
- callEvent.setDetails(NO_TUNER_AVAILABLE);
- }
- else if(!details.contains(NO_TUNER_AVAILABLE))
- {
- callEvent.setDetails(new StringBuilder(NO_TUNER_AVAILABLE).append(" : ")
- .append(callEvent.getDetails()).toString());
- }
- }
- }
- else
- {
- callEvent.setCallEventType(CallEventType.CALL_DETECT);
-
- String details = callEvent.getDetails();
-
- if(details == null || details.isEmpty())
- {
- callEvent.setDetails(UNKNOWN_FREQUENCY);
- }
- else if(!details.contains(UNKNOWN_FREQUENCY))
- {
- callEvent.setDetails(new StringBuilder(UNKNOWN_FREQUENCY).append(" : ")
- .append(callEvent.getDetails()).toString());
- }
- }
-
- final Listener listener = mCallEventListener;
-
- if(listener != null)
- {
- listener.receive(callEvent);
- }
- }
- }
-
- /**
- * Compares the call type, channel and to fields for equivalence and the
- * from field for either both null, or equivalence.
- *
- * @param e1
- * @param e2
- * @return
- */
- public static boolean isSameCallEvent(CallEvent e1, CallEvent e2)
- {
- if(e1 == null || e2 == null)
- {
- return false;
- }
-
- if(e1.getCallEventType() != e2.getCallEventType())
- {
- return false;
- }
-
- if(e1.getChannel() == null || e2.getChannel() == null || !e1.getChannel().contentEquals(e2.getChannel()))
- {
- return false;
- }
-
- if(e1.getToID() == null || e2.getToID() == null || !e1.getToID().contentEquals(e2.getToID()))
- {
- return false;
- }
-
- if(e1.getFromID() == null || e2.getFromID() == null)
- {
- return (e1.getFromID() == null && e2.getFromID() == null);
- }
- else if(e1.getFromID().contentEquals(e2.getFromID()))
- {
- return true;
- }
-
- return false;
- }
-
- @Override
- public Listener getDecoderStateListener()
- {
- return mEventListener;
- }
-
- @Override
- public void addCallEventListener(Listener listener)
- {
- mCallEventListener = listener;
- }
-
- @Override
- public void removeCallEventListener(Listener listener)
- {
- mCallEventListener = null;
- }
-
- @Override
- public void reset()
- {
- }
-
- @Override
- public void start()
- {
- }
-
- @Override
- public void stop()
- {
- if(!mTrafficChannelsInUse.isEmpty())
- {
- List channels = new ArrayList<>();
-
- /* Copy the keyset so we don't get concurrent modification of the map */
- channels.addAll(mTrafficChannelsInUse.keySet());
-
- for(String channel : channels)
- {
- callEnd(channel);
- }
- }
- }
-
- /**
- * Callback used by the TrafficChannelStatusListener class to signal the
- * end of a an allocated traffic channel call event
- *
- * @param channelNumber - channel number from the call event that signaled
- * the start of a traffic channel allocation
- */
- public void callEnd(String channelNumber)
- {
- synchronized(mTrafficChannelsInUse)
- {
- if(channelNumber != null && mTrafficChannelsInUse.containsKey(channelNumber))
- {
- Channel channel = mTrafficChannelsInUse.get(channelNumber);
-
- mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_DISABLE));
-
- mTrafficChannelsInUse.remove(channelNumber);
- }
- }
- }
-
- /**
- * Wrapper class for the decoder state event listener interface to catch
- * traffic channel allocation requests
- */
- public class DecoderStateEventListener implements Listener
- {
- @Override
- public void receive(DecoderStateEvent event)
- {
- switch(event.getEvent())
- {
- case TRAFFIC_CHANNEL_ALLOCATION:
- process((TrafficChannelAllocationEvent) event);
- break;
- default:
- break;
- }
- }
- }
-}
diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java
deleted file mode 100644
index c099b517..00000000
--- a/src/controller/channel/Channel.java
+++ /dev/null
@@ -1,478 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package controller.channel;
-
-import controller.config.Configuration;
-import module.decode.DecoderFactory;
-import module.decode.DecoderType;
-import module.decode.config.AuxDecodeConfiguration;
-import module.decode.config.DecodeConfiguration;
-import module.log.EventLogType;
-import module.log.config.EventLogConfiguration;
-import record.RecorderType;
-import record.config.RecordConfiguration;
-import sample.Listener;
-import source.SourceType;
-import source.config.SourceConfigFactory;
-import source.config.SourceConfigRecording;
-import source.config.SourceConfigTuner;
-import source.config.SourceConfiguration;
-import source.tuner.channel.TunerChannel;
-import source.SourceEvent;
-import source.SourceEvent.Event;
-
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSeeAlso;
-import javax.xml.bind.annotation.XmlTransient;
-
-@XmlSeeAlso({Configuration.class})
-@XmlRootElement(name = "channel")
-public class Channel extends Configuration implements Listener
-{
- // Standard channels are persisted and traffic channels are temporary
- public enum ChannelType
- {
- STANDARD, TRAFFIC
- }
-
- ;
-
-
- // Static unique channel identifier tracking
- private static int UNIQUE_ID = 0;
-
- private DecodeConfiguration mDecodeConfiguration = DecoderFactory.getDefaultDecodeConfiguration();
- private AuxDecodeConfiguration mAuxDecodeConfiguration =
- new AuxDecodeConfiguration();
- private SourceConfiguration mSourceConfiguration =
- SourceConfigFactory.getDefaultSourceConfiguration();
- private EventLogConfiguration mEventLogConfiguration =
- new EventLogConfiguration();
- private RecordConfiguration mRecordConfiguration = new RecordConfiguration();
-
- private String mAliasListName;
- private String mSystem = "System";
- private String mSite = "Site";
- private String mName = "Channel";
-
- private boolean mEnabled;
- private boolean mSelected;
- private TunerChannel mTunerChannel = null;
-
- private ChannelType mChannelType = ChannelType.STANDARD;
-
- private int mChannelID;
- private int mChannelFrequencyCorrection = 0;
-
- /**
- * Channel represents a complete set of configurations needed to setup and
- * manage a processing chain and/or manage as a set of business objects that
- * can be displayed in graphical components.
- *
- * @param channelName
- * @param channelType
- */
- public Channel(String channelName, ChannelType channelType)
- {
- this(channelName);
- mChannelType = channelType;
- }
-
- /**
- * Constructs a new standard channel with the specified channel name
- */
- public Channel(String channelName)
- {
- this();
- mName = channelName;
- }
-
- /**
- * Constructs a new standard channel with a default name of "Channel"
- */
- public Channel()
- {
- mChannelID = UNIQUE_ID++;
- }
-
- /**
- * Creates a (new) deep copy of this channel
- */
- public Channel copyOf()
- {
- Channel channel = new Channel(mName);
- channel.setSystem(mSystem);
- channel.setSite(mSite);
- channel.setAliasListName(mAliasListName);
-
- AuxDecodeConfiguration aux = new AuxDecodeConfiguration();
-
- for(DecoderType auxType : aux.getAuxDecoders())
- {
- aux.addAuxDecoder(auxType);
- }
-
- channel.setAuxDecodeConfiguration(aux);
-
- channel.setDecodeConfiguration(DecoderFactory.copy(mDecodeConfiguration));
-
- EventLogConfiguration log = new EventLogConfiguration();
-
- for(EventLogType logType : mEventLogConfiguration.getLoggers())
- {
- log.addLogger(logType);
- }
-
- channel.setEventLogConfiguration(log);
-
- RecordConfiguration record = new RecordConfiguration();
-
- for(RecorderType recordType : mRecordConfiguration.getRecorders())
- {
- record.addRecorder(recordType);
- }
-
- channel.setRecordConfiguration(record);
-
- channel.setSourceConfiguration(SourceConfigFactory.copy(mSourceConfiguration));
-
- return channel;
- }
-
- /**
- * Unique identifier for this channel. Value is transient (not persisted).
- */
- @XmlTransient
- public int getChannelID()
- {
- return mChannelID;
- }
-
- /**
- * Indicates if this channel has been selected for prioritized audio output
- * and display of processing chain products
- */
- @XmlTransient
- public boolean isSelected()
- {
- return mSelected;
- }
-
- /**
- * Sets selection status of the channel.
- *
- * NOTE: this method is package private so that only the ChannelSelectionManager
- * can toggle the selection state. Use the channel model to broadcast a
- * request select/deselect channel event.
- *
- * @param selected
- */
- void setSelected(boolean selected)
- {
- mSelected = selected;
- }
-
- @XmlTransient
- public ChannelType getChannelType()
- {
- return mChannelType;
- }
-
- /**
- * Returns the owning system for this channel.
- */
- @XmlAttribute(name = "system")
- public String getSystem()
- {
- return mSystem;
- }
-
- public void setSystem(String system)
- {
- mSystem = system;
- }
-
- public boolean hasSystem()
- {
- return mSystem != null;
- }
-
- /**
- * Returns the owning site for this channel.
- */
- @XmlAttribute(name = "site")
- public String getSite()
- {
- return mSite;
- }
-
- public void setSite(String site)
- {
- mSite = site;
- }
-
- public boolean hasSite()
- {
- return mSite != null;
- }
-
- /**
- * Returns the name of this channel.
- */
- @XmlAttribute(name = "name")
- public String getName()
- {
- return mName;
- }
-
- /**
- * Sets the name of this channel.
- */
- public void setName(String name)
- {
- mName = name;
- }
-
- /**
- * Default display string for this channel: SYSTEM_SITE_NAME
- */
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append(hasSystem() ? getSystem() : "SYSTEM");
- sb.append("_");
- sb.append(hasSite() ? getSite() : "SITE");
- sb.append("_");
- sb.append(getName());
-
- return sb.toString();
- }
-
- /**
- * Indicates if this channel is enabled for processing.
- */
- @XmlAttribute(name = "enabled")
- public boolean getEnabled()
- {
- return mEnabled;
- }
-
- /**
- * Indicates if this channel will automatically start processing on
- * application startup or if this channel is currently processing after
- * application startup.
- *
- * Note: this method is package private and is intended to be managed
- * by a co-package central processing manager
- *
- * @see controller.channel.ChannelProcessingManager
- */
- void setEnabled(boolean enabled)
- {
- mEnabled = enabled;
- }
-
- /**
- * Returns the alias list that is used by this channel for looking up alias
- * values for the various identifiers produced by the decoder(s).
- */
- @XmlElement(name = "alias_list_name")
- public String getAliasListName()
- {
- return mAliasListName;
- }
-
- /**
- * Sets the alias list to be used for looking up the alias values for the
- * various identifiers produced by the decoder
- */
- public void setAliasListName(String name)
- {
- mAliasListName = name;
- }
-
- /**
- * Gets the primary decoder configuration used by this channel
- */
- @XmlElement(name = "decode_configuration")
- public DecodeConfiguration getDecodeConfiguration()
- {
- return mDecodeConfiguration;
- }
-
- /**
- * Sets the primary decoder configuration used by this channel
- */
- public void setDecodeConfiguration(DecodeConfiguration config)
- {
- mDecodeConfiguration = config;
- }
-
- /**
- * Gets the aux decoder configuration used by this channel. Aux decoders
- * operate on demodulated audio
- */
- @XmlElement(name = "aux_decode_configuration")
- public AuxDecodeConfiguration getAuxDecodeConfiguration()
- {
- return mAuxDecodeConfiguration;
- }
-
- /**
- * Sets the decoder configuration used by this channel
- */
- public void setAuxDecodeConfiguration(AuxDecodeConfiguration config)
- {
- mAuxDecodeConfiguration = config;
- }
-
- /**
- * Returns the source configuration for this channel. A channel source
- * identifies where the processing chain will get source sample data from.
- */
- @XmlElement(name = "source_configuration")
- public SourceConfiguration getSourceConfiguration()
- {
- return mSourceConfiguration;
- }
-
- /**
- * Sets the source configuration for this channel.
- */
- public void setSourceConfiguration(SourceConfiguration config)
- {
- mSourceConfiguration = config;
-
- //Clear the tune channel object so that it can be recreated if the
- //source configuration changes
- mTunerChannel = null;
- }
-
- /**
- * Returns the event logger configuration for this channel.
- */
- @XmlElement(name = "event_log_configuration")
- public EventLogConfiguration getEventLogConfiguration()
- {
- return mEventLogConfiguration;
- }
-
- /**
- * Sets the event logger configuration for this channel.
- */
- public void setEventLogConfiguration(EventLogConfiguration config)
- {
- mEventLogConfiguration = config;
- }
-
- /**
- * Returns the recorder configuration for this channel.
- */
- @XmlElement(name = "record_configuration")
- public RecordConfiguration getRecordConfiguration()
- {
- return mRecordConfiguration;
- }
-
- /**
- * Sets the recorder configuration for this channel.
- */
- public void setRecordConfiguration(RecordConfiguration config)
- {
- mRecordConfiguration = config;
- }
-
- /**
- * Convenience method to construct a tuner channel object representing a
- * tuner or recording source frequency and bandwidth that can be used by
- * application components for graphically representing this channel.
- *
- * If the source configuration is not a tuner or recording, this method
- * returns null.
- */
- @XmlTransient
- public TunerChannel getTunerChannel()
- {
- if(mTunerChannel == null)
- {
- if(mSourceConfiguration.getSourceType() == SourceType.TUNER)
- {
- SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
-
- mTunerChannel = new TunerChannel(config.getFrequency(),
- mDecodeConfiguration.getDecoderType().getChannelBandwidth());
- }
- else if(mSourceConfiguration.getSourceType() == SourceType.RECORDING)
- {
- SourceConfigRecording config = (SourceConfigRecording)mSourceConfiguration;
-
- mTunerChannel = new TunerChannel(config.getFrequency(),
- mDecodeConfiguration.getDecoderType().getChannelBandwidth());
- }
- }
-
- return mTunerChannel;
- }
-
- /**
- * Convenience method to indicate if any part of this channel is contained
- * within the minimum and maximum frequency values.
- */
- public boolean isWithin(long minimum, long maximum)
- {
- TunerChannel tunerChannel = getTunerChannel();
-
- return tunerChannel != null && tunerChannel.overlaps(minimum, maximum);
- }
-
- /**
- * Convenience method to make the current channel frequency correction value
- * available for use outside of a constructed processing chain while maintaining
- * linkage to the source channel. This hack allows the correction value to
- * be used to visually show in the spectral display.
- */
- @Override
- public void receive(SourceEvent event)
- {
- if(event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE)
- {
- mChannelFrequencyCorrection = event.getValue().intValue();
- }
- }
-
- /**
- * Current channel frequency correction value (when this channel is processing)
- * @return
- */
- @XmlTransient
- public int getChannelFrequencyCorrection()
- {
- return mChannelFrequencyCorrection;
- }
-
- /**
- * Resets frequency correction value to 0
- */
- public void resetFrequencyCorrection()
- {
- mChannelFrequencyCorrection = 0;
- }
-}
diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java
deleted file mode 100644
index 0a8103cc..00000000
--- a/src/dsp/filter/FilterFactory.java
+++ /dev/null
@@ -1,910 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package dsp.filter;
-
-import dsp.filter.Window.WindowType;
-import dsp.filter.cic.ComplexPrimeCICDecimate;
-import dsp.filter.design.FilterDesignException;
-import dsp.filter.fir.FIRFilterSpecification;
-import dsp.filter.fir.remez.RemezFIRFilterDesigner;
-import dsp.filter.fir.remez.RemezFIRFilterDesignerWithLagrange;
-import org.jtransforms.fft.FloatFFT_1D;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * Utility methods for designing various types of filters.
- */
-public class FilterFactory
-{
- private final static Logger mLog = LoggerFactory.getLogger(FilterFactory.class);
- private static final double PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE = -6.020599842071533; //decibel(0.5, 0.0)
-
- /**
- * Generates coefficients for a unity-gain, windowed low-pass filter
- *
- * @param sampleRate - hertz
- * @param frequency - cutoff in hertz
- * @param length - filter length
- * @param window - to apply against the coefficients
- * @return
- */
- public static float[] getSinc(int sampleRate, long frequency, int length, WindowType window)
- {
- int evenLength = length % 2 == 0 ? length : length + 1;
-
- //Get unity response array (one element longer to align with IDFT size)
- float[] frequencyResponse = getUnityResponseArray(sampleRate, frequency, evenLength);
-
- //Apply Inverse DFT against frequency response unity values, leaving the
- //IDFT bin results in the frequency response array
- FloatFFT_1D idft = new FloatFFT_1D(evenLength);
- idft.realInverseFull(frequencyResponse, true);
-
- //Transfer the IDFT results to the return array
- float[] coefficients = new float[length];
- int middleCoefficient = (int)(length / 2);
-
- if(length % 2 == 0) //Even length
- {
- //The remaining idft bins from 1 to (middle - 1) are the mirror image
- //coefficients
- for(int x = 0; x < middleCoefficient; x++)
- {
- coefficients[middleCoefficient + x] = frequencyResponse[2 * x];
- coefficients[middleCoefficient - x] = frequencyResponse[2 * x];
- }
- }
- else
- {
- //Bin 0 of the idft is our center coefficient
- coefficients[middleCoefficient] = frequencyResponse[0];
-
- //The remaining idft bins from 1 to (middle - 1) are the mirror image
- //coefficients
- for(int x = 1; x < middleCoefficient; x++)
- {
- coefficients[middleCoefficient + x] = frequencyResponse[2 * x];
- coefficients[middleCoefficient - x] = frequencyResponse[2 * x];
- }
- }
-
- //Apply the window against the coefficients
- coefficients = Window.apply(window, coefficients);
-
- return coefficients;
- }
-
- /**
- * Normalizes all filter coefficients to achieve unity (1) gain, by ensuring
- * that the sum of the absolute value of all coefficients adds up to 1.
- *
- * @param coefficients
- * @return
- */
- public static float[] normalize(float[] coefficients)
- {
- float accumulator = 0;
-
- for(int x = 0; x < coefficients.length; x++)
- {
- accumulator += Math.abs(coefficients[x]);
- }
-
- for(int x = 0; x < coefficients.length; x++)
- {
- coefficients[x] = coefficients[x] / accumulator;
- }
-
- return coefficients;
- }
-
- /**
- * Constructs an array of unity (1) response values representing the
- * desired (pre-windowing) frequency response, used by the sync function
- *
- * Returns an array twice the length, filled with unity (1) response values
- * in the desired pass-band with the positive frequency response starting at
- * the lower end of the array, and the negative frequency response at the
- * higher end of the first half of the array. The remaining zero-valued
- * indexes in the second half will store the results of the JTransforms
- * inverse DFT operation
- *
- * @param sampleRate
- * @param frequency
- * @param length
- * @return
- */
- public static float[] getUnityResponseArray(int sampleRate, long frequency, int length)
- {
- float[] unityArray = new float[length * 2];
-
-
- if(length % 2 == 0) //even length
- {
- int binCount = (int)((Math.round((float)frequency / (float)sampleRate * (float)length)));
-
- for(int x = 0; x < binCount; x++)
- {
- unityArray[x] = 1.0f;
- unityArray[length - 1 - x] = 1.0f;
- }
- }
- else //odd length
- {
- int binCount = (int)((Math.round((float)frequency / (float)sampleRate * (float)length + 0.5)));
-
- unityArray[0] = 1.0f;
-
- for(int x = 1; x < binCount; x++)
- {
- unityArray[x] = 1.0f;
- unityArray[length - x] = 1.0f;
- }
- }
-
- return unityArray;
- }
-
- /**
- * Applies a repeating sequence of 1, -1 to the coefficients to invert
- * the frequency response of the filter. Used in converting a low-pass
- * filter to a high-pass filter.
- *
- * Inverts the sign of all odd index coefficients ( 1, 3, 5, etc.)
- * returning:
- * Index 0: same
- * Index 1: inverted
- * Index 2: same
- * ...
- * Index length - 1: same
- */
- public static float[] invert(float[] coefficients)
- {
- for(int x = 1; x < coefficients.length; x += 2)
- {
- coefficients[x] = -coefficients[x];
- }
-
- return coefficients;
- }
-
- /**
- * Generates filter coefficients for a unity-gain, odd-length, windowed,
- * low pass filter with passband from 0-hertz to the cutoff frequency.
- *
- * @param sampleRate - hertz
- * @param cutoff - frequency in hertz
- * @param filterLength - odd filter length
- * @param windowType - window to apply against the generated coefficients
- * @return
- */
- public static float[] getLowPass(int sampleRate, long cutoff, int filterLength, WindowType windowType)
- {
- return getSinc(sampleRate, cutoff, filterLength, windowType);
- }
-
- /**
- * Creates a low-pass filter with ~ 0.1 db ripple in the pass band
- *
- * Note: stop frequency - pass frequency defines the transition band.
- */
- /**
- * Creates a low-pass filter with ~0.1 db ripple in the pass band. The
- * transition region is defined by the stop frequency minus the pass
- * frequency.
- *
- * Requires:
- * - passFrequency < stopFrequency
- * - stopFrequency <= sampleRate/2
- */
- public static float[] getLowPass(int sampleRate, int passFrequency, int stopFrequency, int attenuation,
- WindowType windowType, boolean forceOddLength)
- {
- if(stopFrequency < passFrequency || stopFrequency > (sampleRate / 2))
- {
- throw new IllegalArgumentException("FilterFactory - low pass filter pass frequency [" + passFrequency +
- "] must be less than the stop frequency [" + stopFrequency + "] and must be less than half [" +
- (int)(sampleRate / 2) + "] of the sample rate [" + sampleRate + "]");
- }
-
- int tapCount = getTapCount(sampleRate, passFrequency, stopFrequency, attenuation);
-
- if(forceOddLength)
- {
- if(tapCount % 2 == 0)
- {
- tapCount--;
- }
- }
-
- return getLowPass(sampleRate, passFrequency, tapCount, windowType);
- }
-
- /**
- * Generates filter coefficients for a unity-gain, odd-length, windowed,
- * high pass filter with passband from cutoff frequency to half the sample
- * rate.
- *
- * @param sampleRate - hertz
- * @param cutoff - frequency in hertz
- * @param filterLength - odd filter length
- * @param windowType - window to apply against the generated coefficients
- * @return
- */
- public static float[] getHighPass(int sampleRate, long cutoff, int filterLength, WindowType windowType)
- {
- //Convert the high frequency cutoff to its low frequency cutoff
- //equivalent, so that when we generate the low pass filter, prior to
- //inversion, its at the correct frequency
- long convertedCutoff = sampleRate / 2 - cutoff;
-
- return invert(getSinc(sampleRate, convertedCutoff, filterLength, windowType));
- }
-
- public static float[] getHighPass(int sampleRate, long stopFrequency, long passFrequency, int attenuation,
- WindowType windowType, boolean forceOddLength)
- {
- /* reverse the stop and pass frequency to get the low pass variant */
- int tapCount = getTapCount(sampleRate, stopFrequency, passFrequency, attenuation);
-
- if(forceOddLength)
- {
- if(tapCount % 2 == 0)
- {
- tapCount--;
- }
- }
-
- return invert(getLowPass(sampleRate, stopFrequency, tapCount, windowType));
- }
-
- /**
- * Utility to log the arrays of doubles with line breaks
- */
- public static String arrayToString(float[] array, boolean breaks)
- {
- StringBuilder sb = new StringBuilder();
- for(int x = 0; x < array.length; x++)
- {
- sb.append(x + ": " + array[x]);
-
- if(breaks)
- {
- sb.append("\n");
- }
- else
- {
- if(x % 8 == 7)
- {
- sb.append("\n");
- }
- else
- {
- sb.append("\t");
- }
- }
- }
-
- return sb.toString();
- }
-
- /**
- * Determines the number of fir filter taps required to produce the specified frequency response with passband
- * ripple near 0.1dB.
- *
- * Implements the algorithm from Understanding Digital Signal Processing, 3e, Lyons, section 5.10.5.
- *
- * @param sampleRate in hertz
- * @param pass pass frequency in hertz
- * @param stop stop frequency in hertz
- * @param attenuation in dB
- * @return
- */
- public static int getTapCount(int sampleRate, long pass, long stop, int attenuation)
- {
- double frequency = ((double)stop - (double)pass) / (double)sampleRate;
-
- return (int)(Math.round((double)attenuation / (22.0d * frequency)));
- }
-
- public static ComplexPrimeCICDecimate getDecimationFilter(int sampleRate, int decimatedRate, int order,
- int passFrequency, int attenuation, WindowType windowType)
- {
- int decimationRate = (int)(sampleRate / decimatedRate);
-
- return new ComplexPrimeCICDecimate(decimationRate, order,
- passFrequency, attenuation, windowType);
- }
-
- /**
- * Determines decimation rate(s) for a polyphase decimation filter.
- *
- * @param sampleRate - starting sample rate
- * @param decimatedRate - final (decimated) output rate
- * @return - set of integer decimation rates for a single or multi-stage
- * polyphase filter decimation chain
- * @throws - AssertionException if sample rate is not a multiple of 48 kHz
- */
- public static int[] getPolyphaseDecimationRates(int sampleRate, int decimatedRate, long passFrequency, long stopFrequency)
- {
- int[] rates;
-
- if(sampleRate % decimatedRate != 0)
- {
- throw new IllegalArgumentException("Decimated rate must be an "
- + "integer multiple of sample rate");
- }
-
- int decimation = (int)(sampleRate / decimatedRate);
-
- //Decimation rates below 20 will use single stage polyphase filter
- if(decimation < 20)
- {
- rates = new int[1];
- rates[0] = decimation;
- return rates;
- }
- else
- {
- int optimalStage1 =
- getOptimalStageOneRate(sampleRate, decimation, passFrequency, stopFrequency);
-
- Set factors = getFactors(decimation);
-
- int stage1 = findClosest(optimalStage1, factors);
-
- if(stage1 == decimation || stage1 == 1)
- {
- rates = new int[1];
- rates[0] = decimation;
- return rates;
- }
- else
- {
- rates = new int[2];
-
- int stage2 = (int)(decimation / stage1);
-
- if(stage1 > stage2)
- {
- rates[0] = stage1;
- rates[1] = stage2;
- }
- else
- {
- rates[0] = stage2;
- rates[1] = stage1;
- }
- return rates;
- }
- }
- }
-
- /**
- * Finds the factor that is closest to the desired factor, from an ordered list of factors.
- */
- private static int findClosest(int desiredFactor, Set factors)
- {
- int bestFactor = 1;
- int bestDelta = desiredFactor;
-
- for(Integer factor : factors)
- {
- int testDelta = Math.abs(desiredFactor - factor);
-
- if(testDelta < bestDelta)
- {
- bestDelta = testDelta;
- bestFactor = factor;
- }
- }
-
- return bestFactor;
- }
-
- /**
- * Determines the factors that make up an integer. Uses a brute force method to iterate all integers from 1 to
- * value, determining which factors are evenly divisible into the value.
- *
- * @param value - integer decimation value
- * @return - ordered set of factors for value
- */
- private static Set getFactors(int value)
- {
- Set factors = new TreeSet();
-
- /* Brute force */
- for(int x = 1; x <= value; x++)
- {
- int remainder = (int)(value / x);
-
- if(remainder * x == value)
- {
- factors.add(x);
- }
- }
-
- return factors;
- }
-
- /**
- * Determines the optimal decimation rate for stage 1 of a two-stage poly-phase decimation filter chain, to produce
- * a final sample rate of 48 kHz using a pass bandwidth of 25 kHz. Use for total decimation rates of 20 or higher.
- *
- * Implements the algorithm described in Lyons, Understanding Digital Signal Processing, 3e, section 10.2.1, page 511.
- *
- * @param sampleRate
- * @param decimation
- * @param passFrequency frequency of the pass band
- * @return optimum integer decimation rate for the first stage decimation
- * filter
- */
- public static int getOptimalStageOneRate(int sampleRate, int decimation, long passFrequency, long stopFrequency)
- {
- double ratio = getBandwidthRatio(passFrequency, stopFrequency);
-
- double numerator = 1.0d - (Math.sqrt((double)decimation * ratio / (2.0d - ratio)));
-
- double denominator = 2.0d - (ratio * (decimation + 1.0d));
-
- int retVal = (int)(2.0d * decimation * (numerator / denominator));
-
- return retVal;
- }
-
- /**
- * Determines the F ratio as described in Lyons, Understanding Digital Signal Processing, 3e, section 10.2.1, page 511
- * Used in conjunction with the optimal stage one decimation rate method above.
- */
- private static double getBandwidthRatio(long passFrequency, long stopFrequency)
- {
- assert (passFrequency < stopFrequency);
-
- return ((double)(stopFrequency - passFrequency) /
- (double)stopFrequency);
- }
-
- /**
- * Assumes that the pass band is 1/4 of the output sample rate.
- *
- * Assumes the stop band is: pass + (pass * .25).
- *
- * @param outputSampleRate
- * @return
- */
- public static float[] getCICCleanupFilter(int outputSampleRate, int passFrequency, int attenuation, WindowType window)
- {
- int taps = getTapCount(outputSampleRate, passFrequency, passFrequency + 1500,
- attenuation);
-
- /* Make tap count odd */
- if(taps % 2 == 0)
- {
- taps++;
- }
-
- float[] frequencyResponse =
- getCICResponseArray(outputSampleRate, passFrequency, taps);
-
- //Apply Inverse DFT against frequency response unity values, leaving the
- //IDFT bin results in the frequency response array
- FloatFFT_1D idft = new FloatFFT_1D(taps);
- idft.realInverseFull(frequencyResponse, false);
-
- //Transfer the IDFT results to the odd length return array
- float[] coefficients = new float[taps];
- int middleCoefficient = (int)(taps / 2);
-
- //Bin 0 of the idft is our center coefficient
- coefficients[middleCoefficient] = frequencyResponse[0];
-
- //The remaining idft bins from 1 to (middle - 1) are the mirror image
- //coefficients
- for(int x = 1; x <= middleCoefficient; x++)
- {
- coefficients[middleCoefficient + x] = frequencyResponse[2 * x];
- coefficients[middleCoefficient - x] = frequencyResponse[2 * x];
- }
-
- //Apply the window against the coefficients
-// coefficients = Window.apply( window, coefficients );
-
- normalize(coefficients);
-
- return coefficients;
- }
-
- public static float[] getCICResponseArray(int sampleRate, int frequency, int length)
- {
- float[] cicArray = new float[length * 2];
-
- int binCount = (int)((Math.round(
- (double)frequency / (double)sampleRate * 2.0d * (double)length)));
-
- cicArray[0] = 1.0f;
-
- float unityResponse = (float)(Math.sin(1.0d / (double)length) /
- (1.0d / (double)length));
-
- for(int x = 1; x <= binCount; x++)
- {
- /* Calculate unity response plus amplification for droop */
- float compensated = 1.0f + (unityResponse - (float)(Math.sin((double)x / (double)length) / ((double)x / (double)length)));
-
- cicArray[x] = compensated;
- cicArray[length - x] = compensated;
- }
-
- return cicArray;
- }
-
- /**
- * Creates root raised cosine filter coefficients with a tap count equal to the symbols x samplesPerSymbol + 1.
- *
- * Symbol count should be an even integer.
- * Ported to java from gnuradio/filter/firdes.cc
- *
- * For 40db attenuation, calculate the number of symbols based on the following formula:
- * Symbols = -44 * alpha + 33
- *
- * Polyphase Channelizer notes:
- * -Set samples Per Symbol at 2 (or more) * number of channels
- * -Set symbolCount to sufficient size to produce required attenuation
- * -Set symbol rate in hertz
- * -Set alpha = (desired channel bandwidth / (symbol rate * samples per symbol) - 1.0
- * -Channel bandwidth must be larger than symbol rate * samples per symbol, in order for there to be a positive alpha
- *
- * @param samplesPerSymbol - number of samples per symbol
- * @param symbolCount - number of symbol intervals for the filter. This directly impacts the filter size
- * @param alpha - roll-off factor.
- * @return - filter coefficients
- */
- public static float[] getRootRaisedCosine(int samplesPerSymbol, int symbolCount, float alpha)
- {
- int taps = samplesPerSymbol * symbolCount;
-
- float scale = 0;
-
- float[] coefficients = new float[taps];
-
- for(int x = 0; x < taps; x++)
- {
- float index = (float)x - ((float)taps / 2.0f);
-
- float x1 = (float)Math.PI * index / (float)samplesPerSymbol;
- float x2 = 4.0f * alpha * index / (float)samplesPerSymbol;
- float x3 = x2 * x2 - 1.0f;
-
- float numerator, denominator;
-
- if(Math.abs(x3) >= 0.000001)
- {
- if(x != taps / 2)
- {
- numerator = (float)Math.cos((1.0 + alpha) * x1) +
- (float)Math.sin((1.0f - alpha) * x1) / (4.0f * alpha * index / (float)samplesPerSymbol);
- }
- else
- {
- numerator = (float)Math.cos((1.0f + alpha) * x1) + (1.0f - alpha) * (float)Math.PI / (4.0f * alpha);
- }
-
- denominator = x3 * (float)Math.PI;
- }
- else
- {
- if(alpha == 1.0f)
- {
- coefficients[x] = -1.0f;
- continue;
- }
-
- x3 = (1.0f - alpha) * x1;
- x2 = (1.0f + alpha) * x1;
-
- numerator = (float)(Math.sin(x2) * (1.0f + alpha) * Math.PI - Math.cos(x3) * ((1.0 - alpha) * Math.PI *
- (double)samplesPerSymbol) / (4.0 * alpha * index) + Math.sin(x3) * (double)samplesPerSymbol *
- (double)samplesPerSymbol / (4.0 * alpha * index * index));
-
- denominator = (float)(-32.0 * Math.PI * alpha * alpha * index / (double)samplesPerSymbol);
- }
-
- coefficients[x] = 4.0f * alpha * numerator / denominator;
-
- scale += coefficients[x];
- }
-
- /**
- * Normalize (scale) coefficients to 1.0 sum and apply gain
- */
- for(int x = 0; x < taps; x++)
- {
- coefficients[x] = coefficients[x] / scale;
- }
-
- return coefficients;
- }
-
- /**
- * Creates a filter from the filter specification using the remez exchange design algorithm
- *
- * @param specification
- * @return filter coefficients
- * @throws FilterDesignException if the filter cannot be designed
- */
- public static float[] getTaps(FIRFilterSpecification specification) throws FilterDesignException
- {
- RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
-
- if(designer.isValid())
- {
- return designer.getImpulseResponse();
- }
-
- return null;
- }
-
- /**
- * Evaluates the filter to determine the magnitude response (dB) at the specified frequency.
- *
- * @param filter to evaluate
- * @param frequency to calculate (0 <> 0.5)
- * @return magnitude frequency response in decibels
- */
- public static double evaluate(float[] filter, double frequency)
- {
- double real = 0.0;
- double imag = 0.0;
-
- for(int x = 0; x < filter.length; x++)
- {
- real += filter[x] * Math.cos(Math.PI * frequency * (double)x);
- imag += filter[x] * Math.sin(Math.PI * frequency * (double)x);
- }
-
- return decibel(real, imag);
- }
-
- /**
- * Calculates the decibel magnitude of the real and imaginary complex sample
- * @param real (x-axis) value
- * @param imag (y-axis) value
- * @return magnitude in decibels
- */
- public static double decibel(double real, double imag)
- {
- return (float)(10.0 * Math.log10(Math.pow(real, 2.0) + Math.pow(imag, 2.0)));
- }
-
-
- public static float[] getRemezChannelizer(int channelBandwidth, int channels, int tapsPerChannel, double alpha,
- double passRipple, double stopRipple) throws FilterDesignException
- {
- FIRFilterSpecification specification = FIRFilterSpecification.channelizerBuilder()
- .sampleRate(channels * channelBandwidth * 4)
- .channels(channels)
- .channelBandwidth(channelBandwidth)
- .tapsPerChannel(tapsPerChannel)
- .alpha(alpha)
- .passRipple(passRipple)
- .stopRipple(stopRipple)
- .build();
-
- RemezFIRFilterDesignerWithLagrange designer = new RemezFIRFilterDesignerWithLagrange(specification);
-
- float[] taps = designer.getImpulseResponse();
-
- double bandEdgeFrequency = (double)channelBandwidth / (double)(channels * channelBandwidth * 2);
-
- double response = decibel(designer.getFrequencyResponse(Math.cos(bandEdgeFrequency * Math.PI)), 0.0);
- mLog.debug("Frequency Response at 1.0: " + response);
- response = decibel(designer.getFrequencyResponse(Math.cos(bandEdgeFrequency * Math.PI * 2.0)), 0.0);
- mLog.debug("Frequency Response at 2.0: " + response);
-
- return taps;
- }
-
- /**
- * Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase Channelizer/Synthesizer.
- * The filter is designed for x2 oversampling of each channel and the filter cutoff frequency is incrementally
- * adjusted to achieve a -6.02 dB attenuation at the channel band edge to enable perfect reconstruction of adjacent
- * channels.
- *
- * The odd length sync filter that meets the design objectives will be increased in length by pre-padding a zero
- * coefficient to produce a filter with a length that is an integral of the channel count.
- *
- * Note: the tapsPerChannel value will be iteratively increased (up to 10x) if a filter cannot be designed to meet
- * the channel band edge attenuation objective.
- *
- * Note: a channelizer filter designed using a Blackman-Harris-7 window and 18-21 taps per channel creates a filter
- * with attenuation in range of -60 to -80 dB at the edge of the 2x sampled channel.
- *
- * @param channelBandwidth - full bandwidth of the channel in hertz
- * @param channels - number of channels
- * @param tapsPerChannel - desired filter tap count per channel (may be increased to meet objective)
- * @param windowType - to use when designing the windowed-sync filter
- * @param logResults - to log a debug summary of the filter characteristics
- *
- * @return filter coefficients
- *
- * @throws FilterDesignException if a filter cannot be designed with a band edge attenuation of -6.02 dB
- */
- public static float[] getSincChannelizer(double channelBandwidth, int channels, int tapsPerChannel,
- WindowType windowType, boolean logResults) throws FilterDesignException
- {
- int currentTapsPerChannel = tapsPerChannel;
- int sampleRate = (int)(channelBandwidth * (double)channels * 2.0);
- int filterLength = (channels * currentTapsPerChannel) - 1;
-
- double cutoffFrequency = channelBandwidth / (double)sampleRate;
- double bandEdge = cutoffFrequency;
- double increment = cutoffFrequency * 0.1;
-
- //Get an initial filter and band edge frequency response using the channel bandwidth as a cutoff
- float[] taps = FilterFactory.getSinc(cutoffFrequency, filterLength, windowType);
- double response = FilterFactory.evaluate(taps, bandEdge);
-
- //Set cutoff adjustment threshold - we'll test cutoff frequencies to around 1 hertz resolution
- double incrementThreshold = 1.0 / (double)sampleRate;
-
- //Iteratively evaluate filters to find the filter with the highest cutoff frequency that meets the objective
- while(increment > incrementThreshold)
- {
- //If the current cutoff meets the objective, test if a higher cutoff also meets the objective
- if(matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE) &&
- (cutoffFrequency + increment <= bandEdge))
- {
- float[] higherCutoffTaps = FilterFactory.getSinc(cutoffFrequency + increment, filterLength, windowType);
- double higherCutoffResponse = FilterFactory.evaluate(higherCutoffTaps, bandEdge);
-
- if(matchesObjective(higherCutoffResponse, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
- {
- cutoffFrequency += increment;
- taps = higherCutoffTaps;
- response = higherCutoffResponse;
- }
- else
- {
- increment /= 2.0;
- }
- }
- //If the current cutoff meets the objective, decrease the increment to test for smaller resolution cutoffs
- else if(matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
- {
- increment /= 2.0;
- }
- //Decrease the cutoff frequency - we can't meet the objective
- else
- {
- cutoffFrequency -= increment;
-
- if(cutoffFrequency <= 0)
- {
- if(logResults)
- {
- mLog.debug("Warning: cannot design channelizer filter with tap count [" + currentTapsPerChannel +
- "] increasing tap count and starting over");
- }
-
- currentTapsPerChannel++;
-
- //After we've attempted increasing the taps per channel 10x, give up
- if(currentTapsPerChannel > (tapsPerChannel + 10))
- {
- throw new FilterDesignException("Couldn't design filter with taps per channel count in the " +
- "range of " + tapsPerChannel + " - " + (tapsPerChannel + 10));
- }
-
- filterLength = channels * currentTapsPerChannel - 1;
- cutoffFrequency = ((double)channelBandwidth / (double)sampleRate);
- increment = cutoffFrequency * 0.1;
- }
-
- taps = FilterFactory.getSinc(cutoffFrequency, filterLength, windowType);
- response = FilterFactory.evaluate(taps, bandEdge);
- }
- }
-
- //This will probably never happen, but ...
- if(!matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
- {
- throw new FilterDesignException("Cannot design filter to specifications");
- }
-
- //This is an odd length filter - increase the length by 1 by pre-padding a zero coefficient
- float[] extendedTaps = new float[taps.length + 1];
- System.arraycopy(taps, 0, extendedTaps, 1, taps.length);
-
- if(logResults)
- {
- mLog.debug("Polyphase Channelizer Filter Design Summary");
- mLog.debug("-----------------------------------------------------");
- mLog.debug("Input Sample Rate: " + sampleRate);
- mLog.debug("Channel Bandwidth: " + channelBandwidth);
- mLog.debug("Channels: " + channels);
- mLog.debug("Window Type: " + windowType.name());
- mLog.debug("Taps Per Channel - Requested:" + tapsPerChannel + " Actual:" + ((double)extendedTaps.length / (double)channels));
- mLog.debug("Filter Length: " + (extendedTaps.length));
- mLog.debug("Requested Cutoff Frequency: " + (sampleRate * bandEdge));
- mLog.debug("Actual Cutoff Frequency: " + (sampleRate * cutoffFrequency));
- mLog.debug("Attenuation at 1.0 OBJECTIVE: " + PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE);
- mLog.debug("Attenuation at 1.00 Channels: " + evaluate(taps, bandEdge * 1.00));
- mLog.debug("Attenuation at 1.25 Channels: " + evaluate(taps, bandEdge * 1.25));
- mLog.debug("Attenuation at 1.50 Channels: " + evaluate(taps, bandEdge * 1.50));
- mLog.debug("Attenuation at 1.75 Channels: " + evaluate(taps, bandEdge * 1.75));
- mLog.debug("Attenuation at 2.00 Channels: " + evaluate(taps, bandEdge * 2.00));
- }
-
- return extendedTaps;
- }
-
- /**
- * Creates a windowed-sync (Nyquist) filter.
- *
- * @param cutoff frequency (0.0 <> 0.5)
- * @param length of the filter - must be odd-length
- * @param windowType to use in designing the filter
- * @return filter coefficients.
- * @throws FilterDesignException if the requested length is not odd
- */
- public static float[] getSinc(double cutoff, int length, WindowType windowType) throws FilterDesignException
- {
- if(length % 2 == 0)
- {
- throw new FilterDesignException("Sinc filters must be odd-length");
- }
-
- float[] coefficients = new float[length];
- int half = length / 2;
-
- double[] window = Window.getWindow(windowType, length);
-
- double scalor = 2.0 * cutoff;
- double piScalor = Math.PI * scalor;
-
- coefficients[half] = (float)(1.0 * scalor);
-
- for(int x = 1; x <= half; x++)
- {
- double a = piScalor * x;
- double coefficient = scalor * Math.sin(a) / a;
-
- coefficient *= window[half + x];
- coefficients[half + x] = (float)coefficient;
- coefficients[half - x] = (float)coefficient;
- }
-
- return coefficients;
- }
-
- /**
- * Compares two doubles for equals and avoids any rounding error that are present.
- * @param a value to compare
- * @param objective value to compare
- * @return true if they are equivalent within the margin of error
- */
- private static boolean matchesObjective(double a, double objective)
- {
- return Math.abs(a - objective) < 0.0000000001;
- }
-}
diff --git a/src/dsp/filter/Window.java b/src/dsp/filter/Window.java
deleted file mode 100644
index 6d1d5ac5..00000000
--- a/src/dsp/filter/Window.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package dsp.filter;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Arrays;
-
-/**
- * Window factory and corresponding utility methods.
- */
-public class Window
-{
- private final static Logger mLog = LoggerFactory.getLogger(Window.class);
-
- private static final double TWO_PI = Math.PI * 2.0d;
- private static final double FOUR_PI = Math.PI * 4.0d;
- private static final double SIX_PI = Math.PI * 6.0d;
- private static final double EIGHT_PI = Math.PI * 8.0d;
-
- /**
- * Creates a window of the specified type and length.
- *
- * @param type of window
- * @param length of window
- * @return window of the specified type and length
- */
- public static double[] getWindow(WindowType type, int length)
- {
- switch(type)
- {
- case BLACKMAN:
- return getBlackman(length);
- case BLACKMAN_HARRIS_4:
- return getBlackmanHarris4(length);
- case BLACKMAN_HARRIS_7:
- return getBlackmanHarris7(length);
- case BLACKMAN_NUTALL:
- return getBlackmanNutall(length);
- case COSINE:
- return getCosine(length);
- case FLAT_TOP:
- return getFlatTop(length);
- case HAMMING:
- return getHamming(length);
- case HANN:
- return getHann(length);
- case NUTALL:
- return getNutall(length);
- case NONE:
- default:
- return getRectangular(length);
- }
- }
-
- /**
- * Creates an all-pass or rectangular window.
- *
- * @param length of the window
- * @return rectangular window
- */
- public static double[] getRectangular(int length)
- {
- double[] coefficients = new double[length];
-
- Arrays.fill(coefficients, 1.0d);
-
- return coefficients;
- }
-
- /**
- * Creates a cosine window of the specified length.
- *
- * @param length of the window
- * @return cosine window
- */
- public static double[] getCosine(int length)
- {
- double[] coefficients = new double[length];
-
- if(length % 2 == 0) //Even length
- {
- int half = (int)((length - 1) / 2);
-
- for(int x = -half; x < length / 2 + 1; x++)
- {
- coefficients[x + half] = Math.cos(
- ((double)x * Math.PI) / ((double)length + 1.0d));
- }
- }
- else //Odd length
- {
- int half = (int)length / 2;
-
- for(int x = -half; x < half + 1; x++)
- {
- coefficients[x + half] = Math.cos(
- ((double)x * Math.PI) / ((double)length + 1.0d));
- }
- }
-
- return coefficients;
- }
-
- /**
- * Creates a 3-term Blackman window.
- *
- * @param length of the window
- * @return Blackman window
- */
- public static double[] getBlackman(int length)
- {
- double[] coefficients = new double[length];
-
- double denominator = length - 1;
-
- double a0 = 0.426590713672;
- double a1 = 0.496560619089;
- double a2 = 0.0768486672399;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 -
- (a1 * Math.cos((TWO_PI * (double)x) / denominator)) +
- (a2 * Math.cos((FOUR_PI * (double)x) / denominator));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a 4-term Nutall window
- *
- * @param length of the window
- * @return Nutall window
- */
- public static double[] getNutall(int length)
- {
- double[] coefficients = new double[length];
-
- double denominator = length - 1;
- double a0 = 0.355768;
- double a1 = 0.487396;
- double a2 = 0.144232;
- double a3 = 0.012604;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 -
- (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
- (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
- (a3 * Math.cos(SIX_PI * (double)x / denominator));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a 4-term Blackman-Nutall window
- *
- * @param length of the window
- * @return Blackman-Nutall window
- */
- public static double[] getBlackmanNutall(int length)
- {
- double[] coefficients = new double[length];
-
- double denominator = length - 1;
- double a0 = 0.3635819;
- double a1 = 0.4891775;
- double a2 = 0.1365995;
- double a3 = 0.0106411;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 -
- (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
- (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
- (a3 * Math.cos(SIX_PI * (double)x / denominator));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a 4-term Blackman-Harris window
- *
- * @param length of the window
- * @return Blackman-Harris window
- */
- public static double[] getBlackmanHarris4(int length)
- {
- double[] coefficients = new double[length];
-
- double denominator = length - 1;
- double a0 = 0.35875;
- double a1 = 0.48829;
- double a2 = 0.14128;
- double a3 = 0.01168;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 -
- (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
- (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
- (a3 * Math.cos(SIX_PI * (double)x / denominator));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a 7-term Blackman-Harris window
- *
- * @param length of the window
- * @return Blackman-Harris window
- */
- public static double[] getBlackmanHarris7(int length)
- {
- double[] coefficients = new double[length];
-
- double denominator = length - 1;
-
- double a0 = 0.27105140069342;
- double a1 = 0.43329793923448;
- double a2 = 0.21812299954311;
- double a3 = 0.06592544638803;
- double a4 = 0.01081174209837;
- double a5 = 0.00077658482522;
- double a6 = 0.00001388721735;
-
-
- for(int x = 0; x < length; x++)
- {
- double w = TWO_PI * (double)x / denominator;
-
- coefficients[x] = a0 -
- (a1 * Math.cos(w)) +
- (a2 * Math.cos(2.0 * w)) -
- (a3 * Math.cos(3.0 * w)) +
- (a4 * Math.cos(4.0 * w)) -
- (a5 * Math.cos(5.0 * w)) +
- (a6 * Math.cos(6.0 * w));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a Hamming window
- *
- * @param length of the window
- * @return Hamming window
- */
- public static double[] getHamming(int length)
- {
- double[] coefficients = new double[length];
-
- double a0 = 0.54;
- double a1 = 0.46;
- double denominator = length;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 - (a1 * Math.cos(TWO_PI * (double)x / denominator));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a Hann window
- *
- * @param length of the window
- * @return Hann window
- */
- public static double[] getHann(int length)
- {
- double[] coefficients = new double[length];
-
- double a0 = 0.5;
- double a1 = 0.5;
- double denominator = length;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 - (a1 * Math.cos(TWO_PI * (double)x / denominator));
- }
-
- return coefficients;
- }
-
- /**
- * Creates a 4-term Blackman-Harris window
- *
- * @param length of the window
- * @return Blackman-Harris window
- */
- public static double[] getFlatTop(int length)
- {
- double[] coefficients = new double[length];
-
- double denominator = length - 1;
-
- double a0 = 0.215578948;
- double a1 = 0.41663158;
- double a2 = 0.277263158;
- double a3 = 0.083578947;
- double a4 = 0.006947368;
-
- for(int x = 0; x < length; x++)
- {
- coefficients[x] = a0 -
- (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
- (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
- (a3 * Math.cos(SIX_PI * (double)x / denominator)) +
- (a4 * Math.cos(EIGHT_PI * (double)x / denominator));
- }
-
- return coefficients;
- }
-
-
- private static double getKaiserBeta(double attenuation)
- {
- if(attenuation > 50.0)
- {
- return 0.1102 * (attenuation - 8.7);
- }
- else if(attenuation >= 21.0)
- {
- return (0.5842 * Math.pow(attenuation - 21.0, 0.4)) + (0.07886 * (attenuation - 21.0));
- }
- else
- {
- return 0.0;
- }
- }
-
- /**
- * Apply the window against an array of float-type samples
- */
- public static float[] apply(double[] coefficients, float[] samples)
- {
- for(int x = 0; x < coefficients.length; x++)
- {
- samples[x] = (float)(samples[x] * coefficients[x]);
- }
-
- return samples;
- }
-
- /**
- * Apply the window type against the float array of samples
- *
- * @param type of window to use
- * @param samples to be windowed
- * @return windowed samples
- */
- public static float[] apply(WindowType type, float[] samples)
- {
- double[] coefficients = getWindow(type, samples.length);
-
- return apply(coefficients, samples);
- }
-
- /**
- * Apply the window against an array of double-type samples
- */
- public static double[] apply(double[] coefficients, double[] samples)
- {
- for(int x = 0; x < coefficients.length; x++)
- {
- samples[x] = samples[x] * coefficients[x];
- }
-
- return samples;
- }
-
- /**
- * Apply the window type against the double array of samples
- *
- * @param type of window to use
- * @param samples to be windowed
- * @return windowed samples
- */
- public static double[] apply(WindowType type, double[] samples)
- {
- double[] coefficients = getWindow(type, samples.length);
-
- return apply(coefficients, samples);
- }
-
- /**
- * Window types
- */
- public enum WindowType
- {
- BLACKMAN("Blackman"),
- BLACKMAN_HARRIS_4("Blackman-Harris 4"),
- BLACKMAN_HARRIS_7("Blackman-Harris 7"),
- BLACKMAN_NUTALL("Blackman-Nutall"),
- COSINE("Cosine"),
- FLAT_TOP("Flat Top"),
- HAMMING("Hamming"),
- HANN("Hann"),
- NUTALL("Nutall"),
- NONE("None");
-
- private String mLabel;
-
- WindowType(String label)
- {
- mLabel = label;
- }
-
- public String toString()
- {
- return mLabel;
- }
- }
-}
diff --git a/src/dsp/filter/design/FilterView.java b/src/dsp/filter/design/FilterView.java
deleted file mode 100644
index de30c8e3..00000000
--- a/src/dsp/filter/design/FilterView.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package dsp.filter.design;
-
-import gui.control.CurveFittedAreaChart;
-import javafx.collections.ObservableList;
-import javafx.scene.chart.NumberAxis;
-import javafx.scene.chart.XYChart;
-import javafx.scene.chart.XYChart.Series;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Priority;
-import org.jtransforms.fft.FloatFFT_1D;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FilterView extends BorderPane
-{
- private final static Logger mLog = LoggerFactory.getLogger(FilterView.class);
-
- private CurveFittedAreaChart mImpulseChart;
- private NumberAxis mImpulseXAxis;
- private NumberAxis mImpulseYAxis;
- private CurveFittedAreaChart mDFTChart;
- private NumberAxis mDFTXAxis;
- private NumberAxis mDFTYAxis;
- private static final int FFT_SIZE = 2048;
- private FloatFFT_1D mFFT = new FloatFFT_1D(FFT_SIZE);
-
- public FilterView(float[] taps)
- {
- init();
-
- if(taps != null)
- {
- setImpulseTaps(taps);
-
- float[] dft = new float[FFT_SIZE];
- System.arraycopy(taps, 0, dft, 0, taps.length);
-
- mFFT.realForward(dft);
-
- setDFTTaps(convertDFTToDecibel(dft, taps.length));
- }
- }
-
- private void init()
- {
- HBox hbox = new HBox();
-
- mImpulseXAxis = new NumberAxis(0.0, 0.5, 1.0);
- mImpulseYAxis = new NumberAxis(-0.2, 1.2, .1);
-
- mImpulseChart = new CurveFittedAreaChart(mImpulseXAxis, mImpulseYAxis);
- mImpulseChart.setLegendVisible(false);
- mImpulseChart.setHorizontalGridLinesVisible(false);
- mImpulseChart.setVerticalGridLinesVisible(false);
- mImpulseChart.setAlternativeColumnFillVisible(false);
- mImpulseChart.setAlternativeRowFillVisible(false);
-
- String css = FilterView.class.getResource("FilterView.css").toExternalForm();
- mImpulseChart.getStylesheets().add(css);
-
- hbox.getChildren().add(mImpulseChart);
- HBox.setHgrow(mImpulseChart, Priority.ALWAYS);
-
- mDFTXAxis = new NumberAxis(0.0, FFT_SIZE / 2, FFT_SIZE / 8);
- mDFTYAxis = new NumberAxis(-200.0, 50.0, 10.0);
-
- mDFTChart = new CurveFittedAreaChart(mDFTXAxis, mDFTYAxis);
- mDFTChart.setLegendVisible(false);
- mDFTChart.setHorizontalGridLinesVisible(false);
- mDFTChart.setVerticalGridLinesVisible(false);
- mDFTChart.setAlternativeColumnFillVisible(false);
- mDFTChart.setAlternativeRowFillVisible(false);
- mDFTChart.getStylesheets().add(css);
-
- hbox.getChildren().add(mDFTChart);
- HBox.setHgrow(mDFTChart, Priority.ALWAYS);
-
- setCenter(hbox);
- }
-
- public void setImpulseTaps(double[] taps)
- {
- float[] convertedTaps = new float[taps.length];
-
- for(int x = 0; x < taps.length; x++)
- {
- convertedTaps[x] = (float)taps[x];
- }
-
- setImpulseTaps(convertedTaps);
- }
-
- public void setImpulseTaps(float[] taps)
- {
- double smallest = 0.0;
- double largest = 0.0;
-
- final XYChart.Series series = new XYChart.Series<>();
-
- for(int x = 0; x < taps.length; x++)
- {
- if(taps[x] < smallest)
- {
- smallest = taps[x];
- }
-
- if(taps[x] > largest)
- {
- largest = taps[x];
- }
-
- series.getData().add(new XYChart.Data(x, taps[x]));
- }
-
- mImpulseXAxis.setLowerBound(0.0);
- mImpulseXAxis.setUpperBound(taps.length - 1);
- mImpulseXAxis.setTickUnit(1.0);
-
- double padding = Math.abs(largest - smallest) * .1;
-
- mImpulseYAxis.setLowerBound(smallest - padding);
- mImpulseYAxis.setUpperBound(largest + padding);
- mImpulseYAxis.setTickUnit(padding);
-
- ObservableList> list = mImpulseChart.getData();
- list.add(series);
- }
-
- public void setDFTTaps(float[] taps)
- {
- double smallest = 0.0;
-
- final XYChart.Series series = new XYChart.Series<>();
-
- for(int x = 0; x < taps.length; x++)
- {
- if(taps[x] < smallest)
- {
- smallest = taps[x];
- }
-
- series.getData().add(new XYChart.Data(x, taps[x]));
- }
-
- ObservableList> list = mDFTChart.getData();
- list.add(series);
- }
-
- public float[] convertDFTToDecibel(float[] dft, int tapLength)
- {
- float[] decibels = new float[dft.length / 2];
-
- int index = 0;
-
- for(int x = 0; x < decibels.length; x++)
- {
- index = x * 2;
- double magnitude = Math.sqrt(Math.pow(dft[index], 2.0) + Math.pow(dft[index + 1], 2.0));
- decibels[x] = (float)(20.0 * Math.log10(magnitude));
- }
-
- return decibels;
- }
-}
diff --git a/src/dsp/filter/design/FilterViewer.java b/src/dsp/filter/design/FilterViewer.java
deleted file mode 100644
index 14db2b9c..00000000
--- a/src/dsp/filter/design/FilterViewer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package dsp.filter.design;
-
-import dsp.filter.FilterFactory;
-import dsp.filter.Window;
-import javafx.application.Application;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FilterViewer extends Application
-{
- private final static Logger mLog = LoggerFactory.getLogger(FilterViewer.class);
-
- /**
- * Developer tool to visualize filter designs
- */
- public FilterViewer()
- {
- }
-
- @Override
- public void start(Stage primaryStage) throws Exception
- {
- float[] taps = getFilter();
-
- if(taps != null)
- {
- Scene scene = new Scene(new FilterView(taps));
-
- primaryStage.setTitle("Filter Viewer");
- primaryStage.setScene(scene);
- primaryStage.show();
- }
- else
- {
- throw new Exception("Couldn't start application - filter error");
- }
- }
-
- public static void main(String[] args)
- {
- launch(args);
- }
-
- /**
- * Provides the filter to visualize. Modify this method to visualize your filter design.
- */
- private float[] getFilter()
- {
- float[] taps = null;
-
-// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
-// .sampleRate(1000)
-// .gridDensity(16)
-// .passBandAmplitude(1.0)
-// .passBandCutoff(100)
-// .passBandRipple(0.01)
-// .stopBandAmplitude(0.0)
-// .stopBandRipple(0.0001)
-// .stopBandStart(150)
-// .build();
-
-// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
-// .sampleRate( 24000 )
-// .stopBandCutoff( 800 )
-// .stopBandAmplitude( 0.0 )
-// .stopBandRipple( 0.03 )
-// .passBandStart( 1000 )
-// .passBandAmplitude( 1.0 )
-// .passBandRipple( 0.08 )
-// .build();
-
-// FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
-// .sampleRate( 48000 )
-// .stopFrequency1( 200 )
-// .passFrequencyBegin( 400 )
-// .passFrequencyEnd( 3200 )
-// .stopFrequency2( 3400 )
-// .stopRipple( 0.0003 )
-// .passRipple( 0.008)
-// .build();
-
-// try
-// {
-// RemezFIRFilterDesignerWithLagrange remez = new RemezFIRFilterDesignerWithLagrange(specification);
-// taps = remez.getImpulseResponse();
-// }
-// catch(Exception e)
-// {
-// mLog.error("Error designing filter", e);
-// }
-
-// taps = FilterFactory.getSinc(0.1, 51, Window.WindowType.BLACKMAN_HARRIS_7);
- try
- {
-// taps = FilterFactory.getSincChannelizer(12500, 50, 14,
-// Window.WindowType.BLACKMAN_HARRIS_7, true);
- taps = FilterFactory.getRemezChannelizer(12500, 4, 22, 0.4,
- 0.01, 0.0001);
- }
- catch(Exception e)
- {
- mLog.error("Couldn't design filter", e);
- }
-
- return taps;
- }
-}
diff --git a/src/dsp/filter/fir/FIRFilterSpecification.java b/src/dsp/filter/fir/FIRFilterSpecification.java
deleted file mode 100644
index 2ffc10a0..00000000
--- a/src/dsp/filter/fir/FIRFilterSpecification.java
+++ /dev/null
@@ -1,1129 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package dsp.filter.fir;
-
-import dsp.filter.FilterFactory;
-import dsp.filter.fir.remez.FIRLinearPhaseFilterType;
-import org.apache.commons.lang3.Validate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class FIRFilterSpecification
-{
- private final static Logger mLog = LoggerFactory.getLogger(FIRFilterSpecification.class);
-
- private static final double PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE = -6.020599842071533; //0.5 magnitude
- private static final DecimalFormat DECIMAL_FORMATTER = new DecimalFormat("0.00000");
-
- private FIRLinearPhaseFilterType mRemezFilterType;
- private int mOrder;
- private int mGridDensity = 16;
- private List mFrequencyBands = new ArrayList<>();
-
- /**
- * Constructs a Remez filter specification. Use one of the filter builders to construct a Remez
- * filter specification.
- *
- * @param type of filter
- * @param order of the filter
- * @param gridDensity to use when calculating the coefficients
- */
- protected FIRFilterSpecification(FIRLinearPhaseFilterType type, int order, int gridDensity)
- {
- mRemezFilterType = type;
- mOrder = order;
- mGridDensity = gridDensity;
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append("Type: ");
- sb.append(mRemezFilterType.name());
- sb.append(" Order: ");
- sb.append(mOrder);
- sb.append(" Extrema: ");
- sb.append(getExtremaCount());
- sb.append(" Grid Density: ");
- sb.append(mGridDensity);
- sb.append(" Grid Size: ");
- sb.append(getGridSize());
-
- for(FrequencyBand band : mFrequencyBands)
- {
- sb.append("\n");
- sb.append(band.toString());
- }
-
- return sb.toString();
- }
-
- public void addFrequencyBand(FrequencyBand band)
- {
- mFrequencyBands.add(band);
-
- updateGridSize();
- }
-
- public void addFrequencyBands(Collection bands)
- {
- mFrequencyBands.addAll(bands);
-
- updateGridSize();
- }
-
- /**
- * Removes all frequency bands from this filter specification
- */
- public void clearFrequencyBands()
- {
- mFrequencyBands.clear();
- }
-
- public List getFrequencyBands()
- {
- return mFrequencyBands;
- }
-
- /**
- * Returns the total bandwidth of the defined frequency bands in the specification.
- *
- * @return total bandwidth -- should be less than or equal to 0.5
- */
- public double getTotalBandwidth()
- {
- double bandwidth = 0.0;
-
- for(FrequencyBand band : mFrequencyBands)
- {
- bandwidth += band.getBandWidth();
- }
-
- return bandwidth;
- }
-
- /**
- * Creates a Low-Pass FIR filter specification builder that allows you to define the filter
- * parameters and create a filter specification.
- */
- public static LowPassBuilder lowPassBuilder()
- {
- return new LowPassBuilder();
- }
-
- /**
- * Creates a High-Pass FIR filter specification builder that allows you to define the filter
- * parameters and create a filter specification.
- */
- public static HighPassBuilder highPassBuilder()
- {
- return new HighPassBuilder();
- }
-
- /**
- * Creates a Band-Pass FIR filter specification builder that allows you to define the filter
- * parameters and create a filter specification.
- */
- public static BandPassBuilder bandPassBuilder()
- {
- return new BandPassBuilder();
- }
-
- /**
- * Creates a Polyphase TunerChannelizer filter specification builder that allows you to define the filter parameters and
- * create a filter specification
- */
- public static ChannelizerBuilder channelizerBuilder()
- {
- return new ChannelizerBuilder();
- }
-
- public FIRLinearPhaseFilterType getFilterType()
- {
- return mRemezFilterType;
- }
-
- public boolean isSymmetrical()
- {
- return mRemezFilterType == FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL ||
- mRemezFilterType == FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL;
- }
-
- public boolean isAntiSymmetrical()
- {
- return mRemezFilterType == FIRLinearPhaseFilterType.TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL ||
- mRemezFilterType == FIRLinearPhaseFilterType.TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL;
- }
-
- public int getOrder()
- {
- return mOrder;
- }
-
- public boolean isEvenOrder()
- {
- return mOrder % 2 == 0;
- }
-
- public boolean isOddOrder()
- {
- return mOrder % 2 == 1;
- }
-
- public int getFilterLength()
- {
- return mOrder + 1;
- }
-
- public int getBandCount()
- {
- return mFrequencyBands.size();
- }
-
- /**
- * Grid density setting. Default value is 16.
- */
- public int getGridDensity()
- {
- return mGridDensity;
- }
-
- /**
- * Sets the grid density setting. Default value is 16.
- */
- public void setGridDensity(int density)
- {
- mGridDensity = density;
-
- updateGridSize();
- }
-
- /**
- * Maximum number of alternations/extrema (L + 2 or M + 2)
- */
- public int getExtremaCount()
- {
- return getHalfFilterOrder() + 2;
- }
-
- /**
- * Half of the filter order.
- *
- * This value is referred to as L or M, depending on the source.
- */
- public int getHalfFilterOrder()
- {
- switch(mRemezFilterType)
- {
- case TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL:
- return (mOrder) / 2;
- case TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL:
- return (mOrder - 1) / 2;
- case TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL:
- return (mOrder - 2) / 2;
- case TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL:
- return (mOrder - 1) / 2;
- default:
- return 0;
- }
- }
-
- /**
- * Determines the max band ripple from across the frequency bands in this specification.
- */
- public double getMaxBandAmplitude()
- {
- double maxRippleAmplitude = 0.0;
-
- for(FrequencyBand band : mFrequencyBands)
- {
- double bandRippleAmplitude = band.getRippleAmplitude();
-
- if(bandRippleAmplitude > maxRippleAmplitude)
- {
- maxRippleAmplitude = bandRippleAmplitude;
- }
- }
-
- return maxRippleAmplitude;
- }
-
- public int getNominalGridSize()
- {
- return (getExtremaCount() - 1) * getGridDensity() + 1;
- }
-
- public int getGridSize()
- {
- int gridSize = 0;
-
- for(FrequencyBand band : mFrequencyBands)
- {
- gridSize += band.getGridSize();
- }
-
- return gridSize;
- }
-
- private void updateGridSize()
- {
- if(!mFrequencyBands.isEmpty())
- {
- int gridSize = getNominalGridSize();
-
- double totalBandwidth = getTotalBandwidth();
-
- for(FrequencyBand band : mFrequencyBands)
- {
- band.setGridSize(gridSize, totalBandwidth);
- }
- }
- }
-
- /**
- * Dense frequency spacing for extrema count * grid density over a frequency range of 0.0 - 1.0
- */
- public double getGridFrequencyInterval()
- {
- return getTotalBandwidth() / (double)((getGridSize() - mFrequencyBands.size()));
- }
-
- /**
- * Low-pass FIR filter specification builder
- */
- public static class LowPassBuilder
- {
- private int mSampleRate;
- private int mOrder;
- private int mGridDensity = 16;
- private int mPassBandEndFrequency;
- private int mStopBandStartFrequency;
- private double mPassBandRipple;
- private double mStopBandRipple;
- private double mPassBandAmplitude = 1.0;
- private double mStopBandAmplitude = 0.0;
-
- public LowPassBuilder()
- {
- }
-
- public LowPassBuilder sampleRate(int sampleRateHz)
- {
- mSampleRate = sampleRateHz;
- return this;
- }
-
- /**
- * Sets the filter order. If the order is not specified, it will be calculated from the
- * other parameters.
- */
- public LowPassBuilder order(int order)
- {
- mOrder = order;
- return this;
- }
-
- public LowPassBuilder gridDensity(int density)
- {
- mGridDensity = density;
- return this;
- }
-
- public LowPassBuilder passBandCutoff(int frequencyHz)
- {
- mPassBandEndFrequency = frequencyHz;
- return this;
- }
-
- public LowPassBuilder stopBandStart(int frequencyHz)
- {
- mStopBandStartFrequency = frequencyHz;
- return this;
- }
-
- public LowPassBuilder passBandRipple(double rippleDb)
- {
- mPassBandRipple = rippleDb;
- return this;
- }
-
- public LowPassBuilder stopBandRipple(double rippleDb)
- {
- mStopBandRipple = rippleDb;
- return this;
- }
-
- public LowPassBuilder passBandAmplitude(double amplitude)
- {
- mPassBandAmplitude = amplitude;
- return this;
- }
-
- public LowPassBuilder stopBandAmplitude(double amplitude)
- {
- mStopBandAmplitude = amplitude;
- return this;
- }
-
- public FIRFilterSpecification build()
- {
- if(mOrder < 6)
- {
- mOrder = estimateFilterOrder(mSampleRate, mPassBandEndFrequency,
- mStopBandStartFrequency, mPassBandRipple, mStopBandRipple);
- }
-
- FIRLinearPhaseFilterType type = (mOrder % 2 == 0
- ? FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL
- : FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL);
-
- FIRFilterSpecification spec = new FIRFilterSpecification(type, mOrder, mGridDensity);
-
- FrequencyBand passBand = new FrequencyBand(mSampleRate, 0, mPassBandEndFrequency,
- mPassBandAmplitude, mPassBandRipple);
- spec.addFrequencyBand(passBand);
-
- FrequencyBand stopBand = new FrequencyBand(mSampleRate, mStopBandStartFrequency,
- mSampleRate / 2, mStopBandAmplitude, mStopBandRipple);
- spec.addFrequencyBand(stopBand);
-
- return spec;
- }
- }
-
- /**
- * Remez high-pass FIR filter specification builder
- */
- public static class HighPassBuilder
- {
- private int mSampleRate;
- private int mOrder = -1;
- private int mGridDensity = 16;
- private int mPassBandStartFrequency;
- private int mStopBandEndFrequency;
- private double mPassBandRipple;
- private double mStopBandRipple;
- private double mPassBandAmplitude = 1.0;
- private double mStopBandAmplitude = 0.0;
-
- public HighPassBuilder()
- {
- }
-
- public HighPassBuilder sampleRate(int sampleRateHz)
- {
- mSampleRate = sampleRateHz;
- return this;
- }
-
- /**
- * Sets the filter order. If the order is not specified, it will be calculated from the
- * other parameters.
- */
- public HighPassBuilder order(int order)
- {
- mOrder = order;
- return this;
- }
-
- public HighPassBuilder gridDensity(int density)
- {
- mGridDensity = density;
- return this;
- }
-
- public HighPassBuilder passBandStart(int frequencyHz)
- {
- mPassBandStartFrequency = frequencyHz;
- return this;
- }
-
- public HighPassBuilder stopBandCutoff(int frequencyHz)
- {
- mStopBandEndFrequency = frequencyHz;
- return this;
- }
-
- public HighPassBuilder passBandRipple(double rippleDb)
- {
- mPassBandRipple = rippleDb;
- return this;
- }
-
- public HighPassBuilder stopBandRipple(double rippleDb)
- {
- mStopBandRipple = rippleDb;
- return this;
- }
-
- public HighPassBuilder passBandAmplitude(double amplitude)
- {
- mPassBandAmplitude = amplitude;
- return this;
- }
-
- public HighPassBuilder stopBandAmplitude(double amplitude)
- {
- mStopBandAmplitude = amplitude;
- return this;
- }
-
- public FIRFilterSpecification build()
- {
- if(mOrder < 6)
- {
- mOrder = estimateFilterOrder(mSampleRate, mStopBandEndFrequency,
- mPassBandStartFrequency, mPassBandRipple, mStopBandRipple);
- }
-
- // High pass is a Type 1 filter -- ensure order is even
- if(mOrder % 2 == 1)
- {
- mOrder++;
- }
-
- FIRFilterSpecification spec = new FIRFilterSpecification(
- FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL, mOrder,
- mGridDensity);
-
- FrequencyBand stopBand = new FrequencyBand(mSampleRate, 0, mStopBandEndFrequency,
- mStopBandAmplitude, mStopBandRipple);
- spec.addFrequencyBand(stopBand);
-
- FrequencyBand passBand = new FrequencyBand(mSampleRate, mPassBandStartFrequency,
- mSampleRate / 2, mPassBandAmplitude, mPassBandRipple);
- spec.addFrequencyBand(passBand);
-
- return spec;
- }
- }
-
- /**
- * Band-pass or band-stop FIR filter specification builder
- */
- public static class BandPassBuilder
- {
- private int mOrder;
- private int mGridDensity = 16;
- private int mSampleRate;
- private int mStopFrequency1;
- private int mPassFrequencyBegin;
- private int mPassFrequencyEnd;
- private int mStopFrequency2;
- private double mStopRipple = 0.01;
- private double mPassRipple = 0.01;
- private double mStopAmplitude = 0.0;
- private double mPassAmplitude = 1.0;
-
- public BandPassBuilder()
- {
- }
-
- /**
- * Sets the filter order. If the order is not specified, it will be calculated from the
- * other parameters. If you specify and odd-length order, it will be corrected (+1) to make
- * it an even order number.
- */
- public BandPassBuilder order(int order)
- {
- mOrder = order;
- return this;
- }
-
- /**
- * Sets the frequency grid density. Default is 16.
- */
- public BandPassBuilder gridDensity(int density)
- {
- mGridDensity = density;
- return this;
- }
-
- /**
- * Sets the filter sample rate in hertz
- */
- public BandPassBuilder sampleRate(int sampleRate)
- {
- mSampleRate = sampleRate;
- return this;
- }
-
- /**
- * Specifies the ending frequency of the first stop band
- */
- public BandPassBuilder stopFrequency1(int stopFrequency)
- {
- mStopFrequency1 = stopFrequency;
- return this;
- }
-
- /**
- * Specifies the beginning frequency of the second stop band
- */
- public BandPassBuilder stopFrequency2(int stopFrequency)
- {
- mStopFrequency2 = stopFrequency;
- return this;
- }
-
- /**
- * Specifies the pass band beginning frequency
- */
- public BandPassBuilder passFrequencyBegin(int passFrequency)
- {
- mPassFrequencyBegin = passFrequency;
- return this;
- }
-
- /**
- * Specifies the pass band ending frequency
- */
- public BandPassBuilder passFrequencyEnd(int passFrequency)
- {
- mPassFrequencyEnd = passFrequency;
- return this;
- }
-
- /**
- * Specifies the ripple in dB for both stop bands
- */
- public BandPassBuilder stopRipple(double rippleDb)
- {
- mStopRipple = rippleDb;
- return this;
- }
-
- /**
- * Specifies the ripple in dB for the pass band
- */
- public BandPassBuilder passRipple(double rippleDb)
- {
- mPassRipple = rippleDb;
- return this;
- }
-
- /**
- * Specifies the amplitude of the stop bands. Default is 0.0
- */
- public BandPassBuilder stopAmplitude(double amplitude)
- {
- mStopAmplitude = amplitude;
- return this;
- }
-
- /**
- * Specifies the amplitude of the pass band. Default is 1.0
- */
- public BandPassBuilder passAmplitude(double amplitude)
- {
- mPassAmplitude = amplitude;
- return this;
- }
-
- /**
- * Validates the frequency bands to test for overlap and completeness.
- */
- private void validateFrequencyBands()
- {
- if(mStopFrequency1 >= mPassFrequencyBegin)
- {
- throw new IllegalArgumentException("Stop band 1 ending frequency ["
- + mStopFrequency1 + "] must be less than the pass band start frequency ["
- + mPassFrequencyBegin + "]");
- }
-
- if(mPassFrequencyBegin >= mPassFrequencyEnd)
- {
- throw new IllegalArgumentException("Pass band begin frequency ["
- + mPassFrequencyBegin + "] must be less than the pass band end frequency ["
- + mPassFrequencyEnd + "]");
- }
-
- if(mPassFrequencyEnd >= mStopFrequency2)
- {
- throw new IllegalArgumentException("Pass band end frequency [" + mPassFrequencyEnd
- + "] must be less than stop band 2 beginning frequency [" + mStopFrequency2
- + "]");
- }
-
- if(mStopFrequency2 >= (mSampleRate / 2))
- {
- throw new IllegalArgumentException("Stop band 2 beginning frequency ["
- + mStopFrequency2 + "] must be less than half of the sample rate ["
- + (mSampleRate / 2) + "]");
- }
- }
-
- public FIRFilterSpecification build()
- {
- validateFrequencyBands();
-
- FIRLinearPhaseFilterType type = FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL;
-
- if(mOrder < 10)
- {
- mOrder = estimateBandPassOrder(mSampleRate, mPassFrequencyBegin, mPassFrequencyEnd,
- mPassRipple, mStopRipple);
- }
-
- // Ensure even order since we're designing a Type 1 filter
- if(mOrder % 2 == 1)
- {
- mOrder++;
- }
-
- FIRFilterSpecification spec = new FIRFilterSpecification(type, mOrder, mGridDensity);
-
- spec.addFrequencyBand(new FrequencyBand(mSampleRate, 0, mStopFrequency1,
- mStopAmplitude, mStopRipple));
- spec.addFrequencyBand(new FrequencyBand(mSampleRate, mPassFrequencyBegin,
- mPassFrequencyEnd, mPassAmplitude, mPassRipple));
- spec.addFrequencyBand(new FrequencyBand(mSampleRate, mStopFrequency2,
- (int)(mSampleRate / 2), mStopAmplitude, mStopRipple));
-
- return spec;
- }
- }
-
- /**
- * Builder for creating polyphase channelizer low-pass prototype filter.
- */
- public static class ChannelizerBuilder
- {
- private int mGridDensity = 16;
- private int mSampleRate;
- private int mChannelCount;
- private int mChannelBandwidth;
- private int mTapsPerChannel;
- private double mStopRipple = 0.001;
- private double mPassRipple = 0.01;
- private double mAlpha = 0.2;
-
- public ChannelizerBuilder()
- {
- }
-
- /**
- * Sets the filter sample rate in hertz
- */
- public ChannelizerBuilder sampleRate(int sampleRate)
- {
- mSampleRate = sampleRate;
- return this;
- }
-
- /**
- * Number of Channels
- * @param channels - must be a multiple of 2
- * @return
- */
- public ChannelizerBuilder channels(int channels)
- {
- Validate.isTrue(channels % 2 == 0, "Channel count must be a multiple of 2");
- mChannelCount = channels;
- return this;
- }
-
- /**
- * Sets the channel bandwidth in hertz
- */
- public ChannelizerBuilder channelBandwidth(int channelBandwidth)
- {
- mChannelBandwidth = channelBandwidth;
- return this;
- }
-
- /**
- * Number of filter taps per channel. Note: total filter length will be channels * tapsPerChannel
- */
- public ChannelizerBuilder tapsPerChannel(int tapsPerChannel)
- {
- mTapsPerChannel = tapsPerChannel;
- return this;
- }
-
- /**
- * Sets the frequency grid density. Default is 16.
- */
- public ChannelizerBuilder gridDensity(int density)
- {
- mGridDensity = density;
- return this;
- }
-
- /**
- * Specifies the ripple in dB for both stop bands
- */
- public ChannelizerBuilder stopRipple(double rippleDb)
- {
- mStopRipple = rippleDb;
- return this;
- }
-
- /**
- * Specifies the ripple in dB for the pass band
- */
- public ChannelizerBuilder passRipple(double rippleDb)
- {
- mPassRipple = rippleDb;
- return this;
- }
-
- /**
- * Rolloff factor for the start band and stop band.
- * @param alpha - value in range 0.0 - 1.0
- */
- public ChannelizerBuilder alpha(double alpha)
- {
- Validate.isTrue(0.0 <= alpha && alpha <= 1.0);
- mAlpha = alpha;
-
- return this;
- }
-
- public FIRFilterSpecification build()
- {
- FIRLinearPhaseFilterType type = FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL;
-
- int passFrequencyEnd = (int)(mChannelBandwidth * (1.0 - mAlpha));
- int stopFrequencyBegin = (int)(mChannelBandwidth * (1.0 + mAlpha));
-
- int estimatedOrder = estimateFilterOrder(mSampleRate, passFrequencyEnd, stopFrequencyBegin, mPassRipple,
- mStopRipple);
-
- // Ensure odd order since we're designing a Type 2 filter
- if(estimatedOrder % 2 == 0)
- {
- estimatedOrder++;
- }
-
- int order = mChannelCount * mTapsPerChannel - 1;
-
- mLog.debug("Order:" + order + " Sample Rate:" + mSampleRate + " Estimated Order:" + estimatedOrder + " Requested Order:" + order);
-
- FIRFilterSpecification spec = new FIRFilterSpecification(type, order, mGridDensity);
-
- double bandEdge = (double)mChannelBandwidth / (double)mSampleRate;
- double passEnd = bandEdge * (1.0 - mAlpha);
- double stopStart = bandEdge * (1.0 + mAlpha);
-
- mLog.debug("Pass End:" + passEnd + " Band Edge:" + bandEdge + " Stop Begin:" + stopStart);
- mLog.debug("Pass End:" + (int)(mSampleRate * passEnd) +
- " Band Edge:" + (int)(mSampleRate * bandEdge) +
- " Stop Begin:" + (int)(mSampleRate * stopStart));
-
- FrequencyBand passBand = new FrequencyBand(0.0, passEnd, 1.0, mPassRipple);
- FrequencyBand edgeBand = new FrequencyBand(bandEdge, bandEdge, 0.5, mPassRipple);
- edgeBand.setWeight(8.0 * edgeBand.getWeight(mPassRipple));
- FrequencyBand stopBand = new FrequencyBand(stopStart, 0.5, 0.0, mStopRipple);
-
- spec.addFrequencyBand(passBand);
- spec.addFrequencyBand(edgeBand);
- spec.addFrequencyBand(stopBand);
-
- mLog.debug(spec.toString());
- return spec;
- }
- }
-
- // /**
- // * Converts the set of band amplitude values into a set of weights
- // * normalized to the largest band amplitude value
- // */
- // private static double[] getBandWeights( double... amplitudes )
- // {
- // double maximum = 0.0;
- //
- // for ( double amplitude : amplitudes )
- // {
- // if ( amplitude > maximum )
- // {
- // maximum = amplitude;
- // }
- // }
- //
- // double[] weights = new double[ amplitudes.length ];
- //
- // for ( int x = 0; x < amplitudes.length; x++ )
- // {
- // weights[ x ] = maximum / amplitudes[ x ];
- // }
- //
- // return weights;
- // }
-
- /**
- * Calculates filter order from filter length
- */
- private static int getFilterOrder(double length)
- {
- return (int)(Math.ceil(length) - 1);
- }
-
- /**
- * Estimates filter length for a two-band (high or low pass) FIR filter.
- *
- * Note: this estimator doesn't work well if the transition frequency is near 0 or sampleRate/2
- *
- * From Herrmann et al (1973), Practical design rules for optimum finite impulse response
- * filters. Bell System Technical J., 52, 769-99
- *
- * @param sampleRate in hertz
- * @param frequency1 in hertz
- * @param frequency2 in hertz
- * @param passBandRipple pass band ripple in dB
- * @param stopBandRipple stop band ripple in dB
- * @return estimated filter length
- */
- public static int estimateFilterOrder(int sampleRate, int frequency1, int frequency2,
- double passBandRipple, double stopBandRipple)
- {
- double df = Math.abs(frequency2 - frequency1) / (double)sampleRate;
-
- double ddp = Math
- .log10(stopBandRipple <= passBandRipple ? passBandRipple : stopBandRipple);
- double dds = Math
- .log10(stopBandRipple <= passBandRipple ? stopBandRipple : passBandRipple);
-
- double a1 = 5.309e-3;
- double a2 = 7.114e-2;
- double a3 = -4.761e-1;
- double a4 = -2.66e-3;
- double a5 = -5.941e-1;
- double a6 = -4.278e-1;
-
- double b1 = 11.01217;
- double b2 = 0.5124401;
-
- double t1 = a1 * ddp * ddp;
- double t2 = a2 * ddp;
- double t3 = a4 * ddp * ddp;
- double t4 = a5 * ddp;
-
- double dinf = ((t1 + t2 + a3) * dds) + (t3 + t4 + a6);
- double ff = b1 + b2 * (ddp - dds);
- double n = dinf / df - ff * df + 1.0;
-
- return (int)Math.ceil(n);
- }
-
- /**
- * Estimates band pass filter length;
- *
- * @param passBandStart frequency normalized to sample rate
- * @param passBandEnd frequency normalized to sample rate
- * @param passBandRippleDb
- * @param stopBandRippleDb
- */
- public static int estimateBandPassOrder(int sampleRate, int passBandStart, int passBandEnd,
- double passBandRippleDb, double stopBandRippleDb)
- {
- double df = (double)Math.abs(passBandEnd - passBandStart) / (double)sampleRate;
- double ddp = (double)Math.log10(passBandRippleDb);
- double dds = (double)Math.log10(stopBandRippleDb);
-
- double a1 = 0.01201;
- double a2 = 0.09664;
- double a3 = -0.51325;
- double a4 = 0.00203;
- double a5 = -0.57054;
- double a6 = -0.44314;
-
- double t1 = a1 * ddp * ddp;
- double t2 = a2 * ddp;
- double t3 = a4 * ddp * ddp;
- double t4 = a5 * ddp;
-
- double cinf = dds * (t1 + t2 + a3) + t3 + t4 + a6;
- double ginf = -14.6f * (double)Math.log10(passBandRippleDb / stopBandRippleDb) - 16.9;
- double n = cinf / df + ginf * df + 1.0;
-
- return (int)Math.ceil(n);
- }
-
- /**
- * Frequency band
- */
- public static class FrequencyBand
- {
- private int mGridSize;
- private double mStart;
- private double mEnd;
- private double mAmplitude;
- private double mRippleDB;
- private Double mWeight;
-
- /**
- * Constructs a frequency band where the start and end frequencies are normalized to a
- * sample rate of 1.
- *
- * @param start edge of the frequency band (0.0 - 1.0)
- * @param end edge of the frequency band (0.0 - 1.0)
- * @param amplitude of the frequency band on the unity scale (0.0 - 1.0)
- * @param ripple of the frequency band specified as db ripple
- */
- public FrequencyBand(double start, double end, double amplitude, double ripple)
- {
- assert (0.0 <= mStart);
- assert (mStart <= mEnd);
- assert (mEnd <= 1.0);
- assert (0.0 <= amplitude && amplitude <= 1.0);
- assert (0.0 <= ripple);
-
- mStart = start;
- mEnd = end;
- mAmplitude = amplitude;
- mRippleDB = ripple;
- }
-
- /**
- * Constructs a frequency band for the sample rate and start and end frequencies.
- *
- * @param sampleRate in hertz
- * @param start edge of the frequency band in hertz
- * @param end edge of the frequency band in hertz
- * @param amplitude of the frequency band relative to unity (0.0 - 1.0)
- * @param ripple of the frequency band specified as db ripple
- */
- public FrequencyBand(int sampleRate, int start, int end, double amplitude, double ripple)
- {
- this((double)start / (double)sampleRate, (double)end / (double)sampleRate,
- amplitude, ripple);
- }
-
- public FrequencyBand(int sampleRate, int start, int end, double amplitude, double ripple, double weight)
- {
- this(sampleRate, start, end, amplitude, ripple);
- mWeight = weight;
- }
-
- /**
- * Explicitly sets the weight of this frequency band.
- */
- public void setWeight(double weight)
- {
- mWeight = weight;
- }
-
- public int getGridSize()
- {
- return mGridSize;
- }
-
- public void setGridSize(int totalGridSize, double totalBandwidth)
- {
- mGridSize = Math.max(1, (int)Math.ceil((double)totalGridSize * (getBandWidth() / totalBandwidth)));
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append("Band Start: ");
- sb.append(mStart);
- sb.append(" End:");
- sb.append(mEnd);
- sb.append(" Amplitude: ");
- sb.append(mAmplitude);
- sb.append(" Ripple dB: ");
- sb.append(mRippleDB);
- sb.append(" Ripple Amplitude: ");
- sb.append(DECIMAL_FORMATTER.format(getRippleAmplitude()));
- sb.append(" Grid Size: ");
- sb.append(mGridSize);
- sb.append(" Weight: ");
- sb.append(getWeight(mRippleDB));
-
- return sb.toString();
- }
-
- /**
- * Start frequency edge for this band normalized to 1 Hz.
- */
- public double getStart()
- {
- return mStart;
- }
-
- /**
- * End frequency edge for this band normalized to 1 Hz.
- */
- public double getEnd()
- {
- return mEnd;
- }
-
- /**
- * Desired amplitude response for this frequency band
- *
- * @return amplitude (0.0 - 1.0)
- */
- public double getAmplitude()
- {
- return mAmplitude;
- }
-
- /**
- * Frequency band ripple in decibels
- */
- public double getRippleDB()
- {
- return mRippleDB;
- }
-
- /**
- * Frequency band ripple as an amplitude value
- */
- public double getRippleAmplitude()
- {
- return (Math.pow(10.0, (mRippleDB / 20)) - 1)
- / (Math.pow(10.0, (mRippleDB / 20)) + 1);
- }
-
- /**
- * Calculates the weighting of this frequency band's ripple relative to the max ripple for a
- * filter specification.
- *
- * @param maxRippleAmplitude for the filter specification
- * @return weight of this frequency band
- */
- public double getWeight(double maxRippleAmplitude)
- {
- if(mWeight == null)
- {
- return 1.0 / (getRippleAmplitude() / maxRippleAmplitude);
- }
-
- return mWeight;
- }
-
- /**
- * Double-sided bandwidth of this frequency band
- */
- public double getBandWidth()
- {
- return mEnd - mStart;
- }
- }
-}
diff --git a/src/dsp/filter/fir/remez/RemezFIRFilterDesigner.java b/src/dsp/filter/fir/remez/RemezFIRFilterDesigner.java
deleted file mode 100644
index dba1fd76..00000000
--- a/src/dsp/filter/fir/remez/RemezFIRFilterDesigner.java
+++ /dev/null
@@ -1,672 +0,0 @@
-package dsp.filter.fir.remez;
-
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import dsp.filter.design.FilterDesignException;
-import dsp.filter.fir.FIRFilterSpecification;
-
-public class RemezFIRFilterDesigner
-{
- private final static Logger mLog = LoggerFactory.getLogger( RemezFIRFilterDesigner.class );
-
- private static final double CONVERGENCE_THRESHOLD = 0.0001;
- public static final int MAXIMUM_ITERATION_COUNT = 40;
- public static final double TWO_PI = 2.0 * Math.PI;
-
- private static final DecimalFormat DECIMAL_FORMATTER = new DecimalFormat( "0.00000000" );
-
- private FIRFilterSpecification mSpecification;
- private Grid mGrid;
- private List mExtremalIndices;
-
- private double[] mD;
- private double[] mGridErrors;
- private double[] mGridFrequencyResponse;
- private double[] mIdealFrequencyResponse;
-
- private double mDelta;
-
- private boolean mConverged;
-
- /**
- * Linear FIR filter designer using the Parks-McClelland Remez exchange algorithm.
- *
- * Construct a filter specification using the builder methods in the FIRFilterSpecification
- * class and construct an instance of this class with the specification. Check the isValid()
- * method after construction to see if the filter design was successful. If successful, use the
- * getFrequencyResponse() and getImpulseResponse() methods to access the filter taps as the
- * specified length.
- *
- * If the filter cannot be designed from the specification the isValid() method will return
- * false and any attempts to access the getFrequencyResponse() or getImpulseResponse() methods
- * will throw a FilterDesignException.
- *
- * @param specification that defines a Type 1-4 linear phase FIR Filter
- */
- public RemezFIRFilterDesigner( FIRFilterSpecification specification )
- {
- mSpecification = specification;
-
- design();
- }
-
- /**
- * Designs the filter according to the specification
- */
- private void design()
- {
- mGrid = new Grid( mSpecification );
- mExtremalIndices = getInitialExtremalIndices();
-
- int iterationCount = 0;
-
- try
- {
- do
- {
- calculateGridFrequencyResponse();
- calculateGridError();
- findExtremalIndices();
- checkConvergence();
-
- iterationCount++;
- }
- while( !mConverged && iterationCount < MAXIMUM_ITERATION_COUNT );
- }
- catch( FilterDesignException fde )
- {
- mLog.error( "Filter design error - couldn't find extremal indices at count [" +
- iterationCount + "] try changing filter order up/down from [" +
- mSpecification.getOrder() + "]" );
-
- mConverged = false;
- }
-
- if( mConverged )
- {
- //Update frequency response using the final set of extremal indices
- calculateGridFrequencyResponse();
- }
- }
-
- /**
- * Indicates if the filter was successfully created from the filter specification. Check this
- * method before accessing frequency response or filter response, otherwise a FilterDesignException
- * is thrown.
- *
- * @return true if the filter was successfully designed.
- */
- public boolean isValid()
- {
- return mConverged;
- }
-
- /**
- * Frequency response of the designed filter with double precision
- *
- * @return half ( length / 2 ) of filter frequency response sampled at 0.0 - PI radians
- * @throws FilterDesignException if the specified filter cannot be designed
- */
- public double[] getFrequencyResponse() throws FilterDesignException
- {
- if( !mConverged )
- {
- throw new FilterDesignException( "Can't create filter from specification - failed "
- + "to converge" );
- }
-
- //Resample the final polynomial at the desired filter length
- double[] resampled = resample();
-
- //Apply Parks/McClelland frequency response corrections according to the filter type
- return correctFrequencyResponse( resampled );
- }
-
- /**
- * Impulse response of the designed filter with floating point precision.
- *
- * @return filter impulse response
- * @throws FilterDesignException if the specified filter cannot be designed
- */
- public float[] getImpulseResponse() throws FilterDesignException
- {
- return convertToFloatArray( getImpulseResponseDoubles() );
- }
-
- /**
- * Impulse response of the designed filter with double precision
- */
- public double[] getImpulseResponseDoubles() throws FilterDesignException
- {
- double[] frequencyResponse = getFrequencyResponse();
-
- int length = mSpecification.getFilterLength();
-
- double[] impulseResponse = new double[ length ];
-
- double M;
-
- switch( mSpecification.getFilterType() )
- {
- case TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL:
- M = ( (double)length - 1.0 ) / 2.0;
-
- for( int n = 0; n < length; n++ )
- {
- double accumulator = frequencyResponse[ 0 ];
-
- double frequency = TWO_PI * ( n - M ) / length;
-
- for( int k = 1; k <= M; k++ )
- {
- accumulator += 2.0 * frequencyResponse[ k ] * Math.cos( frequency * (double)k );
- }
-
- impulseResponse[ n ] = accumulator / (double)length;
- }
- break;
- case TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL:
- double offset = (double)( length - 1 ) / 2.0;
-
- for( int n = 0; n < length; n++ )
- {
- double accumulator = frequencyResponse[ 0 ];
-
- double frequency = TWO_PI * ( (double)n - offset ) / (double)length;
-
- for( int k = 1; k < frequencyResponse.length; k++ )
- {
- accumulator += 2.0 * frequencyResponse[ k ] * Math.cos( frequency * (double)k );
- }
-
- impulseResponse[ n ] = accumulator / (double)length;
- }
- break;
- }
-
- return impulseResponse;
- }
-
- /**
- * Calculates the frequency response of this filter at the specified cosine of the frequency
- *
- * Implements Oppenheim/Schafer Discrete Time Signal Processing, 3e, 2016, equation 116a to
- * determine the actual response of the specified frequency cosine using Lagrange
- * interpolation of the polynomial formed at the extremal index set.
- *
- * @param cosineOfFrequency to calculate the frequency response
- *
- * @return actual frequency response for the specified frequency with the current set of
- * extreaml indices
- */
- private double getFrequencyResponse( double cosineOfFrequency )
- {
- double numerator = 0.0;
- double denominator = 0.0;
-
- for( int k = 0; k < mExtremalIndices.size() - 1; k++ )
- {
- double cosineDelta = cosineOfFrequency - mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( k ) ];
-
- //If this frequency is close to one of the polynomial points, use the polynomial point for the response
- if( Math.abs( cosineDelta ) < 1.0e-7 )
- {
- return mIdealFrequencyResponse[ k ];
- }
- else
- {
- double dkOverCosineDelta = mD[ k ] / cosineDelta;
-
- numerator += dkOverCosineDelta * mIdealFrequencyResponse[ k ];
- denominator += dkOverCosineDelta;
- }
- }
-
- return numerator / denominator;
- }
-
- /**
- * Calculated Delta value
- *
- * @return delta value
- */
- public double getDelta()
- {
- return mDelta;
- }
-
- /**
- * Creates initial array of extremal frequency indices spaced at intervals of the grid frequency
- * set for all frequencies where a frequency band weighting is specified.
- */
- private List getInitialExtremalIndices()
- {
- int count = mSpecification.getExtremaCount();
-
- int density = mSpecification.getGridDensity();
-
- List extremalIndices = new ArrayList<>();
-
- for( int i = 0; i < count; i++ )
- {
- extremalIndices.add( i * density );
- }
-
- return extremalIndices;
- }
-
- /**
- * Calculates the actual frequency response of a dense frequency grid using the polynomial
- * represented by the extremal indices.
- */
- private void calculateGridFrequencyResponse()
- {
- double[] b = calculateB();
-
- calculateDelta( b );
-
- calculateC();
-
- calculateD( b );
-
- updateGridFrequencyResponse();
- }
-
- /**
- * Updates the frequency response of frequencies specified in the dense frequency grid
- */
- private void updateGridFrequencyResponse()
- {
- mGridFrequencyResponse = new double[ mGrid.getSize() ];
-
- double[] gridFrequencyCosines = mGrid.getCosineFrequencyGrid();
-
- for( int i = 0; i < mGridFrequencyResponse.length; i++ )
- {
- mGridFrequencyResponse[ i ] = getFrequencyResponse( gridFrequencyCosines[ i ] );
- }
- }
-
- /**
- * Calculates the value of b across the set of extremal indices.
- *
- * Implements Oppenheim/Schafer Discrete Time Signal Processing, 3e, 2016, equation 115
- *
- * @return the array of b values of length L+2
- */
- private double[] calculateB()
- {
- int length = mExtremalIndices.size();
-
- double[] b = new double[ length ];
-
- for( int k = 0; k < length; k++ )
- {
- b[ k ] = 1.0;
-
- double xk = mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( k ) ];
-
- for( int i = 0; i < length; i++ )
- {
- if( i != k )
- {
- double xi = mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( i ) ];
-
- double denominator = xk - xi;
-
- if( Math.abs( denominator ) < 0.00001 )
- {
- denominator = 0.00001;
- }
-
- b[ k ] *= 1.0 / denominator;
- }
- }
- }
-
- return b;
- }
-
- /**
- * Calculates the value of delta that represents the maximum ripple for the current set of
- * extremal indices (L+2)
- *
- * Implements Oppenheim/Schafer Discrete Time Signal Processing, 3e, 2016, equation 114
- *
- * @param b calculated for the grid from equation 115
- */
- private void calculateDelta( double[] b )
- {
- double numerator = 0.0;
- double denominator = 0.0;
-
- double sign = 1.0;
-
- for( int k = 0; k < b.length; k++ )
- {
- if( k < mExtremalIndices.size() )
- {
- int extremalIndex = mExtremalIndices.get( k );
-
- numerator += ( b[ k ] * mGrid.getDesiredResponse()[ extremalIndex ] );
- denominator += b[ k ] * sign / mGrid.getWeight()[ extremalIndex ];
- sign = -sign;
- }
- else
- {
- mLog.error( "Something went wrong -- the length of b exceeds the set of extremal indices" );
- }
- }
-
- mDelta = numerator / denominator;
- }
-
- /**
- * Calculates the ideal frequency response (C) of the current extremal index set. This reference
- * is used to determine the level of error in the set of actual frequency responses calculated
- * from this set of extremal indices to determine convergence.
- *
- * Implements Oppenheim/Schafer Discrete Time Signal Processing, 3e, 2016, equation 116b
- */
- private void calculateC()
- {
- int length = mSpecification.getExtremaCount() - 1;
-
- mIdealFrequencyResponse = new double[ length ];
-
- double sign = 1.0;
-
- for( int k = 0; k < length; k++ )
- {
- if( k < mExtremalIndices.size() )
- {
- int index = mExtremalIndices.get( k );
-
- mIdealFrequencyResponse[ k ] = mGrid.getDesiredResponse()[ index ] -
- ( sign * mDelta / mGrid.getWeight()[ index ] );
-
- sign = -sign;
- }
- }
- }
-
- /**
- * Calculates the set of D values for the current extremal index set.
- *
- * Implements Oppenheim/Schafer Discrete Time Signal Processing, 3e, 2016, equation 116c
- */
- private void calculateD( double[] b )
- {
- int length = mExtremalIndices.size() - 1;
-
- mD = new double[ length ];
-
- for( int k = 0; k < length; k++ )
- {
- //Note: extremalCosines array is one index longer than d array, so we use length as
- //a pointer to the final (L+2) extremalCosine index
- mD[k] = b[k] * ( mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( k ) ] -
- mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( length ) ] );
- }
- }
-
- /**
- * Calculates the weighted error between the specification desired frequency response and the
- * actual frequency response of the current extremal index set across the frequencies in the
- * dense frequency grid.
- *
- * Implements Oppenheim/Schafer Discrete Time Signal Processing, 3e, 2016, equation 112
- */
- public void calculateGridError()
- {
- int length = mGridFrequencyResponse.length;
-
- mGridErrors = new double[ length ];
-
- for( int i = 0; i < length; i++ )
- {
- mGridErrors[ i ] = mGrid.getWeight()[ i ] *
- ( mGrid.getDesiredResponse()[ i ] - mGridFrequencyResponse[ i ] );
- }
- }
-
- /**
- * Identifies the indexes of the extremal error values using the Alternation theorem
- *
- * @throws FilterDesignException if the full set of extremal indices can't be identified
- */
- private void findExtremalIndices() throws FilterDesignException
- {
- mExtremalIndices.clear();
-
- //Check for extremum at error index 0
- if( ( ( mGridErrors[ 0 ] > 0.0 && mGridErrors[ 0 ] > mGridErrors[ 1 ] ) ||
- ( mGridErrors[ 0 ] < 0.0 && mGridErrors[ 0 ] < mGridErrors[ 1 ] ) ) &&
- isGTEDelta( mGridErrors[ 0 ] ) )
- {
- mExtremalIndices.add( 0 );
- }
-
- //Check for extrema in middle error indices )
- for( int x = 1; x < mGridErrors.length - 1; x++ )
- {
- if( ( ( mGridErrors[ x ] > 0.0 && ( mGridErrors[ x - 1 ] <= mGridErrors[ x ] && mGridErrors[ x ] > mGridErrors[ x + 1 ] ) ) ||
- ( mGridErrors[ x ] < 0.0 && ( mGridErrors[ x - 1 ] >= mGridErrors[ x ] && mGridErrors[ x ] < mGridErrors[ x + 1 ] ) ) ) &&
- isGTEDelta( mGridErrors[ x ] ) )
- {
- mExtremalIndices.add( x );
- }
- }
-
- //Check for extremum at final error index
- int last = mGridErrors.length - 1;
-
- if( ( ( mGridErrors[ last ] > 0.0 && ( mGridErrors[ last ] > mGridErrors[ last - 1 ] ) ) ||
- ( mGridErrors[ last ] < 0.0 && ( mGridErrors[ last ] < mGridErrors[ last - 1 ] ) ) ) &&
- isGTEDelta( mGridErrors[ last ] ) )
- {
- mExtremalIndices.add( last );
- }
-
- //Ensure we have the minimum of extremals before we continue
- if( mExtremalIndices.size() < mSpecification.getExtremaCount() )
- {
- throw new FilterDesignException( "Couldn't find the minimum extremal frequencies in "
- + "error set" );
- }
-
- //Enforce alternation theory -- only one extremal for each transition about the zero axis
- List indicesToRemove = new ArrayList<>();
-
- Iterator it = mExtremalIndices.iterator();
- Integer current = it.next();
- Integer next;
-
- boolean positiveAxis = mGridErrors[ current ] > 0.0;
-
- while( it.hasNext() )
- {
- next = it.next();
-
- //Check for consecutive (redundant) indices on same side of zero axis - retain largest
- if( !( positiveAxis ^ ( mGridErrors[ next ] > 0.0 ) ) )
- {
- if( Math.abs( mGridErrors[ next ] ) <= Math.abs( mGridErrors[ current ] ))
- {
- //Remove next error index that is less than current index and on
- it.remove();
- next = current;
- }
- else
- {
- //Since we can't remove the current index with the iterator, queue for later removal
- indicesToRemove.add( current );
- }
- }
- else
- {
- positiveAxis = !positiveAxis;
- }
-
- current = next;
- }
-
- //Remove redundant extremal indices that couldn't be removed via the iterator
- mExtremalIndices.removeAll( indicesToRemove );
-
- //Truncate the list to one larger than needed by removing excess tailing indices
- while( mExtremalIndices.size() > mSpecification.getExtremaCount() )
- {
- mExtremalIndices.remove( mExtremalIndices.size() - 1 );
- }
-
- //If we have one too many indices, delete the smaller of the first or last
- if( mExtremalIndices.size() > mSpecification.getExtremaCount() )
- {
- int lastIndex = mExtremalIndices.size() - 1;
-
- if( Math.abs( mGridErrors[ mExtremalIndices.get( 0 )] ) >
- Math.abs( mGridErrors[ mExtremalIndices.get( lastIndex )] ) )
- {
- mExtremalIndices.remove( lastIndex );
- }
- else
- {
- mExtremalIndices.remove( 0 );
- }
- }
-
- //Detect if we have too few indices
- if( mExtremalIndices.size() < mSpecification.getExtremaCount() )
- {
- throw new FilterDesignException( "Couldn't find the minimum extremal frequencies in "
- + "error set" );
- }
- }
-
- /**
- * Indicates if the absolute value of the argument is greater than or equal to the delta
- * value with accuracy to 14 digits of precision. This avoids rounding errors at 15 digits or
- * precision or greater.
- *
- * @param value to evaluate
- *
- * @return true if the absolute value is greater than or equal to the absolute delta value
- */
- private boolean isGTEDelta( double value )
- {
- return Math.abs( value ) - Math.abs( mDelta ) > -1.0e-5;
- }
-
- /**
- * Checks for convergence of the frequency response of the current set of extremal indices to
- * the filter specification by comparing the maximum absolute error value against the delta
- * value to determine if these two values are within the convergence threshold.
- */
- public void checkConvergence()
- {
- double maximum = Math.abs( mGridErrors[ mExtremalIndices.get( 0 ) ] );
-
- for( int i = 1; i < mExtremalIndices.size(); i++ )
- {
- double current = Math.abs( mGridErrors[ mExtremalIndices.get( i ) ] );
-
- if( current > maximum )
- {
- maximum = current;
- }
- }
-
- double convergence = maximum - Math.abs( mDelta );
-
- mConverged = convergence < CONVERGENCE_THRESHOLD;
- }
-
- /**
- * Coverts/casts the double array to a float array
- */
- private static float[] convertToFloatArray( double[] samples )
- {
- float[] converted = new float[ samples.length ];
-
- for( int x = 0; x < samples.length; x++ )
- {
- converted[ x ] = (float)samples[ x ];
- }
-
- return converted;
- }
-
- /**
- * Resamples the designed filter at half of the filter length at evenly spaced intervals of the
- * frequency spectrum from 0 to PI radians.
- *
- * @return frequency response of the filter sampled the desired filter length and order
- */
- private double[] resample()
- {
- int length = mSpecification.getFilterLength();
-
- if( length % 2 == 0 )
- {
- length--;
- }
-
- double half = (double)length / 2.0;
-
- double[] resampled = new double[ (int)Math.ceil( half ) ] ;
-
- for( int x = 0; x < resampled.length; x++ )
- {
- resampled[ x ] = getFrequencyResponse( Math.cos( Math.PI * (double)x / half ) );
- }
-
- return resampled;
- }
-
- /**
- * Applies Parks/McClelland filter type correction to the final frequency response array
- *
- * @param frequencyResponse of the resampled filter design from 0 <> ~ PI radians
- *
- * @return corrected frequency response
- */
- private double[] correctFrequencyResponse( double[] frequencyResponse )
- {
- double filterLength = mSpecification.getFilterLength();
-
- switch( mSpecification.getFilterType() )
- {
- case TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL:
- //No correction needed
- break;
- case TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL:
- //No correction needed
- break;
- case TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL:
- for( int x = 0; x < frequencyResponse.length; x++ )
- {
- double frequencyRadians = Math.PI * ( ( (double)x ) / filterLength );
- frequencyResponse[ x ] *= Math.sin( TWO_PI * frequencyRadians );
- }
- break;
- case TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL:
- for( int x = 0; x < frequencyResponse.length; x++ )
- {
- double frequencyRadians = Math.PI * ( ( (double)x ) / filterLength );
- frequencyResponse[ x ] *= Math.sin( Math.PI * frequencyRadians );
- }
- break;
- default:
- break;
- }
-
- return frequencyResponse;
- }
-}
diff --git a/src/dsp/gain/AutomaticGainControl.java b/src/dsp/gain/AutomaticGainControl.java
deleted file mode 100644
index b46dd105..00000000
--- a/src/dsp/gain/AutomaticGainControl.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package dsp.gain;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import buffer.DoubleCircularBuffer;
-import buffer.FloatCircularBuffer;
-
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- * Copyright (C) 2011 Alex Csete
- * Copyright (C) 2010 Moe Wheatley
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- * ---------------------------------------------------
- * Ported from Alex Csete's gqrx source code at:
- * https://github.com/csete/gqrx/blob/74ba2742c89b0dfb66854fc9f72d19e5c6e355b6/dsp/agc_impl.cpp
- *
- * This Software is released under the "Simplified BSD License" + + +
- * Copyright 2010 Moe Wheatley. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are
- * those of the authors and should not be interpreted as representing
- * official policies, either expressed or implied, of Moe Wheatley.
- ******************************************************************************/
-public class AutomaticGainControl
-{
- private static final double SAMPLE_RATE = 48000;
-
- /* Signal delay line - time delay in seconds */
-// private static final double DELAY_TIME_CONSTANT = 0.015;
- private static final double DELAY_TIME_CONSTANT = 0.01;
-
- /* Peak detector window - time delay in seconds */
- private static final double WINDOW_TIME_CONSTANT = 0.018;
-
- /* Attack time constants in seconds */
- private static final double ATTACK_RISE_TIME_CONSTANT = 0.002;
- private static final double ATTACK_FALL_TIME_CONSTANT = 0.005;
-
- private static final double ATTACK_RISE_ALPHA = 1.0 -
- Math.exp( -1.0 / SAMPLE_RATE * ATTACK_RISE_TIME_CONSTANT );
-
- private static final double ATTACK_FALL_ALPHA = 1.0 -
- Math.exp( -1.0 / SAMPLE_RATE * ATTACK_FALL_TIME_CONSTANT );
-
- /* AGC decay value in milliseconds (20 to 5000) */
- private static final double DECAY = 200;
-
- /* Ratio between rise and fall times of decay time constants - adjust for
- * best action with SSB */
- private static final double DECAY_RISEFALL_RATIO = 0.3;
-
- private static final double DECAY_RISE_ALPHA = 1.0 -
- Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 * DECAY_RISEFALL_RATIO ) );
-
- private static final double DECAY_FALL_ALPHA = 1.0 -
- Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 ) );
-
- /* Hang timer release decay time constant in seconds */
- @SuppressWarnings( "unused" )
- private static final double RELEASE_TIME_CONSTANT = 0.05;
-
- /* Specifies the AGC Knee in dB if AGC is active (nominal range -160 to 0 dB) */
- private static final double THRESHOLD = -100;
-
- /* Limit output to about 3db of maximum */
- private static final double AGC_OUT_SCALE = 0.7;
-
- /* Keep max input and output the same */
- private static final double MAX_AMPLITUDE = 32767.0; //1.0;
- private static final double MAX_MANUAL_AMPLITUDE = 32767.0; //1.0;
-
- /* Specifies AGC manual gain in dB if AGC is not active ( 0 to 100 dB) */
- private static final double MANUAL_GAIN = 0.0;
-
- private static final double MANUAL_AGC_GAIN = MAX_MANUAL_AMPLITUDE *
- Math.pow( 10.0, MANUAL_GAIN / 20.0 );
-
- /* Specifies dB reduction in output at knee from max output level (0 - 10dB) */
- private static final double SLOPE_FACTOR = 2.0;
-
- private static final double KNEE = THRESHOLD / 20.0;
-
- private static final double GAIN_SLOPE = SLOPE_FACTOR / 100.0;
-
- private static final double FIXED_GAIN = AGC_OUT_SCALE *
- Math.pow( 10.0, KNEE * ( GAIN_SLOPE - 1.0 ) );
-
- /* Constant for calc log() so that a value of 0 magnitude = -8 */
- private static final double MIN_CONSTANT = 3.2767E-4;
-
- private AtomicBoolean mAGCEnabled = new AtomicBoolean( true );
-
- private double mPeakMagnitude = 0.0;
- private double mAttackAverage = 0.0;
- private double mDecayAverage = 0.0;
-
- private FloatCircularBuffer mDelayBuffer =
- new FloatCircularBuffer( (int)( SAMPLE_RATE * DELAY_TIME_CONSTANT ) );
- private DoubleCircularBuffer mMagnitudeBuffer =
- new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) );
-
- public AutomaticGainControl()
- {
- }
-
- public float process( float currentSample )
- {
- float delayedSample = mDelayBuffer.putAndGet( currentSample );
-
- double gain = MANUAL_AGC_GAIN;
-
- if( mAGCEnabled.get() )
- {
- double currentMagnitude = Math.log10( Math.abs( currentSample ) +
- MIN_CONSTANT ) - Math.log10( MAX_AMPLITUDE );
-
- double delayedMagnitude = mMagnitudeBuffer.get( currentMagnitude );
-
- if( currentMagnitude > mPeakMagnitude )
- {
- /* Use current magnitude as peak if it's larger */
- mPeakMagnitude = currentMagnitude;
- }
- else if( delayedMagnitude == mPeakMagnitude )
- {
- /* If delayed magnitude is the current peak, then find a new peak */
- mPeakMagnitude = mMagnitudeBuffer.max();
- }
-
- /* Exponential decay mode */
- if( mPeakMagnitude > mAttackAverage )
- {
- mAttackAverage = ( ( 1.0 - ATTACK_RISE_ALPHA ) * mAttackAverage ) +
- ( ATTACK_RISE_ALPHA * mPeakMagnitude );
- }
- else
- {
- mAttackAverage = ( ( 1.0 - ATTACK_FALL_ALPHA ) * mAttackAverage ) +
- ( ATTACK_FALL_ALPHA * mPeakMagnitude );
- }
-
- if( mPeakMagnitude > mDecayAverage )
- {
- mDecayAverage = ( ( 1.0 - DECAY_RISE_ALPHA ) * mDecayAverage ) +
- ( DECAY_RISE_ALPHA * mPeakMagnitude );
- }
- else
- {
- mDecayAverage = ( ( 1.0 - DECAY_FALL_ALPHA ) * mDecayAverage ) +
- ( DECAY_RISE_ALPHA * mPeakMagnitude );
- }
-
- double magnitude = ( mAttackAverage > mDecayAverage ) ?
- mAttackAverage : mDecayAverage;
-
- if( magnitude < KNEE )
- {
- gain = FIXED_GAIN;
- }
- else
- {
- gain = AGC_OUT_SCALE * Math.pow( 10.0,
- magnitude * ( GAIN_SLOPE - 1.0 ) );
- }
-
- }
-
- return (float)( delayedSample * gain );
- }
-
- /**
- * Enables or disables Automatic Gain Control (AGC).
- */
- public void setAGCEnabled( boolean enabled )
- {
- mAGCEnabled.set( enabled );
- }
-
- /**
- * Indicates if AGC is enabled
- * @return true=AGC, false=MANUAL GAIN
- */
- public boolean isAGCEnabled()
- {
- return mAGCEnabled.get();
- }
-}
diff --git a/src/dsp/gain/ComplexFeedForwardGainControl.java b/src/dsp/gain/ComplexFeedForwardGainControl.java
deleted file mode 100644
index 20449301..00000000
--- a/src/dsp/gain/ComplexFeedForwardGainControl.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package dsp.gain;
-
-import sample.complex.Complex;
-import sample.complex.ComplexSampleListener;
-import buffer.FloatCircularBuffer;
-
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2015 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- * This class is modeled using concepts detailed in the GNURadio
- * implementation of feed forward AGC class located at:
- * https://github.com/gnuradio/gnuradio/blob/master/gr-analog/lib/
- * feedforward_agc_cc_impl.cc
- *
- ******************************************************************************/
-public class ComplexFeedForwardGainControl implements ComplexSampleListener
-{
- public static final float OBJECTIVE_ENVELOPE = 1.0f;
- public static final float MINIMUM_ENVELOPE = 0.0001f;
-
- private ComplexSampleListener mListener;
-
- private FloatCircularBuffer mEnvelopeHistory;
-
- private float mMaxEnvelope = 0.0f;
- private float mGain = 1.0f;
-
- /**
- * Dynamic gain control for incoming sample stream to amplify or attenuate
- * all samples toward an objective unity)gain, using the maximum envelope
- * value detected in the stream history window.
- *
- * Uses the specified damping factor to limit gain swings. Damping factor
- * is applied against the delta between current gain value and a recalculated
- * gain value to limit how quickly the gain value will increase or decrease.
- *
- * @param window - history size to use in detecting maximum envelope value
- * @param damping - a value between 0 < damping <= 1.0;
- */
- public ComplexFeedForwardGainControl( int window )
- {
- mEnvelopeHistory = new FloatCircularBuffer( window );
- }
-
- public void dispose()
- {
- mListener = null;
- }
-
- @Override
- public void receive( float inphase, float quadrature )
- {
- float envelope = Complex.envelope( inphase, quadrature );
-
- if( envelope > mMaxEnvelope )
- {
- mMaxEnvelope = envelope;
-
- adjustGain();
- }
-
- /* Replace oldest envelope value with current envelope value */
- float oldestEnvelope = mEnvelopeHistory.putAndGet( envelope );
-
- /* If the oldest envelope value was the max envelope value, then we
- * have to rediscover the max value from the envelope history */
- if( mMaxEnvelope == oldestEnvelope && mMaxEnvelope != envelope )
- {
- mMaxEnvelope = MINIMUM_ENVELOPE;
-
- for( float value: mEnvelopeHistory.getBuffer() )
- {
- if( value > mMaxEnvelope )
- {
- mMaxEnvelope = value;
- }
- }
-
- adjustGain();
- }
-
- /* Apply current gain value to the sample and send to the listener */
- if( mListener != null )
- {
- mListener.receive( inphase *= mGain, quadrature *= mGain );
- }
- }
-
- private void adjustGain()
- {
- mGain = OBJECTIVE_ENVELOPE / mMaxEnvelope;
- }
-
- public void setListener( ComplexSampleListener listener )
- {
- mListener = listener;
- }
-}
diff --git a/src/dsp/mixer/Oscillator.java b/src/dsp/mixer/Oscillator.java
deleted file mode 100644
index b711c4ab..00000000
--- a/src/dsp/mixer/Oscillator.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package dsp.mixer;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sample.complex.Complex;
-import sample.complex.ComplexBuffer;
-
-public class Oscillator
-{
- private final static Logger mLog = LoggerFactory.getLogger(Oscillator.class);
-
- private boolean mEnabled = true;
- private double mFrequency;
- private double mSampleRate;
-
- private Complex mAnglePerSample;
- private Complex mCurrentAngle = new Complex(0.0f, -1.0f);
-
- /**
- * Oscillator produces complex or float samples corresponding to a sine wave
- * oscillating at the specified frequency and sample rate
- *
- * @param frequency - positive or negative frequency in hertz
- * @param sampleRate - in hertz
- */
- public Oscillator(long frequency, double sampleRate)
- {
- mEnabled = frequency != 0;
-
- mSampleRate = sampleRate;
- mFrequency = (double)frequency;
-
- update();
- }
-
- /**
- * Indicates if this oscillator has a non-zero frequency setting. When used as a frequency translating
- * mixer, setting a frequency of zero indicates that no frequency correction is needed, so this flag can
- * indicate when to apply the oscillator to frequency correct samples.
- *
- * @return true if the current frequency value is non-zero.
- */
- public boolean enabled()
- {
- return mEnabled;
- }
-
- /**
- * Updates the internal values after a frequency or sample rate change
- */
- private void update()
- {
- float anglePerSample =
- (float)(2.0d * Math.PI * mFrequency / mSampleRate);
-
- mAnglePerSample = Complex.fromAngle(anglePerSample);
- }
-
- /**
- * Sets or changes the frequency of this oscillator
- */
- public void setFrequency(long frequency)
- {
- mEnabled = frequency != 0;
- mFrequency = (double)frequency;
- update();
- }
-
- /**
- * Sets or changes the sample rate of this oscillator
- */
- public void setSampleRate(double sampleRate)
- {
- mSampleRate = sampleRate;
- update();
- }
-
- /**
- * Steps the current angle by the angle per sample amount
- */
- public void rotate()
- {
- mCurrentAngle.multiply(mAnglePerSample);
- mCurrentAngle.fastNormalize();
- }
-
- public float inphase()
- {
- return mCurrentAngle.inphase();
- }
-
- public float quadrature()
- {
- return mCurrentAngle.quadrature();
- }
-
- /**
- * Get next complex sample
- */
- public Complex getComplex()
- {
- return mCurrentAngle.copy();
- }
-
- /**
- * Get the next float sample
- */
- public float getFloat()
- {
- return mCurrentAngle.real();
- }
-
- /**
- * Generates the requested number of complex samples and returns them in a complex buffer
- *
- * @param sampleCount to generate
- * @return complex buffer containing the requested complex samples
- */
- public ComplexBuffer generateComplexBuffer(int sampleCount)
- {
- float[] buffer = new float[sampleCount * 2];
-
- for(int x = 0; x < sampleCount; x++)
- {
- int index = 2 * x;
- buffer[index] = inphase();
- buffer[index + 1] = quadrature();
-
- rotate();
- }
-
- return new ComplexBuffer(buffer);
- }
-}
diff --git a/src/dsp/psk/LSMDemodulator.java b/src/dsp/psk/LSMDemodulator.java
deleted file mode 100644
index 11c5de0d..00000000
--- a/src/dsp/psk/LSMDemodulator.java
+++ /dev/null
@@ -1,589 +0,0 @@
-package dsp.psk;
-
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014, 2015 Dennis Sheirer
- *
- * Gardner detector, Costas loop and Interpolator derived from:
- *
- * OP25 - gardner_costas_cc_impl.cc
- * Copyright 2010, 2011, 2012, 2013 KA1RBI (gardner_costas_cc_impl.cc)
- *
- * OP25 - scope.py
- * Copyright 2008-2011 Steve Glass
- * Copyright 2011, 2012, 2013 KA1RBI
- *
- * GNURadio - control_loop and mpsk_receiver_cc
- * Copyright 2011,2013 Free Software Foundation, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-
-import instrument.Instrumentable;
-import instrument.tap.Tap;
-import instrument.tap.TapGroup;
-import instrument.tap.stream.EyeDiagramData;
-import instrument.tap.stream.EyeDiagramDataTap;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import sample.Listener;
-import sample.complex.Complex;
-import sample.complex.ComplexSampleListener;
-import source.ISourceEventListener;
-import buffer.FloatAveragingBuffer;
-import dsp.filter.interpolator.RealInterpolator;
-import dsp.symbol.Dibit;
-
-/**
- * Implements a LSM (Pi/4) demodulator using a Gardner Detector to determine
- * optimal sampling timing and a Costas Loop as a phase locked loop synchronized
- * with the incoming signal carrier frequency.
- *
- * Sample Rate: 48000
- * Symbol Rate: 4800
- */
-public class LSMDemodulator implements Instrumentable, ComplexSampleListener
-{
- private List mAvailableTaps;
-
- /* 45 degree rotation angle */
- public static final float THETA = (float)( Math.PI / 4.0d );
-
- /* 45 degree point */
- public static final Complex POINT_45_DEGREES =
- new Complex( (float)Math.sin( THETA ), (float)Math.cos( THETA ) );
-
- private Listener mSymbolListener;
-
- private GardnerSymbolTiming mGardnerDetector = new GardnerSymbolTiming();
-
- private CostasLoop mCostasLoop = new CostasLoop();
-
- private ISourceEventListener mFrequencyChangeListener;
-
- private EyeDiagramDataTap mEyeDiagramDataTap;
-
- public LSMDemodulator()
- {
- }
-
- public void dispose()
- {
- mSymbolListener = null;
- mFrequencyChangeListener = null;
- }
-
- public void setSymbolListener( Listener listener )
- {
- mSymbolListener = listener;
- }
-
- public void removeSymbolListener()
- {
- mSymbolListener = null;
- }
-
- @Override
- public void receive( float inphase, float quadrature )
- {
- mGardnerDetector.receive( inphase, quadrature );
- }
-
- /**
- * Applies a phase correction value to the costas loop to correct when a
- * phase lock error is detected in the binary output stream.
- *
- * @param correction - value in radians
- */
- public void correctPhaseError( double correction )
- {
- mCostasLoop.correctPhaseError( correction );
- }
-
- /**
- * Constrains value to the range of ( -maximum <> maximum )
- */
- public static float clip( float value, float maximum )
- {
- if( value > maximum )
- {
- return maximum;
- }
- else if( value < -maximum )
- {
- return -maximum;
- }
-
- return value;
- }
-
- /**
- * Constrains timing error to +/- the maximum value and corrects any
- * floating point invalid numbers
- */
- private float normalize( float error, float maximum )
- {
- if( Float.isNaN( error ) )
- {
- return 0.0f;
- }
- else
- {
- return clip( error, maximum );
- }
- }
-
- public void addListener( ISourceEventListener listener )
- {
- mFrequencyChangeListener = listener;
- }
-
- public void removeListener( ISourceEventListener listener )
- {
- mFrequencyChangeListener = null;
- }
-
- /**
- * Processes the current loop frequency of the costas loop and broadcasts
- * tuner frequency offset adjustments 10 times a second as needed to keep
- * the frequency offset below half of the maximum frequency
- */
- public class FrequencyControl
- {
- /* Set the trigger threshold at half of the maximum costas loop control
- * frequency */
- private static final float CORRECTION_THRESHOLD_FREQUENCY =
- ( 2.0f * (float)Math.PI * 600.0f ) / 48000.0f;
-
- private FloatAveragingBuffer mBuffer = new FloatAveragingBuffer( 40 );
-
- private long mFrequencyError = 0;
-
- private int mCounter = 0;
-
- public void receive( float frequency )
- {
-// mCounter++;
-//
-// if( mCounter >= 480 )
-// {
-// float average = mBuffer.get( frequency );
-// mLog.debug( "Avg:" + average + " Threshold:" + CORRECTION_THRESHOLD_FREQUENCY);
-//
-// boolean correctionNeeded = false;
-//
-// if( average > CORRECTION_THRESHOLD_FREQUENCY )
-// {
-// mFrequencyError--;
-//
-// correctionNeeded = true;
-// }
-// else if( average < -CORRECTION_THRESHOLD_FREQUENCY )
-// {
-// mFrequencyError++;
-//
-// correctionNeeded = true;
-// }
-//
-// if( correctionNeeded && mFrequencyChangeListener != null )
-// {
-// mLog.debug( "Issuing Frequency Correction: " + mFrequencyError );
-// mFrequencyChangeListener.process(
-// new SourceEvent( Attribute.FREQUENCY_ERROR,
-// mFrequencyError ) );
-// }
-//
-// mCounter = 0;
-// }
-// else
-// {
-// mBuffer.get( frequency );
-// }
- }
- }
-
- /**
- * Gardner symbol timing detector
- */
- public class GardnerSymbolTiming implements ComplexSampleListener
- {
- public static final int HALF_SAMPLES_PER_SYMBOL = 5;
- public static final int SAMPLES_PER_SYMBOL = 10;
- public static final int TWICE_SAMPLES_PER_SYMBOL = 20;
-
- private float[] mDelayLineInphase = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ];
- private float[] mDelayLineQuadrature = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ];
-
- private int mDelayLinePointer = 0;
-
- /* Sampling point */
- private float mMu = 10.0f;
- private float mGainMu = 0.05f;
-
- /* Samples per symbol */
- private float mOmega = 10.0f;
- private float mGainOmega = 0.1f * mGainMu * mGainMu;
- private float mOmegaRel = 0.005f;
- private float mOmegaMid = 10.0f;
-
- private RealInterpolator mInterpolator = new RealInterpolator( 1.0f );
-
- private Complex mPreviousSample = new Complex( 0, 0 );
- private Complex mPreviousMiddleSample = new Complex( 0, 0 );
- private Complex mPreviousSymbol = new Complex( 0, 0 );
-
- /**
- * Provides symbol sampling timing control
- */
- public GardnerSymbolTiming()
- {
- }
-
- @Override
- public void receive( float inphase, float quadrature )
- {
- /* Count down samples per symbol until we calculate the symbol */
- mMu--;
-
- mCostasLoop.increment();
-
- /* Mix incoming sample with costas loop to remove any rotation
- * that is present from a mis-tuned carrier frequency */
-
- Complex costas = mCostasLoop.getCurrentVector();
-
- float derotatedInphase = Complex.multiplyInphase( inphase, quadrature, costas.inphase(), costas.quadrature() );
- float derotatedQuadrature = Complex.multiplyQuadrature( inphase, quadrature, costas.inphase(), costas.quadrature() );
-
- /* Fill up the delay line to use with the interpolator */
- mDelayLineInphase[ mDelayLinePointer ] = derotatedInphase;
- mDelayLineInphase[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedInphase;
-
- mDelayLineQuadrature[ mDelayLinePointer ] = derotatedQuadrature;
- mDelayLineQuadrature[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedQuadrature;
-
- /* Increment pointer and keep pointer in bounds */
- mDelayLinePointer = ( mDelayLinePointer + 1 ) % TWICE_SAMPLES_PER_SYMBOL;
-
- /* Calculate the symbol once we've stored enough samples */
- if( mMu <= 1.0f )
- {
- float half_omega = mOmega / 2.0f;
- int half_sps = (int)Math.floor( half_omega );
- float half_mu = mMu + half_omega - (float)half_sps;
-
- if( half_mu > 1.0 )
- {
- half_mu -= 1.0;
- half_sps += 1;
- }
-
- /* Calculate interpolated middle sample and current sample */
- float middleSampleInphase = mInterpolator.filter(
- mDelayLineInphase, mDelayLinePointer, mMu );
- float middleSampleQuadrature = mInterpolator.filter(
- mDelayLineQuadrature, mDelayLinePointer, mMu );
-
- int index = mDelayLinePointer + half_sps;
-
- float currentSampleInphase = mInterpolator.filter(
- mDelayLineInphase, index, half_mu );
- float currentSampleQuadrature = mInterpolator.filter(
- mDelayLineQuadrature, index, half_mu );
-
- /* Multiply current sample and conjugate (negated quadrature) of
- * previous sample to get symbols to use for gardner error feedback */
- Complex middleSymbol = Complex.multiply( middleSampleInphase,
- middleSampleQuadrature, mPreviousMiddleSample.conjugate() );
- Complex currentSymbol = Complex.multiply( currentSampleInphase,
- currentSampleQuadrature, mPreviousSample.conjugate() );
-
- /* Set gain to unity */
- middleSymbol.normalize();
- currentSymbol.normalize();
-
- /* Gardner timing error calculations */
- float errorInphase = ( mPreviousSymbol.inphase() -
- currentSymbol.inphase() ) * middleSymbol.inphase();
-
- float errorQuadrature = ( mPreviousSymbol.quadrature() -
- currentSymbol.quadrature() ) * middleSymbol.quadrature();
-
- float gardnerError = normalize( errorInphase + errorQuadrature, 1.0f );
-
- if( mEyeDiagramDataTap != null )
- {
- mEyeDiagramDataTap.receive(
- new EyeDiagramData(
- Arrays.copyOfRange( mDelayLineInphase, 0, 20 ),
- Arrays.copyOfRange( mDelayLineQuadrature, 0, 20 ),
- mMu, (float)half_sps + half_mu, gardnerError ) );
- }
-
- /* mOmega is samples per symbol and is constrained to floating
- * between +/- .005 of the nominal 10.0 samples per symbol */
- mOmega = mOmega + mGainOmega * gardnerError;
- mOmega = mOmegaMid + clip( mOmega - mOmegaMid, mOmegaRel );
-
- /* Adjust sample timing based on error of current sample */
- mMu += mOmega + ( mGainMu * gardnerError );
-
- /* Store current samples/symbols to use for the next period */
- mPreviousSample.setValues( currentSampleInphase, currentSampleQuadrature );
- mPreviousMiddleSample.setValues( middleSampleInphase, middleSampleQuadrature );
- mPreviousSymbol = currentSymbol;
-
- /* Update costas loop using phase error present in current
- * symbol. The symbol is rotated from star orientation to polar
- * orientation to simplify error calculation */
- mCostasLoop.receive( currentSymbol );
-// mCostasLoop.receive(
-// ComplexSample.multiply( currentSymbol, POINT_45_DEGREES ) );
-
- /* Dispatch the differentiated symbol to the registered listener */
- if( mSymbolListener != null )
- {
- mSymbolListener.receive( currentSymbol );
- }
- }
- }
- }
-
- /**
- * Costas Loop - phase locked loop synchronized to the frequency offset of
- * the incoming signal to enable the signal to be mixed down to zero offset
- * for proper synchronization. The mFrequency value indicates the detected
- * frequency offset. We attempt to keep that value close to zero by issuing
- * frequency adjustments to the tuner channel source.
- *
- * Most of the costas loop code was ported from gnuradio/control_loop and
- * gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's
- * OP25/cqpsk.py
- */
- public class CostasLoop implements Listener
- {
- public static final double TWO_PI = 2.0 * Math.PI;
-
- private static final float MAXIMUM_FREQUENCY =
- ( 2400.0f * (float)TWO_PI ) / 48000.0f;
-
- /* http://www.trondeau.com/blog/2011/8/13/control-loop-gain-values.html */
- private float mDamping = (float)Math.sqrt( 2.0 ) / 2.0f;
-
- /* Use denominator between 100 (most) and 400 (least) to adjust costas
- * loop control level */
- private float mLoopBandwidth = (float)( TWO_PI / 400.0d );
-
- private float mAlphaGain = ( 4.0f * mDamping * mLoopBandwidth ) /
- ( 1.0f + ( 2.0f * mDamping * mLoopBandwidth ) +
- ( mLoopBandwidth * mLoopBandwidth ) );
-
- private float mBetaGain = ( 4.0f * mLoopBandwidth * mLoopBandwidth ) /
- ( 1.0f + ( 2.0f * mDamping * mLoopBandwidth ) +
- ( mLoopBandwidth * mLoopBandwidth ) );
-
- private float mLoopPhase = 0.0f;
-
- private float mLoopFrequency = 0.0f;
-
- private FrequencyControl mFrequencyControl = new FrequencyControl();
-
- public CostasLoop()
- {
- }
-
- /**
- * Applies a phase correction value to the current phasor. Use this
- * method to apply correction when a +/- 90, or 180 degree phase error
- * is detected in the binary output stream.
- *
- * If the supplied correction value places the loop frequency outside
- * of the max frequency, then the frequency will be corrected 360
- * degrees in the opposite direction to maintain within the max
- * frequency bounds.
- *
- * @param correction - correction value in radians
- */
- public void correctPhaseError( double correction )
- {
- mLoopFrequency += correction;
-
- if( mLoopFrequency > MAXIMUM_FREQUENCY )
- {
- mLoopFrequency -= 2.0d * MAXIMUM_FREQUENCY;
- }
-
- if( mLoopFrequency < -MAXIMUM_FREQUENCY )
- {
- mLoopFrequency += 2.0d * MAXIMUM_FREQUENCY;
- }
- }
-
- /**
- * Increments the phase of the loop
- */
- public void increment()
- {
- mLoopPhase += mLoopFrequency;
-
- /* Keep the loop phase in bounds */
- unwrapPhase();
- }
-
- private void unwrapPhase()
- {
- while( mLoopPhase > TWO_PI )
- {
- mLoopPhase -= TWO_PI;
- }
-
- while( mLoopPhase < -TWO_PI )
- {
- mLoopPhase += TWO_PI;
- }
- }
-
- /**
- * Current vector of the loop rotated by THETA degrees.
- */
- public Complex getCurrentVector()
- {
-// return ComplexSample.fromAngle( mLoopPhase + THETA );
- return Complex.fromAngle( mLoopPhase );
- }
-
- @Override
- public void receive( Complex complex )
- {
- /* Calculate phase error */
- float phaseError = getPhaseError( complex );
-
- Dibit d = QPSKPolarSlicer.decide( complex );
-// mLog.debug( "Dibit: " + d.name() + " Error: " + phaseError );
-
- /* Adjust for phase error */
- adjust( phaseError );
- }
-
- /**
- * Updates the costas loop frequency and phase to adjust for the phase
- * error value
- *
- * @param phase_error - (-)= late and (+)= early
- */
- private void adjust( float phase_error )
- {
- mLoopFrequency += mBetaGain * phase_error;
- mLoopPhase += mLoopFrequency + mAlphaGain * phase_error;
-
- /* Maintain phase between +/- 2 * PI */
- unwrapPhase();
-
- /* Limit frequency to +/- maximum loop frequency */
- limitFrequency();
- }
-
- /**
- * Constrains the frequency within the bounds of +/- loop frequency
- */
- private void limitFrequency()
- {
- /* Check for and issue tuner offset correction */
- mFrequencyControl.receive( mLoopFrequency );
-
- if( mLoopFrequency > MAXIMUM_FREQUENCY )
- {
- mLoopFrequency = MAXIMUM_FREQUENCY;
- }
-
- if( mLoopFrequency < -MAXIMUM_FREQUENCY )
- {
- mLoopFrequency = -MAXIMUM_FREQUENCY;
- }
- }
-
- /**
- * Detects rotational error present when the costas loop is not aligned
- * with the carrier frequency. Provides error feedback to adjust
- * mixer frequency.
- */
- public float getPhaseError( Complex complex )
- {
- float phase_error = 0;
-
- if( Math.abs( complex.inphase() ) > Math.abs( complex.quadrature() ) )
- {
- if( complex.inphase() > 0 )
- {
- phase_error = -complex.quadrature();
- }
- else
- {
- phase_error = complex.quadrature();
- }
- }
- else
- {
- if( complex.quadrature() > 0 )
- {
- phase_error = complex.inphase();
- }
- else
- {
- phase_error = -complex.inphase();
- }
- }
-
- return phase_error;
- }
- }
-
- @Override
- public List getTapGroups()
- {
- if( mAvailableTaps == null )
- {
- mAvailableTaps = new ArrayList<>();
-
- TapGroup group = new TapGroup( "LSM Demodulator" );
-
- group.add( new EyeDiagramDataTap( "Eye Diagram", 0, 4800 ) );
-
- mAvailableTaps.add( group );
- }
-
- return mAvailableTaps;
- }
-
- @Override
- public void registerTap( Tap tap )
- {
- if( tap instanceof EyeDiagramDataTap )
- {
- mEyeDiagramDataTap = (EyeDiagramDataTap)tap;
- }
- }
-
- @Override
- public void unregisterTap( Tap tap )
- {
- if( tap instanceof EyeDiagramDataTap )
- {
- mEyeDiagramDataTap = null;
- }
- }
-}
diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java
deleted file mode 100644
index aed75ab0..00000000
--- a/src/gui/control/JFrequencyControl.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package gui.control;
-
-import net.miginfocom.swing.MigLayout;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import source.ISourceEventProcessor;
-import source.InvalidFrequencyException;
-import source.SourceEvent;
-import source.SourceEvent.Event;
-import source.SourceException;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseWheelEvent;
-import java.awt.event.MouseWheelListener;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-public class JFrequencyControl extends JPanel implements ISourceEventProcessor
-{
- private static final long serialVersionUID = 1L;
- private final static Logger mLog = LoggerFactory.getLogger(JFrequencyControl.class);
-
- private List mProcessors = new ArrayList<>();
-
- private Color mHighlightColor = Color.YELLOW;
-
- private long mFrequency;
-
- private Cursor mBlankCursor;
-
- private HashMap mDigits = new HashMap();
-
- public JFrequencyControl()
- {
- init();
- }
-
- public JFrequencyControl(long value)
- {
- this();
- mFrequency = value;
- }
-
- private void init()
- {
- setLayout(new MigLayout("insets 0 0 0 0", "[]0[]", ""));
-
- Font font = new Font(Font.MONOSPACED, Font.BOLD, 30);
-
- for(int x = 9; x >= 0; x--)
- {
- Digit digit = null;
-
- try
- {
- digit = new Digit(x);
- }
- catch(ParseException e)
- {
- mLog.error("JFrequencyControl - parse exception "
- + "constructing a digit - " + e);
- }
-
- if(digit != null)
- {
- mDigits.put(x, digit);
-
- add(digit);
-
- digit.setFont(font);
-
- if(x == 6)
- {
- JLabel period = new JLabel(".");
-
- add(period);
- }
- }
- }
-
- add(new JLabel(" MHz"));
-
- /**
- * Create a blank cursor to use when the mouse is over the digits
- */
- //Create an empty byte array
- byte[] imageByte = new byte[0];
-
- //Create image for cursor using empty array
- Image cursorImage = Toolkit.getDefaultToolkit().createImage(imageByte);
-
- mBlankCursor = Toolkit.getDefaultToolkit()
- .createCustomCursor(cursorImage, new Point(0, 0), "cursor");
-
- revalidate();
- }
-
- @Override
- public void setEnabled(boolean enabled)
- {
- super.setEnabled(enabled);
-
- for(Digit digit : mDigits.values())
- {
- digit.setEnabled(enabled);
- }
- }
-
- /**
- * Receives a frequency change event invoked by another control. We don't
- * rebroadcast this event, just set the control to indicate the new frequency.
- */
- @Override
- public void process(SourceEvent event)
- {
- if(event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE)
- {
- setFrequency(event.getValue().longValue(), false);
- }
- }
-
- /**
- * Loads the frequency into the display and optionally fires a change event
- * to all registered listeners
- */
- public void setFrequency(long frequency, boolean fireChangeEvent)
- {
- mFrequency = frequency;
-
- for(Digit digit : mDigits.values())
- {
- digit.setFrequency(frequency, fireChangeEvent);
- }
- }
-
- public long getFrequency()
- {
- return mFrequency;
- }
-
- private void updateFrequency()
- {
- long frequency = 0;
-
- for(Digit digit : mDigits.values())
- {
- frequency += digit.getFrequency();
- }
-
- mFrequency = frequency;
- }
-
- private void fireSourceChanged() throws SourceException
- {
- updateFrequency();
-
- Iterator it = mProcessors.iterator();
-
- SourceEvent event = SourceEvent.frequencyRequest(mFrequency);
-
- while(it.hasNext())
- {
- it.next().process(event);
- }
- }
-
- public void addListener(ISourceEventProcessor processor)
- {
- mProcessors.add(processor);
- }
-
- public void removeListener(ISourceEventProcessor processor)
- {
- mProcessors.remove(processor);
- }
-
- public class Digit extends JTextField
- {
- private static final long serialVersionUID = 1L;
- private int mPower = 0;
- private long mValue = 0;
-
- private Digit(int position) throws ParseException
- {
- super("0");
-
- mPower = position;
-
- Listener listener = new Listener();
-
- this.addKeyListener(listener);
- this.addMouseListener(listener);
- this.addMouseWheelListener(listener);
- }
-
- /**
- * Sets this digit to the value of the column in frequency that corresponds
- * to the power (ie column) set for this digit. Optionally, fires a
- * value change event to all listeners.
- */
- public void setFrequency(long frequency, boolean fireChangeEvent)
- {
- //Strip the digits higher than this one
- long lower = frequency % (long)(Math.pow(10, mPower + 1));
-
- //Set the value to int value of dividing by 10 to this power
- long value = (long)(lower / (long)(Math.pow(10, mPower)));
-
- set(value, fireChangeEvent);
- }
-
- public long getFrequency()
- {
- return mValue * (long)Math.pow(10, mPower);
- }
-
- public void increment()
- {
- increment(true);
- }
-
- public void increment(boolean fireChangeEvent)
- {
- if(isEnabled())
- {
- set(mValue + 1, fireChangeEvent);
- }
- }
-
- public void decrement()
- {
- decrement(true);
- }
-
- public void decrement(boolean fireChangeEvent)
- {
- if(isEnabled())
- {
- set(mValue - 1, fireChangeEvent);
- }
- }
-
- /**
- * Convenience wrapper to change amount and fire change event
- */
- private void set(long amount)
- {
- set(amount, true);
- }
-
- /**
- * Changes the value and optionally fires change event to listeners
- */
- private void set(long amount, boolean fireChangeEvent)
- {
- long previous = mValue;
- boolean higherDigitIncremented = false;
- boolean higherDigitDecremented = false;
-
- mValue = amount;
-
-
- while(mValue < 0)
- {
- mValue += 10;
-
- Digit nextHigherDigit = mDigits.get(mPower + 1);
-
- if(nextHigherDigit != null)
- {
- nextHigherDigit.decrement(false);
- higherDigitDecremented = true;
- }
-
- }
-
- while(mValue > 9)
- {
- mValue -= 10;
-
- Digit nextHigherDigit = mDigits.get(mPower + 1);
-
- if(nextHigherDigit != null)
- {
- nextHigherDigit.increment(false);
- higherDigitIncremented = true;
- }
- }
-
- if(fireChangeEvent)
- {
- try
- {
- fireSourceChanged();
- }
- catch(SourceException se)
- {
- mValue = previous;
-
- if(higherDigitIncremented)
- {
- Digit nextHigherDigit = mDigits.get(mPower + 1);
-
- if(nextHigherDigit != null)
- {
- nextHigherDigit.decrement(false);
- }
- }
-
- if(higherDigitDecremented)
- {
- Digit nextHigherDigit = mDigits.get(mPower + 1);
-
- if(nextHigherDigit != null)
- {
- nextHigherDigit.increment(false);
- }
- }
-
- if(se instanceof InvalidFrequencyException)
- {
- InvalidFrequencyException ife = (InvalidFrequencyException)se;
-
- JOptionPane.showMessageDialog(this, "Frequency [" + ife.getInvalidFrequency() +
- "] exceeds the frequency limit [" + ife.getValidFrequency() + "] for this tuner.");
- }
- }
- }
-
-
- setText(String.valueOf(mValue));
-
- repaint();
- }
-
- private class Listener implements KeyListener,
- MouseListener,
- MouseWheelListener
- {
- @Override
- public void keyReleased(KeyEvent e)
- {
- int key = e.getKeyCode();
-
- switch(key)
- {
- case KeyEvent.VK_0:
- case KeyEvent.VK_NUMPAD0:
- set(0);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_1:
- case KeyEvent.VK_NUMPAD1:
- set(1);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_2:
- case KeyEvent.VK_NUMPAD2:
- set(2);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_3:
- case KeyEvent.VK_NUMPAD3:
- set(3);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_4:
- case KeyEvent.VK_NUMPAD4:
- set(4);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_5:
- case KeyEvent.VK_NUMPAD5:
- set(5);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_6:
- case KeyEvent.VK_NUMPAD6:
- set(6);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_7:
- case KeyEvent.VK_NUMPAD7:
- set(7);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_8:
- case KeyEvent.VK_NUMPAD8:
- set(8);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_9:
- case KeyEvent.VK_NUMPAD9:
- set(9);
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_LEFT:
- Digit.this.transferFocusBackward();
- break;
- case KeyEvent.VK_RIGHT:
- Digit.this.transferFocus();
- break;
- case KeyEvent.VK_UP:
- increment();
- break;
- case KeyEvent.VK_DOWN:
- decrement();
- break;
- case KeyEvent.VK_TAB:
- break;
- default:
- set(mValue);
- break;
- }
-
- repaint();
- }
-
- @Override
- public void keyPressed(KeyEvent e)
- {
- }
-
- @Override
- public void keyTyped(KeyEvent e)
- {
- }
-
- @Override
- public void mouseClicked(MouseEvent e)
- {
- switch(e.getButton())
- {
- case MouseEvent.BUTTON1:
- increment();
- break;
- case MouseEvent.BUTTON2:
- break;
- case MouseEvent.BUTTON3:
- decrement();
- break;
- }
- }
-
- public void mousePressed(MouseEvent e)
- {
- }
-
- public void mouseReleased(MouseEvent e)
- {
- }
-
- @Override
- public void mouseEntered(MouseEvent e)
- {
- Digit.this.setBackground(mHighlightColor);
-
- setCursor(mBlankCursor);
-
- repaint();
- }
-
- @Override
- public void mouseExited(MouseEvent e)
- {
- Digit.this.setBackground(Color.WHITE);
-
- setCursor(Cursor.getDefaultCursor());
-
- repaint();
- }
-
- @Override
- public void mouseWheelMoved(MouseWheelEvent e)
- {
- set(mValue - e.getWheelRotation());
- }
- }
- }
-}
diff --git a/src/instrument/tap/stream/BinaryTapViewPanel.java b/src/instrument/tap/stream/BinaryTapViewPanel.java
deleted file mode 100644
index 03ae5fdf..00000000
--- a/src/instrument/tap/stream/BinaryTapViewPanel.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package instrument.tap.stream;
-
-import instrument.gui.SampleModel;
-import instrument.tap.Tap;
-import instrument.tap.TapViewPanel;
-
-import java.awt.Color;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.geom.Path2D;
-import java.awt.geom.Rectangle2D;
-import java.util.List;
-import java.util.Observable;
-
-public class BinaryTapViewPanel extends TapViewPanel
-{
- private static final long serialVersionUID = 1L;
-
- private Tap mTap;
- private List mSamples;
- private int mSampleCount;
-
- public BinaryTapViewPanel( BinaryTap tap )
- {
- super( new SampleModel(), tap.getName() );
-
- mTap = tap;
- mTap.addListener( getModel() );
-
- getModel().setSampleCount(
- (int)( getModel().getSampleCount() * tap.getSampleRateRatio() ) );
-
- getModel().setDelay( tap.getDelay() );
- }
-
- @Override
- public void update( Observable arg0, Object arg1 )
- {
- mSamples = (List)getModel().getSamples();
- mSampleCount = getModel().getSampleCount();
- repaint();
- }
-
- public void paintComponent( Graphics g )
- {
- super.paintComponent( g );
-
- Graphics2D g2 = (Graphics2D)g;
-
- int height = getHeight();
- int width = getWidth();
-
- g2.setColor( Color.DARK_GRAY );
-
- float middle = (float)height / 2.0f;
-
- g2.drawLine( 0, (int)middle, width, (int)middle );
-
- if( mSamples != null && mSamples.size() > 0 )
- {
- g2.setColor( getForeground() );
-
- g2.drawString( mLabel, 5 , 15 );
-
- for( int x = 1; x < mSamples.size(); x++ )
- {
- paintBit( g, x, mSamples.get( x ) );
- }
- }
-
- g2.dispose();
- }
-
- /**
- * Paints a polyline representing a boolean value with a 0 or 1 label in
- * the middle of it
- *
- * @param graphics object
- * @param index - index of the sample int the sample buffer
- * @param value - bit value, 0 or 1
- */
- private void paintBit( Graphics graphics, int index, boolean value )
- {
- Graphics2D g2 = (Graphics2D)graphics;
-
- Path2D.Float polyline =
- new Path2D.Float( Path2D.Float.WIND_EVEN_ODD, mSamples.size() );
-
- float middleY = ( (float)getHeight() / 2.0f );
-
- float indexWidth = (float)( getWidth() ) / (float)mSampleCount;
-
- float startX = (float)index * indexWidth;
- float stopX = (float)(index + 1 ) * indexWidth;
- float fivePercent = indexWidth * .05f;
- float leftX = startX + fivePercent;
- float rightX = stopX - fivePercent;
-
- /* Start */
- polyline.moveTo( startX, middleY );
-
- /* Left horizonal segment */
- polyline.lineTo( leftX, middleY );
-
- float y = ( value ? ( middleY - ( middleY * 0.5f) ) :
- ( middleY + ( middleY * 0.5f) ) );
-
- /* Left vertical segment */
- polyline.lineTo( leftX, y );
-
- /* Middle horizontal segment */
- polyline.lineTo( rightX, y );
-
- /* Right vertical segment */
- polyline.lineTo( rightX, middleY );
-
- /* Right horizontal segment */
- polyline.lineTo( stopX, middleY );
-
- g2.draw( polyline );
-
- /* Draw the bit value label */
- String bit = value ? "1" : "0";
-
- FontMetrics fontMetrics = graphics.getFontMetrics( this.getFont() );
-
- Rectangle2D label = fontMetrics.getStringBounds( bit, graphics );
-
- float offsetX = (float)label.getWidth() / 2.0f;
- float offsetY = (float)label.getWidth() / 2.0f;
-
- float middleX = startX + ( indexWidth / 2.0f );
-
- graphics.drawString( bit,
- (int)( middleX - offsetX ),
- (int)( middleY - offsetY ) );
- }
-}
diff --git a/src/main/java/io/github/dsheirer/audio/AudioModule.java b/src/main/java/io/github/dsheirer/audio/AudioModule.java
index 369dd8ed..81f3451c 100644
--- a/src/main/java/io/github/dsheirer/audio/AudioModule.java
+++ b/src/main/java/io/github/dsheirer/audio/AudioModule.java
@@ -32,8 +32,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ScheduledExecutorService;
-
/**
* Provides packaging of demodulated audio sample buffers into audio packets for
* broadcast to registered audio packet listeners. Includes audio packet
@@ -117,7 +115,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, IFilter
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
/* No start operations provided */
}
diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/StreamManager.java b/src/main/java/io/github/dsheirer/audio/broadcast/StreamManager.java
index ce98e155..c7c7b7ad 100644
--- a/src/main/java/io/github/dsheirer/audio/broadcast/StreamManager.java
+++ b/src/main/java/io/github/dsheirer/audio/broadcast/StreamManager.java
@@ -140,7 +140,7 @@ public class StreamManager implements Listener
{
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
mBroadcastFormat);
- recorder.start(ThreadPool.SCHEDULED);
+ recorder.start();
recorder.receive(audioPacket);
mStreamRecorders.put(channelMetadataID, recorder);
}
diff --git a/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java b/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java
index ce3d0bd3..855a7e2a 100644
--- a/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java
+++ b/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java
@@ -1,118 +1,124 @@
/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * sdrtrunk
+ * Copyright (C) 2014-2017 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ *
******************************************************************************/
package io.github.dsheirer.buffer;
-import java.util.Arrays;
-
/**
* Circular sample buffer - allocates a buffer and stores samples in a circular
- * fashion, overwriting older samples with newly arrived samples. Initially
- * fills buffer with 0-valued samples
- *
- * Can be used as a delay-type buffer, to delay samples by the 'size' amount
- * @author denny
+ * fashion, overwriting older samples with newly arrived samples.
*
+ * Can be used as a delay-type buffer, to delay samples by the 'size' amount
*/
public class FloatCircularBuffer
{
- float[] mBuffer;
- int mBufferPointer = 0;
-
- public FloatCircularBuffer( int size )
- {
- mBuffer = new float[ size ];
-
- Arrays.fill( mBuffer, (float)0.0 );
- }
-
- public int getSize()
- {
- return mBuffer.length;
- }
-
- public float[] getBuffer()
- {
- return mBuffer;
- }
+ float[] mBuffer;
+ int mBufferPointer = 0;
- /**
- * Puts the new value into the buffer and returns the oldest buffer value
- * that it replaced
- * @param newValue
- * @return
- */
- public float get( float newValue )
- {
- float oldestSample = mBuffer[ mBufferPointer ];
+ public FloatCircularBuffer(int size)
+ {
+ mBuffer = new float[size];
+ }
- mBuffer[ mBufferPointer ] = newValue;
-
- mBufferPointer++;
-
- if( mBufferPointer >= mBuffer.length )
- {
- mBufferPointer = 0;
- }
-
- return oldestSample;
- }
+ public int getSize()
+ {
+ return mBuffer.length;
+ }
- /**
- * Convenience method to put a new value in the buffer, while discarding
- * the old value
- */
- public void put( float newValue )
- {
- get( newValue );
- }
+ public float[] getBuffer()
+ {
+ return mBuffer;
+ }
- /**
- * Returns an array of arraySize, loaded with sampleCount of the
- * oldest samples, loaded into the array starting at index 0.
- *
- * This is useful for fetching an array filled half way with samples to
- * use in FFT processing.
- * @param arraySize - size of the array (must be larger than sampleCount)
- * @param sampleCount - number of the oldest samples in the buffer to load
- * @return
- */
- public float[] get( int arraySize, int sampleCount )
- {
- float[] samples = new float[ arraySize ];
+ /**
+ * Puts the new value into the buffer and returns the oldest buffer value
+ * that it replaced
+ *
+ * @param newValue
+ * @return
+ */
+ public float putAndGet(float newValue)
+ {
+ float oldestSample = mBuffer[mBufferPointer];
- if( arraySize > sampleCount )
- {
- int tempCounter = mBufferPointer;
+ put(newValue);
- for( int x = 0; x < sampleCount; x++ )
- {
- samples[ x ] = mBuffer[ tempCounter ];
-
- tempCounter++;
-
- if( tempCounter > mBuffer.length )
- {
- tempCounter = 0;
- }
- }
- }
-
- return samples;
- }
-
+ return oldestSample;
+ }
+
+ /**
+ * Adds the new value to this buffer.
+ */
+ public void put(float newValue)
+ {
+ mBuffer[mBufferPointer] = newValue;
+
+ mBufferPointer++;
+
+ if(mBufferPointer >= mBuffer.length)
+ {
+ mBufferPointer = 0;
+ }
+ }
+
+ /**
+ * Returns the sample at the specified index, where index 0 is the oldest sample and index (length - 1) is the
+ * newest sample.
+ * @param index in range of 0 to length-1
+ * @return value at indexed position
+ */
+
+ public float get(int index)
+ {
+ if(index < mBuffer.length)
+ {
+ int pointer = mBufferPointer + index;
+
+ if(pointer >= mBuffer.length)
+ {
+ pointer -= mBuffer.length;
+ }
+
+ return mBuffer[pointer];
+ }
+ else
+ {
+ throw new ArrayIndexOutOfBoundsException("Index [" + index + "] is not valid for the buffer size of [" +
+ mBuffer.length + "]");
+ }
+ }
+
+ /**
+ * Returns the contents of the circular buffer unwrapped where index 0 is the oldest sample and index length-1
+ * is the newest sample.
+ *
+ * @return array of samples
+ */
+ public float[] get()
+ {
+ float[] samples = new float[mBuffer.length];
+
+ System.arraycopy(mBuffer, mBufferPointer, samples, 0, mBuffer.length - mBufferPointer);
+
+ if(mBufferPointer != 0)
+ {
+ System.arraycopy(mBuffer, 0, samples, mBuffer.length - mBufferPointer, mBufferPointer);
+ }
+
+ return samples;
+ }
}
diff --git a/src/main/java/io/github/dsheirer/channel/heartbeat/Heartbeat.java b/src/main/java/io/github/dsheirer/channel/heartbeat/Heartbeat.java
deleted file mode 100644
index 72946df0..00000000
--- a/src/main/java/io/github/dsheirer/channel/heartbeat/Heartbeat.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package io.github.dsheirer.channel.heartbeat;
-
-public class Heartbeat
-{
- /**
- * Periodic pulse that is broadcast to subscribing modules within the demodulating chain so that
- * monitoring of state and other attributes can occur on the primary demodulation and decoding
- * thread.
- */
- public Heartbeat()
- {
- }
-}
diff --git a/src/main/java/io/github/dsheirer/channel/heartbeat/IHeartbeatProvider.java b/src/main/java/io/github/dsheirer/channel/heartbeat/IHeartbeatProvider.java
deleted file mode 100644
index 8d2ab3c3..00000000
--- a/src/main/java/io/github/dsheirer/channel/heartbeat/IHeartbeatProvider.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package io.github.dsheirer.channel.heartbeat;
-
-import io.github.dsheirer.sample.Listener;
-
-public interface IHeartbeatProvider
-{
- public void setHeartbeatListener(Listener listener);
-
- public void removeHeartbeatListener();
-}
diff --git a/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java b/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java
index bd2432fb..1d3795e8 100644
--- a/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java
+++ b/src/main/java/io/github/dsheirer/channel/state/AlwaysUnsquelchedDecoderState.java
@@ -25,8 +25,6 @@ import io.github.dsheirer.module.decode.DecoderType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ScheduledExecutorService;
-
/**
* Basic decoder channel state - provides the minimum channel state functionality
* to support an always un-squelched audio decoder.
@@ -94,7 +92,7 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, NO_SQUELCH));
broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE));
diff --git a/src/main/java/io/github/dsheirer/channel/state/ChannelState.java b/src/main/java/io/github/dsheirer/channel/state/ChannelState.java
index ae83ec65..2ba741e4 100644
--- a/src/main/java/io/github/dsheirer/channel/state/ChannelState.java
+++ b/src/main/java/io/github/dsheirer/channel/state/ChannelState.java
@@ -20,8 +20,6 @@ package io.github.dsheirer.channel.state;
import io.github.dsheirer.audio.squelch.ISquelchStateProvider;
import io.github.dsheirer.audio.squelch.SquelchState;
-import io.github.dsheirer.channel.heartbeat.Heartbeat;
-import io.github.dsheirer.channel.heartbeat.IHeartbeatListener;
import io.github.dsheirer.channel.metadata.Attribute;
import io.github.dsheirer.channel.metadata.AttributeChangeRequest;
import io.github.dsheirer.channel.metadata.IAttributeChangeRequestListener;
@@ -36,17 +34,17 @@ import io.github.dsheirer.module.decode.event.CallEvent;
import io.github.dsheirer.module.decode.event.ICallEventProvider;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.IOverflowListener;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
+import io.github.dsheirer.source.ISourceEventListener;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.heartbeat.Heartbeat;
+import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ScheduledExecutorService;
-
-import static io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
+import static io.github.dsheirer.source.SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
- IDecoderStateEventProvider, IFrequencyChangeListener, IHeartbeatListener, ISquelchStateProvider,
+ IDecoderStateEventProvider, ISourceEventListener, IHeartbeatListener, ISquelchStateProvider,
IAttributeChangeRequestListener, IOverflowListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
@@ -64,7 +62,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
private ChannelType mChannelType;
private TrafficChannelManager mTrafficChannelEndListener;
private CallEvent mTrafficChannelCallEvent;
- private FrequencyChangeListener mFrequencyChangeListener;
+ private SourceChangeListener mSourceChangeListener;
private boolean mSquelchLocked = false;
private boolean mSelected = false;
@@ -124,7 +122,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState));
@@ -160,14 +158,14 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
}
@Override
- public Listener getFrequencyChangeListener()
+ public Listener getSourceEventListener()
{
- if(mFrequencyChangeListener == null)
+ if(mSourceChangeListener == null)
{
- mFrequencyChangeListener = new FrequencyChangeListener();
+ mSourceChangeListener = new SourceChangeListener();
}
- return mFrequencyChangeListener;
+ return mSourceChangeListener;
}
private boolean isStandardChannel()
@@ -520,14 +518,14 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
/**
* Listener to receive frequency change events and rebroadcast them to the decoder states
*/
- public class FrequencyChangeListener implements Listener
+ public class SourceChangeListener implements Listener
{
@Override
- public void receive(FrequencyChangeEvent frequencyChangeEvent)
+ public void receive(SourceEvent sourceEvent)
{
- if(frequencyChangeEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
+ if(sourceEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
{
- long frequency = frequencyChangeEvent.getValue().longValue();
+ long frequency = sourceEvent.getValue().longValue();
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
}
diff --git a/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java b/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java
index dcd12ce5..6c284f3c 100644
--- a/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java
+++ b/src/main/java/io/github/dsheirer/channel/traffic/TrafficChannelManager.java
@@ -1,19 +1,16 @@
/*******************************************************************************
- * sdrtrunk
+ * sdr-trunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
*
******************************************************************************/
package io.github.dsheirer.channel.traffic;
@@ -29,13 +26,12 @@ import io.github.dsheirer.controller.channel.TrafficChannelEvent;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.event.CallEvent;
+import io.github.dsheirer.module.decode.event.CallEvent.CallEventType;
import io.github.dsheirer.module.decode.event.ICallEventProvider;
-import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.config.SourceConfigTuner;
-import io.github.dsheirer.source.tuner.TunerChannel;
-import io.github.dsheirer.source.tuner.TunerChannel.Type;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,7 +39,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener
{
@@ -61,7 +56,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
private ChannelModel mChannelModel;
private DecodeConfiguration mDecodeConfiguration;
- private EventLogConfiguration mEventLogConfiguration;
private RecordConfiguration mRecordConfiguration;
private String mSystem;
private String mSite;
@@ -75,7 +69,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
* @param channelModel containing channels currently in use
* @param decodeConfiguration - decoder configuration to use for each
* traffic channel allocation.
- * @param eventLogConfiguration - logging configuration to use for traffic channels
* @param recordConfiguration - recording options for each traffic channel
* @param system label to use to describe the system
* @param site label to use to describe the site
@@ -85,7 +78,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
*/
public TrafficChannelManager(ChannelModel channelModel,
DecodeConfiguration decodeConfiguration,
- EventLogConfiguration eventLogConfiguration,
RecordConfiguration recordConfiguration,
String system,
String site,
@@ -94,7 +86,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
{
mChannelModel = channelModel;
mDecodeConfiguration = decodeConfiguration;
- mEventLogConfiguration = eventLogConfiguration;
mRecordConfiguration = recordConfiguration;
mSystem = system;
mSite = site;
@@ -150,8 +141,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
channel.setDecodeConfiguration(mDecodeConfiguration);
- channel.setEventLogConfiguration(mEventLogConfiguration);
-
channel.setRecordConfiguration(mRecordConfiguration);
channel.setAliasListName(mAliasListName);
@@ -161,7 +150,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
mTrafficChannelPool.add(channel);
}
- /* If we have a configured channel, update metadata */
+ /* If we have a configured channel, update metadata */
if(channel != null)
{
channel.setSourceConfiguration(new SourceConfigTuner(tunerChannel));
@@ -184,7 +173,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
{
CallEvent callEvent = event.getCallEvent();
- /* Check for duplicate events and suppress */
+ /* Check for duplicate events and suppress */
synchronized(mTrafficChannelsInUse)
{
if(mTrafficChannelsInUse.containsKey(callEvent.getChannel()))
@@ -196,8 +185,8 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
if(frequency > 0)
{
- Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC,
- frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
+ Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(frequency,
+ mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
if(channel != null)
{
@@ -213,7 +202,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
}
else
{
- callEvent.setCallEventType(CallEvent.CallEventType.CALL_DETECT);
+ callEvent.setCallEventType(CallEventType.CALL_DETECT);
String details = callEvent.getDetails();
@@ -230,7 +219,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
}
else
{
- callEvent.setCallEventType(CallEvent.CallEventType.CALL_DETECT);
+ callEvent.setCallEventType(CallEventType.CALL_DETECT);
String details = callEvent.getDetails();
@@ -247,7 +236,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
}
else
{
- callEvent.setCallEventType(CallEvent.CallEventType.CALL_DETECT);
+ callEvent.setCallEventType(CallEventType.CALL_DETECT);
String details = callEvent.getDetails();
@@ -337,7 +326,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
}
@@ -348,7 +337,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
{
List channels = new ArrayList<>();
- /* Copy the keyset so we don't get concurrent modification of the map */
+ /* Copy the keyset so we don't get concurrent modification of the map */
channels.addAll(mTrafficChannelsInUse.keySet());
for(String channel : channels)
@@ -399,4 +388,4 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/controller/channel/Channel.java b/src/main/java/io/github/dsheirer/controller/channel/Channel.java
index 9c0cee7f..4f66c37f 100644
--- a/src/main/java/io/github/dsheirer/controller/channel/Channel.java
+++ b/src/main/java/io/github/dsheirer/controller/channel/Channel.java
@@ -31,18 +31,16 @@ import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceType;
import io.github.dsheirer.source.config.SourceConfigFactory;
import io.github.dsheirer.source.config.SourceConfigRecording;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.source.config.SourceConfiguration;
-import io.github.dsheirer.source.tuner.TunerChannel;
-import io.github.dsheirer.source.tuner.TunerChannel.Type;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
@JacksonXmlRootElement(localName = "channel")
-public class Channel extends Configuration implements Listener
+public class Channel extends Configuration implements Listener
{
// Standard channels are persisted and traffic channels are temporary
public enum ChannelType {STANDARD, TRAFFIC}
@@ -51,12 +49,9 @@ public class Channel extends Configuration implements Listener (sampleRate / 2))
{
- throw new IllegalArgumentException("FilterFactory - low pass "
- + "filter pass frequency [" + passFrequency + "] must be "
- + "less than the stop frequency [" + stopFrequency + "] "
- + "and must be less than half [" + (int) (sampleRate / 2) +
- "] of the sample rate [" + sampleRate + "]");
+ throw new IllegalArgumentException("FilterFactory - low pass filter pass frequency [" + passFrequency +
+ "] must be less than the stop frequency [" + stopFrequency + "] and must be less than half [" +
+ (int)(sampleRate / 2) + "] of the sample rate [" + sampleRate + "]");
}
- int tapCount = getTapCount(sampleRate,
- passFrequency,
- stopFrequency,
- attenuation);
+ int tapCount = getTapCount(sampleRate, passFrequency, stopFrequency, attenuation);
if(forceOddLength)
{
@@ -289,34 +254,21 @@ public class FilterFactory
* @param windowType - window to apply against the generated coefficients
* @return
*/
- public static float[] getHighPass(int sampleRate,
- long cutoff,
- int filterLength,
- Window.WindowType windowType)
+ public static float[] getHighPass(int sampleRate, long cutoff, int filterLength, Window.WindowType windowType)
{
//Convert the high frequency cutoff to its low frequency cutoff
//equivalent, so that when we generate the low pass filter, prior to
//inversion, its at the correct frequency
long convertedCutoff = sampleRate / 2 - cutoff;
- return invert(getSinc(sampleRate,
- convertedCutoff,
- filterLength,
- windowType));
+ return invert(getSinc(sampleRate, convertedCutoff, filterLength, windowType));
}
- public static float[] getHighPass(int sampleRate,
- long stopFrequency,
- long passFrequency,
- int attenuation,
- Window.WindowType windowType,
- boolean forceOddLength)
+ public static float[] getHighPass(int sampleRate, long stopFrequency, long passFrequency, int attenuation,
+ Window.WindowType windowType, boolean forceOddLength)
{
/* reverse the stop and pass frequency to get the low pass variant */
- int tapCount = getTapCount(sampleRate,
- stopFrequency,
- passFrequency,
- attenuation);
+ int tapCount = getTapCount(sampleRate, stopFrequency, passFrequency, attenuation);
if(forceOddLength)
{
@@ -360,11 +312,10 @@ public class FilterFactory
}
/**
- * Determines the number of fir filter taps required to produce the
- * specified frequency response with passband ripple near .1dB.
+ * Determines the number of fir filter taps required to produce the specified frequency response with passband
+ * ripple near 0.1dB.
*
- * Implements the algorithm from Understanding Digital Signal Processing, 3e
- * , Lyons, section 5.10.5.
+ * Implements the algorithm from Understanding Digital Signal Processing, 3e, Lyons, section 5.10.5.
*
* @param sampleRate in hertz
* @param pass pass frequency in hertz
@@ -372,21 +323,17 @@ public class FilterFactory
* @param attenuation in dB
* @return
*/
- public static int getTapCount(int sampleRate,
- long pass,
- long stop,
- int attenuation)
+ public static int getTapCount(int sampleRate, long pass, long stop, int attenuation)
{
- double frequency = ((double) stop - (double) pass) / (double) sampleRate;
+ double frequency = ((double)stop - (double)pass) / (double)sampleRate;
- return (int) (Math.round((double) attenuation / (22.0d * frequency)));
+ return (int)(Math.round((double)attenuation / (22.0d * frequency)));
}
- public static ComplexPrimeCICDecimate getDecimationFilter(int sampleRate,
- int decimatedRate, int order, int passFrequency, int attenuation,
- Window.WindowType windowType)
+ public static ComplexPrimeCICDecimate getDecimationFilter(int sampleRate, int decimatedRate, int order,
+ int passFrequency, int attenuation, Window.WindowType windowType)
{
- int decimationRate = (int) (sampleRate / decimatedRate);
+ int decimationRate = (int)(sampleRate / decimatedRate);
return new ComplexPrimeCICDecimate(decimationRate, order,
passFrequency, attenuation, windowType);
@@ -401,10 +348,7 @@ public class FilterFactory
* polyphase filter decimation chain
* @throws - AssertionException if sample rate is not a multiple of 48 kHz
*/
- public static int[] getPolyphaseDecimationRates(int sampleRate,
- int decimatedRate,
- long passFrequency,
- long stopFrequency)
+ public static int[] getPolyphaseDecimationRates(int sampleRate, int decimatedRate, long passFrequency, long stopFrequency)
{
int[] rates;
@@ -414,7 +358,7 @@ public class FilterFactory
+ "integer multiple of sample rate");
}
- int decimation = (int) (sampleRate / decimatedRate);
+ int decimation = (int)(sampleRate / decimatedRate);
//Decimation rates below 20 will use single stage polyphase filter
if(decimation < 20)
@@ -432,11 +376,6 @@ public class FilterFactory
int stage1 = findClosest(optimalStage1, factors);
-// mLog.info( "Decimation rate [" + decimation +
-// "] stage1 optimal [" + optimalStage1 +
-// "] stage1 actual [" + stage1 +
-// "]");
-
if(stage1 == decimation || stage1 == 1)
{
rates = new int[1];
@@ -447,7 +386,7 @@ public class FilterFactory
{
rates = new int[2];
- int stage2 = (int) (decimation / stage1);
+ int stage2 = (int)(decimation / stage1);
if(stage1 > stage2)
{
@@ -465,8 +404,7 @@ public class FilterFactory
}
/**
- * Finds the factor that is closest to the desired factor, from an ordered
- * list of factors.
+ * Finds the factor that is closest to the desired factor, from an ordered list of factors.
*/
private static int findClosest(int desiredFactor, Set factors)
{
@@ -488,9 +426,8 @@ public class FilterFactory
}
/**
- * Determines the factors that make up an integer. Uses a brute force
- * method to iterate all integers from 1 to value, determining which factors
- * are evenly divisible into the value.
+ * Determines the factors that make up an integer. Uses a brute force method to iterate all integers from 1 to
+ * value, determining which factors are evenly divisible into the value.
*
* @param value - integer decimation value
* @return - ordered set of factors for value
@@ -498,11 +435,11 @@ public class FilterFactory
private static Set getFactors(int value)
{
Set factors = new TreeSet();
-
+
/* Brute force */
for(int x = 1; x <= value; x++)
{
- int remainder = (int) (value / x);
+ int remainder = (int)(value / x);
if(remainder * x == value)
{
@@ -514,14 +451,10 @@ public class FilterFactory
}
/**
- * Determines the optimal decimation rate for stage 1 of a two-stage
- * poly-phase decimation filter chain, to produce a final sample rate of
- * 48 kHz using a pass bandwidth of 25 kHz.
+ * Determines the optimal decimation rate for stage 1 of a two-stage poly-phase decimation filter chain, to produce
+ * a final sample rate of 48 kHz using a pass bandwidth of 25 kHz. Use for total decimation rates of 20 or higher.
*
- * Use for total decimation rates of 20 or higher.
- *
- * Implements the algorithm described in Lyons, Understanding Digital Signal
- * Processing, 3e, section 10.2.1, page 511.
+ * Implements the algorithm described in Lyons, Understanding Digital Signal Processing, 3e, section 10.2.1, page 511.
*
* @param sampleRate
* @param decimation
@@ -529,42 +462,29 @@ public class FilterFactory
* @return optimum integer decimation rate for the first stage decimation
* filter
*/
- public static int getOptimalStageOneRate(int sampleRate,
- int decimation,
- long passFrequency,
- long stopFrequency)
+ public static int getOptimalStageOneRate(int sampleRate, int decimation, long passFrequency, long stopFrequency)
{
double ratio = getBandwidthRatio(passFrequency, stopFrequency);
- double numerator = 1.0d - (
- Math.sqrt((double) decimation * ratio / (2.0d - ratio)));
+ double numerator = 1.0d - (Math.sqrt((double)decimation * ratio / (2.0d - ratio)));
double denominator = 2.0d - (ratio * (decimation + 1.0d));
- int retVal = (int) (2.0d * decimation * (numerator / denominator));
-
-// mLog.info( "Optimal Stage 1 Decimation - rate [" + sampleRate +
-// "] pass [" + passFrequency +
-// "] bw ratio [" + ratio +
-// "] optimal [" + retVal +
-// "]" );
+ int retVal = (int)(2.0d * decimation * (numerator / denominator));
return retVal;
}
/**
- * Determines the F ratio as described in Lyons, Understanding Digital
- * Signal Processing, 3e, section 10.2.1, page 511
- *
- * Used in conjunction with the optimal stage one decimation rate method
- * above.
+ * Determines the F ratio as described in Lyons, Understanding Digital Signal Processing, 3e, section 10.2.1, page 511
+ * Used in conjunction with the optimal stage one decimation rate method above.
*/
private static double getBandwidthRatio(long passFrequency, long stopFrequency)
{
assert (passFrequency < stopFrequency);
- return ((double) (stopFrequency - passFrequency) /
- (double) stopFrequency);
+ return ((double)(stopFrequency - passFrequency) /
+ (double)stopFrequency);
}
/**
@@ -575,14 +495,11 @@ public class FilterFactory
* @param outputSampleRate
* @return
*/
- public static float[] getCICCleanupFilter(int outputSampleRate,
- int passFrequency,
- int attenuation,
- Window.WindowType window)
+ public static float[] getCICCleanupFilter(int outputSampleRate, int passFrequency, int attenuation, Window.WindowType window)
{
int taps = getTapCount(outputSampleRate, passFrequency, passFrequency + 1500,
attenuation);
-
+
/* Make tap count odd */
if(taps % 2 == 0)
{
@@ -599,7 +516,7 @@ public class FilterFactory
//Transfer the IDFT results to the odd length return array
float[] coefficients = new float[taps];
- int middleCoefficient = (int) (taps / 2);
+ int middleCoefficient = (int)(taps / 2);
//Bin 0 of the idft is our center coefficient
coefficients[middleCoefficient] = frequencyResponse[0];
@@ -620,26 +537,22 @@ public class FilterFactory
return coefficients;
}
- public static float[] getCICResponseArray(int sampleRate,
- int frequency,
- int length)
+ public static float[] getCICResponseArray(int sampleRate, int frequency, int length)
{
float[] cicArray = new float[length * 2];
- int binCount = (int) ((Math.round(
- (double) frequency / (double) sampleRate * 2.0d * (double) length)));
+ int binCount = (int)((Math.round(
+ (double)frequency / (double)sampleRate * 2.0d * (double)length)));
cicArray[0] = 1.0f;
- float unityResponse = (float) (Math.sin(1.0d / (double) length) /
- (1.0d / (double) length));
+ float unityResponse = (float)(Math.sin(1.0d / (double)length) /
+ (1.0d / (double)length));
for(int x = 1; x <= binCount; x++)
{
- /* Calculate unity response plus amplification for droop */
- float compensated = 1.0f + (unityResponse -
- (float) (Math.sin((double) x / (double) length) /
- ((double) x / (double) length)));
+ /* Calculate unity response plus amplification for droop */
+ float compensated = 1.0f + (unityResponse - (float)(Math.sin((double)x / (double)length) / ((double)x / (double)length)));
cicArray[x] = compensated;
cicArray[length - x] = compensated;
@@ -649,28 +562,29 @@ public class FilterFactory
}
/**
- * Creates root raised cosine filter coefficients with a tap count equal
- * to the symbols x samplesPerSymbol + 1.
+ * Creates root raised cosine filter coefficients with a tap count equal to the symbols x samplesPerSymbol + 1.
*
* Symbol count should be an even integer.
- *
* Ported to java from gnuradio/filter/firdes.cc
*
- * For 40db attenuation, calculate the number of symbols based on the
- * following formula:
+ * For 40db attenuation, calculate the number of symbols based on the following formula:
+ * Symbols = -44 * alpha + 33
*
- * Symbols = -44 * alpha + 33
+ * Polyphase Channelizer notes:
+ * -Set samples Per Symbol at 2 (or more) * number of channels
+ * -Set symbolCount to sufficient size to produce required attenuation
+ * -Set symbol rate in hertz
+ * -Set alpha = (desired channel bandwidth / (symbol rate * samples per symbol) - 1.0
+ * -Channel bandwidth must be larger than symbol rate * samples per symbol, in order for there to be a positive alpha
*
* @param samplesPerSymbol - number of samples per symbol
- * @param symbols - number of symbols - must be even
- * @param alpha - roll-off factor
+ * @param symbolCount - number of symbol intervals for the filter. This directly impacts the filter size
+ * @param alpha - roll-off factor.
* @return - filter coefficients
*/
- public static float[] getRootRaisedCosine(int samplesPerSymbol,
- int symbols,
- float alpha)
+ public static float[] getRootRaisedCosine(int samplesPerSymbol, int symbolCount, float alpha)
{
- int taps = samplesPerSymbol * symbols + 1;
+ int taps = samplesPerSymbol * symbolCount;
float scale = 0;
@@ -678,10 +592,10 @@ public class FilterFactory
for(int x = 0; x < taps; x++)
{
- float index = (float) x - ((float) taps / 2.0f);
+ float index = (float)x - ((float)taps / 2.0f);
- float x1 = (float) Math.PI * index / (float) samplesPerSymbol;
- float x2 = 4.0f * alpha * index / (float) samplesPerSymbol;
+ float x1 = (float)Math.PI * index / (float)samplesPerSymbol;
+ float x2 = 4.0f * alpha * index / (float)samplesPerSymbol;
float x3 = x2 * x2 - 1.0f;
float numerator, denominator;
@@ -690,17 +604,15 @@ public class FilterFactory
{
if(x != taps / 2)
{
- numerator = (float) Math.cos((1.0 + alpha) * x1) +
- (float) Math.sin((1.0f - alpha) * x1) /
- (4.0f * alpha * index / (float) samplesPerSymbol);
+ numerator = (float)Math.cos((1.0 + alpha) * x1) +
+ (float)Math.sin((1.0f - alpha) * x1) / (4.0f * alpha * index / (float)samplesPerSymbol);
}
else
{
- numerator = (float) Math.cos((1.0f + alpha) * x1) +
- (1.0f - alpha) * (float) Math.PI / (4.0f * alpha);
+ numerator = (float)Math.cos((1.0f + alpha) * x1) + (1.0f - alpha) * (float)Math.PI / (4.0f * alpha);
}
- denominator = x3 * (float) Math.PI;
+ denominator = x3 * (float)Math.PI;
}
else
{
@@ -713,14 +625,11 @@ public class FilterFactory
x3 = (1.0f - alpha) * x1;
x2 = (1.0f + alpha) * x1;
- numerator = (float) (Math.sin(x2) * (1.0f + alpha) * Math.PI -
- Math.cos(x3) * ((1.0 - alpha) * Math.PI *
- (double) samplesPerSymbol) / (4 * alpha * index) +
- Math.sin(x3) * (double) samplesPerSymbol *
- (double) samplesPerSymbol / (4.0 * alpha * index * index));
+ numerator = (float)(Math.sin(x2) * (1.0f + alpha) * Math.PI - Math.cos(x3) * ((1.0 - alpha) * Math.PI *
+ (double)samplesPerSymbol) / (4.0 * alpha * index) + Math.sin(x3) * (double)samplesPerSymbol *
+ (double)samplesPerSymbol / (4.0 * alpha * index * index));
- denominator = (float) (-32.0 * Math.PI * alpha * alpha * index /
- (double) samplesPerSymbol);
+ denominator = (float)(-32.0 * Math.PI * alpha * alpha * index / (double)samplesPerSymbol);
}
coefficients[x] = 4.0f * alpha * numerator / denominator;
@@ -740,10 +649,10 @@ public class FilterFactory
}
/**
- * Creates a filter from the filter specification
+ * Creates a filter from the filter specification using the remez exchange design algorithm
*
* @param specification
- * @return
+ * @return filter coefficients
* @throws FilterDesignException if the filter cannot be designed
*/
public static float[] getTaps(FIRFilterSpecification specification) throws FilterDesignException
@@ -757,4 +666,248 @@ public class FilterFactory
return null;
}
+
+ /**
+ * Evaluates the filter to determine the magnitude response (dB) at the specified frequency.
+ *
+ * @param filter to evaluate
+ * @param frequency to calculate (0 <> 0.5)
+ * @return magnitude frequency response in decibels
+ */
+ public static double evaluate(float[] filter, double frequency)
+ {
+ double real = 0.0;
+ double imag = 0.0;
+
+ for(int x = 0; x < filter.length; x++)
+ {
+ real += filter[x] * Math.cos(Math.PI * frequency * (double)x);
+ imag += filter[x] * Math.sin(Math.PI * frequency * (double)x);
+ }
+
+ return decibel(real, imag);
+ }
+
+ /**
+ * Calculates the decibel magnitude of the real and imaginary complex sample
+ * @param real (x-axis) value
+ * @param imag (y-axis) value
+ * @return magnitude in decibels
+ */
+ public static double decibel(double real, double imag)
+ {
+ return (float)(10.0 * Math.log10(Math.pow(real, 2.0) + Math.pow(imag, 2.0)));
+ }
+
+
+ public static float[] getRemezChannelizer(int channelBandwidth, int channels, int tapsPerChannel, double alpha,
+ double passRipple, double stopRipple) throws FilterDesignException
+ {
+ FIRFilterSpecification specification = FIRFilterSpecification.channelizerBuilder()
+ .sampleRate(channels * channelBandwidth * 4)
+ .channels(channels)
+ .channelBandwidth(channelBandwidth)
+ .tapsPerChannel(tapsPerChannel)
+ .alpha(alpha)
+ .passRipple(passRipple)
+ .stopRipple(stopRipple)
+ .build();
+
+ RemezFIRFilterDesignerWithLagrange designer = new RemezFIRFilterDesignerWithLagrange(specification);
+
+ float[] taps = designer.getImpulseResponse();
+
+ double bandEdgeFrequency = (double)channelBandwidth / (double)(channels * channelBandwidth * 2);
+
+ double response = decibel(designer.getFrequencyResponse(Math.cos(bandEdgeFrequency * Math.PI)), 0.0);
+ mLog.debug("Frequency Response at 1.0: " + response);
+ response = decibel(designer.getFrequencyResponse(Math.cos(bandEdgeFrequency * Math.PI * 2.0)), 0.0);
+ mLog.debug("Frequency Response at 2.0: " + response);
+
+ return taps;
+ }
+
+ /**
+ * Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase Channelizer/Synthesizer.
+ * The filter is designed for x2 oversampling of each channel and the filter cutoff frequency is incrementally
+ * adjusted to achieve a -6.02 dB attenuation at the channel band edge to enable perfect reconstruction of adjacent
+ * channels.
+ *
+ * The odd length sync filter that meets the design objectives will be increased in length by pre-padding a zero
+ * coefficient to produce a filter with a length that is an integral of the channel count.
+ *
+ * Note: the tapsPerChannel value will be iteratively increased (up to 10x) if a filter cannot be designed to meet
+ * the channel band edge attenuation objective.
+ *
+ * Note: a channelizer filter designed using a Blackman-Harris-7 window and 18-21 taps per channel creates a filter
+ * with attenuation in range of -60 to -80 dB at the edge of the 2x sampled channel.
+ *
+ * @param channelBandwidth - full bandwidth of the channel in hertz
+ * @param channels - number of channels
+ * @param tapsPerChannel - desired filter tap count per channel (may be increased to meet objective)
+ * @param windowType - to use when designing the windowed-sync filter
+ * @param logResults - to log a debug summary of the filter characteristics
+ *
+ * @return filter coefficients
+ *
+ * @throws FilterDesignException if a filter cannot be designed with a band edge attenuation of -6.02 dB
+ */
+ public static float[] getSincChannelizer(double channelBandwidth, int channels, int tapsPerChannel,
+ Window.WindowType windowType, boolean logResults) throws FilterDesignException
+ {
+ int currentTapsPerChannel = tapsPerChannel;
+ int sampleRate = (int)(channelBandwidth * (double)channels * 2.0);
+ int filterLength = (channels * currentTapsPerChannel) - 1;
+
+ double cutoffFrequency = channelBandwidth / (double)sampleRate;
+ double bandEdge = cutoffFrequency;
+ double increment = cutoffFrequency * 0.1;
+
+ //Get an initial filter and band edge frequency response using the channel bandwidth as a cutoff
+ float[] taps = FilterFactory.getSinc(cutoffFrequency, filterLength, windowType);
+ double response = FilterFactory.evaluate(taps, bandEdge);
+
+ //Set cutoff adjustment threshold - we'll test cutoff frequencies to around 1 hertz resolution
+ double incrementThreshold = 1.0 / (double)sampleRate;
+
+ //Iteratively evaluate filters to find the filter with the highest cutoff frequency that meets the objective
+ while(increment > incrementThreshold)
+ {
+ //If the current cutoff meets the objective, test if a higher cutoff also meets the objective
+ if(matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE) &&
+ (cutoffFrequency + increment <= bandEdge))
+ {
+ float[] higherCutoffTaps = FilterFactory.getSinc(cutoffFrequency + increment, filterLength, windowType);
+ double higherCutoffResponse = FilterFactory.evaluate(higherCutoffTaps, bandEdge);
+
+ if(matchesObjective(higherCutoffResponse, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
+ {
+ cutoffFrequency += increment;
+ taps = higherCutoffTaps;
+ response = higherCutoffResponse;
+ }
+ else
+ {
+ increment /= 2.0;
+ }
+ }
+ //If the current cutoff meets the objective, decrease the increment to test for smaller resolution cutoffs
+ else if(matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
+ {
+ increment /= 2.0;
+ }
+ //Decrease the cutoff frequency - we can't meet the objective
+ else
+ {
+ cutoffFrequency -= increment;
+
+ if(cutoffFrequency <= 0)
+ {
+ if(logResults)
+ {
+ mLog.debug("Warning: cannot design channelizer filter with tap count [" + currentTapsPerChannel +
+ "] increasing tap count and starting over");
+ }
+
+ currentTapsPerChannel++;
+
+ //After we've attempted increasing the taps per channel 10x, give up
+ if(currentTapsPerChannel > (tapsPerChannel + 10))
+ {
+ throw new FilterDesignException("Couldn't design filter with taps per channel count in the " +
+ "range of " + tapsPerChannel + " - " + (tapsPerChannel + 10));
+ }
+
+ filterLength = channels * currentTapsPerChannel - 1;
+ cutoffFrequency = ((double)channelBandwidth / (double)sampleRate);
+ increment = cutoffFrequency * 0.1;
+ }
+
+ taps = FilterFactory.getSinc(cutoffFrequency, filterLength, windowType);
+ response = FilterFactory.evaluate(taps, bandEdge);
+ }
+ }
+
+ //This will probably never happen, but ...
+ if(!matchesObjective(response, PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE))
+ {
+ throw new FilterDesignException("Cannot design filter to specifications");
+ }
+
+ //This is an odd length filter - increase the length by 1 by pre-padding a zero coefficient
+ float[] extendedTaps = new float[taps.length + 1];
+ System.arraycopy(taps, 0, extendedTaps, 1, taps.length);
+
+ if(logResults)
+ {
+ mLog.debug("Polyphase Channelizer Filter Design Summary");
+ mLog.debug("-----------------------------------------------------");
+ mLog.debug("Input Sample Rate: " + sampleRate);
+ mLog.debug("Channel Bandwidth: " + channelBandwidth);
+ mLog.debug("Channels: " + channels);
+ mLog.debug("Window Type: " + windowType.name());
+ mLog.debug("Taps Per Channel - Requested:" + tapsPerChannel + " Actual:" + ((double)extendedTaps.length / (double)channels));
+ mLog.debug("Filter Length: " + (extendedTaps.length));
+ mLog.debug("Requested Cutoff Frequency: " + (sampleRate * bandEdge));
+ mLog.debug("Actual Cutoff Frequency: " + (sampleRate * cutoffFrequency));
+ mLog.debug("Attenuation at 1.0 OBJECTIVE: " + PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE);
+ mLog.debug("Attenuation at 1.00 Channels: " + evaluate(taps, bandEdge * 1.00));
+ mLog.debug("Attenuation at 1.25 Channels: " + evaluate(taps, bandEdge * 1.25));
+ mLog.debug("Attenuation at 1.50 Channels: " + evaluate(taps, bandEdge * 1.50));
+ mLog.debug("Attenuation at 1.75 Channels: " + evaluate(taps, bandEdge * 1.75));
+ mLog.debug("Attenuation at 2.00 Channels: " + evaluate(taps, bandEdge * 2.00));
+ }
+
+ return extendedTaps;
+ }
+
+ /**
+ * Creates a windowed-sync (Nyquist) filter.
+ *
+ * @param cutoff frequency (0.0 <> 0.5)
+ * @param length of the filter - must be odd-length
+ * @param windowType to use in designing the filter
+ * @return filter coefficients.
+ * @throws FilterDesignException if the requested length is not odd
+ */
+ public static float[] getSinc(double cutoff, int length, Window.WindowType windowType) throws FilterDesignException
+ {
+ if(length % 2 == 0)
+ {
+ throw new FilterDesignException("Sinc filters must be odd-length");
+ }
+
+ float[] coefficients = new float[length];
+ int half = length / 2;
+
+ double[] window = Window.getWindow(windowType, length);
+
+ double scalor = 2.0 * cutoff;
+ double piScalor = Math.PI * scalor;
+
+ coefficients[half] = (float)(1.0 * scalor);
+
+ for(int x = 1; x <= half; x++)
+ {
+ double a = piScalor * x;
+ double coefficient = scalor * Math.sin(a) / a;
+
+ coefficient *= window[half + x];
+ coefficients[half + x] = (float)coefficient;
+ coefficients[half - x] = (float)coefficient;
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Compares two doubles for equals and avoids any rounding error that are present.
+ * @param a value to compare
+ * @param objective value to compare
+ * @return true if they are equivalent within the margin of error
+ */
+ private static boolean matchesObjective(double a, double objective)
+ {
+ return Math.abs(a - objective) < 0.0000000001;
+ }
}
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/Window.java b/src/main/java/io/github/dsheirer/dsp/filter/Window.java
index 5ca75acd..ceb68afb 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/Window.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/Window.java
@@ -1,234 +1,439 @@
/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * sdrtrunk
+ * Copyright (C) 2014-2017 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ *
******************************************************************************/
package io.github.dsheirer.dsp.filter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Arrays;
/**
- * Window creator
- *
- * Blackman, Hamming and Hanning windows
- * based on windowing methods described at:
- * http://www.mstarlabs.com/dsp/goertzel/goertzel.html
- * and
- * Understanding Digital Signal Processing 3e, Lyons, p.91
- * and
- * Digital Filters for Everyone, Allred, p. 132
- *
+ * Window factory and corresponding utility methods.
*/
public class Window
{
- public static double[] getWindow( WindowType type, int length )
+ private final static Logger mLog = LoggerFactory.getLogger(Window.class);
+
+ private static final double TWO_PI = Math.PI * 2.0d;
+ private static final double FOUR_PI = Math.PI * 4.0d;
+ private static final double SIX_PI = Math.PI * 6.0d;
+ private static final double EIGHT_PI = Math.PI * 8.0d;
+
+ /**
+ * Creates a window of the specified type and length.
+ *
+ * @param type of window
+ * @param length of window
+ * @return window of the specified type and length
+ */
+ public static double[] getWindow(WindowType type, int length)
{
- switch( type )
- {
- case BLACKMAN:
- return getBlackmanWindow( length );
- case COSINE:
- return getCosineWindow( length );
- case HAMMING:
- return getHammingWindow( length );
- case HANNING:
- return getHanningWindow( length );
- case NONE:
- default:
- return getRectangularWindow( length );
- }
+ switch(type)
+ {
+ case BLACKMAN:
+ return getBlackman(length);
+ case BLACKMAN_HARRIS_4:
+ return getBlackmanHarris4(length);
+ case BLACKMAN_HARRIS_7:
+ return getBlackmanHarris7(length);
+ case BLACKMAN_NUTALL:
+ return getBlackmanNutall(length);
+ case COSINE:
+ return getCosine(length);
+ case FLAT_TOP:
+ return getFlatTop(length);
+ case HAMMING:
+ return getHamming(length);
+ case HANN:
+ return getHann(length);
+ case NUTALL:
+ return getNutall(length);
+ case NONE:
+ default:
+ return getRectangular(length);
+ }
}
- public static double[] getRectangularWindow( int length )
+ /**
+ * Creates an all-pass or rectangular window.
+ *
+ * @param length of the window
+ * @return rectangular window
+ */
+ public static double[] getRectangular(int length)
{
- double[] coefficients = new double[ length ];
+ double[] coefficients = new double[length];
- Arrays.fill( coefficients, 1.0d );
-
- return coefficients;
- }
-
- public static double[] getCosineWindow( int length )
- {
- double[] coefficients = new double[ length ];
+ Arrays.fill(coefficients, 1.0d);
-
- if( length % 2 == 0 ) //Even length
- {
- int half = (int)( ( length - 1 ) / 2 );
-
- for( int x = -half; x < length / 2 + 1; x++ )
- {
- coefficients[ x + half ] = Math.cos(
- ( (double)x * Math.PI ) / ( (double)length + 1.0d ) );
- }
- }
- else //Odd length
- {
- int half = (int) length / 2;
-
- for( int x = -half; x < half + 1; x++ )
- {
- coefficients[ x + half ] = Math.cos(
- ( (double)x * Math.PI ) / ( (double)length + 1.0d ) );
- }
- }
-
- return coefficients;
+ return coefficients;
}
- public static double[] getBlackmanWindow( int length )
+ /**
+ * Creates a cosine window of the specified length.
+ *
+ * @param length of the window
+ * @return cosine window
+ */
+ public static double[] getCosine(int length)
{
- double[] coefficients = new double[ length ];
-
- for( int x = 0; x < length; x++ )
- {
- coefficients[ x ] = .426591D -
- ( .496561D * Math.cos( ( Math.PI * 2.0D * (double)x ) / (double)( length - 1 ) ) +
- ( .076848D * Math.cos( ( Math.PI * 4.0D * (double)x ) / (double)( length - 1 ) ) ) );
- }
+ double[] coefficients = new double[length];
- return coefficients;
- }
-
- public static double[] getHammingWindow( int length )
- {
- double[] coefficients = new double[ length ];
-
- if( length % 2 == 0 ) //Even length
- {
- for( int x = 0; x < length; x++ )
- {
- coefficients[ x ] = .54D - ( .46D *
- Math.cos( ( Math.PI * ( 2.0D * (double)x + 1.0d ) ) / (double)( length - 1 ) ) );
- }
- }
- else //Odd length
- {
- for( int x = 0; x < length; x++ )
- {
- coefficients[ x ] = .54D - ( .46D *
- Math.cos( ( 2.0D * Math.PI * (double)x ) / (double)( length - 1 ) ) );
- }
- }
+ if(length % 2 == 0) //Even length
+ {
+ int half = (int)((length - 1) / 2);
- return coefficients;
+ for(int x = -half; x < length / 2 + 1; x++)
+ {
+ coefficients[x + half] = Math.cos(
+ ((double)x * Math.PI) / ((double)length + 1.0d));
+ }
+ }
+ else //Odd length
+ {
+ int half = (int)length / 2;
+
+ for(int x = -half; x < half + 1; x++)
+ {
+ coefficients[x + half] = Math.cos(
+ ((double)x * Math.PI) / ((double)length + 1.0d));
+ }
+ }
+
+ return coefficients;
}
- public static double[] getHanningWindow( int length )
+ /**
+ * Creates a 3-term Blackman window.
+ *
+ * @param length of the window
+ * @return Blackman window
+ */
+ public static double[] getBlackman(int length)
{
- double[] coefficients = new double[ length ];
+ double[] coefficients = new double[length];
- if( length % 2 == 0 ) //Even length
- {
- for( int x = 0; x < length; x++ )
- {
- coefficients[ x ] = .5D - ( .5D *
- Math.cos( ( Math.PI * ( 2.0D * (double)x + 1 ) ) / (double)( length - 1 ) ) );
- }
- }
- else //Odd length
- {
- for( int x = 0; x < length; x++ )
- {
- coefficients[ x ] = .5D - ( .5D *
- Math.cos( ( 2.0D * Math.PI * (double)x ) / (double)( length - 1 ) ) );
- }
- }
+ double denominator = length - 1;
+
+ double a0 = 0.426590713672;
+ double a1 = 0.496560619089;
+ double a2 = 0.0768486672399;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 -
+ (a1 * Math.cos((TWO_PI * (double)x) / denominator)) +
+ (a2 * Math.cos((FOUR_PI * (double)x) / denominator));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a 4-term Nutall window
+ *
+ * @param length of the window
+ * @return Nutall window
+ */
+ public static double[] getNutall(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double denominator = length - 1;
+ double a0 = 0.355768;
+ double a1 = 0.487396;
+ double a2 = 0.144232;
+ double a3 = 0.012604;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 -
+ (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
+ (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
+ (a3 * Math.cos(SIX_PI * (double)x / denominator));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a 4-term Blackman-Nutall window
+ *
+ * @param length of the window
+ * @return Blackman-Nutall window
+ */
+ public static double[] getBlackmanNutall(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double denominator = length - 1;
+ double a0 = 0.3635819;
+ double a1 = 0.4891775;
+ double a2 = 0.1365995;
+ double a3 = 0.0106411;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 -
+ (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
+ (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
+ (a3 * Math.cos(SIX_PI * (double)x / denominator));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a 4-term Blackman-Harris window
+ *
+ * @param length of the window
+ * @return Blackman-Harris window
+ */
+ public static double[] getBlackmanHarris4(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double denominator = length - 1;
+ double a0 = 0.35875;
+ double a1 = 0.48829;
+ double a2 = 0.14128;
+ double a3 = 0.01168;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 -
+ (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
+ (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
+ (a3 * Math.cos(SIX_PI * (double)x / denominator));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a 7-term Blackman-Harris window
+ *
+ * @param length of the window
+ * @return Blackman-Harris window
+ */
+ public static double[] getBlackmanHarris7(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double denominator = length - 1;
+
+ double a0 = 0.27105140069342;
+ double a1 = 0.43329793923448;
+ double a2 = 0.21812299954311;
+ double a3 = 0.06592544638803;
+ double a4 = 0.01081174209837;
+ double a5 = 0.00077658482522;
+ double a6 = 0.00001388721735;
- return coefficients;
+ for(int x = 0; x < length; x++)
+ {
+ double w = TWO_PI * (double)x / denominator;
+
+ coefficients[x] = a0 -
+ (a1 * Math.cos(w)) +
+ (a2 * Math.cos(2.0 * w)) -
+ (a3 * Math.cos(3.0 * w)) +
+ (a4 * Math.cos(4.0 * w)) -
+ (a5 * Math.cos(5.0 * w)) +
+ (a6 * Math.cos(6.0 * w));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a Hamming window
+ *
+ * @param length of the window
+ * @return Hamming window
+ */
+ public static double[] getHamming(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double a0 = 0.54;
+ double a1 = 0.46;
+ double denominator = length;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 - (a1 * Math.cos(TWO_PI * (double)x / denominator));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a Hann window
+ *
+ * @param length of the window
+ * @return Hann window
+ */
+ public static double[] getHann(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double a0 = 0.5;
+ double a1 = 0.5;
+ double denominator = length;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 - (a1 * Math.cos(TWO_PI * (double)x / denominator));
+ }
+
+ return coefficients;
+ }
+
+ /**
+ * Creates a 4-term Blackman-Harris window
+ *
+ * @param length of the window
+ * @return Blackman-Harris window
+ */
+ public static double[] getFlatTop(int length)
+ {
+ double[] coefficients = new double[length];
+
+ double denominator = length - 1;
+
+ double a0 = 0.215578948;
+ double a1 = 0.41663158;
+ double a2 = 0.277263158;
+ double a3 = 0.083578947;
+ double a4 = 0.006947368;
+
+ for(int x = 0; x < length; x++)
+ {
+ coefficients[x] = a0 -
+ (a1 * Math.cos(TWO_PI * (double)x / denominator)) +
+ (a2 * Math.cos(FOUR_PI * (double)x / denominator)) -
+ (a3 * Math.cos(SIX_PI * (double)x / denominator)) +
+ (a4 * Math.cos(EIGHT_PI * (double)x / denominator));
+ }
+
+ return coefficients;
+ }
+
+
+ private static double getKaiserBeta(double attenuation)
+ {
+ if(attenuation > 50.0)
+ {
+ return 0.1102 * (attenuation - 8.7);
+ }
+ else if(attenuation >= 21.0)
+ {
+ return (0.5842 * Math.pow(attenuation - 21.0, 0.4)) + (0.07886 * (attenuation - 21.0));
+ }
+ else
+ {
+ return 0.0;
+ }
}
/**
* Apply the window against an array of float-type samples
*/
- public static float[] apply( double[] coefficients, float[] samples )
+ public static float[] apply(double[] coefficients, float[] samples)
{
- for( int x = 0; x < coefficients.length; x++ )
- {
- samples[ x ] = (float)( samples[ x ] * coefficients[ x ] );
- }
-
- return samples;
- }
-
- public static float[] apply( WindowType type, float[] samples )
- {
- double[] coefficients = getWindow( type, samples.length );
-
- return apply( coefficients, samples );
- }
-
- /**
- * Apply the window against an array of float-type samples
- */
- public static double[] apply( double[] coefficients, double[] samples )
- {
- for( int x = 0; x < coefficients.length; x++ )
- {
- samples[ x ] = samples[ x ] * coefficients[ x ];
- }
-
- return samples;
+ for(int x = 0; x < coefficients.length; x++)
+ {
+ samples[x] = (float)(samples[x] * coefficients[x]);
+ }
+
+ return samples;
}
- public static double[] apply( WindowType type, double[] samples )
+ /**
+ * Apply the window type against the float array of samples
+ *
+ * @param type of window to use
+ * @param samples to be windowed
+ * @return windowed samples
+ */
+ public static float[] apply(WindowType type, float[] samples)
{
- double[] coefficients = getWindow( type, samples.length );
-
- return apply( coefficients, samples );
+ double[] coefficients = getWindow(type, samples.length);
+
+ return apply(coefficients, samples);
}
-
-
-
+
+ /**
+ * Apply the window against an array of double-type samples
+ */
+ public static double[] apply(double[] coefficients, double[] samples)
+ {
+ for(int x = 0; x < coefficients.length; x++)
+ {
+ samples[x] = samples[x] * coefficients[x];
+ }
+
+ return samples;
+ }
+
+ /**
+ * Apply the window type against the double array of samples
+ *
+ * @param type of window to use
+ * @param samples to be windowed
+ * @return windowed samples
+ */
+ public static double[] apply(WindowType type, double[] samples)
+ {
+ double[] coefficients = getWindow(type, samples.length);
+
+ return apply(coefficients, samples);
+ }
+
/**
* Window types
*/
public enum WindowType
{
- BLACKMAN, COSINE, HAMMING, HANNING, NONE;
- }
-
- /**
- * Utility to log the double arrays
- */
- public static String arrayToString( double[] array, boolean breaks )
- {
- StringBuilder sb = new StringBuilder();
- for( int x = 0; x < array.length; x++ )
- {
- sb.append( x + ":" + array[ x ] );
+ BLACKMAN("Blackman"),
+ BLACKMAN_HARRIS_4("Blackman-Harris 4"),
+ BLACKMAN_HARRIS_7("Blackman-Harris 7"),
+ BLACKMAN_NUTALL("Blackman-Nutall"),
+ COSINE("Cosine"),
+ FLAT_TOP("Flat Top"),
+ HAMMING("Hamming"),
+ HANN("Hann"),
+ NUTALL("Nutall"),
+ NONE("None");
- if( breaks )
- {
- sb.append( "\n" );
- }
- else
- {
- if( x % 8 == 7 )
- {
- sb.append( "\n" );
- }
- else
- {
- sb.append( "\t" );
- }
- }
- }
-
- return sb.toString();
- }
+ private String mLabel;
+
+ WindowType(String label)
+ {
+ mLabel = label;
+ }
+
+ public String toString()
+ {
+ return mLabel;
+ }
+ }
}
diff --git a/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java
similarity index 93%
rename from src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java
index 0505c1b0..85b2e832 100644
--- a/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java
@@ -16,16 +16,15 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.channelizer;
+package io.github.dsheirer.dsp.filter.channelizer;
+import io.github.dsheirer.sample.Broadcaster;
+import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
+import io.github.dsheirer.source.ISourceEventListener;
+import io.github.dsheirer.source.SourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.Broadcaster;
-import sample.Listener;
-import sample.complex.ComplexBuffer;
-import source.ISourceEventListener;
-import source.SourceEvent;
-import source.tuner.channel.polyphase.PolyphaseChannelSource;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
diff --git a/src/source/tuner/channel/polyphase/ChannelCalculator.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java
similarity index 96%
rename from src/source/tuner/channel/polyphase/ChannelCalculator.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java
index a1b0940c..1210e45a 100644
--- a/src/source/tuner/channel/polyphase/ChannelCalculator.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java
@@ -1,26 +1,24 @@
/*******************************************************************************
- * sdrtrunk
+ * *********************************************************************************************************************
+ * sdr-trunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
+ * *********************************************************************************************************************
******************************************************************************/
-package source.tuner.channel.polyphase;
+package io.github.dsheirer.dsp.filter.channelizer;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import source.tuner.channel.TunerChannel;
import java.text.DecimalFormat;
import java.util.ArrayList;
diff --git a/src/source/tuner/channel/polyphase/ChannelizerViewer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java
similarity index 81%
rename from src/source/tuner/channel/polyphase/ChannelizerViewer.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java
index b4dbcd30..e6d01e0c 100644
--- a/src/source/tuner/channel/polyphase/ChannelizerViewer.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java
@@ -1,46 +1,49 @@
/*******************************************************************************
- * sdrtrunk
+ * *********************************************************************************************************************
+ * sdr-trunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
+ * *********************************************************************************************************************
******************************************************************************/
-package source.tuner.channel.polyphase;
+package io.github.dsheirer.dsp.filter.channelizer;
+import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.SampleType;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
+import io.github.dsheirer.settings.SettingsManager;
+import io.github.dsheirer.source.ISourceEventProcessor;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.SourceException;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
+import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
+import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel;
+import io.github.dsheirer.source.tuner.test.TestTuner;
+import io.github.dsheirer.spectrum.DFTProcessor;
+import io.github.dsheirer.spectrum.DFTSize;
+import io.github.dsheirer.spectrum.SpectrumPanel;
+import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.Listener;
-import sample.SampleType;
-import sample.complex.ComplexBuffer;
-import settings.SettingsManager;
-import source.ISourceEventProcessor;
-import source.SourceEvent;
-import source.SourceException;
-import source.tuner.channel.TunerChannel;
-import source.tuner.channel.TunerChannelSource;
-import source.tuner.configuration.TunerConfigurationModel;
-import source.tuner.test.TestTuner;
-import spectrum.DFTProcessor;
-import spectrum.DFTSize;
-import spectrum.SpectrumPanel;
-import spectrum.converter.ComplexDecibelConverter;
-import javax.swing.*;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-import java.awt.*;
+import java.awt.Dimension;
+import java.awt.EventQueue;
public class ChannelizerViewer extends JFrame
{
@@ -70,10 +73,10 @@ public class ChannelizerViewer extends JFrame
{
mTestTuner = new TestTuner();
- mChannelCount = (int)(mTestTuner.getTunerController().getFrequencyController().getSampleRate() / CHANNEL_BANDWIDTH);
+ mChannelCount = (int)(mTestTuner.getTunerController().getSampleRate() / CHANNEL_BANDWIDTH);
mChannelsPerRow = channelsPerRow;
- mToneFrequency = mTestTuner.getTunerController().getFrequencyController().getFrequency() + 19200;
+ mToneFrequency = mTestTuner.getTunerController().getFrequency() + 19200;
mTestTuner.getTunerController().setToneFrequency(mToneFrequency);
@@ -109,7 +112,7 @@ public class ChannelizerViewer extends JFrame
if(mPrimarySpectrumPanel == null)
{
mPrimarySpectrumPanel = new PrimarySpectrumPanel(mSettingsManager,
- mTestTuner.getTunerController().getFrequencyController().getSampleRate());
+ mTestTuner.getTunerController().getSampleRate());
mPrimarySpectrumPanel.setPreferredSize(new Dimension(1000, 200));
mPrimarySpectrumPanel.setDFTSize(mMainPanelDFTSize);
mTestTuner.getSourceManager().addComplexBufferListener(mPrimarySpectrumPanel);
@@ -126,9 +129,9 @@ public class ChannelizerViewer extends JFrame
mControlPanel.setLayout(new MigLayout("insets 0 0 0 0", "", ""));
mControlPanel.add(new JLabel("Tone:"), "align left");
- int maximumFrequencyOffset = (int)(mTestTuner.getTunerController().getFrequencyController().getSampleRate() / 2);
- long minimumFrequency = mTestTuner.getTunerController().getFrequencyController().getFrequency() - maximumFrequencyOffset;
- long maximumFrequency = mTestTuner.getTunerController().getFrequencyController().getFrequency() + maximumFrequencyOffset;
+ int maximumFrequencyOffset = (int)(mTestTuner.getTunerController().getSampleRate() / 2);
+ long minimumFrequency = mTestTuner.getTunerController().getFrequency() - maximumFrequencyOffset;
+ long maximumFrequency = mTestTuner.getTunerController().getFrequency() + maximumFrequencyOffset;
SpinnerNumberModel model = new SpinnerNumberModel(mToneFrequency, minimumFrequency, maximumFrequency,
100 );
@@ -148,11 +151,9 @@ public class ChannelizerViewer extends JFrame
mControlPanel.add(spinner);
mControlPanel.add(new JLabel("Hz"));
- mControlPanel.add(new JLabel("Sample Rate: " +
- mTestTuner.getTunerController().getFrequencyController().getSampleRate() + " Hz"), "push,align right");
+ mControlPanel.add(new JLabel("Sample Rate: " + mTestTuner.getTunerController().getSampleRate() + " Hz"), "push,align right");
- mControlPanel.add(new JLabel("Max: " +
- (mTestTuner.getTunerController().getFrequencyController().getSampleRate() / 2) + " Hz"), "push,align right");
+ mControlPanel.add(new JLabel("Max: " + (mTestTuner.getTunerController().getSampleRate() / 2) + " Hz"), "push,align right");
mControlPanel.add(new JLabel("Channels: " + mChannelCount), "push,align right");
}
@@ -186,7 +187,7 @@ public class ChannelizerViewer extends JFrame
{
setLayout(new MigLayout("insets 0 0 0 0", "fill", "fill"));
- double spectralBandwidth = mTestTuner.getTunerController().getFrequencyController().getSampleRate();
+ double spectralBandwidth = mTestTuner.getTunerController().getSampleRate();
double halfSpectralBandwidth = spectralBandwidth / 2.0;
int channelToLog = -1;
@@ -194,7 +195,7 @@ public class ChannelizerViewer extends JFrame
for(int x = 0; x < mChannelCount; x++)
{
//place the channels left and right of 10.0 MHz
- long frequency = (long)(mTestTuner.getTunerController().getFrequencyController().getFrequency() +
+ long frequency = (long)(mTestTuner.getTunerController().getFrequency() +
((x * CHANNEL_BANDWIDTH) - halfSpectralBandwidth) + (CHANNEL_BANDWIDTH / 2));
mLog.debug("Channel " + x + " Frequency: " + frequency);
@@ -340,4 +341,4 @@ public class ChannelizerViewer extends JFrame
});
}
-}
+}
\ No newline at end of file
diff --git a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java
similarity index 94%
rename from src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java
index dca0f4ca..9ab818b9 100644
--- a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizer.java
@@ -16,16 +16,16 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.channelizer;
+package io.github.dsheirer.dsp.filter.channelizer;
-import dsp.filter.FilterFactory;
-import dsp.filter.Window;
-import dsp.filter.fir.real.RealFIRFilter;
-import dsp.mixer.Oscillator;
+import io.github.dsheirer.dsp.filter.FilterFactory;
+import io.github.dsheirer.dsp.filter.Window;
+import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter;
+import io.github.dsheirer.dsp.mixer.Oscillator;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.complex.ComplexBuffer;
public class ComplexPolyphaseChannelizer extends AbstractComplexPolyphaseChannelizer
{
diff --git a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java
similarity index 97%
rename from src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java
index d3bdded6..3e0995dc 100644
--- a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java
@@ -16,17 +16,15 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.channelizer;
+package io.github.dsheirer.dsp.filter.channelizer;
-import dsp.filter.FilterFactory;
-import dsp.filter.Window;
-import dsp.filter.design.FilterDesignException;
+import io.github.dsheirer.dsp.filter.FilterFactory;
+import io.github.dsheirer.dsp.filter.Window.WindowType;
+import io.github.dsheirer.dsp.filter.design.FilterDesignException;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.complex.ComplexBuffer;
-
-import java.text.DecimalFormat;
public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChannelizer
{
@@ -83,7 +81,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
super(sampleRate, (int)(sampleRate / DEFAULT_MINIMUM_CHANNEL_BANDWIDTH));
float[] filterTaps = FilterFactory.getSincChannelizer(getChannelSampleRate(), getChannelCount(),
- tapsPerFilter, Window.WindowType.BLACKMAN_HARRIS_7, true);
+ tapsPerFilter, WindowType.BLACKMAN_HARRIS_7, true);
init(filterTaps);
}
@@ -101,7 +99,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
super.setSampleRate(sampleRate);
float[] filterTaps = FilterFactory.getSincChannelizer(getChannelSampleRate(), getChannelCount(),
- 15, Window.WindowType.BLACKMAN_HARRIS_7, true);
+ 15, WindowType.BLACKMAN_HARRIS_7, true);
init(filterTaps);
}
diff --git a/src/source/tuner/channel/polyphase/PolyphaseChannelManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java
similarity index 84%
rename from src/source/tuner/channel/polyphase/PolyphaseChannelManager.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java
index c15b64a2..2d673111 100644
--- a/src/source/tuner/channel/polyphase/PolyphaseChannelManager.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java
@@ -1,43 +1,37 @@
/*******************************************************************************
- * sdrtrunk
+ * sdr-trunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
*
******************************************************************************/
-package source.tuner.channel.polyphase;
+package io.github.dsheirer.dsp.filter.channelizer;
-import dsp.filter.channelizer.ComplexPolyphaseChannelizerM2;
-import dsp.filter.design.FilterDesignException;
+import io.github.dsheirer.dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor;
+import io.github.dsheirer.dsp.filter.channelizer.output.OneChannelOutputProcessor;
+import io.github.dsheirer.dsp.filter.channelizer.output.TwoChannelOutputProcessor;
+import io.github.dsheirer.dsp.filter.design.FilterDesignException;
+import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.complex.IComplexBufferProvider;
+import io.github.dsheirer.source.Source;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.SourceException;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.Listener;
-import sample.complex.IComplexBufferProvider;
-import source.SourceEvent;
-import source.SourceException;
-import source.tuner.channel.ChannelManager;
-import source.tuner.channel.TunerChannel;
-import source.tuner.channel.TunerChannelSource;
-import source.tuner.channel.polyphase.output.IPolyphaseChannelOutputProcessor;
-import source.tuner.channel.polyphase.output.OneChannelOutputProcessor;
-import source.tuner.channel.polyphase.output.TwoChannelOutputProcessor;
-import source.tuner.channel.FrequencyController;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-public class PolyphaseChannelManager extends ChannelManager
+public class PolyphaseChannelManager implements Listener
{
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelManager.class);
private static final int CHANNEL_BANDWIDTH = 12500;
@@ -63,13 +57,12 @@ public class PolyphaseChannelManager extends ChannelManager
* Note: add this channel manager as a source event listener to the complex buffer provider to ensure this manager
* adapts to changes in source frequency and sample rate.
*
- * @param frequencyController that can be controlled by this channel manager
* @param complexBufferProvider that supports register/deregister of this channel manager
+ * @param frequency of the provided complex buffer samples
+ * @param sampleRate of the provided complex buffer samples
*/
- public PolyphaseChannelManager(FrequencyController frequencyController, IComplexBufferProvider complexBufferProvider)
+ public PolyphaseChannelManager(IComplexBufferProvider complexBufferProvider, long frequency, int sampleRate)
{
- super(frequencyController);
-
if(complexBufferProvider == null)
{
throw new IllegalArgumentException("Complex buffer provider argument cannot be null");
@@ -78,37 +71,28 @@ public class PolyphaseChannelManager extends ChannelManager
mComplexBufferProvider = complexBufferProvider;
//Ensure that sample rate is a multiple of channel sample rate since polyphase channelizer is M2
- if(getFrequencyController().getSampleRate() % (CHANNEL_SAMPLE_RATE) != 0)
+ if(sampleRate % (CHANNEL_SAMPLE_RATE) != 0)
{
- throw new IllegalArgumentException("Tuner sample rate [" + getFrequencyController().getSampleRate() +
- "] must be an even multiple of " + CHANNEL_SAMPLE_RATE + " Hz");
+ throw new IllegalArgumentException("Tuner sample rate [" + sampleRate + "] must be an even multiple of " +
+ CHANNEL_SAMPLE_RATE + " Hz");
}
- int channelCount = (int)(getFrequencyController().getSampleRate() / CHANNEL_BANDWIDTH);
+ int channelCount = sampleRate / CHANNEL_BANDWIDTH;
- mChannelCalculator = new ChannelCalculator(getFrequencyController().getSampleRate(), channelCount,
- getFrequencyController().getFrequency(), CHANNEL_OVERSAMPLING);
+ mChannelCalculator = new ChannelCalculator(sampleRate, channelCount, frequency, CHANNEL_OVERSAMPLING);
mBufferProcessor = new ScheduledBufferProcessor(500, 100, 50, 50);
- updateChannelizer(getFrequencyController().getSampleRate(), null);
- }
-
- @Override
- protected long calculateCenterFrequency()
- {
- return 0;
+ updateChannelizer(sampleRate, null);
}
/**
* Provides a Digital Drop Channel (DDC) for the specified tuner channel or returns null if the channel can't be
* sourced due to the current center frequency and/or sample rate.
- *
* @param tunerChannel specifying center frequency and bandwidth.
* @return source or null.
*/
- @Override
- protected TunerChannelSource getTunerChannelSource(TunerChannel tunerChannel)
+ public Source getChannel(TunerChannel tunerChannel)
{
PolyphaseChannelSource channelSource = null;
@@ -123,8 +107,6 @@ public class PolyphaseChannelManager extends ChannelManager
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
CHANNEL_SAMPLE_RATE);
- channelSource.setFrequency(mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes));
-
mChannelSources.add(channelSource);
}
}
@@ -141,7 +123,6 @@ public class PolyphaseChannelManager extends ChannelManager
/**
* Creates a processor to process the channelizer channel indexes into a composite output stream providing
* channelized complex sample buffers to a registered source listener.
- *
* @param indexes to target by the output processor
* @return output processor compatible with the number of indexes to monitor
*/
@@ -190,8 +171,6 @@ public class PolyphaseChannelManager extends ChannelManager
{
synchronized(mBufferProcessor)
{
- super.releaseSource(channelSource);
-
mChannelSources.remove(channelSource);
if(mPolyphaseChannelizer.getRegisteredChannelCount() == 0)
@@ -231,7 +210,7 @@ public class PolyphaseChannelManager extends ChannelManager
* event will be passed to all output processors to ensure the change is fully promulgated before restarting the
* buffer processor if it was already running.
*/
- private void updateChannelizer(double sampleRate, SourceEvent sourceEvent)
+ private void updateChannelizer(int sampleRate, SourceEvent sourceEvent)
{
mChannelCalculator.setSampleRate(sampleRate);
@@ -288,7 +267,7 @@ public class PolyphaseChannelManager extends ChannelManager
*/
private void updateOutputProcessors(SourceEvent sourceEvent)
{
- for(PolyphaseChannelSource channelSource : mChannelSources)
+ for(PolyphaseChannelSource channelSource: mChannelSources)
{
updateOutputProcessor(channelSource);
@@ -381,4 +360,4 @@ public class PolyphaseChannelManager extends ChannelManager
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/source/tuner/channel/polyphase/PolyphaseChannelSource.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
similarity index 87%
rename from src/source/tuner/channel/polyphase/PolyphaseChannelSource.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
index 8b7babaa..e2985c2a 100644
--- a/src/source/tuner/channel/polyphase/PolyphaseChannelSource.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
@@ -1,32 +1,30 @@
/*******************************************************************************
- * sdrtrunk
+ * *********************************************************************************************************************
+ * sdr-trunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
+ * *********************************************************************************************************************
******************************************************************************/
-package source.tuner.channel.polyphase;
+package io.github.dsheirer.dsp.filter.channelizer;
-import source.tuner.channel.polyphase.output.IPolyphaseChannelOutputProcessor;
+import io.github.dsheirer.dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor;
+import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
+import io.github.dsheirer.sample.complex.ComplexToComplexBufferAssembler;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
+import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.Listener;
-import sample.complex.ComplexBuffer;
-import sample.complex.ComplexToComplexBufferAssembler;
-import source.SourceEvent;
-import source.tuner.channel.TunerChannel;
-import source.tuner.channel.TunerChannelSource;
public class PolyphaseChannelSource extends TunerChannelSource
{
diff --git a/src/source/tuner/channel/polyphase/ScheduledBufferProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java
similarity index 88%
rename from src/source/tuner/channel/polyphase/ScheduledBufferProcessor.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java
index 810cb838..f9b3563f 100644
--- a/src/source/tuner/channel/polyphase/ScheduledBufferProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java
@@ -1,29 +1,26 @@
/*******************************************************************************
- * sdrtrunk
+ * sdr-trunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
*
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
*
******************************************************************************/
-package source.tuner.channel.polyphase;
+package io.github.dsheirer.dsp.filter.channelizer;
+import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.OverflowableTransferQueue;
+import io.github.dsheirer.sample.real.IOverflowListener;
+import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.Listener;
-import sample.OverflowableTransferQueue;
-import sample.real.IOverflowListener;
-import util.ThreadPool;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/source/tuner/channel/polyphase/output/ChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java
similarity index 94%
rename from src/source/tuner/channel/polyphase/output/ChannelOutputProcessor.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java
index 2a32baa4..272f69d0 100644
--- a/src/source/tuner/channel/polyphase/output/ChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java
@@ -16,16 +16,16 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.tuner.channel.polyphase.output;
+package io.github.dsheirer.dsp.filter.channelizer.output;
-import dsp.mixer.Oscillator;
+import io.github.dsheirer.dsp.mixer.Oscillator;
+import io.github.dsheirer.sample.OverflowableTransferQueue;
+import io.github.dsheirer.sample.complex.Complex;
+import io.github.dsheirer.sample.complex.ComplexSampleListener;
+import io.github.dsheirer.sample.real.IOverflowListener;
+import io.github.dsheirer.source.Source;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.OverflowableTransferQueue;
-import sample.complex.Complex;
-import sample.complex.ComplexSampleListener;
-import sample.real.IOverflowListener;
-import source.Source;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/source/tuner/channel/polyphase/output/IPolyphaseChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java
similarity index 93%
rename from src/source/tuner/channel/polyphase/output/IPolyphaseChannelOutputProcessor.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java
index f8348990..ce56168a 100644
--- a/src/source/tuner/channel/polyphase/output/IPolyphaseChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java
@@ -16,10 +16,10 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.tuner.channel.polyphase.output;
+package io.github.dsheirer.dsp.filter.channelizer.output;
-import sample.complex.ComplexSampleListener;
-import source.Source;
+import io.github.dsheirer.sample.complex.ComplexSampleListener;
+import io.github.dsheirer.source.Source;
import java.util.List;
diff --git a/src/source/tuner/channel/polyphase/output/OneChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java
similarity index 96%
rename from src/source/tuner/channel/polyphase/output/OneChannelOutputProcessor.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java
index 44fd12f9..e8adb854 100644
--- a/src/source/tuner/channel/polyphase/output/OneChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java
@@ -16,11 +16,11 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.tuner.channel.polyphase.output;
+package io.github.dsheirer.dsp.filter.channelizer.output;
+import io.github.dsheirer.sample.complex.ComplexSampleListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.complex.ComplexSampleListener;
import java.util.List;
diff --git a/src/source/tuner/channel/polyphase/output/TwoChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java
similarity index 96%
rename from src/source/tuner/channel/polyphase/output/TwoChannelOutputProcessor.java
rename to src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java
index e96818e5..350567a3 100644
--- a/src/source/tuner/channel/polyphase/output/TwoChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java
@@ -16,11 +16,11 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.tuner.channel.polyphase.output;
+package io.github.dsheirer.dsp.filter.channelizer.output;
+import io.github.dsheirer.sample.complex.ComplexSampleListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.complex.ComplexSampleListener;
import java.util.List;
diff --git a/src/dsp/filter/correction/IQCorrectionFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java
similarity index 93%
rename from src/dsp/filter/correction/IQCorrectionFilter.java
rename to src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java
index 14f84de8..ca219ac5 100644
--- a/src/dsp/filter/correction/IQCorrectionFilter.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java
@@ -16,9 +16,9 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.correction;
+package io.github.dsheirer.dsp.filter.correction;
-import sample.complex.ComplexBuffer;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
public class IQCorrectionFilter
{
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/FIRFilterSpecification.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/FIRFilterSpecification.java
index 661038df..d26483dd 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/fir/FIRFilterSpecification.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/FIRFilterSpecification.java
@@ -1,6 +1,9 @@
package io.github.dsheirer.dsp.filter.fir;
import io.github.dsheirer.dsp.filter.fir.remez.FIRLinearPhaseFilterType;
+import org.apache.commons.lang3.Validate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -9,1123 +12,1099 @@ import java.util.List;
public class FIRFilterSpecification
{
- private static final DecimalFormat DECIMAL_FORMATTER = new DecimalFormat( "0.00000" );
-
- private FIRLinearPhaseFilterType mRemezFilterType;
- private int mOrder;
- private int mGridDensity = 16;
- private List mFrequencyBands = new ArrayList<>();
-
- /**
- * Constructs a Remez filter specification. Use one of the filter builders to construct a Remez
- * filter specification.
- *
- * @param order of the filter
- * @param frequencyBands containing the start and stop values for each frequency band normalized
- * to the sample rate where each frequency value is in the range (0.0 <> 0.5)
- * @param amplitudes of each frequency band
- * @param weights of the band ripples normalized to the largest ripple value
- */
- private FIRFilterSpecification( FIRLinearPhaseFilterType type, int order, int gridDensity )
- {
- mRemezFilterType = type;
- mOrder = order;
- mGridDensity = gridDensity;
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append( "Type: " );
- sb.append( mRemezFilterType.name() );
- sb.append( " Order: " );
- sb.append( mOrder );
- sb.append( " Extrema: " );
- sb.append( getExtremaCount() );
- sb.append( " Grid Density: " );
- sb.append( mGridDensity );
- sb.append( " Grid Size: " );
- sb.append( getGridSize() );
-
- for ( FrequencyBand band : mFrequencyBands )
- {
- sb.append( "\n" );
- sb.append( band.toString() );
- }
-
- return sb.toString();
- }
-
- public void addFrequencyBand( FrequencyBand band )
- {
- mFrequencyBands.add( band );
-
- updateGridSize();
- }
-
- public void addFrequencyBands( Collection bands )
- {
- mFrequencyBands.addAll( bands );
-
- updateGridSize();
- }
-
- public List getFrequencyBands()
- {
- return mFrequencyBands;
- }
-
- /**
- * Returns the total bandwidth of the defined frequency bands in the specification.
- *
- * @return total bandwidth -- should be less than or equal to 0.5
- */
- public double getTotalBandwidth()
- {
- double bandwidth = 0.0;
-
- for ( FrequencyBand band : mFrequencyBands )
- {
- bandwidth += band.getBandWidth();
- }
-
- return bandwidth;
- }
-
- /**
- * Creates a Low-Pass FIR filter specification builder that allows you to define the filter
- * parameters and create a filter specification.
- */
- public static LowPassBuilder lowPassBuilder()
- {
- return new LowPassBuilder();
- }
-
- /**
- * Creates a High-Pass FIR filter specification builder that allows you to define the filter
- * parameters and create a filter specification.
- */
- public static HighPassBuilder highPassBuilder()
- {
- return new HighPassBuilder();
- }
-
- /**
- * Creates a Band-Pass FIR filter specification builder that allows you to define the filter
- * parameters and create a filter specification.
- */
- public static BandPassBuilder bandPassBuilder()
- {
- return new BandPassBuilder();
- }
-
- public FIRLinearPhaseFilterType getFilterType()
- {
- return mRemezFilterType;
- }
-
- public boolean isSymmetrical()
- {
- return mRemezFilterType == FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL ||
- mRemezFilterType == FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL;
- }
-
- public boolean isAntiSymmetrical()
- {
- return mRemezFilterType == FIRLinearPhaseFilterType.TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL ||
- mRemezFilterType == FIRLinearPhaseFilterType.TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL;
- }
-
- public int getOrder()
- {
- return mOrder;
- }
-
- public boolean isEvenOrder()
- {
- return mOrder % 2 == 0;
- }
-
- public boolean isOddOrder()
- {
- return mOrder % 2 == 1;
- }
-
- public int getFilterLength()
- {
- return mOrder + 1;
- }
-
- public int getBandCount()
- {
- return mFrequencyBands.size();
- }
-
- /**
- * Grid density setting. Default value is 16.
- */
- public int getGridDensity()
- {
- return mGridDensity;
- }
-
- /**
- * Sets the grid density setting. Default value is 16.
- */
- public void setGridDensity( int density )
- {
- mGridDensity = density;
-
- updateGridSize();
- }
-
- /**
- * Maximum number of alternations/extrema (L + 2 or M + 2)
- */
- public int getExtremaCount()
- {
- return getHalfFilterOrder() + 2;
- }
-
- /**
- * Half of the filter order.
- *
- * This value is referred to as L or M, depending on the source.
- */
- public int getHalfFilterOrder()
- {
- switch ( mRemezFilterType )
- {
- case TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL:
- return ( mOrder ) / 2;
- case TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL:
- return ( mOrder - 1 ) / 2;
- case TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL:
- return ( mOrder - 2 ) / 2;
- case TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL:
- return ( mOrder - 1 ) / 2;
- default:
- return 0;
- }
- }
-
- /**
- * Determines the max band ripple from across the frequency bands in this specification.
- */
- public double getMaxBandAmplitude()
- {
- double maxRippleAmplitude = 0.0;
-
- for ( FrequencyBand band : mFrequencyBands )
- {
- double bandRippleAmplitude = band.getRippleAmplitude();
-
- if ( bandRippleAmplitude > maxRippleAmplitude )
- {
- maxRippleAmplitude = bandRippleAmplitude;
- }
- }
-
- return maxRippleAmplitude;
- }
-
- public int getNominalGridSize()
- {
- return ( getExtremaCount() - 1 ) * getGridDensity() + 1;
- }
-
- public int getGridSize()
- {
- int gridSize = 0;
-
- for ( FrequencyBand band : mFrequencyBands )
- {
- gridSize += band.getGridSize();
- }
-
- return gridSize;
- }
-
- private void updateGridSize()
- {
- if ( !mFrequencyBands.isEmpty() )
- {
- int gridSize = getNominalGridSize();
-
- double totalBandwidth = getTotalBandwidth();
-
- for ( FrequencyBand band : mFrequencyBands )
- {
- band.setGridSize( gridSize, totalBandwidth );
- }
- }
- }
-
- // /**
- // * Determines the grid size needed to represent the percentage of weighted
- // frequency bands
- // * over the entire frequency band.
- // */
- // public int getGridSize()
- // {
- // int gridSize = 0;
- //
- // int maxGridSize = getMaxGridSize();
- //
- // //Add the proportional index counts from each weighted band
- // for( FrequencyBand band: mFrequencyBands )
- // {
- // gridSize += band.getBandGridSize( maxGridSize );
- // }
- //
- // if( getSymmetry() == Symmetry.NEGATIVE )
- // {
- // gridSize--;
- // }
- //
- // return gridSize;
- // }
-
- /**
- * Dense frequency spacing for extrema count * grid density over a frequency range of 0.0 - 1.0
- */
- public double getGridFrequencyInterval()
- {
- return getTotalBandwidth() / (double) ( ( getGridSize() - mFrequencyBands.size() ) );
- }
-
- // /**
- // * ''' FIR order estimator (lowpass, highpass, bandpass, mulitiband).
- // *
- // * (n, fo, ao, w) = remezord (f, a, dev) (n, fo, ao, w) = remezord (f, a,
- // * dev, fs)
- // *
- // * (n, fo, ao, w) = remezord (f, a, dev) finds the approximate order,
- // * normalized frequency band edges, frequency band amplitudes, and weights
- // * that meet input specifications f, a, and dev, to use with the remez
- // * command.
- // *
- // * f is a sequence of frequency band edges (between 0 and Fs/2, where Fs
- // is
- // * the sampling frequency), and a is a sequence specifying the desired
- // * amplitude on the bands defined by f. The length of f is twice the
- // length
- // * of a, minus 2. The desired function is piecewise constant.
- // *
- // * dev is a sequence the same size as a that specifies the maximum
- // allowable
- // * deviation or ripples between the frequency response and the desired
- // * amplitude of the output filter, for each band.
- // *
- // * Use remez with the resulting order n, frequency sequence fo, amplitude
- // * response sequence ao, and weights w to design the filter b which
- // * approximately meets the specifications given by remezord input
- // parameters
- // * f, a, and dev:
- // *
- // * b = remez (n, fo, ao, w)
- // *
- // * (n, fo, ao, w) = remezord (f, a, dev, Fs) specifies a sampling
- // frequency
- // * Fs.
- // *
- // * Fs defaults to 2 Hz, implying a Nyquist frequency of 1 Hz. You can
- // * therefore specify band edges scaled to a particular applications
- // sampling
- // * frequency.
- // *
- // * In some cases remezord underestimates the order n. If the filter does
- // not
- // * meet the specifications, try a higher order such as n+1 or n+2. '''
- // *
- // * @param fcuts
- // * @param mags
- // * @param devs
- // * @param fsamp
- // */
- // // TODO: rename to calculateFilterOrder()
- // public static FIRFilterSpecification remezOrd( int[] fcutoffs,
- // double[] mags, double[] devs, int fsamp )
- // {
- // // def remezord (fcuts, mags, devs, fsamp = 2):
- //
- // double[] fcuts = new double[ fcutoffs.length ];
- //
- // for ( int x = 0; x < fcuts.length; x++ )
- // {
- // fcuts[ x ] = (double) fcutoffs[ x ] / (double) fsamp;
- // }
- //
- // int nf = fcuts.length;
- // int nm = mags.length;
- // int nd = devs.length;
- // int nbands = nm;
- //
- // if ( mags.length != devs.length )
- // {
- // throw new IllegalArgumentException(
- // "Length of magnitudes and deviations must be equal" );
- // }
- //
- // if ( fcuts.length != ( 2 * ( mags.length - 1 ) ) )
- // {
- // throw new IllegalArgumentException(
- // "Length of frequency band edges must be 2 * mags length - 2" );
- // }
- //
- // for ( int x = 0; x < mags.length; x++ )
- // {
- // if ( mags[ x ] != 0 )
- // {
- // devs[ x ] = devs[ x ] / mags[ x ];
- // }
- // }
- //
- // // Separate the passband and stopband edges
- // double[] f1 = new double[ fcuts.length / 2 ];
- // double[] f2 = new double[ fcuts.length / 2 ];
- //
- // for ( int x = 0; x < fcuts.length / 2; x++ )
- // {
- // f1[ x ] = fcuts[ 2 * x ];
- // f2[ x ] = fcuts[ 2 * x + 1 ];
- // }
- //
- // int n = 0;
- //
- // double min_delta = 2.0;
- //
- // for ( int x = 0; x < f1.length; x++ )
- // {
- // if ( f2[ x ] - f1[ x ] < min_delta )
- // {
- // n = x;
- // min_delta = f2[ x ] - f1[ x ];
- // }
- // }
- //
- // double l;
- //
- // if ( nbands == 2 )
- // {
- // // lowpass or highpass case (use formula)
- // l = lporder( f1[ n ], f2[ n ], devs[ 0 ], devs[ 1 ] );
- // } else
- // {
- // // bandpass or multipass case
- // // try different lowpasses and take the worst one that
- // // goes through the BP specs
- // l = 0;
- //
- // for ( int x = 1; x < nbands - 1; x++ )
- // {
- // double l1 = lporder( f1[ x - 1 ], f2[ x - 1 ], devs[ x ],
- // devs[ x - 1 ] );
- // double l2 = lporder( f1[ x ], f2[ x ], devs[ x ],
- // devs[ x + 1 ] );
- // l = Math.max( l1, l2 );
- // }
- // }
- //
- // n = (int) ( Math.ceil( l ) - 1 ); // need order, not length for remez
- //
- // // // cook up remez compatible result
- // double[] ff = new double[ fcuts.length + 2 ];
- // ff[ 0 ] = 0.0;
- // ff[ ff.length - 1 ] = 1.0;
- //
- // System.arraycopy( fcuts, 0, ff, 1, fcuts.length );
- //
- // for ( int x = 1; x < ff.length - 1; x++ )
- // {
- // ff[ x ] *= 2;
- // }
- //
- // double[] aa = new double[ mags.length * 2 ];
- //
- // for ( int x = 0; x < mags.length; x++ )
- // {
- // aa[ 2 * x ] = mags[ x ];
- // aa[ 2 * x + 1 ] = mags[ x ];
- // }
- //
- // double max_dev = 0;
- //
- // for ( double dev : devs )
- // {
- // if ( dev > max_dev )
- // {
- // max_dev = dev;
- // }
- // }
- //
- // double[] wts = new double[ devs.length ];
- //
- // for ( int x = 0; x < devs.length; x++ )
- // {
- // wts[ x ] = max_dev / devs[ x ];
- // }
- //
- // return null;
- // // return new RemezFilterSpecification( RemezFilterType.BANDPASS, n, ff,
- // // aa, wts, 16);
- // }
-
- /**
- * Low-pass FIR filter specification builder
- */
- public static class LowPassBuilder
- {
- private int mSampleRate;
- private int mOrder;
- private int mGridDensity = 16;
- private int mPassBandEndFrequency;
- private int mStopBandStartFrequency;
- private double mPassBandRipple;
- private double mStopBandRipple;
- private double mPassBandAmplitude = 1.0;
- private double mStopBandAmplitude = 0.0;
-
- public LowPassBuilder()
- {
- }
-
- public LowPassBuilder sampleRate( int sampleRateHz )
- {
- mSampleRate = sampleRateHz;
- return this;
- }
-
- /**
- * Sets the filter order. If the order is not specified, it will be calculated from the
- * other parameters.
- */
- public LowPassBuilder order( int order )
- {
- mOrder = order;
- return this;
- }
-
- public LowPassBuilder gridDensity( int density )
- {
- mGridDensity = density;
- return this;
- }
-
- public LowPassBuilder passBandCutoff( int frequencyHz )
- {
- mPassBandEndFrequency = frequencyHz;
- return this;
- }
-
- public LowPassBuilder stopBandStart( int frequencyHz )
- {
- mStopBandStartFrequency = frequencyHz;
- return this;
- }
-
- public LowPassBuilder passBandRipple( double rippleDb )
- {
- mPassBandRipple = rippleDb;
- return this;
- }
-
- public LowPassBuilder stopBandRipple( double rippleDb )
- {
- mStopBandRipple = rippleDb;
- return this;
- }
-
- public LowPassBuilder passBandAmplitude( double amplitude )
- {
- mPassBandAmplitude = amplitude;
- return this;
- }
-
- public LowPassBuilder stopBandAmplitude( double amplitude )
- {
- mStopBandAmplitude = amplitude;
- return this;
- }
-
- public FIRFilterSpecification build()
- {
- if ( mOrder < 6 )
- {
- mOrder = estimateFilterOrder( mSampleRate, mPassBandEndFrequency,
- mStopBandStartFrequency, mPassBandRipple, mStopBandRipple );
- }
-
- FIRLinearPhaseFilterType type = ( mOrder % 2 == 0
- ? FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL
- : FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL );
-
- FIRFilterSpecification spec = new FIRFilterSpecification( type, mOrder, mGridDensity );
-
- FrequencyBand passBand = new FrequencyBand( mSampleRate, 0, mPassBandEndFrequency,
- mPassBandAmplitude, mPassBandRipple );
- spec.addFrequencyBand( passBand );
-
- FrequencyBand stopBand = new FrequencyBand( mSampleRate, mStopBandStartFrequency,
- mSampleRate / 2, mStopBandAmplitude, mStopBandRipple );
- spec.addFrequencyBand( stopBand );
-
- return spec;
- }
- }
-
- /**
- * Remez high-pass FIR filter specification builder
- */
- public static class HighPassBuilder
- {
- private int mSampleRate;
- private int mOrder = -1;
- private int mGridDensity = 16;
- private int mPassBandStartFrequency;
- private int mStopBandEndFrequency;
- private double mPassBandRipple;
- private double mStopBandRipple;
- private double mPassBandAmplitude = 1.0;
- private double mStopBandAmplitude = 0.0;
-
- public HighPassBuilder()
- {
- }
-
- public HighPassBuilder sampleRate( int sampleRateHz )
- {
- mSampleRate = sampleRateHz;
- return this;
- }
-
- /**
- * Sets the filter order. If the order is not specified, it will be calculated from the
- * other parameters.
- */
- public HighPassBuilder order( int order )
- {
- mOrder = order;
- return this;
- }
-
- public HighPassBuilder gridDensity( int density )
- {
- mGridDensity = density;
- return this;
- }
-
- public HighPassBuilder passBandStart( int frequencyHz )
- {
- mPassBandStartFrequency = frequencyHz;
- return this;
- }
-
- public HighPassBuilder stopBandCutoff( int frequencyHz )
- {
- mStopBandEndFrequency = frequencyHz;
- return this;
- }
-
- public HighPassBuilder passBandRipple( double rippleDb )
- {
- mPassBandRipple = rippleDb;
- return this;
- }
-
- public HighPassBuilder stopBandRipple( double rippleDb )
- {
- mStopBandRipple = rippleDb;
- return this;
- }
-
- public HighPassBuilder passBandAmplitude( double amplitude )
- {
- mPassBandAmplitude = amplitude;
- return this;
- }
-
- public HighPassBuilder stopBandAmplitude( double amplitude )
- {
- mStopBandAmplitude = amplitude;
- return this;
- }
-
- public FIRFilterSpecification build()
- {
- if ( mOrder < 6 )
- {
- mOrder = estimateFilterOrder( mSampleRate, mStopBandEndFrequency,
- mPassBandStartFrequency, mPassBandRipple, mStopBandRipple );
- }
-
- // High pass is a Type 1 filter -- ensure order is even
- if ( mOrder % 2 == 1 )
- {
- mOrder++;
- }
-
- FIRFilterSpecification spec = new FIRFilterSpecification(
- FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL, mOrder,
- mGridDensity );
-
- FrequencyBand stopBand = new FrequencyBand( mSampleRate, 0, mStopBandEndFrequency,
- mStopBandAmplitude, mStopBandRipple );
- spec.addFrequencyBand( stopBand );
-
- FrequencyBand passBand = new FrequencyBand( mSampleRate, mPassBandStartFrequency,
- mSampleRate / 2, mPassBandAmplitude, mPassBandRipple );
- spec.addFrequencyBand( passBand );
-
- return spec;
- }
- }
-
- /**
- * Band-pass or band-stop FIR filter specification builder
- */
- public static class BandPassBuilder
- {
- private int mOrder;
- private int mGridDensity = 16;
- private int mSampleRate;
- private int mStopFrequency1;
- private int mPassFrequencyBegin;
- private int mPassFrequencyEnd;
- private int mStopFrequency2;
- private double mStopRipple = 0.01;
- private double mPassRipple = 0.01;
- private double mStopAmplitude = 0.0;
- private double mPassAmplitude = 1.0;
-
- public BandPassBuilder()
- {
- }
-
- /**
- * Sets the filter order. If the order is not specified, it will be calculated from the
- * other parameters. If you specify and odd-length order, it will be corrected (+1) to make
- * it an even order number.
- */
- public BandPassBuilder order( int order )
- {
- mOrder = order;
- return this;
- }
-
- /**
- * Sets the frequency grid density. Default is 16.
- */
- public BandPassBuilder gridDensity( int density )
- {
- mGridDensity = density;
- return this;
- }
-
- /**
- * Sets the filter sample rate in hertz
- */
- public BandPassBuilder sampleRate( int sampleRate )
- {
- mSampleRate = sampleRate;
- return this;
- }
-
- /**
- * Specifies the ending frequency of the first stop band
- */
- public BandPassBuilder stopFrequency1( int stopFrequency )
- {
- mStopFrequency1 = stopFrequency;
- return this;
- }
-
- /**
- * Specifies the beginning frequency of the second stop band
- */
- public BandPassBuilder stopFrequency2( int stopFrequency )
- {
- mStopFrequency2 = stopFrequency;
- return this;
- }
-
- /**
- * Specifies the pass band beginning frequency
- */
- public BandPassBuilder passFrequencyBegin( int passFrequency )
- {
- mPassFrequencyBegin = passFrequency;
- return this;
- }
-
- /**
- * Specifies the pass band ending frequency
- */
- public BandPassBuilder passFrequencyEnd( int passFrequency )
- {
- mPassFrequencyEnd = passFrequency;
- return this;
- }
-
- /**
- * Specifies the ripple in dB for both stop bands
- */
- public BandPassBuilder stopRipple( double rippleDb )
- {
- mStopRipple = rippleDb;
- return this;
- }
-
- /**
- * Specifies the ripple in dB for the pass band
- */
- public BandPassBuilder passRipple( double rippleDb )
- {
- mPassRipple = rippleDb;
- return this;
- }
-
- /**
- * Specifies the amplitude of the stop bands. Default is 0.0
- */
- public BandPassBuilder stopAmplitude( double amplitude )
- {
- mStopAmplitude = amplitude;
- return this;
- }
-
- /**
- * Specifies the amplitude of the pass band. Default is 1.0
- */
- public BandPassBuilder passAmplitude( double amplitude )
- {
- mPassAmplitude = amplitude;
- return this;
- }
-
- /**
- * Validates the frequency bands to test for overlap and completeness.
- */
- private void validateFrequencyBands()
- {
- if ( mStopFrequency1 >= mPassFrequencyBegin )
- {
- throw new IllegalArgumentException( "Stop band 1 ending frequency ["
- + mStopFrequency1 + "] must be less than the pass band start frequency ["
- + mPassFrequencyBegin + "]" );
- }
-
- if ( mPassFrequencyBegin >= mPassFrequencyEnd )
- {
- throw new IllegalArgumentException( "Pass band begin frequency ["
- + mPassFrequencyBegin + "] must be less than the pass band end frequency ["
- + mPassFrequencyEnd + "]" );
- }
-
- if ( mPassFrequencyEnd >= mStopFrequency2 )
- {
- throw new IllegalArgumentException( "Pass band end frequency [" + mPassFrequencyEnd
- + "] must be less than stop band 2 beginning frequency [" + mStopFrequency2
- + "]" );
- }
-
- if ( mStopFrequency2 >= ( mSampleRate / 2 ) )
- {
- throw new IllegalArgumentException( "Stop band 2 beginning frequency ["
- + mStopFrequency2 + "] must be less than half of the sample rate ["
- + ( mSampleRate / 2 ) + "]" );
- }
- }
-
- public FIRFilterSpecification build()
- {
- validateFrequencyBands();
-
- FIRLinearPhaseFilterType type = FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL;
-
- if ( mOrder < 10 )
- {
- mOrder = estimateBandPassOrder( mSampleRate, mPassFrequencyBegin, mPassFrequencyEnd,
- mPassRipple, mStopRipple );
- }
-
- // Ensure even order since we're designing a Type 1 filter
- if ( mOrder % 2 == 1 )
- {
- mOrder++;
- }
-
- FIRFilterSpecification spec = new FIRFilterSpecification( type, mOrder, mGridDensity );
-
- spec.addFrequencyBand( new FrequencyBand( mSampleRate, 0, mStopFrequency1,
- mStopAmplitude, mStopRipple ) );
- spec.addFrequencyBand( new FrequencyBand( mSampleRate, mPassFrequencyBegin,
- mPassFrequencyEnd, mPassAmplitude, mPassRipple ) );
- spec.addFrequencyBand( new FrequencyBand( mSampleRate, mStopFrequency2,
- (int) ( mSampleRate / 2 ), mStopAmplitude, mStopRipple ) );
-
- return spec;
- }
- }
-
- // /**
- // * Converts the set of band amplitude values into a set of weights
- // * normalized to the largest band amplitude value
- // */
- // private static double[] getBandWeights( double... amplitudes )
- // {
- // double maximum = 0.0;
- //
- // for ( double amplitude : amplitudes )
- // {
- // if ( amplitude > maximum )
- // {
- // maximum = amplitude;
- // }
- // }
- //
- // double[] weights = new double[ amplitudes.length ];
- //
- // for ( int x = 0; x < amplitudes.length; x++ )
- // {
- // weights[ x ] = maximum / amplitudes[ x ];
- // }
- //
- // return weights;
- // }
-
- /**
- * Calculates filter order from filter length
- */
- private static int getFilterOrder( double length )
- {
- return (int) ( Math.ceil( length ) - 1 );
- }
-
- /**
- * Estimates filter length for a two-band (high or low pass) FIR filter.
- *
- * Note: this estimator doesn't work well if the transition frequency is near 0 or sampleRate/2
- *
- * From Herrmann et al (1973), Practical design rules for optimum finite impulse response
- * filters. Bell System Technical J., 52, 769-99
- *
- * @param sampleRate in hertz
- * @param frequency1 in hertz
- * @param frequency2 in hertz
- * @param passBandRipple pass band ripple in dB
- * @param stopBandRipple stop band ripple in dB
- *
- * @return estimated filter length
- */
- private static int estimateFilterOrder( int sampleRate, int frequency1, int frequency2,
- double passBandRipple, double stopBandRipple )
- {
- double df = Math.abs( frequency2 - frequency1 ) / (double) sampleRate;
-
- double ddp = Math
- .log10( stopBandRipple <= passBandRipple ? passBandRipple : stopBandRipple );
- double dds = Math
- .log10( stopBandRipple <= passBandRipple ? stopBandRipple : passBandRipple );
-
- double a1 = 5.309e-3;
- double a2 = 7.114e-2;
- double a3 = -4.761e-1;
- double a4 = -2.66e-3;
- double a5 = -5.941e-1;
- double a6 = -4.278e-1;
-
- double b1 = 11.01217;
- double b2 = 0.5124401;
-
- double t1 = a1 * ddp * ddp;
- double t2 = a2 * ddp;
- double t3 = a4 * ddp * ddp;
- double t4 = a5 * ddp;
-
- double dinf = ( ( t1 + t2 + a3 ) * dds ) + ( t3 + t4 + a6 );
- double ff = b1 + b2 * ( ddp - dds );
- double n = dinf / df - ff * df + 1.0;
-
- return (int) Math.ceil( n );
- }
-
- /**
- * Estimates band pass filter length;
- *
- * @param passBandStart frequency normalized to sample rate
- * @param passBandEnd frequency normalized to sample rate
- * @param passBandRippleDb
- * @param stopBandRippleDb
- */
- public static int estimateBandPassOrder( int sampleRate, int passBandStart, int passBandEnd,
- double passBandRippleDb, double stopBandRippleDb )
- {
- double df = (double) Math.abs( passBandEnd - passBandStart ) / (double) sampleRate;
- double ddp = (double) Math.log10( passBandRippleDb );
- double dds = (double) Math.log10( stopBandRippleDb );
-
- double a1 = 0.01201;
- double a2 = 0.09664;
- double a3 = -0.51325;
- double a4 = 0.00203;
- double a5 = -0.57054;
- double a6 = -0.44314;
-
- double t1 = a1 * ddp * ddp;
- double t2 = a2 * ddp;
- double t3 = a4 * ddp * ddp;
- double t4 = a5 * ddp;
-
- double cinf = dds * ( t1 + t2 + a3 ) + t3 + t4 + a6;
- double ginf = -14.6f * (double) Math.log10( passBandRippleDb / stopBandRippleDb ) - 16.9;
- double n = cinf / df + ginf * df + 1.0;
-
- return (int) Math.ceil( n );
- }
-
- /**
- * Frequency band
- */
- public static class FrequencyBand
- {
- private int mGridSize;
- private double mStart;
- private double mEnd;
- private double mAmplitude;
- private double mRippleDB;
-
- /**
- * Constructs a frequency band where the start and end frequencies are normalized to a
- * sample rate of 1.
- *
- * @param start edge of the frequency band (0.0 - 1.0)
- * @param end edge of the frequency band (0.0 - 1.0)
- * @param amplitude of the frequency band on the unity scale (0.0 - 1.0)
- * @param ripple of the frequency band specified as db ripple
- */
- public FrequencyBand( double start, double end, double amplitude, double ripple )
- {
- assert ( 0.0 <= mStart );
- assert ( mStart < mEnd );
- assert ( mEnd <= 1.0 );
- assert ( 0.0 <= amplitude && amplitude <= 1.0 );
- assert ( 0.0 <= ripple );
-
- mStart = start;
- mEnd = end;
- mAmplitude = amplitude;
- mRippleDB = ripple;
- }
-
- /**
- * Constructs a frequency band for the sample rate and start and end frequencies.
- *
- * @param sampleRate in hertz
- * @param start edge of the frequency band in hertz
- * @param end edge of the frequency band in hertz
- * @param amplitude of the frequency band relative to unity (0.0 - 1.0)
- * @param ripple of the frequency band specified as db ripple
- */
- public FrequencyBand( int sampleRate, int start, int end, double amplitude, double ripple )
- {
- this( (double) start / (double) sampleRate, (double) end / (double) sampleRate,
- amplitude, ripple );
- }
-
- public int getGridSize()
- {
- return mGridSize;
- }
-
- public void setGridSize( int totalGridSize, double totalBandwidth )
- {
- mGridSize = (int) Math
- .ceil( (double) totalGridSize * ( getBandWidth() / totalBandwidth ) );
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "Band Start: " );
- sb.append( mStart );
- sb.append( " End:" );
- sb.append( mEnd );
- sb.append( " Amplitude: " );
- sb.append( mAmplitude );
- sb.append( " Ripple dB: " );
- sb.append( mRippleDB );
- sb.append( " Ripple Amplitude: " );
- sb.append( DECIMAL_FORMATTER.format( getRippleAmplitude() ) );
- sb.append( " Grid Size: " );
- sb.append( mGridSize );
-
- return sb.toString();
- }
-
- /**
- * Start frequency edge for this band normalized to 1 Hz.
- */
- public double getStart()
- {
- return mStart;
- }
-
- /**
- * End frequency edge for this band normalized to 1 Hz.
- */
- public double getEnd()
- {
- return mEnd;
- }
-
- /**
- * Desired amplitude response for this frequency band
- *
- * @return amplitude (0.0 - 1.0)
- */
- public double getAmplitude()
- {
- return mAmplitude;
- }
-
- /**
- * Frequency band ripple in decibels
- */
- public double getRippleDB()
- {
- return mRippleDB;
- }
-
- /**
- * Frequency band ripple as an amplitude value
- */
- public double getRippleAmplitude()
- {
- return ( Math.pow( 10.0, ( mRippleDB / 20 ) ) - 1 )
- / ( Math.pow( 10.0, ( mRippleDB / 20 ) ) + 1 );
- }
-
- /**
- * Calculates the weighting of this frequency band's ripple relative to the max ripple for a
- * filter specification.
- *
- * @param maxRippleAmplitude for the filter specification
- *
- * @return weight of this frequency band
- */
- public double getWeight( double maxRippleAmplitude )
- {
- return 1.0 / ( getRippleAmplitude() / maxRippleAmplitude );
- }
-
- /**
- * Double-sided bandwidth of this frequency band
- */
- public double getBandWidth()
- {
- return mEnd - mStart;
- }
- }
+ private final static Logger mLog = LoggerFactory.getLogger(FIRFilterSpecification.class);
+
+ private static final double PERFECT_RECONSTRUCTION_GAIN_AT_BAND_EDGE = -6.020599842071533; //0.5 magnitude
+ private static final DecimalFormat DECIMAL_FORMATTER = new DecimalFormat("0.00000");
+
+ private FIRLinearPhaseFilterType mRemezFilterType;
+ private int mOrder;
+ private int mGridDensity = 16;
+ private List mFrequencyBands = new ArrayList<>();
+
+ /**
+ * Constructs a Remez filter specification. Use one of the filter builders to construct a Remez
+ * filter specification.
+ *
+ * @param type of filter
+ * @param order of the filter
+ * @param gridDensity to use when calculating the coefficients
+ */
+ protected FIRFilterSpecification(FIRLinearPhaseFilterType type, int order, int gridDensity)
+ {
+ mRemezFilterType = type;
+ mOrder = order;
+ mGridDensity = gridDensity;
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Type: ");
+ sb.append(mRemezFilterType.name());
+ sb.append(" Order: ");
+ sb.append(mOrder);
+ sb.append(" Extrema: ");
+ sb.append(getExtremaCount());
+ sb.append(" Grid Density: ");
+ sb.append(mGridDensity);
+ sb.append(" Grid Size: ");
+ sb.append(getGridSize());
+
+ for(FrequencyBand band : mFrequencyBands)
+ {
+ sb.append("\n");
+ sb.append(band.toString());
+ }
+
+ return sb.toString();
+ }
+
+ public void addFrequencyBand(FrequencyBand band)
+ {
+ mFrequencyBands.add(band);
+
+ updateGridSize();
+ }
+
+ public void addFrequencyBands(Collection bands)
+ {
+ mFrequencyBands.addAll(bands);
+
+ updateGridSize();
+ }
+
+ /**
+ * Removes all frequency bands from this filter specification
+ */
+ public void clearFrequencyBands()
+ {
+ mFrequencyBands.clear();
+ }
+
+ public List getFrequencyBands()
+ {
+ return mFrequencyBands;
+ }
+
+ /**
+ * Returns the total bandwidth of the defined frequency bands in the specification.
+ *
+ * @return total bandwidth -- should be less than or equal to 0.5
+ */
+ public double getTotalBandwidth()
+ {
+ double bandwidth = 0.0;
+
+ for(FrequencyBand band : mFrequencyBands)
+ {
+ bandwidth += band.getBandWidth();
+ }
+
+ return bandwidth;
+ }
+
+ /**
+ * Creates a Low-Pass FIR filter specification builder that allows you to define the filter
+ * parameters and create a filter specification.
+ */
+ public static LowPassBuilder lowPassBuilder()
+ {
+ return new LowPassBuilder();
+ }
+
+ /**
+ * Creates a High-Pass FIR filter specification builder that allows you to define the filter
+ * parameters and create a filter specification.
+ */
+ public static HighPassBuilder highPassBuilder()
+ {
+ return new HighPassBuilder();
+ }
+
+ /**
+ * Creates a Band-Pass FIR filter specification builder that allows you to define the filter
+ * parameters and create a filter specification.
+ */
+ public static BandPassBuilder bandPassBuilder()
+ {
+ return new BandPassBuilder();
+ }
+
+ /**
+ * Creates a Polyphase TunerChannelizer filter specification builder that allows you to define the filter parameters and
+ * create a filter specification
+ */
+ public static ChannelizerBuilder channelizerBuilder()
+ {
+ return new ChannelizerBuilder();
+ }
+
+ public FIRLinearPhaseFilterType getFilterType()
+ {
+ return mRemezFilterType;
+ }
+
+ public boolean isSymmetrical()
+ {
+ return mRemezFilterType == FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL ||
+ mRemezFilterType == FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL;
+ }
+
+ public boolean isAntiSymmetrical()
+ {
+ return mRemezFilterType == FIRLinearPhaseFilterType.TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL ||
+ mRemezFilterType == FIRLinearPhaseFilterType.TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL;
+ }
+
+ public int getOrder()
+ {
+ return mOrder;
+ }
+
+ public boolean isEvenOrder()
+ {
+ return mOrder % 2 == 0;
+ }
+
+ public boolean isOddOrder()
+ {
+ return mOrder % 2 == 1;
+ }
+
+ public int getFilterLength()
+ {
+ return mOrder + 1;
+ }
+
+ public int getBandCount()
+ {
+ return mFrequencyBands.size();
+ }
+
+ /**
+ * Grid density setting. Default value is 16.
+ */
+ public int getGridDensity()
+ {
+ return mGridDensity;
+ }
+
+ /**
+ * Sets the grid density setting. Default value is 16.
+ */
+ public void setGridDensity(int density)
+ {
+ mGridDensity = density;
+
+ updateGridSize();
+ }
+
+ /**
+ * Maximum number of alternations/extrema (L + 2 or M + 2)
+ */
+ public int getExtremaCount()
+ {
+ return getHalfFilterOrder() + 2;
+ }
+
+ /**
+ * Half of the filter order.
+ *
+ * This value is referred to as L or M, depending on the source.
+ */
+ public int getHalfFilterOrder()
+ {
+ switch(mRemezFilterType)
+ {
+ case TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL:
+ return (mOrder) / 2;
+ case TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL:
+ return (mOrder - 1) / 2;
+ case TYPE_3_ODD_LENGTH_EVEN_ORDER_ANTI_SYMMETRICAL:
+ return (mOrder - 2) / 2;
+ case TYPE_4_EVEN_LENGTH_ODD_ORDER_ANTI_SYMMETRICAL:
+ return (mOrder - 1) / 2;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Determines the max band ripple from across the frequency bands in this specification.
+ */
+ public double getMaxBandAmplitude()
+ {
+ double maxRippleAmplitude = 0.0;
+
+ for(FrequencyBand band : mFrequencyBands)
+ {
+ double bandRippleAmplitude = band.getRippleAmplitude();
+
+ if(bandRippleAmplitude > maxRippleAmplitude)
+ {
+ maxRippleAmplitude = bandRippleAmplitude;
+ }
+ }
+
+ return maxRippleAmplitude;
+ }
+
+ public int getNominalGridSize()
+ {
+ return (getExtremaCount() - 1) * getGridDensity() + 1;
+ }
+
+ public int getGridSize()
+ {
+ int gridSize = 0;
+
+ for(FrequencyBand band : mFrequencyBands)
+ {
+ gridSize += band.getGridSize();
+ }
+
+ return gridSize;
+ }
+
+ private void updateGridSize()
+ {
+ if(!mFrequencyBands.isEmpty())
+ {
+ int gridSize = getNominalGridSize();
+
+ double totalBandwidth = getTotalBandwidth();
+
+ for(FrequencyBand band : mFrequencyBands)
+ {
+ band.setGridSize(gridSize, totalBandwidth);
+ }
+ }
+ }
+
+ /**
+ * Dense frequency spacing for extrema count * grid density over a frequency range of 0.0 - 1.0
+ */
+ public double getGridFrequencyInterval()
+ {
+ return getTotalBandwidth() / (double)((getGridSize() - mFrequencyBands.size()));
+ }
+
+ /**
+ * Low-pass FIR filter specification builder
+ */
+ public static class LowPassBuilder
+ {
+ private int mSampleRate;
+ private int mOrder;
+ private int mGridDensity = 16;
+ private int mPassBandEndFrequency;
+ private int mStopBandStartFrequency;
+ private double mPassBandRipple;
+ private double mStopBandRipple;
+ private double mPassBandAmplitude = 1.0;
+ private double mStopBandAmplitude = 0.0;
+
+ public LowPassBuilder()
+ {
+ }
+
+ public LowPassBuilder sampleRate(int sampleRateHz)
+ {
+ mSampleRate = sampleRateHz;
+ return this;
+ }
+
+ /**
+ * Sets the filter order. If the order is not specified, it will be calculated from the
+ * other parameters.
+ */
+ public LowPassBuilder order(int order)
+ {
+ mOrder = order;
+ return this;
+ }
+
+ public LowPassBuilder gridDensity(int density)
+ {
+ mGridDensity = density;
+ return this;
+ }
+
+ public LowPassBuilder passBandCutoff(int frequencyHz)
+ {
+ mPassBandEndFrequency = frequencyHz;
+ return this;
+ }
+
+ public LowPassBuilder stopBandStart(int frequencyHz)
+ {
+ mStopBandStartFrequency = frequencyHz;
+ return this;
+ }
+
+ public LowPassBuilder passBandRipple(double rippleDb)
+ {
+ mPassBandRipple = rippleDb;
+ return this;
+ }
+
+ public LowPassBuilder stopBandRipple(double rippleDb)
+ {
+ mStopBandRipple = rippleDb;
+ return this;
+ }
+
+ public LowPassBuilder passBandAmplitude(double amplitude)
+ {
+ mPassBandAmplitude = amplitude;
+ return this;
+ }
+
+ public LowPassBuilder stopBandAmplitude(double amplitude)
+ {
+ mStopBandAmplitude = amplitude;
+ return this;
+ }
+
+ public FIRFilterSpecification build()
+ {
+ if(mOrder < 6)
+ {
+ mOrder = estimateFilterOrder(mSampleRate, mPassBandEndFrequency,
+ mStopBandStartFrequency, mPassBandRipple, mStopBandRipple);
+ }
+
+ FIRLinearPhaseFilterType type = (mOrder % 2 == 0
+ ? FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL
+ : FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL);
+
+ FIRFilterSpecification spec = new FIRFilterSpecification(type, mOrder, mGridDensity);
+
+ FrequencyBand passBand = new FrequencyBand(mSampleRate, 0, mPassBandEndFrequency,
+ mPassBandAmplitude, mPassBandRipple);
+ spec.addFrequencyBand(passBand);
+
+ FrequencyBand stopBand = new FrequencyBand(mSampleRate, mStopBandStartFrequency,
+ mSampleRate / 2, mStopBandAmplitude, mStopBandRipple);
+ spec.addFrequencyBand(stopBand);
+
+ return spec;
+ }
+ }
+
+ /**
+ * Remez high-pass FIR filter specification builder
+ */
+ public static class HighPassBuilder
+ {
+ private int mSampleRate;
+ private int mOrder = -1;
+ private int mGridDensity = 16;
+ private int mPassBandStartFrequency;
+ private int mStopBandEndFrequency;
+ private double mPassBandRipple;
+ private double mStopBandRipple;
+ private double mPassBandAmplitude = 1.0;
+ private double mStopBandAmplitude = 0.0;
+
+ public HighPassBuilder()
+ {
+ }
+
+ public HighPassBuilder sampleRate(int sampleRateHz)
+ {
+ mSampleRate = sampleRateHz;
+ return this;
+ }
+
+ /**
+ * Sets the filter order. If the order is not specified, it will be calculated from the
+ * other parameters.
+ */
+ public HighPassBuilder order(int order)
+ {
+ mOrder = order;
+ return this;
+ }
+
+ public HighPassBuilder gridDensity(int density)
+ {
+ mGridDensity = density;
+ return this;
+ }
+
+ public HighPassBuilder passBandStart(int frequencyHz)
+ {
+ mPassBandStartFrequency = frequencyHz;
+ return this;
+ }
+
+ public HighPassBuilder stopBandCutoff(int frequencyHz)
+ {
+ mStopBandEndFrequency = frequencyHz;
+ return this;
+ }
+
+ public HighPassBuilder passBandRipple(double rippleDb)
+ {
+ mPassBandRipple = rippleDb;
+ return this;
+ }
+
+ public HighPassBuilder stopBandRipple(double rippleDb)
+ {
+ mStopBandRipple = rippleDb;
+ return this;
+ }
+
+ public HighPassBuilder passBandAmplitude(double amplitude)
+ {
+ mPassBandAmplitude = amplitude;
+ return this;
+ }
+
+ public HighPassBuilder stopBandAmplitude(double amplitude)
+ {
+ mStopBandAmplitude = amplitude;
+ return this;
+ }
+
+ public FIRFilterSpecification build()
+ {
+ if(mOrder < 6)
+ {
+ mOrder = estimateFilterOrder(mSampleRate, mStopBandEndFrequency,
+ mPassBandStartFrequency, mPassBandRipple, mStopBandRipple);
+ }
+
+ // High pass is a Type 1 filter -- ensure order is even
+ if(mOrder % 2 == 1)
+ {
+ mOrder++;
+ }
+
+ FIRFilterSpecification spec = new FIRFilterSpecification(
+ FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL, mOrder,
+ mGridDensity);
+
+ FrequencyBand stopBand = new FrequencyBand(mSampleRate, 0, mStopBandEndFrequency,
+ mStopBandAmplitude, mStopBandRipple);
+ spec.addFrequencyBand(stopBand);
+
+ FrequencyBand passBand = new FrequencyBand(mSampleRate, mPassBandStartFrequency,
+ mSampleRate / 2, mPassBandAmplitude, mPassBandRipple);
+ spec.addFrequencyBand(passBand);
+
+ return spec;
+ }
+ }
+
+ /**
+ * Band-pass or band-stop FIR filter specification builder
+ */
+ public static class BandPassBuilder
+ {
+ private int mOrder;
+ private int mGridDensity = 16;
+ private int mSampleRate;
+ private int mStopFrequency1;
+ private int mPassFrequencyBegin;
+ private int mPassFrequencyEnd;
+ private int mStopFrequency2;
+ private double mStopRipple = 0.01;
+ private double mPassRipple = 0.01;
+ private double mStopAmplitude = 0.0;
+ private double mPassAmplitude = 1.0;
+
+ public BandPassBuilder()
+ {
+ }
+
+ /**
+ * Sets the filter order. If the order is not specified, it will be calculated from the
+ * other parameters. If you specify and odd-length order, it will be corrected (+1) to make
+ * it an even order number.
+ */
+ public BandPassBuilder order(int order)
+ {
+ mOrder = order;
+ return this;
+ }
+
+ /**
+ * Sets the frequency grid density. Default is 16.
+ */
+ public BandPassBuilder gridDensity(int density)
+ {
+ mGridDensity = density;
+ return this;
+ }
+
+ /**
+ * Sets the filter sample rate in hertz
+ */
+ public BandPassBuilder sampleRate(int sampleRate)
+ {
+ mSampleRate = sampleRate;
+ return this;
+ }
+
+ /**
+ * Specifies the ending frequency of the first stop band
+ */
+ public BandPassBuilder stopFrequency1(int stopFrequency)
+ {
+ mStopFrequency1 = stopFrequency;
+ return this;
+ }
+
+ /**
+ * Specifies the beginning frequency of the second stop band
+ */
+ public BandPassBuilder stopFrequency2(int stopFrequency)
+ {
+ mStopFrequency2 = stopFrequency;
+ return this;
+ }
+
+ /**
+ * Specifies the pass band beginning frequency
+ */
+ public BandPassBuilder passFrequencyBegin(int passFrequency)
+ {
+ mPassFrequencyBegin = passFrequency;
+ return this;
+ }
+
+ /**
+ * Specifies the pass band ending frequency
+ */
+ public BandPassBuilder passFrequencyEnd(int passFrequency)
+ {
+ mPassFrequencyEnd = passFrequency;
+ return this;
+ }
+
+ /**
+ * Specifies the ripple in dB for both stop bands
+ */
+ public BandPassBuilder stopRipple(double rippleDb)
+ {
+ mStopRipple = rippleDb;
+ return this;
+ }
+
+ /**
+ * Specifies the ripple in dB for the pass band
+ */
+ public BandPassBuilder passRipple(double rippleDb)
+ {
+ mPassRipple = rippleDb;
+ return this;
+ }
+
+ /**
+ * Specifies the amplitude of the stop bands. Default is 0.0
+ */
+ public BandPassBuilder stopAmplitude(double amplitude)
+ {
+ mStopAmplitude = amplitude;
+ return this;
+ }
+
+ /**
+ * Specifies the amplitude of the pass band. Default is 1.0
+ */
+ public BandPassBuilder passAmplitude(double amplitude)
+ {
+ mPassAmplitude = amplitude;
+ return this;
+ }
+
+ /**
+ * Validates the frequency bands to test for overlap and completeness.
+ */
+ private void validateFrequencyBands()
+ {
+ if(mStopFrequency1 >= mPassFrequencyBegin)
+ {
+ throw new IllegalArgumentException("Stop band 1 ending frequency ["
+ + mStopFrequency1 + "] must be less than the pass band start frequency ["
+ + mPassFrequencyBegin + "]");
+ }
+
+ if(mPassFrequencyBegin >= mPassFrequencyEnd)
+ {
+ throw new IllegalArgumentException("Pass band begin frequency ["
+ + mPassFrequencyBegin + "] must be less than the pass band end frequency ["
+ + mPassFrequencyEnd + "]");
+ }
+
+ if(mPassFrequencyEnd >= mStopFrequency2)
+ {
+ throw new IllegalArgumentException("Pass band end frequency [" + mPassFrequencyEnd
+ + "] must be less than stop band 2 beginning frequency [" + mStopFrequency2
+ + "]");
+ }
+
+ if(mStopFrequency2 >= (mSampleRate / 2))
+ {
+ throw new IllegalArgumentException("Stop band 2 beginning frequency ["
+ + mStopFrequency2 + "] must be less than half of the sample rate ["
+ + (mSampleRate / 2) + "]");
+ }
+ }
+
+ public FIRFilterSpecification build()
+ {
+ validateFrequencyBands();
+
+ FIRLinearPhaseFilterType type = FIRLinearPhaseFilterType.TYPE_1_ODD_LENGTH_EVEN_ORDER_SYMMETRICAL;
+
+ if(mOrder < 10)
+ {
+ mOrder = estimateBandPassOrder(mSampleRate, mPassFrequencyBegin, mPassFrequencyEnd,
+ mPassRipple, mStopRipple);
+ }
+
+ // Ensure even order since we're designing a Type 1 filter
+ if(mOrder % 2 == 1)
+ {
+ mOrder++;
+ }
+
+ FIRFilterSpecification spec = new FIRFilterSpecification(type, mOrder, mGridDensity);
+
+ spec.addFrequencyBand(new FrequencyBand(mSampleRate, 0, mStopFrequency1,
+ mStopAmplitude, mStopRipple));
+ spec.addFrequencyBand(new FrequencyBand(mSampleRate, mPassFrequencyBegin,
+ mPassFrequencyEnd, mPassAmplitude, mPassRipple));
+ spec.addFrequencyBand(new FrequencyBand(mSampleRate, mStopFrequency2,
+ (int)(mSampleRate / 2), mStopAmplitude, mStopRipple));
+
+ return spec;
+ }
+ }
+
+ /**
+ * Builder for creating polyphase channelizer low-pass prototype filter.
+ */
+ public static class ChannelizerBuilder
+ {
+ private int mGridDensity = 16;
+ private int mSampleRate;
+ private int mChannelCount;
+ private int mChannelBandwidth;
+ private int mTapsPerChannel;
+ private double mStopRipple = 0.001;
+ private double mPassRipple = 0.01;
+ private double mAlpha = 0.2;
+
+ public ChannelizerBuilder()
+ {
+ }
+
+ /**
+ * Sets the filter sample rate in hertz
+ */
+ public ChannelizerBuilder sampleRate(int sampleRate)
+ {
+ mSampleRate = sampleRate;
+ return this;
+ }
+
+ /**
+ * Number of Channels
+ * @param channels - must be a multiple of 2
+ * @return
+ */
+ public ChannelizerBuilder channels(int channels)
+ {
+ Validate.isTrue(channels % 2 == 0, "Channel count must be a multiple of 2");
+ mChannelCount = channels;
+ return this;
+ }
+
+ /**
+ * Sets the channel bandwidth in hertz
+ */
+ public ChannelizerBuilder channelBandwidth(int channelBandwidth)
+ {
+ mChannelBandwidth = channelBandwidth;
+ return this;
+ }
+
+ /**
+ * Number of filter taps per channel. Note: total filter length will be channels * tapsPerChannel
+ */
+ public ChannelizerBuilder tapsPerChannel(int tapsPerChannel)
+ {
+ mTapsPerChannel = tapsPerChannel;
+ return this;
+ }
+
+ /**
+ * Sets the frequency grid density. Default is 16.
+ */
+ public ChannelizerBuilder gridDensity(int density)
+ {
+ mGridDensity = density;
+ return this;
+ }
+
+ /**
+ * Specifies the ripple in dB for both stop bands
+ */
+ public ChannelizerBuilder stopRipple(double rippleDb)
+ {
+ mStopRipple = rippleDb;
+ return this;
+ }
+
+ /**
+ * Specifies the ripple in dB for the pass band
+ */
+ public ChannelizerBuilder passRipple(double rippleDb)
+ {
+ mPassRipple = rippleDb;
+ return this;
+ }
+
+ /**
+ * Rolloff factor for the start band and stop band.
+ * @param alpha - value in range 0.0 - 1.0
+ */
+ public ChannelizerBuilder alpha(double alpha)
+ {
+ Validate.isTrue(0.0 <= alpha && alpha <= 1.0);
+ mAlpha = alpha;
+
+ return this;
+ }
+
+ public FIRFilterSpecification build()
+ {
+ FIRLinearPhaseFilterType type = FIRLinearPhaseFilterType.TYPE_2_EVEN_LENGTH_ODD_ORDER_SYMMETRICAL;
+
+ int passFrequencyEnd = (int)(mChannelBandwidth * (1.0 - mAlpha));
+ int stopFrequencyBegin = (int)(mChannelBandwidth * (1.0 + mAlpha));
+
+ int estimatedOrder = estimateFilterOrder(mSampleRate, passFrequencyEnd, stopFrequencyBegin, mPassRipple,
+ mStopRipple);
+
+ // Ensure odd order since we're designing a Type 2 filter
+ if(estimatedOrder % 2 == 0)
+ {
+ estimatedOrder++;
+ }
+
+ int order = mChannelCount * mTapsPerChannel - 1;
+
+ mLog.debug("Order:" + order + " Sample Rate:" + mSampleRate + " Estimated Order:" + estimatedOrder + " Requested Order:" + order);
+
+ FIRFilterSpecification spec = new FIRFilterSpecification(type, order, mGridDensity);
+
+ double bandEdge = (double)mChannelBandwidth / (double)mSampleRate;
+ double passEnd = bandEdge * (1.0 - mAlpha);
+ double stopStart = bandEdge * (1.0 + mAlpha);
+
+ mLog.debug("Pass End:" + passEnd + " Band Edge:" + bandEdge + " Stop Begin:" + stopStart);
+ mLog.debug("Pass End:" + (int)(mSampleRate * passEnd) +
+ " Band Edge:" + (int)(mSampleRate * bandEdge) +
+ " Stop Begin:" + (int)(mSampleRate * stopStart));
+
+ FrequencyBand passBand = new FrequencyBand(0.0, passEnd, 1.0, mPassRipple);
+ FrequencyBand edgeBand = new FrequencyBand(bandEdge, bandEdge, 0.5, mPassRipple);
+ edgeBand.setWeight(8.0 * edgeBand.getWeight(mPassRipple));
+ FrequencyBand stopBand = new FrequencyBand(stopStart, 0.5, 0.0, mStopRipple);
+
+ spec.addFrequencyBand(passBand);
+ spec.addFrequencyBand(edgeBand);
+ spec.addFrequencyBand(stopBand);
+
+ mLog.debug(spec.toString());
+ return spec;
+ }
+ }
+
+ // /**
+ // * Converts the set of band amplitude values into a set of weights
+ // * normalized to the largest band amplitude value
+ // */
+ // private static double[] getBandWeights( double... amplitudes )
+ // {
+ // double maximum = 0.0;
+ //
+ // for ( double amplitude : amplitudes )
+ // {
+ // if ( amplitude > maximum )
+ // {
+ // maximum = amplitude;
+ // }
+ // }
+ //
+ // double[] weights = new double[ amplitudes.length ];
+ //
+ // for ( int x = 0; x < amplitudes.length; x++ )
+ // {
+ // weights[ x ] = maximum / amplitudes[ x ];
+ // }
+ //
+ // return weights;
+ // }
+
+ /**
+ * Calculates filter order from filter length
+ */
+ private static int getFilterOrder(double length)
+ {
+ return (int)(Math.ceil(length) - 1);
+ }
+
+ /**
+ * Estimates filter length for a two-band (high or low pass) FIR filter.
+ *
+ * Note: this estimator doesn't work well if the transition frequency is near 0 or sampleRate/2
+ *
+ * From Herrmann et al (1973), Practical design rules for optimum finite impulse response
+ * filters. Bell System Technical J., 52, 769-99
+ *
+ * @param sampleRate in hertz
+ * @param frequency1 in hertz
+ * @param frequency2 in hertz
+ * @param passBandRipple pass band ripple in dB
+ * @param stopBandRipple stop band ripple in dB
+ * @return estimated filter length
+ */
+ public static int estimateFilterOrder(int sampleRate, int frequency1, int frequency2,
+ double passBandRipple, double stopBandRipple)
+ {
+ double df = Math.abs(frequency2 - frequency1) / (double)sampleRate;
+
+ double ddp = Math
+ .log10(stopBandRipple <= passBandRipple ? passBandRipple : stopBandRipple);
+ double dds = Math
+ .log10(stopBandRipple <= passBandRipple ? stopBandRipple : passBandRipple);
+
+ double a1 = 5.309e-3;
+ double a2 = 7.114e-2;
+ double a3 = -4.761e-1;
+ double a4 = -2.66e-3;
+ double a5 = -5.941e-1;
+ double a6 = -4.278e-1;
+
+ double b1 = 11.01217;
+ double b2 = 0.5124401;
+
+ double t1 = a1 * ddp * ddp;
+ double t2 = a2 * ddp;
+ double t3 = a4 * ddp * ddp;
+ double t4 = a5 * ddp;
+
+ double dinf = ((t1 + t2 + a3) * dds) + (t3 + t4 + a6);
+ double ff = b1 + b2 * (ddp - dds);
+ double n = dinf / df - ff * df + 1.0;
+
+ return (int)Math.ceil(n);
+ }
+
+ /**
+ * Estimates band pass filter length;
+ *
+ * @param passBandStart frequency normalized to sample rate
+ * @param passBandEnd frequency normalized to sample rate
+ * @param passBandRippleDb
+ * @param stopBandRippleDb
+ */
+ public static int estimateBandPassOrder(int sampleRate, int passBandStart, int passBandEnd,
+ double passBandRippleDb, double stopBandRippleDb)
+ {
+ double df = (double)Math.abs(passBandEnd - passBandStart) / (double)sampleRate;
+ double ddp = (double)Math.log10(passBandRippleDb);
+ double dds = (double)Math.log10(stopBandRippleDb);
+
+ double a1 = 0.01201;
+ double a2 = 0.09664;
+ double a3 = -0.51325;
+ double a4 = 0.00203;
+ double a5 = -0.57054;
+ double a6 = -0.44314;
+
+ double t1 = a1 * ddp * ddp;
+ double t2 = a2 * ddp;
+ double t3 = a4 * ddp * ddp;
+ double t4 = a5 * ddp;
+
+ double cinf = dds * (t1 + t2 + a3) + t3 + t4 + a6;
+ double ginf = -14.6f * (double)Math.log10(passBandRippleDb / stopBandRippleDb) - 16.9;
+ double n = cinf / df + ginf * df + 1.0;
+
+ return (int)Math.ceil(n);
+ }
+
+ /**
+ * Frequency band
+ */
+ public static class FrequencyBand
+ {
+ private int mGridSize;
+ private double mStart;
+ private double mEnd;
+ private double mAmplitude;
+ private double mRippleDB;
+ private Double mWeight;
+
+ /**
+ * Constructs a frequency band where the start and end frequencies are normalized to a
+ * sample rate of 1.
+ *
+ * @param start edge of the frequency band (0.0 - 1.0)
+ * @param end edge of the frequency band (0.0 - 1.0)
+ * @param amplitude of the frequency band on the unity scale (0.0 - 1.0)
+ * @param ripple of the frequency band specified as db ripple
+ */
+ public FrequencyBand(double start, double end, double amplitude, double ripple)
+ {
+ assert (0.0 <= mStart);
+ assert (mStart <= mEnd);
+ assert (mEnd <= 1.0);
+ assert (0.0 <= amplitude && amplitude <= 1.0);
+ assert (0.0 <= ripple);
+
+ mStart = start;
+ mEnd = end;
+ mAmplitude = amplitude;
+ mRippleDB = ripple;
+ }
+
+ /**
+ * Constructs a frequency band for the sample rate and start and end frequencies.
+ *
+ * @param sampleRate in hertz
+ * @param start edge of the frequency band in hertz
+ * @param end edge of the frequency band in hertz
+ * @param amplitude of the frequency band relative to unity (0.0 - 1.0)
+ * @param ripple of the frequency band specified as db ripple
+ */
+ public FrequencyBand(int sampleRate, int start, int end, double amplitude, double ripple)
+ {
+ this((double)start / (double)sampleRate, (double)end / (double)sampleRate,
+ amplitude, ripple);
+ }
+
+ public FrequencyBand(int sampleRate, int start, int end, double amplitude, double ripple, double weight)
+ {
+ this(sampleRate, start, end, amplitude, ripple);
+ mWeight = weight;
+ }
+
+ /**
+ * Explicitly sets the weight of this frequency band.
+ */
+ public void setWeight(double weight)
+ {
+ mWeight = weight;
+ }
+
+ public int getGridSize()
+ {
+ return mGridSize;
+ }
+
+ public void setGridSize(int totalGridSize, double totalBandwidth)
+ {
+ mGridSize = Math.max(1, (int)Math.ceil((double)totalGridSize * (getBandWidth() / totalBandwidth)));
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Band Start: ");
+ sb.append(mStart);
+ sb.append(" End:");
+ sb.append(mEnd);
+ sb.append(" Amplitude: ");
+ sb.append(mAmplitude);
+ sb.append(" Ripple dB: ");
+ sb.append(mRippleDB);
+ sb.append(" Ripple Amplitude: ");
+ sb.append(DECIMAL_FORMATTER.format(getRippleAmplitude()));
+ sb.append(" Grid Size: ");
+ sb.append(mGridSize);
+ sb.append(" Weight: ");
+ sb.append(getWeight(mRippleDB));
+
+ return sb.toString();
+ }
+
+ /**
+ * Start frequency edge for this band normalized to 1 Hz.
+ */
+ public double getStart()
+ {
+ return mStart;
+ }
+
+ /**
+ * End frequency edge for this band normalized to 1 Hz.
+ */
+ public double getEnd()
+ {
+ return mEnd;
+ }
+
+ /**
+ * Desired amplitude response for this frequency band
+ *
+ * @return amplitude (0.0 - 1.0)
+ */
+ public double getAmplitude()
+ {
+ return mAmplitude;
+ }
+
+ /**
+ * Frequency band ripple in decibels
+ */
+ public double getRippleDB()
+ {
+ return mRippleDB;
+ }
+
+ /**
+ * Frequency band ripple as an amplitude value
+ */
+ public double getRippleAmplitude()
+ {
+ return (Math.pow(10.0, (mRippleDB / 20)) - 1)
+ / (Math.pow(10.0, (mRippleDB / 20)) + 1);
+ }
+
+ /**
+ * Calculates the weighting of this frequency band's ripple relative to the max ripple for a
+ * filter specification.
+ *
+ * @param maxRippleAmplitude for the filter specification
+ * @return weight of this frequency band
+ */
+ public double getWeight(double maxRippleAmplitude)
+ {
+ if(mWeight == null)
+ {
+ return 1.0 / (getRippleAmplitude() / maxRippleAmplitude);
+ }
+
+ return mWeight;
+ }
+
+ /**
+ * Double-sided bandwidth of this frequency band
+ */
+ public double getBandWidth()
+ {
+ return mEnd - mStart;
+ }
+ }
}
diff --git a/src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java
similarity index 92%
rename from src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java
rename to src/main/java/io/github/dsheirer/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java
index 01d8a8ff..6930b127 100644
--- a/src/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/PolyphaseChannelizerDesigner.java
@@ -16,9 +16,9 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.fir.remez;
+package io.github.dsheirer.dsp.filter.fir.remez;
-import dsp.filter.fir.FIRFilterSpecification;
+import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
public class PolyphaseChannelizerDesigner extends FIRFilterSpecification
{
diff --git a/src/dsp/filter/fir/remez/PolyphaseChannelizerFilterFactory.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/PolyphaseChannelizerFilterFactory.java
similarity index 97%
rename from src/dsp/filter/fir/remez/PolyphaseChannelizerFilterFactory.java
rename to src/main/java/io/github/dsheirer/dsp/filter/fir/remez/PolyphaseChannelizerFilterFactory.java
index 6b0ea0ff..9cad5751 100644
--- a/src/dsp/filter/fir/remez/PolyphaseChannelizerFilterFactory.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/PolyphaseChannelizerFilterFactory.java
@@ -16,9 +16,9 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.fir.remez;
+package io.github.dsheirer.dsp.filter.fir.remez;
-import dsp.filter.fir.FIRFilterSpecification;
+import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java
index 9a6dca91..bbfbf209 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner.java
@@ -211,15 +211,12 @@ public class RemezFIRFilterDesigner
for( int k = 0; k < mExtremalIndices.size() - 1; k++ )
{
- double cosineDelta = cosineOfFrequency -
- mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( k ) ];
+ double cosineDelta = cosineOfFrequency - mGrid.getCosineFrequencyGrid()[ mExtremalIndices.get( k ) ];
- //If this frequency is or is close to one of the polynomial points, use c[k] for that point
+ //If this frequency is close to one of the polynomial points, use the polynomial point for the response
if( Math.abs( cosineDelta ) < 1.0e-7 )
{
- numerator = mIdealFrequencyResponse[ k ];
- denominator = 1.0;
- break;
+ return mIdealFrequencyResponse[ k ];
}
else
{
diff --git a/src/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java
similarity index 99%
rename from src/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java
rename to src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java
index 521b4f65..42c4c6d5 100644
--- a/src/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesigner2.java
@@ -16,10 +16,10 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.fir.remez;
+package io.github.dsheirer.dsp.filter.fir.remez;
-import dsp.filter.design.FilterDesignException;
-import dsp.filter.fir.FIRFilterSpecification;
+import io.github.dsheirer.dsp.filter.design.FilterDesignException;
+import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java
similarity index 99%
rename from src/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java
rename to src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java
index d3fee4cf..07765625 100644
--- a/src/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/fir/remez/RemezFIRFilterDesignerWithLagrange.java
@@ -16,10 +16,10 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.filter.fir.remez;
+package io.github.dsheirer.dsp.filter.fir.remez;
-import dsp.filter.design.FilterDesignException;
-import dsp.filter.fir.FIRFilterSpecification;
+import io.github.dsheirer.dsp.filter.design.FilterDesignException;
+import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import org.apache.commons.math3.analysis.polynomials.PolynomialFunctionLagrangeForm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/src/main/java/io/github/dsheirer/dsp/gain/AutomaticGainControl.java b/src/main/java/io/github/dsheirer/dsp/gain/AutomaticGainControl.java
index d6873037..77e4d7f7 100644
--- a/src/main/java/io/github/dsheirer/dsp/gain/AutomaticGainControl.java
+++ b/src/main/java/io/github/dsheirer/dsp/gain/AutomaticGainControl.java
@@ -142,7 +142,7 @@ public class AutomaticGainControl
public float process( float currentSample )
{
- float delayedSample = mDelayBuffer.get( currentSample );
+ float delayedSample = mDelayBuffer.putAndGet( currentSample );
double gain = MANUAL_AGC_GAIN;
diff --git a/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java b/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java
index c939b952..d1ca98a1 100644
--- a/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java
+++ b/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java
@@ -74,7 +74,7 @@ public class ComplexFeedForwardGainControl implements ComplexSampleListener
}
/* Replace oldest envelope value with current envelope value */
- float oldestEnvelope = mEnvelopeHistory.get( envelope );
+ float oldestEnvelope = mEnvelopeHistory.putAndGet( envelope );
/* If the oldest envelope value was the max envelope value, then we
* have to rediscover the max value from the envelope history */
diff --git a/src/dsp/mixer/LowPhaseNoiseOscillator.java b/src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java
similarity index 98%
rename from src/dsp/mixer/LowPhaseNoiseOscillator.java
rename to src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java
index 160d0e1e..bc33df62 100644
--- a/src/dsp/mixer/LowPhaseNoiseOscillator.java
+++ b/src/main/java/io/github/dsheirer/dsp/mixer/LowPhaseNoiseOscillator.java
@@ -16,13 +16,11 @@
* along with this program. If not, see
*
******************************************************************************/
-package dsp.mixer;
+package io.github.dsheirer.dsp.mixer;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.complex.ComplexBuffer;
-
-import java.util.Arrays;
public class LowPhaseNoiseOscillator
{
diff --git a/src/main/java/io/github/dsheirer/dsp/mixer/Oscillator.java b/src/main/java/io/github/dsheirer/dsp/mixer/Oscillator.java
index 50511208..3099a5bf 100644
--- a/src/main/java/io/github/dsheirer/dsp/mixer/Oscillator.java
+++ b/src/main/java/io/github/dsheirer/dsp/mixer/Oscillator.java
@@ -1,112 +1,152 @@
/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * sdrtrunk
+ * Copyright (C) 2014-2017 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ *
******************************************************************************/
package io.github.dsheirer.dsp.mixer;
import io.github.dsheirer.sample.complex.Complex;
+import io.github.dsheirer.sample.complex.ComplexBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Oscillator
{
- private final static Logger mLog = LoggerFactory.getLogger( Oscillator.class );
+ private final static Logger mLog = LoggerFactory.getLogger(Oscillator.class);
- private double mFrequency;
- private double mSampleRate;
+ private boolean mEnabled = true;
+ private double mFrequency;
+ private double mSampleRate;
- private Complex mAnglePerSample;
- private Complex mCurrentAngle = new Complex( 0.0f, -1.0f );
+ private Complex mAnglePerSample;
+ private Complex mCurrentAngle = new Complex(0.0f, -1.0f);
- /**
- * Oscillator produces complex or float samples corresponding to a sine wave
- * oscillating at the specified frequency and sample rate
- *
- * @param frequency - positive or negative frequency in hertz
- * @param sampleRate - in hertz
- */
- public Oscillator( long frequency, int sampleRate )
- {
- mSampleRate = (double)sampleRate;
- mFrequency = (double)frequency;
-
- update();
- }
-
- /**
- * Updates the internal values after a frequency or sample rate change
- */
- private void update()
- {
- float anglePerSample =
- (float)( 2.0d * Math.PI * mFrequency / mSampleRate );
+ /**
+ * Oscillator produces complex or float samples corresponding to a sine wave
+ * oscillating at the specified frequency and sample rate
+ *
+ * @param frequency - positive or negative frequency in hertz
+ * @param sampleRate - in hertz
+ */
+ public Oscillator(long frequency, double sampleRate)
+ {
+ mEnabled = frequency != 0;
- mAnglePerSample = Complex.fromAngle( anglePerSample );
- }
+ mSampleRate = sampleRate;
+ mFrequency = (double)frequency;
- /**
- * Sets or changes the frequency of this oscillator
- */
- public void setFrequency( long frequency )
- {
- mFrequency = (double)frequency;
- update();
- }
+ update();
+ }
- /**
- * Sets or changes the sample rate of this oscillator
- */
- public void setSampleRate( int sampleRate )
- {
- mSampleRate = (double)sampleRate;
- update();
- }
+ /**
+ * Indicates if this oscillator has a non-zero frequency setting. When used as a frequency translating
+ * mixer, setting a frequency of zero indicates that no frequency correction is needed, so this flag can
+ * indicate when to apply the oscillator to frequency correct samples.
+ *
+ * @return true if the current frequency value is non-zero.
+ */
+ public boolean enabled()
+ {
+ return mEnabled;
+ }
- /**
- * Steps the current angle by the angle per sample amount
- */
- public void rotate()
- {
- mCurrentAngle.multiply( mAnglePerSample );
- mCurrentAngle.fastNormalize();
- }
+ /**
+ * Updates the internal values after a frequency or sample rate change
+ */
+ private void update()
+ {
+ float anglePerSample =
+ (float)(2.0d * Math.PI * mFrequency / mSampleRate);
- public float inphase()
- {
- return mCurrentAngle.inphase();
- }
-
- public float quadrature()
- {
- return mCurrentAngle.quadrature();
- }
-
- /**
- * Get next complex sample
- */
- public Complex getComplex()
- {
- return mCurrentAngle.copy();
- }
+ mAnglePerSample = Complex.fromAngle(anglePerSample);
+ }
- /**
- * Get the next float sample
- */
- public float getFloat()
- {
- return mCurrentAngle.real();
- }
+ /**
+ * Sets or changes the frequency of this oscillator
+ */
+ public void setFrequency(long frequency)
+ {
+ mEnabled = frequency != 0;
+ mFrequency = (double)frequency;
+ update();
+ }
+
+ /**
+ * Sets or changes the sample rate of this oscillator
+ */
+ public void setSampleRate(double sampleRate)
+ {
+ mSampleRate = sampleRate;
+ update();
+ }
+
+ /**
+ * Steps the current angle by the angle per sample amount
+ */
+ public void rotate()
+ {
+ mCurrentAngle.multiply(mAnglePerSample);
+ mCurrentAngle.fastNormalize();
+ }
+
+ public float inphase()
+ {
+ return mCurrentAngle.inphase();
+ }
+
+ public float quadrature()
+ {
+ return mCurrentAngle.quadrature();
+ }
+
+ /**
+ * Get next complex sample
+ */
+ public Complex getComplex()
+ {
+ return mCurrentAngle.copy();
+ }
+
+ /**
+ * Get the next float sample
+ */
+ public float getFloat()
+ {
+ return mCurrentAngle.real();
+ }
+
+ /**
+ * Generates the requested number of complex samples and returns them in a complex buffer
+ *
+ * @param sampleCount to generate
+ * @return complex buffer containing the requested complex samples
+ */
+ public ComplexBuffer generateComplexBuffer(int sampleCount)
+ {
+ float[] buffer = new float[sampleCount * 2];
+
+ for(int x = 0; x < sampleCount; x++)
+ {
+ int index = 2 * x;
+ buffer[index] = inphase();
+ buffer[index + 1] = quadrature();
+
+ rotate();
+ }
+
+ return new ComplexBuffer(buffer);
+ }
}
diff --git a/src/main/java/io/github/dsheirer/dsp/psk/LSMDemodulator.java b/src/main/java/io/github/dsheirer/dsp/psk/LSMDemodulator.java
index 030ef0d8..8eac3fd7 100644
--- a/src/main/java/io/github/dsheirer/dsp/psk/LSMDemodulator.java
+++ b/src/main/java/io/github/dsheirer/dsp/psk/LSMDemodulator.java
@@ -41,7 +41,7 @@ import io.github.dsheirer.instrument.tap.stream.EyeDiagramDataTap;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.Complex;
import io.github.dsheirer.sample.complex.ComplexSampleListener;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
+import io.github.dsheirer.source.ISourceEventListener;
import java.util.ArrayList;
import java.util.Arrays;
@@ -72,7 +72,7 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
private CostasLoop mCostasLoop = new CostasLoop();
- private IFrequencyChangeListener mFrequencyChangeListener;
+ private ISourceEventListener mFrequencyChangeListener;
private EyeDiagramDataTap mEyeDiagramDataTap;
@@ -146,12 +146,12 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
}
}
- public void addListener( IFrequencyChangeListener listener )
+ public void addListener( ISourceEventListener listener )
{
mFrequencyChangeListener = listener;
}
- public void removeListener( IFrequencyChangeListener listener )
+ public void removeListener( ISourceEventListener listener )
{
mFrequencyChangeListener = null;
}
@@ -201,8 +201,8 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
// if( correctionNeeded && mFrequencyChangeListener != null )
// {
// mLog.debug( "Issuing Frequency Correction: " + mFrequencyError );
-// mFrequencyChangeListener.frequencyChanged(
-// new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR,
+// mFrequencyChangeListener.process(
+// new SourceEvent( Attribute.FREQUENCY_ERROR,
// mFrequencyError ) );
// }
//
diff --git a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java
index ee417149..4dfef7a2 100644
--- a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java
+++ b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java
@@ -1,6 +1,6 @@
/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * sdrtrunk
+ * Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,17 +17,25 @@
******************************************************************************/
package io.github.dsheirer.gui.control;
+import io.github.dsheirer.source.ISourceEventProcessor;
import io.github.dsheirer.source.InvalidFrequencyException;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.SourceEvent.Event;
import io.github.dsheirer.source.SourceException;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.swing.*;
-import java.awt.*;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Font;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
@@ -40,13 +48,12 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-public class JFrequencyControl extends JPanel implements IFrequencyChangeProcessor
+public class JFrequencyControl extends JPanel implements ISourceEventProcessor
{
private static final long serialVersionUID = 1L;
- private final static Logger mLog =
- LoggerFactory.getLogger(JFrequencyControl.class);
+ private final static Logger mLog = LoggerFactory.getLogger(JFrequencyControl.class);
- private List mProcessors = new ArrayList<>();
+ private List mProcessors = new ArrayList<>();
private Color mHighlightColor = Color.YELLOW;
@@ -54,7 +61,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
private Cursor mBlankCursor;
- private HashMap mDigits = new HashMap();
+ private HashMap mDigits = new HashMap();
public JFrequencyControl()
{
@@ -73,7 +80,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
Font font = new Font(Font.MONOSPACED, Font.BOLD, 30);
- for (int x = 9; x >= 0; x--)
+ for(int x = 9; x >= 0; x--)
{
Digit digit = null;
@@ -81,13 +88,13 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
digit = new Digit(x);
}
- catch (ParseException e)
+ catch(ParseException e)
{
mLog.error("JFrequencyControl - parse exception "
- + "constructing a digit - " + e);
+ + "constructing a digit - " + e);
}
- if (digit != null)
+ if(digit != null)
{
mDigits.put(x, digit);
@@ -95,7 +102,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
digit.setFont(font);
- if (x == 6)
+ if(x == 6)
{
JLabel period = new JLabel(".");
@@ -116,7 +123,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
Image cursorImage = Toolkit.getDefaultToolkit().createImage(imageByte);
mBlankCursor = Toolkit.getDefaultToolkit()
- .createCustomCursor(cursorImage, new Point(0, 0), "cursor");
+ .createCustomCursor(cursorImage, new Point(0, 0), "cursor");
revalidate();
}
@@ -126,7 +133,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
super.setEnabled(enabled);
- for (Digit digit : mDigits.values())
+ for(Digit digit : mDigits.values())
{
digit.setEnabled(enabled);
}
@@ -137,9 +144,9 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
* rebroadcast this event, just set the control to indicate the new frequency.
*/
@Override
- public void frequencyChanged(FrequencyChangeEvent event)
+ public void process(SourceEvent event)
{
- if (event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE)
+ if(event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE)
{
setFrequency(event.getValue().longValue(), false);
}
@@ -153,7 +160,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
mFrequency = frequency;
- for (Digit digit : mDigits.values())
+ for(Digit digit : mDigits.values())
{
digit.setFrequency(frequency, fireChangeEvent);
}
@@ -168,7 +175,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
{
long frequency = 0;
- for (Digit digit : mDigits.values())
+ for(Digit digit : mDigits.values())
{
frequency += digit.getFrequency();
}
@@ -176,27 +183,26 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
mFrequency = frequency;
}
- private void fireFrequencyChanged() throws SourceException
+ private void fireSourceChanged() throws SourceException
{
updateFrequency();
- Iterator it = mProcessors.iterator();
+ Iterator it = mProcessors.iterator();
- FrequencyChangeEvent event = new FrequencyChangeEvent(
- Event.REQUEST_FREQUENCY_CHANGE, mFrequency);
+ SourceEvent event = SourceEvent.frequencyRequest(mFrequency);
- while (it.hasNext())
+ while(it.hasNext())
{
- it.next().frequencyChanged(event);
+ it.next().process(event);
}
}
- public void addListener(IFrequencyChangeProcessor processor)
+ public void addListener(ISourceEventProcessor processor)
{
mProcessors.add(processor);
}
- public void removeListener(IFrequencyChangeProcessor processor)
+ public void removeListener(ISourceEventProcessor processor)
{
mProcessors.remove(processor);
}
@@ -228,17 +234,17 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
public void setFrequency(long frequency, boolean fireChangeEvent)
{
//Strip the digits higher than this one
- long lower = frequency % (long) (Math.pow(10, mPower + 1));
+ long lower = frequency % (long)(Math.pow(10, mPower + 1));
//Set the value to int value of dividing by 10 to this power
- long value = (long) (lower / (long) (Math.pow(10, mPower)));
+ long value = (long)(lower / (long)(Math.pow(10, mPower)));
set(value, fireChangeEvent);
}
public long getFrequency()
{
- return mValue * (long) Math.pow(10, mPower);
+ return mValue * (long)Math.pow(10, mPower);
}
public void increment()
@@ -248,7 +254,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
public void increment(boolean fireChangeEvent)
{
- if (isEnabled())
+ if(isEnabled())
{
set(mValue + 1, fireChangeEvent);
}
@@ -261,7 +267,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
public void decrement(boolean fireChangeEvent)
{
- if (isEnabled())
+ if(isEnabled())
{
set(mValue - 1, fireChangeEvent);
}
@@ -287,13 +293,13 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
mValue = amount;
- while (mValue < 0)
+ while(mValue < 0)
{
mValue += 10;
Digit nextHigherDigit = mDigits.get(mPower + 1);
- if (nextHigherDigit != null)
+ if(nextHigherDigit != null)
{
nextHigherDigit.decrement(false);
higherDigitDecremented = true;
@@ -301,55 +307,55 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
}
- while (mValue > 9)
+ while(mValue > 9)
{
mValue -= 10;
Digit nextHigherDigit = mDigits.get(mPower + 1);
- if (nextHigherDigit != null)
+ if(nextHigherDigit != null)
{
nextHigherDigit.increment(false);
higherDigitIncremented = true;
}
}
- if (fireChangeEvent)
+ if(fireChangeEvent)
{
try
{
- fireFrequencyChanged();
+ fireSourceChanged();
}
- catch (SourceException se)
+ catch(SourceException se)
{
mValue = previous;
- if (higherDigitIncremented)
+ if(higherDigitIncremented)
{
Digit nextHigherDigit = mDigits.get(mPower + 1);
- if (nextHigherDigit != null)
+ if(nextHigherDigit != null)
{
nextHigherDigit.decrement(false);
}
}
- if (higherDigitDecremented)
+ if(higherDigitDecremented)
{
Digit nextHigherDigit = mDigits.get(mPower + 1);
- if (nextHigherDigit != null)
+ if(nextHigherDigit != null)
{
nextHigherDigit.increment(false);
}
}
- if (se instanceof InvalidFrequencyException)
+ if(se instanceof InvalidFrequencyException)
{
- InvalidFrequencyException ife = (InvalidFrequencyException) se;
+ InvalidFrequencyException ife = (InvalidFrequencyException)se;
JOptionPane.showMessageDialog(this, "Frequency [" + ife.getInvalidFrequency() +
- "] exceeds the frequency limit [" + ife.getValidFrequency() + "] for this tuner.");
+ "] exceeds the frequency limit [" + ife.getValidFrequency() + "] for this tuner.");
}
}
}
@@ -361,15 +367,15 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
}
private class Listener implements KeyListener,
- MouseListener,
- MouseWheelListener
+ MouseListener,
+ MouseWheelListener
{
@Override
public void keyReleased(KeyEvent e)
{
int key = e.getKeyCode();
- switch (key)
+ switch(key)
{
case KeyEvent.VK_0:
case KeyEvent.VK_NUMPAD0:
@@ -456,7 +462,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess
@Override
public void mouseClicked(MouseEvent e)
{
- switch (e.getButton())
+ switch(e.getButton())
{
case MouseEvent.BUTTON1:
increment();
diff --git a/src/main/java/io/github/dsheirer/instrument/tap/stream/BinaryTapViewPanel.java b/src/main/java/io/github/dsheirer/instrument/tap/stream/BinaryTapViewPanel.java
index 3172dfe7..76f6bab6 100644
--- a/src/main/java/io/github/dsheirer/instrument/tap/stream/BinaryTapViewPanel.java
+++ b/src/main/java/io/github/dsheirer/instrument/tap/stream/BinaryTapViewPanel.java
@@ -21,7 +21,10 @@ import io.github.dsheirer.instrument.gui.SampleModel;
import io.github.dsheirer.instrument.tap.Tap;
import io.github.dsheirer.instrument.tap.TapViewPanel;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
@@ -90,7 +93,7 @@ public class BinaryTapViewPanel extends TapViewPanel
* Paints a polyline representing a boolean value with a 0 or 1 label in
* the middle of it
*
- * @param graphics
+ * @param graphics object
* @param index - index of the sample int the sample buffer
* @param value - bit value, 0 or 1
*/
diff --git a/src/main/java/io/github/dsheirer/module/Module.java b/src/main/java/io/github/dsheirer/module/Module.java
index ce1253d6..afeb566a 100644
--- a/src/main/java/io/github/dsheirer/module/Module.java
+++ b/src/main/java/io/github/dsheirer/module/Module.java
@@ -18,8 +18,6 @@
package io.github.dsheirer.module;
-import java.util.concurrent.ScheduledExecutorService;
-
public abstract class Module
{
/**
@@ -59,9 +57,9 @@ public abstract class Module
public abstract void reset();
/**
- * Start processing. Provides an executor to use for scheduling tasks
+ * Start processing.
*/
- public abstract void start( ScheduledExecutorService executor );
+ public abstract void start();
/**
* Stop processing
diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
index 391d999f..7dddae2a 100644
--- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java
+++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
@@ -24,9 +24,6 @@ import io.github.dsheirer.audio.IAudioPacketProvider;
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
import io.github.dsheirer.audio.squelch.ISquelchStateProvider;
import io.github.dsheirer.audio.squelch.SquelchState;
-import io.github.dsheirer.channel.heartbeat.Heartbeat;
-import io.github.dsheirer.channel.heartbeat.IHeartbeatListener;
-import io.github.dsheirer.channel.heartbeat.IHeartbeatProvider;
import io.github.dsheirer.channel.metadata.AttributeChangeRequest;
import io.github.dsheirer.channel.metadata.IAttributeChangeRequestListener;
import io.github.dsheirer.channel.metadata.IAttributeChangeRequestProvider;
@@ -60,12 +57,14 @@ import io.github.dsheirer.sample.real.IUnFilteredRealBufferListener;
import io.github.dsheirer.sample.real.IUnFilteredRealBufferProvider;
import io.github.dsheirer.sample.real.RealBuffer;
import io.github.dsheirer.source.ComplexSource;
+import io.github.dsheirer.source.ISourceEventListener;
+import io.github.dsheirer.source.ISourceEventProvider;
import io.github.dsheirer.source.RealSource;
import io.github.dsheirer.source.Source;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProvider;
-import io.github.dsheirer.util.ThreadPool;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.heartbeat.Heartbeat;
+import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
+import io.github.dsheirer.source.heartbeat.IHeartbeatProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -101,8 +100,8 @@ public class ProcessingChain implements IChannelEventListener
private Broadcaster mChannelEventBroadcaster = new Broadcaster<>();
private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>();
private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>();
- private Broadcaster mFrequencyChangeEventBroadcaster = new Broadcaster<>();
private Broadcaster mHeartbeatBroadcaster = new Broadcaster<>();
+ private Broadcaster mSourceEventBroadcaster = new Broadcaster<>();
private Broadcaster mMessageBroadcaster = new Broadcaster<>();
private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>();
private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>();
@@ -190,11 +189,13 @@ public class ProcessingChain implements IChannelEventListener
}
/**
- * Applies a source to this processing chain. Processing won't start until the start() method is invoked.
+ * Applies a sample source to this processing chain. Processing won't
+ * start until the start() method is invoked.
*
- * @param source - real or complex source
- * @throws IllegalStateException if the processing chain is currently processing with another source. Invoke stop()
- * before applying a new source.
+ * @param source - real or complex sample source
+ * @throws IllegalStateException if the processing chain is currently
+ * processing with another source. Invoke stop() before applying a new
+ * source.
*/
public void setSource(Source source) throws IllegalStateException
{
@@ -316,14 +317,13 @@ public class ProcessingChain implements IChannelEventListener
mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
- if(module instanceof IFrequencyChangeListener)
+ if(module instanceof ISourceEventListener)
{
- Listener frequencyChangeEventListener =
- ((IFrequencyChangeListener)module).getFrequencyChangeListener();
+ Listener listener = ((ISourceEventListener)module).getSourceEventListener();
- if(frequencyChangeEventListener != null)
+ if(listener != null)
{
- mFrequencyChangeEventBroadcaster.addListener(frequencyChangeEventListener);
+ mSourceEventBroadcaster.addListener(listener);
}
}
@@ -389,9 +389,9 @@ public class ProcessingChain implements IChannelEventListener
mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
- if(module instanceof IFrequencyChangeListener)
+ if(module instanceof ISourceEventListener)
{
- mFrequencyChangeEventBroadcaster.removeListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
+ mSourceEventBroadcaster.removeListener(((ISourceEventListener) module).getSourceEventListener());
}
if(module instanceof IHeartbeatListener)
@@ -451,14 +451,9 @@ public class ProcessingChain implements IChannelEventListener
((IDecoderStateEventProvider) module).setDecoderStateListener(mDecoderStateEventBroadcaster);
}
- if(module instanceof IFrequencyChangeProvider)
- {
- ((IFrequencyChangeProvider) module).setFrequencyChangeListener(mFrequencyChangeEventBroadcaster);
- }
-
if(module instanceof IHeartbeatProvider)
{
- ((IHeartbeatProvider)module).setHeartbeatListener(mHeartbeatBroadcaster);
+ ((IHeartbeatProvider)module).getHeartbeatManager().addHeartbeatListener(mHeartbeatBroadcaster);
}
if(module instanceof IMessageProvider)
@@ -471,6 +466,16 @@ public class ProcessingChain implements IChannelEventListener
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster);
}
+ if(module instanceof IMessageProvider)
+ {
+ ((IMessageProvider) module).setMessageListener(mMessageBroadcaster);
+ }
+
+ if(module instanceof ISourceEventProvider)
+ {
+ ((ISourceEventProvider) module).setSourceEventListener(mSourceEventBroadcaster);
+ }
+
if(module instanceof ISquelchStateProvider)
{
((ISquelchStateProvider) module).setSquelchStateListener(mSquelchStateBroadcaster);
@@ -513,14 +518,9 @@ public class ProcessingChain implements IChannelEventListener
((IDecoderStateEventProvider) module).setDecoderStateListener(null);
}
- if(module instanceof IFrequencyChangeProvider)
- {
- ((IFrequencyChangeProvider) module).setFrequencyChangeListener(null);
- }
-
if(module instanceof IHeartbeatProvider)
{
- ((IHeartbeatProvider)module).removeHeartbeatListener();
+ ((IHeartbeatProvider)module).getHeartbeatManager().removeHeartbeatListener(mHeartbeatBroadcaster);
}
if(module instanceof IMessageProvider)
@@ -533,6 +533,16 @@ public class ProcessingChain implements IChannelEventListener
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(null);
}
+ if(module instanceof IMessageProvider)
+ {
+ ((IMessageProvider) module).setMessageListener(null);
+ }
+
+ if(module instanceof ISourceEventProvider)
+ {
+ ((ISourceEventProvider) module).setSourceEventListener(null);
+ }
+
if(module instanceof ISquelchStateProvider)
{
((ISquelchStateProvider) module).setSquelchStateListener(null);
@@ -584,7 +594,7 @@ public class ProcessingChain implements IChannelEventListener
{
try
{
- module.start(ThreadPool.SCHEDULED);
+ module.start();
}
catch(Exception e)
{
@@ -600,8 +610,7 @@ public class ProcessingChain implements IChannelEventListener
}
/**
- * Stops processing if the chain is currently processing. Invocations on
- * an already stopped chain have no effect.
+ * Stops processing if the chain is currently processing. Invocations on an already stopped chain have no effect.
*/
public void stop()
{
@@ -628,15 +637,14 @@ public class ProcessingChain implements IChannelEventListener
((RealSource) mSource).removeListener(mFilteredRealBufferBroadcaster);
break;
default:
- throw new IllegalArgumentException("Unrecognized source "
- + "sample type - cannot start processing chain");
+ throw new IllegalArgumentException("Unrecognized source sample type - cannot start processing " +
+ "chain");
}
/* Release the source */
mSource.dispose();
mSource = null;
}
-
}
}
@@ -767,18 +775,18 @@ public class ProcessingChain implements IChannelEventListener
* Adds the listener to receive frequency change events from the processing chain
* @param listener to receive events
*/
- public void addFrequencyChangeListener(Listener listener)
+ public void addFrequencyChangeListener(Listener listener)
{
- mFrequencyChangeEventBroadcaster.addListener(listener);
+ mSourceEventBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving frequency change events
* @param listener to remove
*/
- public void removeFrequencyChangeListener(Listener listener)
+ public void removeFrequencyChangeListener(Listener listener)
{
- mFrequencyChangeEventBroadcaster.removeListener(listener);
+ mSourceEventBroadcaster.removeListener(listener);
}
/**
diff --git a/src/main/java/io/github/dsheirer/module/decode/Decoder.java b/src/main/java/io/github/dsheirer/module/decode/Decoder.java
index 9efeb0e7..dedac6df 100644
--- a/src/main/java/io/github/dsheirer/module/decode/Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/Decoder.java
@@ -22,8 +22,7 @@ import io.github.dsheirer.message.Message;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.Listener;
-public abstract class Decoder extends Module
- implements IMessageProvider, Listener
+public abstract class Decoder extends Module implements IMessageProvider, Listener
{
/* This has to be a broadcaster in order for references to persist */
protected Listener mMessageListener;
@@ -82,4 +81,19 @@ public abstract class Decoder extends Module
{
broadcast( message );
}
+
+ @Override
+ public void reset()
+ {
+ }
+
+ @Override
+ public void start()
+ {
+ }
+
+ @Override
+ public void stop()
+ {
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
index 29dbcf5b..5092c317 100644
--- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
@@ -66,6 +66,7 @@ import io.github.dsheirer.module.decode.mdc1200.MDCDecoderState;
import io.github.dsheirer.module.decode.mdc1200.MDCMessageFilter;
import io.github.dsheirer.module.decode.mpt1327.DecodeConfigMPT1327;
import io.github.dsheirer.module.decode.mpt1327.MPT1327Decoder;
+import io.github.dsheirer.module.decode.mpt1327.MPT1327Decoder.Sync;
import io.github.dsheirer.module.decode.mpt1327.MPT1327DecoderEditor;
import io.github.dsheirer.module.decode.mpt1327.MPT1327DecoderState;
import io.github.dsheirer.module.decode.mpt1327.MPT1327MessageFilter;
@@ -73,7 +74,7 @@ import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
import io.github.dsheirer.module.decode.nbfm.NBFMDecoder;
import io.github.dsheirer.module.decode.nbfm.NBFMDecoderEditor;
import io.github.dsheirer.module.decode.p25.DecodeConfigP25Phase1;
-import io.github.dsheirer.module.decode.p25.P25Decoder;
+import io.github.dsheirer.module.decode.p25.P25Decoder.Modulation;
import io.github.dsheirer.module.decode.p25.P25DecoderEditor;
import io.github.dsheirer.module.decode.p25.P25DecoderState;
import io.github.dsheirer.module.decode.p25.P25_C4FMDecoder;
@@ -145,7 +146,7 @@ public class DecoderFactory
Metadata metadata)
{
- /* Get the optional alias list for the decode modules to use */
+ /* Get the optional alias list for the decode modules to use */
AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName());
List modules = getPrimaryModules(channelModel, channelMapModel, channelProcessingManager, aliasList,
@@ -170,7 +171,7 @@ public class DecoderFactory
ChannelType channelType = channel.getChannelType();
- /* Baseband low-pass filter pass and stop frequencies */
+ /* Baseband low-pass filter pass and stop frequencies */
DecodeConfiguration decodeConfig = channel.getDecodeConfiguration();
int iqPass = decodeConfig.getDecoderType().getChannelBandwidth() / 2;
@@ -212,7 +213,7 @@ public class DecoderFactory
ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName());
- MPT1327Decoder.Sync sync = mptConfig.getSync();
+ Sync sync = mptConfig.getSync();
modules.add(new MPT1327Decoder(aliasList, sync));
@@ -221,7 +222,7 @@ public class DecoderFactory
if(channelType == ChannelType.STANDARD)
{
- modules.add(new TrafficChannelManager(channelModel, decodeConfig, channel.getEventLogConfiguration(),
+ modules.add(new TrafficChannelManager(channelModel, decodeConfig,
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize()));
}
@@ -240,7 +241,7 @@ public class DecoderFactory
case P25_PHASE1:
DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1) decodeConfig;
- P25Decoder.Modulation modulation = p25Config.getModulation();
+ Modulation modulation = p25Config.getModulation();
switch(modulation)
@@ -249,12 +250,12 @@ public class DecoderFactory
modules.add(new FMDemodulatorModule(P25_C4FM_IQ_FILTER));
modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
- modules.add(new P25DecoderState(aliasList, channelType, P25Decoder.Modulation.C4FM,
+ modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
p25Config.getIgnoreDataCalls()));
break;
case CQPSK:
modules.add(new P25_LSMDecoder(aliasList));
- modules.add(new P25DecoderState(aliasList, channelType, P25Decoder.Modulation.CQPSK, p25Config.getIgnoreDataCalls()));
+ modules.add(new P25DecoderState(aliasList, channelType, Modulation.CQPSK, p25Config.getIgnoreDataCalls()));
break;
default:
throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + modulation + "]");
@@ -262,7 +263,7 @@ public class DecoderFactory
if(channelType == ChannelType.STANDARD)
{
- modules.add(new TrafficChannelManager(channelModel, decodeConfig, channel.getEventLogConfiguration(),
+ modules.add(new TrafficChannelManager(channelModel, decodeConfig,
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize()));
}
@@ -337,7 +338,7 @@ public class DecoderFactory
}
}
- /* If we don't have any filters, add an ALL-PASS filter */
+ /* If we don't have any filters, add an ALL-PASS filter */
if(filterSet.getFilters().isEmpty())
{
filterSet.addFilter(new AllPassFilter());
@@ -510,4 +511,4 @@ public class DecoderFactory
return null;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/am/AMDecoder.java b/src/main/java/io/github/dsheirer/module/decode/am/AMDecoder.java
index 31362eba..73f9a824 100644
--- a/src/main/java/io/github/dsheirer/module/decode/am/AMDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/am/AMDecoder.java
@@ -22,8 +22,6 @@ import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.PrimaryDecoder;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
-import java.util.concurrent.ScheduledExecutorService;
-
public class AMDecoder extends PrimaryDecoder
{
public AMDecoder( DecodeConfiguration config )
@@ -42,10 +40,10 @@ public class AMDecoder extends PrimaryDecoder
{
}
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
+ @Override
+ public void start()
+ {
+ }
@Override
public void stop()
diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java
index 072d28f4..4a75ab70 100644
--- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java
@@ -38,7 +38,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
/**
* Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
@@ -173,19 +172,4 @@ public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferLis
{
return mDecimationFilter;
}
-
- @Override
- public void reset()
- {
- }
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
-
- @Override
- public void stop()
- {
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java
index 0c49e560..e65848d3 100644
--- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java
@@ -34,7 +34,6 @@ import io.github.dsheirer.util.StringUtils;
import java.util.Iterator;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class Fleetsync2DecoderState extends DecoderState
{
@@ -63,27 +62,12 @@ public class Fleetsync2DecoderState extends DecoderState
return DecoderType.FLEETSYNC2;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void stop()
- {
- }
-
@Override
public void dispose()
{
super.dispose();
}
- @Override
- public void init()
- {
- }
-
/**
* Resets the overall decoder state and clears any accumulated event details
*/
@@ -96,6 +80,24 @@ public class Fleetsync2DecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
/**
* Resets this decoder state
*/
diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java
index fb19a8d2..2556e207 100644
--- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200Decoder.java
@@ -41,7 +41,6 @@ import io.github.dsheirer.sample.real.RealSampleListener;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
/**
* LJ1200 - 1200 baud 2FSK decoder
@@ -191,19 +190,4 @@ public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListene
{
return mDecimationFilter;
}
-
- @Override
- public void reset()
- {
- }
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
-
- @Override
- public void stop()
- {
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java
index c4154dc5..e479c434 100644
--- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java
@@ -36,7 +36,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class LJ1200DecoderState extends DecoderState
{
@@ -60,16 +59,6 @@ public class LJ1200DecoderState extends DecoderState
return DecoderType.LJ_1200;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void stop()
- {
- }
-
@Override
public void receive(Message message)
{
@@ -122,12 +111,6 @@ public class LJ1200DecoderState extends DecoderState
return sb.toString();
}
- @Override
- public void init()
- {
- /* No initialization steps */
- }
-
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
@@ -148,4 +131,22 @@ public class LJ1200DecoderState extends DecoderState
mAddresses.clear();
resetState();
}
+
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java
index 08041b72..78d3db90 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java
@@ -33,7 +33,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
public class LTRNetDecoder extends Decoder
implements IUnFilteredRealBufferListener, Instrumentable
@@ -119,14 +118,4 @@ public class LTRNetDecoder extends Decoder
{
mLTRMessageFramer.reset();
}
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
-
- @Override
- public void stop()
- {
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java
index 48c3b3ef..7d8ce721 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java
@@ -39,7 +39,6 @@ import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class LTRNetDecoderState extends DecoderState
{
@@ -695,6 +694,24 @@ public class LTRNetDecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
/**
* Resets the decoder state after a call or other decode event
*/
@@ -764,12 +781,6 @@ public class LTRNetDecoderState extends DecoderState
}
}
- @Override
- public void init()
- {
- /* No initialization steps required */
- }
-
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
@@ -785,14 +796,4 @@ public class LTRNetDecoderState extends DecoderState
break;
}
}
-
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void stop()
- {
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java
index 0a96486b..8a6355a9 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java
@@ -28,8 +28,6 @@ import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.IUnFilteredRealBufferListener;
import io.github.dsheirer.sample.real.RealBuffer;
-import java.util.concurrent.ScheduledExecutorService;
-
public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBufferListener
{
public static final int LTR_STANDARD_MESSAGE_LENGTH = 40;
@@ -84,14 +82,4 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer
{
mLTRMessageFramer.reset();
}
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
-
- @Override
- public void stop()
- {
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java
index 788c8d9b..32d7647c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java
@@ -46,7 +46,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class LTRStandardDecoderState extends DecoderState
{
@@ -75,18 +74,6 @@ public class LTRStandardDecoderState extends DecoderState
return DecoderType.LTR_STANDARD;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void stop()
- {
- }
-
@Override
public void receive(Message message)
{
@@ -211,6 +198,24 @@ public class LTRStandardDecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
/**
* Performs a temporal reset following a call or other decode event
*/
@@ -256,11 +261,6 @@ public class LTRStandardDecoderState extends DecoderState
}
}
- @Override
- public void init()
- {
- }
-
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java
index beaf51da..1764c5db 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoder.java
@@ -40,7 +40,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
/**
* MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
@@ -201,25 +200,4 @@ public class MDCDecoder extends Decoder implements IFilteredRealBufferListener,
{
return mDecimationFilter;
}
-
- @Override
- public void reset()
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void stop()
- {
- // TODO Auto-generated method stub
-
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java
index 379b4037..8f8667fb 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java
@@ -34,7 +34,6 @@ import io.github.dsheirer.util.StringUtils;
import java.util.Iterator;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class MDCDecoderState extends DecoderState
{
@@ -59,16 +58,6 @@ public class MDCDecoderState extends DecoderState
return DecoderType.MDC1200;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void stop()
- {
- }
-
@Override
public void reset()
{
@@ -78,6 +67,24 @@ public class MDCDecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
private void resetState()
{
mFromAttribute.reset();
@@ -245,12 +252,6 @@ public class MDCDecoderState extends DecoderState
return sb.toString();
}
- @Override
- public void init()
- {
- /* No initialization required */
- }
-
/**
* Responds to reset events issued by the channel state
*/
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java
index d78d3670..c9455d95 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java
@@ -44,7 +44,6 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
/**
* MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
@@ -296,16 +295,4 @@ public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListen
mControlMessageFramer.reset();
mTrafficMessageFramer.reset();
}
-
- @Override
- public void start(ScheduledExecutorService executor)
- {
- // TODO Auto-generated method stub
- }
-
- @Override
- public void stop()
- {
- // TODO Auto-generated method stub
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java
index ca931381..d09cf40d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java
@@ -46,7 +46,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class MPT1327DecoderState extends DecoderState
{
@@ -85,16 +84,6 @@ public class MPT1327DecoderState extends DecoderState
return DecoderType.MPT1327;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void stop()
- {
- }
-
public ChannelType getChannelType()
{
return mChannelType;
@@ -300,6 +289,24 @@ public class MPT1327DecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
private void resetState()
{
mFromAttribute.reset();
@@ -499,12 +506,6 @@ public class MPT1327DecoderState extends DecoderState
return sb.toString();
}
- @Override
- public void init()
- {
- /* No initialization required */
- }
-
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
diff --git a/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java b/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java
index 3e0e326d..31752072 100644
--- a/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java
@@ -22,8 +22,6 @@ import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.PrimaryDecoder;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
-import java.util.concurrent.ScheduledExecutorService;
-
public class NBFMDecoder extends PrimaryDecoder
{
public NBFMDecoder( DecodeConfiguration config )
@@ -43,7 +41,7 @@ public class NBFMDecoder extends PrimaryDecoder
}
@Override
- public void start( ScheduledExecutorService executor )
+ public void start()
{
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java
index 7279a511..bca6a3a2 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java
@@ -10,499 +10,499 @@ import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.RealBuffer;
import io.github.dsheirer.sample.real.RealSampleListener;
import io.github.dsheirer.sample.real.RealSampleProvider;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
+import io.github.dsheirer.source.ISourceEventListener;
+import io.github.dsheirer.source.SourceEvent;
import java.util.ArrayList;
import java.util.List;
-public class C4FMSymbolFilter implements Listener, IFrequencyChangeListener, RealSampleProvider, Instrumentable
+public class C4FMSymbolFilter implements Listener, ISourceEventListener, RealSampleProvider, Instrumentable
{
- private static final float TAPS[][] =
- {
- { 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 0/128
- { -1.54700e-04f, 8.53777e-04f, -2.76968e-03f, 7.89295e-03f, 9.98534e-01f, -5.41054e-03f, 1.24642e-03f, -1.98993e-04f }, // 1/128
- { -3.09412e-04f, 1.70888e-03f, -5.55134e-03f, 1.58840e-02f, 9.96891e-01f, -1.07209e-02f, 2.47942e-03f, -3.96391e-04f }, // 2/128
- { -4.64053e-04f, 2.56486e-03f, -8.34364e-03f, 2.39714e-02f, 9.95074e-01f, -1.59305e-02f, 3.69852e-03f, -5.92100e-04f }, // 3/128
- { -6.18544e-04f, 3.42130e-03f, -1.11453e-02f, 3.21531e-02f, 9.93082e-01f, -2.10389e-02f, 4.90322e-03f, -7.86031e-04f }, // 4/128
- { -7.72802e-04f, 4.27773e-03f, -1.39548e-02f, 4.04274e-02f, 9.90917e-01f, -2.60456e-02f, 6.09305e-03f, -9.78093e-04f }, // 5/128
- { -9.26747e-04f, 5.13372e-03f, -1.67710e-02f, 4.87921e-02f, 9.88580e-01f, -3.09503e-02f, 7.26755e-03f, -1.16820e-03f }, // 6/128
- { -1.08030e-03f, 5.98883e-03f, -1.95925e-02f, 5.72454e-02f, 9.86071e-01f, -3.57525e-02f, 8.42626e-03f, -1.35627e-03f }, // 7/128
- { -1.23337e-03f, 6.84261e-03f, -2.24178e-02f, 6.57852e-02f, 9.83392e-01f, -4.04519e-02f, 9.56876e-03f, -1.54221e-03f }, // 8/128
- { -1.38589e-03f, 7.69462e-03f, -2.52457e-02f, 7.44095e-02f, 9.80543e-01f, -4.50483e-02f, 1.06946e-02f, -1.72594e-03f }, // 9/128
- { -1.53777e-03f, 8.54441e-03f, -2.80746e-02f, 8.31162e-02f, 9.77526e-01f, -4.95412e-02f, 1.18034e-02f, -1.90738e-03f }, // 10/128
- { -1.68894e-03f, 9.39154e-03f, -3.09033e-02f, 9.19033e-02f, 9.74342e-01f, -5.39305e-02f, 1.28947e-02f, -2.08645e-03f }, // 11/128
- { -1.83931e-03f, 1.02356e-02f, -3.37303e-02f, 1.00769e-01f, 9.70992e-01f, -5.82159e-02f, 1.39681e-02f, -2.26307e-03f }, // 12/128
- { -1.98880e-03f, 1.10760e-02f, -3.65541e-02f, 1.09710e-01f, 9.67477e-01f, -6.23972e-02f, 1.50233e-02f, -2.43718e-03f }, // 13/128
- { -2.13733e-03f, 1.19125e-02f, -3.93735e-02f, 1.18725e-01f, 9.63798e-01f, -6.64743e-02f, 1.60599e-02f, -2.60868e-03f }, // 14/128
- { -2.28483e-03f, 1.27445e-02f, -4.21869e-02f, 1.27812e-01f, 9.59958e-01f, -7.04471e-02f, 1.70776e-02f, -2.77751e-03f }, // 15/128
- { -2.43121e-03f, 1.35716e-02f, -4.49929e-02f, 1.36968e-01f, 9.55956e-01f, -7.43154e-02f, 1.80759e-02f, -2.94361e-03f }, // 16/128
- { -2.57640e-03f, 1.43934e-02f, -4.77900e-02f, 1.46192e-01f, 9.51795e-01f, -7.80792e-02f, 1.90545e-02f, -3.10689e-03f }, // 17/128
- { -2.72032e-03f, 1.52095e-02f, -5.05770e-02f, 1.55480e-01f, 9.47477e-01f, -8.17385e-02f, 2.00132e-02f, -3.26730e-03f }, // 18/128
- { -2.86289e-03f, 1.60193e-02f, -5.33522e-02f, 1.64831e-01f, 9.43001e-01f, -8.52933e-02f, 2.09516e-02f, -3.42477e-03f }, // 19/128
- { -3.00403e-03f, 1.68225e-02f, -5.61142e-02f, 1.74242e-01f, 9.38371e-01f, -8.87435e-02f, 2.18695e-02f, -3.57923e-03f }, // 20/128
- { -3.14367e-03f, 1.76185e-02f, -5.88617e-02f, 1.83711e-01f, 9.33586e-01f, -9.20893e-02f, 2.27664e-02f, -3.73062e-03f }, // 21/128
- { -3.28174e-03f, 1.84071e-02f, -6.15931e-02f, 1.93236e-01f, 9.28650e-01f, -9.53307e-02f, 2.36423e-02f, -3.87888e-03f }, // 22/128
- { -3.41815e-03f, 1.91877e-02f, -6.43069e-02f, 2.02814e-01f, 9.23564e-01f, -9.84679e-02f, 2.44967e-02f, -4.02397e-03f }, // 23/128
- { -3.55283e-03f, 1.99599e-02f, -6.70018e-02f, 2.12443e-01f, 9.18329e-01f, -1.01501e-01f, 2.53295e-02f, -4.16581e-03f }, // 24/128
- { -3.68570e-03f, 2.07233e-02f, -6.96762e-02f, 2.22120e-01f, 9.12947e-01f, -1.04430e-01f, 2.61404e-02f, -4.30435e-03f }, // 25/128
- { -3.81671e-03f, 2.14774e-02f, -7.23286e-02f, 2.31843e-01f, 9.07420e-01f, -1.07256e-01f, 2.69293e-02f, -4.43955e-03f }, // 26/128
- { -3.94576e-03f, 2.22218e-02f, -7.49577e-02f, 2.41609e-01f, 9.01749e-01f, -1.09978e-01f, 2.76957e-02f, -4.57135e-03f }, // 27/128
- { -4.07279e-03f, 2.29562e-02f, -7.75620e-02f, 2.51417e-01f, 8.95936e-01f, -1.12597e-01f, 2.84397e-02f, -4.69970e-03f }, // 28/128
- { -4.19774e-03f, 2.36801e-02f, -8.01399e-02f, 2.61263e-01f, 8.89984e-01f, -1.15113e-01f, 2.91609e-02f, -4.82456e-03f }, // 29/128
- { -4.32052e-03f, 2.43930e-02f, -8.26900e-02f, 2.71144e-01f, 8.83893e-01f, -1.17526e-01f, 2.98593e-02f, -4.94589e-03f }, // 30/128
- { -4.44107e-03f, 2.50946e-02f, -8.52109e-02f, 2.81060e-01f, 8.77666e-01f, -1.19837e-01f, 3.05345e-02f, -5.06363e-03f }, // 31/128
- { -4.55932e-03f, 2.57844e-02f, -8.77011e-02f, 2.91006e-01f, 8.71305e-01f, -1.22047e-01f, 3.11866e-02f, -5.17776e-03f }, // 32/128
- { -4.67520e-03f, 2.64621e-02f, -9.01591e-02f, 3.00980e-01f, 8.64812e-01f, -1.24154e-01f, 3.18153e-02f, -5.28823e-03f }, // 33/128
- { -4.78866e-03f, 2.71272e-02f, -9.25834e-02f, 3.10980e-01f, 8.58189e-01f, -1.26161e-01f, 3.24205e-02f, -5.39500e-03f }, // 34/128
- { -4.89961e-03f, 2.77794e-02f, -9.49727e-02f, 3.21004e-01f, 8.51437e-01f, -1.28068e-01f, 3.30021e-02f, -5.49804e-03f }, // 35/128
- { -5.00800e-03f, 2.84182e-02f, -9.73254e-02f, 3.31048e-01f, 8.44559e-01f, -1.29874e-01f, 3.35600e-02f, -5.59731e-03f }, // 36/128
- { -5.11376e-03f, 2.90433e-02f, -9.96402e-02f, 3.41109e-01f, 8.37557e-01f, -1.31581e-01f, 3.40940e-02f, -5.69280e-03f }, // 37/128
- { -5.21683e-03f, 2.96543e-02f, -1.01915e-01f, 3.51186e-01f, 8.30432e-01f, -1.33189e-01f, 3.46042e-02f, -5.78446e-03f }, // 38/128
- { -5.31716e-03f, 3.02507e-02f, -1.04150e-01f, 3.61276e-01f, 8.23188e-01f, -1.34699e-01f, 3.50903e-02f, -5.87227e-03f }, // 39/128
- { -5.41467e-03f, 3.08323e-02f, -1.06342e-01f, 3.71376e-01f, 8.15826e-01f, -1.36111e-01f, 3.55525e-02f, -5.95620e-03f }, // 40/128
- { -5.50931e-03f, 3.13987e-02f, -1.08490e-01f, 3.81484e-01f, 8.08348e-01f, -1.37426e-01f, 3.59905e-02f, -6.03624e-03f }, // 41/128
- { -5.60103e-03f, 3.19495e-02f, -1.10593e-01f, 3.91596e-01f, 8.00757e-01f, -1.38644e-01f, 3.64044e-02f, -6.11236e-03f }, // 42/128
- { -5.68976e-03f, 3.24843e-02f, -1.12650e-01f, 4.01710e-01f, 7.93055e-01f, -1.39767e-01f, 3.67941e-02f, -6.18454e-03f }, // 43/128
- { -5.77544e-03f, 3.30027e-02f, -1.14659e-01f, 4.11823e-01f, 7.85244e-01f, -1.40794e-01f, 3.71596e-02f, -6.25277e-03f }, // 44/128
- { -5.85804e-03f, 3.35046e-02f, -1.16618e-01f, 4.21934e-01f, 7.77327e-01f, -1.41727e-01f, 3.75010e-02f, -6.31703e-03f }, // 45/128
- { -5.93749e-03f, 3.39894e-02f, -1.18526e-01f, 4.32038e-01f, 7.69305e-01f, -1.42566e-01f, 3.78182e-02f, -6.37730e-03f }, // 46/128
- { -6.01374e-03f, 3.44568e-02f, -1.20382e-01f, 4.42134e-01f, 7.61181e-01f, -1.43313e-01f, 3.81111e-02f, -6.43358e-03f }, // 47/128
- { -6.08674e-03f, 3.49066e-02f, -1.22185e-01f, 4.52218e-01f, 7.52958e-01f, -1.43968e-01f, 3.83800e-02f, -6.48585e-03f }, // 48/128
- { -6.15644e-03f, 3.53384e-02f, -1.23933e-01f, 4.62289e-01f, 7.44637e-01f, -1.44531e-01f, 3.86247e-02f, -6.53412e-03f }, // 49/128
- { -6.22280e-03f, 3.57519e-02f, -1.25624e-01f, 4.72342e-01f, 7.36222e-01f, -1.45004e-01f, 3.88454e-02f, -6.57836e-03f }, // 50/128
- { -6.28577e-03f, 3.61468e-02f, -1.27258e-01f, 4.82377e-01f, 7.27714e-01f, -1.45387e-01f, 3.90420e-02f, -6.61859e-03f }, // 51/128
- { -6.34530e-03f, 3.65227e-02f, -1.28832e-01f, 4.92389e-01f, 7.19116e-01f, -1.45682e-01f, 3.92147e-02f, -6.65479e-03f }, // 52/128
- { -6.40135e-03f, 3.68795e-02f, -1.30347e-01f, 5.02377e-01f, 7.10431e-01f, -1.45889e-01f, 3.93636e-02f, -6.68698e-03f }, // 53/128
- { -6.45388e-03f, 3.72167e-02f, -1.31800e-01f, 5.12337e-01f, 7.01661e-01f, -1.46009e-01f, 3.94886e-02f, -6.71514e-03f }, // 54/128
- { -6.50285e-03f, 3.75341e-02f, -1.33190e-01f, 5.22267e-01f, 6.92808e-01f, -1.46043e-01f, 3.95900e-02f, -6.73929e-03f }, // 55/128
- { -6.54823e-03f, 3.78315e-02f, -1.34515e-01f, 5.32164e-01f, 6.83875e-01f, -1.45993e-01f, 3.96678e-02f, -6.75943e-03f }, // 56/128
- { -6.58996e-03f, 3.81085e-02f, -1.35775e-01f, 5.42025e-01f, 6.74865e-01f, -1.45859e-01f, 3.97222e-02f, -6.77557e-03f }, // 57/128
- { -6.62802e-03f, 3.83650e-02f, -1.36969e-01f, 5.51849e-01f, 6.65779e-01f, -1.45641e-01f, 3.97532e-02f, -6.78771e-03f }, // 58/128
- { -6.66238e-03f, 3.86006e-02f, -1.38094e-01f, 5.61631e-01f, 6.56621e-01f, -1.45343e-01f, 3.97610e-02f, -6.79588e-03f }, // 59/128
- { -6.69300e-03f, 3.88151e-02f, -1.39150e-01f, 5.71370e-01f, 6.47394e-01f, -1.44963e-01f, 3.97458e-02f, -6.80007e-03f }, // 60/128
- { -6.71985e-03f, 3.90083e-02f, -1.40136e-01f, 5.81063e-01f, 6.38099e-01f, -1.44503e-01f, 3.97077e-02f, -6.80032e-03f }, // 61/128
- { -6.74291e-03f, 3.91800e-02f, -1.41050e-01f, 5.90706e-01f, 6.28739e-01f, -1.43965e-01f, 3.96469e-02f, -6.79662e-03f }, // 62/128
- { -6.76214e-03f, 3.93299e-02f, -1.41891e-01f, 6.00298e-01f, 6.19318e-01f, -1.43350e-01f, 3.95635e-02f, -6.78902e-03f }, // 63/128
- { -6.77751e-03f, 3.94578e-02f, -1.42658e-01f, 6.09836e-01f, 6.09836e-01f, -1.42658e-01f, 3.94578e-02f, -6.77751e-03f }, // 64/128
- { -6.78902e-03f, 3.95635e-02f, -1.43350e-01f, 6.19318e-01f, 6.00298e-01f, -1.41891e-01f, 3.93299e-02f, -6.76214e-03f }, // 65/128
- { -6.79662e-03f, 3.96469e-02f, -1.43965e-01f, 6.28739e-01f, 5.90706e-01f, -1.41050e-01f, 3.91800e-02f, -6.74291e-03f }, // 66/128
- { -6.80032e-03f, 3.97077e-02f, -1.44503e-01f, 6.38099e-01f, 5.81063e-01f, -1.40136e-01f, 3.90083e-02f, -6.71985e-03f }, // 67/128
- { -6.80007e-03f, 3.97458e-02f, -1.44963e-01f, 6.47394e-01f, 5.71370e-01f, -1.39150e-01f, 3.88151e-02f, -6.69300e-03f }, // 68/128
- { -6.79588e-03f, 3.97610e-02f, -1.45343e-01f, 6.56621e-01f, 5.61631e-01f, -1.38094e-01f, 3.86006e-02f, -6.66238e-03f }, // 69/128
- { -6.78771e-03f, 3.97532e-02f, -1.45641e-01f, 6.65779e-01f, 5.51849e-01f, -1.36969e-01f, 3.83650e-02f, -6.62802e-03f }, // 70/128
- { -6.77557e-03f, 3.97222e-02f, -1.45859e-01f, 6.74865e-01f, 5.42025e-01f, -1.35775e-01f, 3.81085e-02f, -6.58996e-03f }, // 71/128
- { -6.75943e-03f, 3.96678e-02f, -1.45993e-01f, 6.83875e-01f, 5.32164e-01f, -1.34515e-01f, 3.78315e-02f, -6.54823e-03f }, // 72/128
- { -6.73929e-03f, 3.95900e-02f, -1.46043e-01f, 6.92808e-01f, 5.22267e-01f, -1.33190e-01f, 3.75341e-02f, -6.50285e-03f }, // 73/128
- { -6.71514e-03f, 3.94886e-02f, -1.46009e-01f, 7.01661e-01f, 5.12337e-01f, -1.31800e-01f, 3.72167e-02f, -6.45388e-03f }, // 74/128
- { -6.68698e-03f, 3.93636e-02f, -1.45889e-01f, 7.10431e-01f, 5.02377e-01f, -1.30347e-01f, 3.68795e-02f, -6.40135e-03f }, // 75/128
- { -6.65479e-03f, 3.92147e-02f, -1.45682e-01f, 7.19116e-01f, 4.92389e-01f, -1.28832e-01f, 3.65227e-02f, -6.34530e-03f }, // 76/128
- { -6.61859e-03f, 3.90420e-02f, -1.45387e-01f, 7.27714e-01f, 4.82377e-01f, -1.27258e-01f, 3.61468e-02f, -6.28577e-03f }, // 77/128
- { -6.57836e-03f, 3.88454e-02f, -1.45004e-01f, 7.36222e-01f, 4.72342e-01f, -1.25624e-01f, 3.57519e-02f, -6.22280e-03f }, // 78/128
- { -6.53412e-03f, 3.86247e-02f, -1.44531e-01f, 7.44637e-01f, 4.62289e-01f, -1.23933e-01f, 3.53384e-02f, -6.15644e-03f }, // 79/128
- { -6.48585e-03f, 3.83800e-02f, -1.43968e-01f, 7.52958e-01f, 4.52218e-01f, -1.22185e-01f, 3.49066e-02f, -6.08674e-03f }, // 80/128
- { -6.43358e-03f, 3.81111e-02f, -1.43313e-01f, 7.61181e-01f, 4.42134e-01f, -1.20382e-01f, 3.44568e-02f, -6.01374e-03f }, // 81/128
- { -6.37730e-03f, 3.78182e-02f, -1.42566e-01f, 7.69305e-01f, 4.32038e-01f, -1.18526e-01f, 3.39894e-02f, -5.93749e-03f }, // 82/128
- { -6.31703e-03f, 3.75010e-02f, -1.41727e-01f, 7.77327e-01f, 4.21934e-01f, -1.16618e-01f, 3.35046e-02f, -5.85804e-03f }, // 83/128
- { -6.25277e-03f, 3.71596e-02f, -1.40794e-01f, 7.85244e-01f, 4.11823e-01f, -1.14659e-01f, 3.30027e-02f, -5.77544e-03f }, // 84/128
- { -6.18454e-03f, 3.67941e-02f, -1.39767e-01f, 7.93055e-01f, 4.01710e-01f, -1.12650e-01f, 3.24843e-02f, -5.68976e-03f }, // 85/128
- { -6.11236e-03f, 3.64044e-02f, -1.38644e-01f, 8.00757e-01f, 3.91596e-01f, -1.10593e-01f, 3.19495e-02f, -5.60103e-03f }, // 86/128
- { -6.03624e-03f, 3.59905e-02f, -1.37426e-01f, 8.08348e-01f, 3.81484e-01f, -1.08490e-01f, 3.13987e-02f, -5.50931e-03f }, // 87/128
- { -5.95620e-03f, 3.55525e-02f, -1.36111e-01f, 8.15826e-01f, 3.71376e-01f, -1.06342e-01f, 3.08323e-02f, -5.41467e-03f }, // 88/128
- { -5.87227e-03f, 3.50903e-02f, -1.34699e-01f, 8.23188e-01f, 3.61276e-01f, -1.04150e-01f, 3.02507e-02f, -5.31716e-03f }, // 89/128
- { -5.78446e-03f, 3.46042e-02f, -1.33189e-01f, 8.30432e-01f, 3.51186e-01f, -1.01915e-01f, 2.96543e-02f, -5.21683e-03f }, // 90/128
- { -5.69280e-03f, 3.40940e-02f, -1.31581e-01f, 8.37557e-01f, 3.41109e-01f, -9.96402e-02f, 2.90433e-02f, -5.11376e-03f }, // 91/128
- { -5.59731e-03f, 3.35600e-02f, -1.29874e-01f, 8.44559e-01f, 3.31048e-01f, -9.73254e-02f, 2.84182e-02f, -5.00800e-03f }, // 92/128
- { -5.49804e-03f, 3.30021e-02f, -1.28068e-01f, 8.51437e-01f, 3.21004e-01f, -9.49727e-02f, 2.77794e-02f, -4.89961e-03f }, // 93/128
- { -5.39500e-03f, 3.24205e-02f, -1.26161e-01f, 8.58189e-01f, 3.10980e-01f, -9.25834e-02f, 2.71272e-02f, -4.78866e-03f }, // 94/128
- { -5.28823e-03f, 3.18153e-02f, -1.24154e-01f, 8.64812e-01f, 3.00980e-01f, -9.01591e-02f, 2.64621e-02f, -4.67520e-03f }, // 95/128
- { -5.17776e-03f, 3.11866e-02f, -1.22047e-01f, 8.71305e-01f, 2.91006e-01f, -8.77011e-02f, 2.57844e-02f, -4.55932e-03f }, // 96/128
- { -5.06363e-03f, 3.05345e-02f, -1.19837e-01f, 8.77666e-01f, 2.81060e-01f, -8.52109e-02f, 2.50946e-02f, -4.44107e-03f }, // 97/128
- { -4.94589e-03f, 2.98593e-02f, -1.17526e-01f, 8.83893e-01f, 2.71144e-01f, -8.26900e-02f, 2.43930e-02f, -4.32052e-03f }, // 98/128
- { -4.82456e-03f, 2.91609e-02f, -1.15113e-01f, 8.89984e-01f, 2.61263e-01f, -8.01399e-02f, 2.36801e-02f, -4.19774e-03f }, // 99/128
- { -4.69970e-03f, 2.84397e-02f, -1.12597e-01f, 8.95936e-01f, 2.51417e-01f, -7.75620e-02f, 2.29562e-02f, -4.07279e-03f }, // 100/128
- { -4.57135e-03f, 2.76957e-02f, -1.09978e-01f, 9.01749e-01f, 2.41609e-01f, -7.49577e-02f, 2.22218e-02f, -3.94576e-03f }, // 101/128
- { -4.43955e-03f, 2.69293e-02f, -1.07256e-01f, 9.07420e-01f, 2.31843e-01f, -7.23286e-02f, 2.14774e-02f, -3.81671e-03f }, // 102/128
- { -4.30435e-03f, 2.61404e-02f, -1.04430e-01f, 9.12947e-01f, 2.22120e-01f, -6.96762e-02f, 2.07233e-02f, -3.68570e-03f }, // 103/128
- { -4.16581e-03f, 2.53295e-02f, -1.01501e-01f, 9.18329e-01f, 2.12443e-01f, -6.70018e-02f, 1.99599e-02f, -3.55283e-03f }, // 104/128
- { -4.02397e-03f, 2.44967e-02f, -9.84679e-02f, 9.23564e-01f, 2.02814e-01f, -6.43069e-02f, 1.91877e-02f, -3.41815e-03f }, // 105/128
- { -3.87888e-03f, 2.36423e-02f, -9.53307e-02f, 9.28650e-01f, 1.93236e-01f, -6.15931e-02f, 1.84071e-02f, -3.28174e-03f }, // 106/128
- { -3.73062e-03f, 2.27664e-02f, -9.20893e-02f, 9.33586e-01f, 1.83711e-01f, -5.88617e-02f, 1.76185e-02f, -3.14367e-03f }, // 107/128
- { -3.57923e-03f, 2.18695e-02f, -8.87435e-02f, 9.38371e-01f, 1.74242e-01f, -5.61142e-02f, 1.68225e-02f, -3.00403e-03f }, // 108/128
- { -3.42477e-03f, 2.09516e-02f, -8.52933e-02f, 9.43001e-01f, 1.64831e-01f, -5.33522e-02f, 1.60193e-02f, -2.86289e-03f }, // 109/128
- { -3.26730e-03f, 2.00132e-02f, -8.17385e-02f, 9.47477e-01f, 1.55480e-01f, -5.05770e-02f, 1.52095e-02f, -2.72032e-03f }, // 110/128
- { -3.10689e-03f, 1.90545e-02f, -7.80792e-02f, 9.51795e-01f, 1.46192e-01f, -4.77900e-02f, 1.43934e-02f, -2.57640e-03f }, // 111/128
- { -2.94361e-03f, 1.80759e-02f, -7.43154e-02f, 9.55956e-01f, 1.36968e-01f, -4.49929e-02f, 1.35716e-02f, -2.43121e-03f }, // 112/128
- { -2.77751e-03f, 1.70776e-02f, -7.04471e-02f, 9.59958e-01f, 1.27812e-01f, -4.21869e-02f, 1.27445e-02f, -2.28483e-03f }, // 113/128
- { -2.60868e-03f, 1.60599e-02f, -6.64743e-02f, 9.63798e-01f, 1.18725e-01f, -3.93735e-02f, 1.19125e-02f, -2.13733e-03f }, // 114/128
- { -2.43718e-03f, 1.50233e-02f, -6.23972e-02f, 9.67477e-01f, 1.09710e-01f, -3.65541e-02f, 1.10760e-02f, -1.98880e-03f }, // 115/128
- { -2.26307e-03f, 1.39681e-02f, -5.82159e-02f, 9.70992e-01f, 1.00769e-01f, -3.37303e-02f, 1.02356e-02f, -1.83931e-03f }, // 116/128
- { -2.08645e-03f, 1.28947e-02f, -5.39305e-02f, 9.74342e-01f, 9.19033e-02f, -3.09033e-02f, 9.39154e-03f, -1.68894e-03f }, // 117/128
- { -1.90738e-03f, 1.18034e-02f, -4.95412e-02f, 9.77526e-01f, 8.31162e-02f, -2.80746e-02f, 8.54441e-03f, -1.53777e-03f }, // 118/128
- { -1.72594e-03f, 1.06946e-02f, -4.50483e-02f, 9.80543e-01f, 7.44095e-02f, -2.52457e-02f, 7.69462e-03f, -1.38589e-03f }, // 119/128
- { -1.54221e-03f, 9.56876e-03f, -4.04519e-02f, 9.83392e-01f, 6.57852e-02f, -2.24178e-02f, 6.84261e-03f, -1.23337e-03f }, // 120/128
- { -1.35627e-03f, 8.42626e-03f, -3.57525e-02f, 9.86071e-01f, 5.72454e-02f, -1.95925e-02f, 5.98883e-03f, -1.08030e-03f }, // 121/128
- { -1.16820e-03f, 7.26755e-03f, -3.09503e-02f, 9.88580e-01f, 4.87921e-02f, -1.67710e-02f, 5.13372e-03f, -9.26747e-04f }, // 122/128
- { -9.78093e-04f, 6.09305e-03f, -2.60456e-02f, 9.90917e-01f, 4.04274e-02f, -1.39548e-02f, 4.27773e-03f, -7.72802e-04f }, // 123/128
- { -7.86031e-04f, 4.90322e-03f, -2.10389e-02f, 9.93082e-01f, 3.21531e-02f, -1.11453e-02f, 3.42130e-03f, -6.18544e-04f }, // 124/128
- { -5.92100e-04f, 3.69852e-03f, -1.59305e-02f, 9.95074e-01f, 2.39714e-02f, -8.34364e-03f, 2.56486e-03f, -4.64053e-04f }, // 125/128
- { -3.96391e-04f, 2.47942e-03f, -1.07209e-02f, 9.96891e-01f, 1.58840e-02f, -5.55134e-03f, 1.70888e-03f, -3.09412e-04f }, // 126/128
- { -1.98993e-04f, 1.24642e-03f, -5.41054e-03f, 9.98534e-01f, 7.89295e-03f, -2.76968e-03f, 8.53777e-04f, -1.54700e-04f }, // 127/128
- { 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 128/128
- };
-
+ private static final float TAPS[][] =
+ {
+ {0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f}, // 0/128
+ {-1.54700e-04f, 8.53777e-04f, -2.76968e-03f, 7.89295e-03f, 9.98534e-01f, -5.41054e-03f, 1.24642e-03f, -1.98993e-04f}, // 1/128
+ {-3.09412e-04f, 1.70888e-03f, -5.55134e-03f, 1.58840e-02f, 9.96891e-01f, -1.07209e-02f, 2.47942e-03f, -3.96391e-04f}, // 2/128
+ {-4.64053e-04f, 2.56486e-03f, -8.34364e-03f, 2.39714e-02f, 9.95074e-01f, -1.59305e-02f, 3.69852e-03f, -5.92100e-04f}, // 3/128
+ {-6.18544e-04f, 3.42130e-03f, -1.11453e-02f, 3.21531e-02f, 9.93082e-01f, -2.10389e-02f, 4.90322e-03f, -7.86031e-04f}, // 4/128
+ {-7.72802e-04f, 4.27773e-03f, -1.39548e-02f, 4.04274e-02f, 9.90917e-01f, -2.60456e-02f, 6.09305e-03f, -9.78093e-04f}, // 5/128
+ {-9.26747e-04f, 5.13372e-03f, -1.67710e-02f, 4.87921e-02f, 9.88580e-01f, -3.09503e-02f, 7.26755e-03f, -1.16820e-03f}, // 6/128
+ {-1.08030e-03f, 5.98883e-03f, -1.95925e-02f, 5.72454e-02f, 9.86071e-01f, -3.57525e-02f, 8.42626e-03f, -1.35627e-03f}, // 7/128
+ {-1.23337e-03f, 6.84261e-03f, -2.24178e-02f, 6.57852e-02f, 9.83392e-01f, -4.04519e-02f, 9.56876e-03f, -1.54221e-03f}, // 8/128
+ {-1.38589e-03f, 7.69462e-03f, -2.52457e-02f, 7.44095e-02f, 9.80543e-01f, -4.50483e-02f, 1.06946e-02f, -1.72594e-03f}, // 9/128
+ {-1.53777e-03f, 8.54441e-03f, -2.80746e-02f, 8.31162e-02f, 9.77526e-01f, -4.95412e-02f, 1.18034e-02f, -1.90738e-03f}, // 10/128
+ {-1.68894e-03f, 9.39154e-03f, -3.09033e-02f, 9.19033e-02f, 9.74342e-01f, -5.39305e-02f, 1.28947e-02f, -2.08645e-03f}, // 11/128
+ {-1.83931e-03f, 1.02356e-02f, -3.37303e-02f, 1.00769e-01f, 9.70992e-01f, -5.82159e-02f, 1.39681e-02f, -2.26307e-03f}, // 12/128
+ {-1.98880e-03f, 1.10760e-02f, -3.65541e-02f, 1.09710e-01f, 9.67477e-01f, -6.23972e-02f, 1.50233e-02f, -2.43718e-03f}, // 13/128
+ {-2.13733e-03f, 1.19125e-02f, -3.93735e-02f, 1.18725e-01f, 9.63798e-01f, -6.64743e-02f, 1.60599e-02f, -2.60868e-03f}, // 14/128
+ {-2.28483e-03f, 1.27445e-02f, -4.21869e-02f, 1.27812e-01f, 9.59958e-01f, -7.04471e-02f, 1.70776e-02f, -2.77751e-03f}, // 15/128
+ {-2.43121e-03f, 1.35716e-02f, -4.49929e-02f, 1.36968e-01f, 9.55956e-01f, -7.43154e-02f, 1.80759e-02f, -2.94361e-03f}, // 16/128
+ {-2.57640e-03f, 1.43934e-02f, -4.77900e-02f, 1.46192e-01f, 9.51795e-01f, -7.80792e-02f, 1.90545e-02f, -3.10689e-03f}, // 17/128
+ {-2.72032e-03f, 1.52095e-02f, -5.05770e-02f, 1.55480e-01f, 9.47477e-01f, -8.17385e-02f, 2.00132e-02f, -3.26730e-03f}, // 18/128
+ {-2.86289e-03f, 1.60193e-02f, -5.33522e-02f, 1.64831e-01f, 9.43001e-01f, -8.52933e-02f, 2.09516e-02f, -3.42477e-03f}, // 19/128
+ {-3.00403e-03f, 1.68225e-02f, -5.61142e-02f, 1.74242e-01f, 9.38371e-01f, -8.87435e-02f, 2.18695e-02f, -3.57923e-03f}, // 20/128
+ {-3.14367e-03f, 1.76185e-02f, -5.88617e-02f, 1.83711e-01f, 9.33586e-01f, -9.20893e-02f, 2.27664e-02f, -3.73062e-03f}, // 21/128
+ {-3.28174e-03f, 1.84071e-02f, -6.15931e-02f, 1.93236e-01f, 9.28650e-01f, -9.53307e-02f, 2.36423e-02f, -3.87888e-03f}, // 22/128
+ {-3.41815e-03f, 1.91877e-02f, -6.43069e-02f, 2.02814e-01f, 9.23564e-01f, -9.84679e-02f, 2.44967e-02f, -4.02397e-03f}, // 23/128
+ {-3.55283e-03f, 1.99599e-02f, -6.70018e-02f, 2.12443e-01f, 9.18329e-01f, -1.01501e-01f, 2.53295e-02f, -4.16581e-03f}, // 24/128
+ {-3.68570e-03f, 2.07233e-02f, -6.96762e-02f, 2.22120e-01f, 9.12947e-01f, -1.04430e-01f, 2.61404e-02f, -4.30435e-03f}, // 25/128
+ {-3.81671e-03f, 2.14774e-02f, -7.23286e-02f, 2.31843e-01f, 9.07420e-01f, -1.07256e-01f, 2.69293e-02f, -4.43955e-03f}, // 26/128
+ {-3.94576e-03f, 2.22218e-02f, -7.49577e-02f, 2.41609e-01f, 9.01749e-01f, -1.09978e-01f, 2.76957e-02f, -4.57135e-03f}, // 27/128
+ {-4.07279e-03f, 2.29562e-02f, -7.75620e-02f, 2.51417e-01f, 8.95936e-01f, -1.12597e-01f, 2.84397e-02f, -4.69970e-03f}, // 28/128
+ {-4.19774e-03f, 2.36801e-02f, -8.01399e-02f, 2.61263e-01f, 8.89984e-01f, -1.15113e-01f, 2.91609e-02f, -4.82456e-03f}, // 29/128
+ {-4.32052e-03f, 2.43930e-02f, -8.26900e-02f, 2.71144e-01f, 8.83893e-01f, -1.17526e-01f, 2.98593e-02f, -4.94589e-03f}, // 30/128
+ {-4.44107e-03f, 2.50946e-02f, -8.52109e-02f, 2.81060e-01f, 8.77666e-01f, -1.19837e-01f, 3.05345e-02f, -5.06363e-03f}, // 31/128
+ {-4.55932e-03f, 2.57844e-02f, -8.77011e-02f, 2.91006e-01f, 8.71305e-01f, -1.22047e-01f, 3.11866e-02f, -5.17776e-03f}, // 32/128
+ {-4.67520e-03f, 2.64621e-02f, -9.01591e-02f, 3.00980e-01f, 8.64812e-01f, -1.24154e-01f, 3.18153e-02f, -5.28823e-03f}, // 33/128
+ {-4.78866e-03f, 2.71272e-02f, -9.25834e-02f, 3.10980e-01f, 8.58189e-01f, -1.26161e-01f, 3.24205e-02f, -5.39500e-03f}, // 34/128
+ {-4.89961e-03f, 2.77794e-02f, -9.49727e-02f, 3.21004e-01f, 8.51437e-01f, -1.28068e-01f, 3.30021e-02f, -5.49804e-03f}, // 35/128
+ {-5.00800e-03f, 2.84182e-02f, -9.73254e-02f, 3.31048e-01f, 8.44559e-01f, -1.29874e-01f, 3.35600e-02f, -5.59731e-03f}, // 36/128
+ {-5.11376e-03f, 2.90433e-02f, -9.96402e-02f, 3.41109e-01f, 8.37557e-01f, -1.31581e-01f, 3.40940e-02f, -5.69280e-03f}, // 37/128
+ {-5.21683e-03f, 2.96543e-02f, -1.01915e-01f, 3.51186e-01f, 8.30432e-01f, -1.33189e-01f, 3.46042e-02f, -5.78446e-03f}, // 38/128
+ {-5.31716e-03f, 3.02507e-02f, -1.04150e-01f, 3.61276e-01f, 8.23188e-01f, -1.34699e-01f, 3.50903e-02f, -5.87227e-03f}, // 39/128
+ {-5.41467e-03f, 3.08323e-02f, -1.06342e-01f, 3.71376e-01f, 8.15826e-01f, -1.36111e-01f, 3.55525e-02f, -5.95620e-03f}, // 40/128
+ {-5.50931e-03f, 3.13987e-02f, -1.08490e-01f, 3.81484e-01f, 8.08348e-01f, -1.37426e-01f, 3.59905e-02f, -6.03624e-03f}, // 41/128
+ {-5.60103e-03f, 3.19495e-02f, -1.10593e-01f, 3.91596e-01f, 8.00757e-01f, -1.38644e-01f, 3.64044e-02f, -6.11236e-03f}, // 42/128
+ {-5.68976e-03f, 3.24843e-02f, -1.12650e-01f, 4.01710e-01f, 7.93055e-01f, -1.39767e-01f, 3.67941e-02f, -6.18454e-03f}, // 43/128
+ {-5.77544e-03f, 3.30027e-02f, -1.14659e-01f, 4.11823e-01f, 7.85244e-01f, -1.40794e-01f, 3.71596e-02f, -6.25277e-03f}, // 44/128
+ {-5.85804e-03f, 3.35046e-02f, -1.16618e-01f, 4.21934e-01f, 7.77327e-01f, -1.41727e-01f, 3.75010e-02f, -6.31703e-03f}, // 45/128
+ {-5.93749e-03f, 3.39894e-02f, -1.18526e-01f, 4.32038e-01f, 7.69305e-01f, -1.42566e-01f, 3.78182e-02f, -6.37730e-03f}, // 46/128
+ {-6.01374e-03f, 3.44568e-02f, -1.20382e-01f, 4.42134e-01f, 7.61181e-01f, -1.43313e-01f, 3.81111e-02f, -6.43358e-03f}, // 47/128
+ {-6.08674e-03f, 3.49066e-02f, -1.22185e-01f, 4.52218e-01f, 7.52958e-01f, -1.43968e-01f, 3.83800e-02f, -6.48585e-03f}, // 48/128
+ {-6.15644e-03f, 3.53384e-02f, -1.23933e-01f, 4.62289e-01f, 7.44637e-01f, -1.44531e-01f, 3.86247e-02f, -6.53412e-03f}, // 49/128
+ {-6.22280e-03f, 3.57519e-02f, -1.25624e-01f, 4.72342e-01f, 7.36222e-01f, -1.45004e-01f, 3.88454e-02f, -6.57836e-03f}, // 50/128
+ {-6.28577e-03f, 3.61468e-02f, -1.27258e-01f, 4.82377e-01f, 7.27714e-01f, -1.45387e-01f, 3.90420e-02f, -6.61859e-03f}, // 51/128
+ {-6.34530e-03f, 3.65227e-02f, -1.28832e-01f, 4.92389e-01f, 7.19116e-01f, -1.45682e-01f, 3.92147e-02f, -6.65479e-03f}, // 52/128
+ {-6.40135e-03f, 3.68795e-02f, -1.30347e-01f, 5.02377e-01f, 7.10431e-01f, -1.45889e-01f, 3.93636e-02f, -6.68698e-03f}, // 53/128
+ {-6.45388e-03f, 3.72167e-02f, -1.31800e-01f, 5.12337e-01f, 7.01661e-01f, -1.46009e-01f, 3.94886e-02f, -6.71514e-03f}, // 54/128
+ {-6.50285e-03f, 3.75341e-02f, -1.33190e-01f, 5.22267e-01f, 6.92808e-01f, -1.46043e-01f, 3.95900e-02f, -6.73929e-03f}, // 55/128
+ {-6.54823e-03f, 3.78315e-02f, -1.34515e-01f, 5.32164e-01f, 6.83875e-01f, -1.45993e-01f, 3.96678e-02f, -6.75943e-03f}, // 56/128
+ {-6.58996e-03f, 3.81085e-02f, -1.35775e-01f, 5.42025e-01f, 6.74865e-01f, -1.45859e-01f, 3.97222e-02f, -6.77557e-03f}, // 57/128
+ {-6.62802e-03f, 3.83650e-02f, -1.36969e-01f, 5.51849e-01f, 6.65779e-01f, -1.45641e-01f, 3.97532e-02f, -6.78771e-03f}, // 58/128
+ {-6.66238e-03f, 3.86006e-02f, -1.38094e-01f, 5.61631e-01f, 6.56621e-01f, -1.45343e-01f, 3.97610e-02f, -6.79588e-03f}, // 59/128
+ {-6.69300e-03f, 3.88151e-02f, -1.39150e-01f, 5.71370e-01f, 6.47394e-01f, -1.44963e-01f, 3.97458e-02f, -6.80007e-03f}, // 60/128
+ {-6.71985e-03f, 3.90083e-02f, -1.40136e-01f, 5.81063e-01f, 6.38099e-01f, -1.44503e-01f, 3.97077e-02f, -6.80032e-03f}, // 61/128
+ {-6.74291e-03f, 3.91800e-02f, -1.41050e-01f, 5.90706e-01f, 6.28739e-01f, -1.43965e-01f, 3.96469e-02f, -6.79662e-03f}, // 62/128
+ {-6.76214e-03f, 3.93299e-02f, -1.41891e-01f, 6.00298e-01f, 6.19318e-01f, -1.43350e-01f, 3.95635e-02f, -6.78902e-03f}, // 63/128
+ {-6.77751e-03f, 3.94578e-02f, -1.42658e-01f, 6.09836e-01f, 6.09836e-01f, -1.42658e-01f, 3.94578e-02f, -6.77751e-03f}, // 64/128
+ {-6.78902e-03f, 3.95635e-02f, -1.43350e-01f, 6.19318e-01f, 6.00298e-01f, -1.41891e-01f, 3.93299e-02f, -6.76214e-03f}, // 65/128
+ {-6.79662e-03f, 3.96469e-02f, -1.43965e-01f, 6.28739e-01f, 5.90706e-01f, -1.41050e-01f, 3.91800e-02f, -6.74291e-03f}, // 66/128
+ {-6.80032e-03f, 3.97077e-02f, -1.44503e-01f, 6.38099e-01f, 5.81063e-01f, -1.40136e-01f, 3.90083e-02f, -6.71985e-03f}, // 67/128
+ {-6.80007e-03f, 3.97458e-02f, -1.44963e-01f, 6.47394e-01f, 5.71370e-01f, -1.39150e-01f, 3.88151e-02f, -6.69300e-03f}, // 68/128
+ {-6.79588e-03f, 3.97610e-02f, -1.45343e-01f, 6.56621e-01f, 5.61631e-01f, -1.38094e-01f, 3.86006e-02f, -6.66238e-03f}, // 69/128
+ {-6.78771e-03f, 3.97532e-02f, -1.45641e-01f, 6.65779e-01f, 5.51849e-01f, -1.36969e-01f, 3.83650e-02f, -6.62802e-03f}, // 70/128
+ {-6.77557e-03f, 3.97222e-02f, -1.45859e-01f, 6.74865e-01f, 5.42025e-01f, -1.35775e-01f, 3.81085e-02f, -6.58996e-03f}, // 71/128
+ {-6.75943e-03f, 3.96678e-02f, -1.45993e-01f, 6.83875e-01f, 5.32164e-01f, -1.34515e-01f, 3.78315e-02f, -6.54823e-03f}, // 72/128
+ {-6.73929e-03f, 3.95900e-02f, -1.46043e-01f, 6.92808e-01f, 5.22267e-01f, -1.33190e-01f, 3.75341e-02f, -6.50285e-03f}, // 73/128
+ {-6.71514e-03f, 3.94886e-02f, -1.46009e-01f, 7.01661e-01f, 5.12337e-01f, -1.31800e-01f, 3.72167e-02f, -6.45388e-03f}, // 74/128
+ {-6.68698e-03f, 3.93636e-02f, -1.45889e-01f, 7.10431e-01f, 5.02377e-01f, -1.30347e-01f, 3.68795e-02f, -6.40135e-03f}, // 75/128
+ {-6.65479e-03f, 3.92147e-02f, -1.45682e-01f, 7.19116e-01f, 4.92389e-01f, -1.28832e-01f, 3.65227e-02f, -6.34530e-03f}, // 76/128
+ {-6.61859e-03f, 3.90420e-02f, -1.45387e-01f, 7.27714e-01f, 4.82377e-01f, -1.27258e-01f, 3.61468e-02f, -6.28577e-03f}, // 77/128
+ {-6.57836e-03f, 3.88454e-02f, -1.45004e-01f, 7.36222e-01f, 4.72342e-01f, -1.25624e-01f, 3.57519e-02f, -6.22280e-03f}, // 78/128
+ {-6.53412e-03f, 3.86247e-02f, -1.44531e-01f, 7.44637e-01f, 4.62289e-01f, -1.23933e-01f, 3.53384e-02f, -6.15644e-03f}, // 79/128
+ {-6.48585e-03f, 3.83800e-02f, -1.43968e-01f, 7.52958e-01f, 4.52218e-01f, -1.22185e-01f, 3.49066e-02f, -6.08674e-03f}, // 80/128
+ {-6.43358e-03f, 3.81111e-02f, -1.43313e-01f, 7.61181e-01f, 4.42134e-01f, -1.20382e-01f, 3.44568e-02f, -6.01374e-03f}, // 81/128
+ {-6.37730e-03f, 3.78182e-02f, -1.42566e-01f, 7.69305e-01f, 4.32038e-01f, -1.18526e-01f, 3.39894e-02f, -5.93749e-03f}, // 82/128
+ {-6.31703e-03f, 3.75010e-02f, -1.41727e-01f, 7.77327e-01f, 4.21934e-01f, -1.16618e-01f, 3.35046e-02f, -5.85804e-03f}, // 83/128
+ {-6.25277e-03f, 3.71596e-02f, -1.40794e-01f, 7.85244e-01f, 4.11823e-01f, -1.14659e-01f, 3.30027e-02f, -5.77544e-03f}, // 84/128
+ {-6.18454e-03f, 3.67941e-02f, -1.39767e-01f, 7.93055e-01f, 4.01710e-01f, -1.12650e-01f, 3.24843e-02f, -5.68976e-03f}, // 85/128
+ {-6.11236e-03f, 3.64044e-02f, -1.38644e-01f, 8.00757e-01f, 3.91596e-01f, -1.10593e-01f, 3.19495e-02f, -5.60103e-03f}, // 86/128
+ {-6.03624e-03f, 3.59905e-02f, -1.37426e-01f, 8.08348e-01f, 3.81484e-01f, -1.08490e-01f, 3.13987e-02f, -5.50931e-03f}, // 87/128
+ {-5.95620e-03f, 3.55525e-02f, -1.36111e-01f, 8.15826e-01f, 3.71376e-01f, -1.06342e-01f, 3.08323e-02f, -5.41467e-03f}, // 88/128
+ {-5.87227e-03f, 3.50903e-02f, -1.34699e-01f, 8.23188e-01f, 3.61276e-01f, -1.04150e-01f, 3.02507e-02f, -5.31716e-03f}, // 89/128
+ {-5.78446e-03f, 3.46042e-02f, -1.33189e-01f, 8.30432e-01f, 3.51186e-01f, -1.01915e-01f, 2.96543e-02f, -5.21683e-03f}, // 90/128
+ {-5.69280e-03f, 3.40940e-02f, -1.31581e-01f, 8.37557e-01f, 3.41109e-01f, -9.96402e-02f, 2.90433e-02f, -5.11376e-03f}, // 91/128
+ {-5.59731e-03f, 3.35600e-02f, -1.29874e-01f, 8.44559e-01f, 3.31048e-01f, -9.73254e-02f, 2.84182e-02f, -5.00800e-03f}, // 92/128
+ {-5.49804e-03f, 3.30021e-02f, -1.28068e-01f, 8.51437e-01f, 3.21004e-01f, -9.49727e-02f, 2.77794e-02f, -4.89961e-03f}, // 93/128
+ {-5.39500e-03f, 3.24205e-02f, -1.26161e-01f, 8.58189e-01f, 3.10980e-01f, -9.25834e-02f, 2.71272e-02f, -4.78866e-03f}, // 94/128
+ {-5.28823e-03f, 3.18153e-02f, -1.24154e-01f, 8.64812e-01f, 3.00980e-01f, -9.01591e-02f, 2.64621e-02f, -4.67520e-03f}, // 95/128
+ {-5.17776e-03f, 3.11866e-02f, -1.22047e-01f, 8.71305e-01f, 2.91006e-01f, -8.77011e-02f, 2.57844e-02f, -4.55932e-03f}, // 96/128
+ {-5.06363e-03f, 3.05345e-02f, -1.19837e-01f, 8.77666e-01f, 2.81060e-01f, -8.52109e-02f, 2.50946e-02f, -4.44107e-03f}, // 97/128
+ {-4.94589e-03f, 2.98593e-02f, -1.17526e-01f, 8.83893e-01f, 2.71144e-01f, -8.26900e-02f, 2.43930e-02f, -4.32052e-03f}, // 98/128
+ {-4.82456e-03f, 2.91609e-02f, -1.15113e-01f, 8.89984e-01f, 2.61263e-01f, -8.01399e-02f, 2.36801e-02f, -4.19774e-03f}, // 99/128
+ {-4.69970e-03f, 2.84397e-02f, -1.12597e-01f, 8.95936e-01f, 2.51417e-01f, -7.75620e-02f, 2.29562e-02f, -4.07279e-03f}, // 100/128
+ {-4.57135e-03f, 2.76957e-02f, -1.09978e-01f, 9.01749e-01f, 2.41609e-01f, -7.49577e-02f, 2.22218e-02f, -3.94576e-03f}, // 101/128
+ {-4.43955e-03f, 2.69293e-02f, -1.07256e-01f, 9.07420e-01f, 2.31843e-01f, -7.23286e-02f, 2.14774e-02f, -3.81671e-03f}, // 102/128
+ {-4.30435e-03f, 2.61404e-02f, -1.04430e-01f, 9.12947e-01f, 2.22120e-01f, -6.96762e-02f, 2.07233e-02f, -3.68570e-03f}, // 103/128
+ {-4.16581e-03f, 2.53295e-02f, -1.01501e-01f, 9.18329e-01f, 2.12443e-01f, -6.70018e-02f, 1.99599e-02f, -3.55283e-03f}, // 104/128
+ {-4.02397e-03f, 2.44967e-02f, -9.84679e-02f, 9.23564e-01f, 2.02814e-01f, -6.43069e-02f, 1.91877e-02f, -3.41815e-03f}, // 105/128
+ {-3.87888e-03f, 2.36423e-02f, -9.53307e-02f, 9.28650e-01f, 1.93236e-01f, -6.15931e-02f, 1.84071e-02f, -3.28174e-03f}, // 106/128
+ {-3.73062e-03f, 2.27664e-02f, -9.20893e-02f, 9.33586e-01f, 1.83711e-01f, -5.88617e-02f, 1.76185e-02f, -3.14367e-03f}, // 107/128
+ {-3.57923e-03f, 2.18695e-02f, -8.87435e-02f, 9.38371e-01f, 1.74242e-01f, -5.61142e-02f, 1.68225e-02f, -3.00403e-03f}, // 108/128
+ {-3.42477e-03f, 2.09516e-02f, -8.52933e-02f, 9.43001e-01f, 1.64831e-01f, -5.33522e-02f, 1.60193e-02f, -2.86289e-03f}, // 109/128
+ {-3.26730e-03f, 2.00132e-02f, -8.17385e-02f, 9.47477e-01f, 1.55480e-01f, -5.05770e-02f, 1.52095e-02f, -2.72032e-03f}, // 110/128
+ {-3.10689e-03f, 1.90545e-02f, -7.80792e-02f, 9.51795e-01f, 1.46192e-01f, -4.77900e-02f, 1.43934e-02f, -2.57640e-03f}, // 111/128
+ {-2.94361e-03f, 1.80759e-02f, -7.43154e-02f, 9.55956e-01f, 1.36968e-01f, -4.49929e-02f, 1.35716e-02f, -2.43121e-03f}, // 112/128
+ {-2.77751e-03f, 1.70776e-02f, -7.04471e-02f, 9.59958e-01f, 1.27812e-01f, -4.21869e-02f, 1.27445e-02f, -2.28483e-03f}, // 113/128
+ {-2.60868e-03f, 1.60599e-02f, -6.64743e-02f, 9.63798e-01f, 1.18725e-01f, -3.93735e-02f, 1.19125e-02f, -2.13733e-03f}, // 114/128
+ {-2.43718e-03f, 1.50233e-02f, -6.23972e-02f, 9.67477e-01f, 1.09710e-01f, -3.65541e-02f, 1.10760e-02f, -1.98880e-03f}, // 115/128
+ {-2.26307e-03f, 1.39681e-02f, -5.82159e-02f, 9.70992e-01f, 1.00769e-01f, -3.37303e-02f, 1.02356e-02f, -1.83931e-03f}, // 116/128
+ {-2.08645e-03f, 1.28947e-02f, -5.39305e-02f, 9.74342e-01f, 9.19033e-02f, -3.09033e-02f, 9.39154e-03f, -1.68894e-03f}, // 117/128
+ {-1.90738e-03f, 1.18034e-02f, -4.95412e-02f, 9.77526e-01f, 8.31162e-02f, -2.80746e-02f, 8.54441e-03f, -1.53777e-03f}, // 118/128
+ {-1.72594e-03f, 1.06946e-02f, -4.50483e-02f, 9.80543e-01f, 7.44095e-02f, -2.52457e-02f, 7.69462e-03f, -1.38589e-03f}, // 119/128
+ {-1.54221e-03f, 9.56876e-03f, -4.04519e-02f, 9.83392e-01f, 6.57852e-02f, -2.24178e-02f, 6.84261e-03f, -1.23337e-03f}, // 120/128
+ {-1.35627e-03f, 8.42626e-03f, -3.57525e-02f, 9.86071e-01f, 5.72454e-02f, -1.95925e-02f, 5.98883e-03f, -1.08030e-03f}, // 121/128
+ {-1.16820e-03f, 7.26755e-03f, -3.09503e-02f, 9.88580e-01f, 4.87921e-02f, -1.67710e-02f, 5.13372e-03f, -9.26747e-04f}, // 122/128
+ {-9.78093e-04f, 6.09305e-03f, -2.60456e-02f, 9.90917e-01f, 4.04274e-02f, -1.39548e-02f, 4.27773e-03f, -7.72802e-04f}, // 123/128
+ {-7.86031e-04f, 4.90322e-03f, -2.10389e-02f, 9.93082e-01f, 3.21531e-02f, -1.11453e-02f, 3.42130e-03f, -6.18544e-04f}, // 124/128
+ {-5.92100e-04f, 3.69852e-03f, -1.59305e-02f, 9.95074e-01f, 2.39714e-02f, -8.34364e-03f, 2.56486e-03f, -4.64053e-04f}, // 125/128
+ {-3.96391e-04f, 2.47942e-03f, -1.07209e-02f, 9.96891e-01f, 1.58840e-02f, -5.55134e-03f, 1.70888e-03f, -3.09412e-04f}, // 126/128
+ {-1.98993e-04f, 1.24642e-03f, -5.41054e-03f, 9.98534e-01f, 7.89295e-03f, -2.76968e-03f, 8.53777e-04f, -1.54700e-04f}, // 127/128
+ {0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f}, // 128/128
+ };
+
/* Instrumentation Taps */
- private static final String INSTRUMENT_SYMBOL_SPREAD = "Tap Point: Symbol Spread (Goal=2.0)";
- private FloatTap mSymbolSpreadTap;
+ private static final String INSTRUMENT_SYMBOL_SPREAD = "Tap Point: Symbol Spread (Goal=2.0)";
+ private FloatTap mSymbolSpreadTap;
private List mAvailableTaps;
-
- private static final int NUMBER_FILTER_TAPS = 8;
- private static final int NUMBER_FILTER_STEPS = 128;
-
- private static final int SAMPLE_RATE = 48000;
- private static final int SYMBOL_RATE = 4800;
-
- /* Tracking loop gain constant */
- private static final double K_SYMBOL_SPREAD = 0.0100;
- /* Constraints on symbol spreading */
- private static final float SYMBOL_SPREAD_MAX = 2.4f; // upper range limit: +20%
- private static final float SYMBOL_SPREAD_MIN = 1.6f; // lower range limit: -20%
+ private static final int NUMBER_FILTER_TAPS = 8;
+ private static final int NUMBER_FILTER_STEPS = 128;
- /* Symbol clock tracking loop gain */
- private static final double K_SYMBOL_TIMING = 0.025;
-
- /* Coarse and fine frequency tracking constants */
- private static final double K_COARSE_FREQUENCY = 0.00125;
- private static final double K_FINE_FREQUENCY = 0.125;
-
- /* Frequency correction broadcast threshold */
+ private static final int SAMPLE_RATE = 48000;
+ private static final int SYMBOL_RATE = 4800;
+
+ /* Tracking loop gain constant */
+ private static final double K_SYMBOL_SPREAD = 0.0100;
+
+ /* Constraints on symbol spreading */
+ private static final float SYMBOL_SPREAD_MAX = 2.4f; // upper range limit: +20%
+ private static final float SYMBOL_SPREAD_MIN = 1.6f; // lower range limit: -20%
+
+ /* Symbol clock tracking loop gain */
+ private static final double K_SYMBOL_TIMING = 0.025;
+
+ /* Coarse and fine frequency tracking constants */
+ private static final double K_COARSE_FREQUENCY = 0.00125;
+ private static final double K_FINE_FREQUENCY = 0.125;
+
+ /* Frequency correction broadcast threshold */
// private static final double COARSE_FREQUENCY_DEADBAND = 1.66;
- private static final double COARSE_FREQUENCY_THRESHOLD = 1.20;
-
- /* 2.0 symbol spread gives -3, -1, 1, 3 */
- private float mSymbolSpread = 2.0f;
- private float mSymbolClock = 0.0f;
- private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
-
- private float mFineFrequencyCorrection = 0.0f;
- private float mCoarseFrequencyCorrection = 0.0f;
+ private static final double COARSE_FREQUENCY_THRESHOLD = 1.20;
- private float mHistory[] = new float[ NUMBER_FILTER_TAPS ];
- private int mHistoryLast = 0;
-
- private RealSampleListener mListener;
-
- private DirectGainControl mGainController = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f );
+ /* 2.0 symbol spread gives -3, -1, 1, 3 */
+ private float mSymbolSpread = 2.0f;
+ private float mSymbolClock = 0.0f;
+ private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
- private int mFrequencyAdjustmentRequested = 0;
- private int mFrequencyCorrection = 0;
- private int mFrequencyCorrectionMaximum = 3000;
- private boolean mResetFrequencyTracker = false;
- private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor;
- private Listener mFrequencyChangeListener;
-
- /**
- * C4FM Symbol Filter
- *
- * Sample Gain values - the gain of incoming sample values will critically
- * impact the performance of this filter. Gain should be adjusted to
- * optimize the value of mSymbolSpread toward a value of 2.0. If the value
- * is less than 2.0, then increase gain. If the value is over 2.0, then
- * decrease gain.
- *
- * When preceded by the RealAutomaticGainFilter class, an optimal gain
- * setting for this filter is 15.4 and that yields a symbol spread that
- * is centered on 2.0, ranging 1.92 to 2.08.
- */
- public C4FMSymbolFilter( int frequencyCorrectionMaximum )
- {
- mFrequencyCorrectionMaximum = frequencyCorrectionMaximum;
- }
-
- public void dispose()
- {
- mGainController = null;
+ private float mFineFrequencyCorrection = 0.0f;
+ private float mCoarseFrequencyCorrection = 0.0f;
+
+ private float mHistory[] = new float[NUMBER_FILTER_TAPS];
+ private int mHistoryLast = 0;
+
+ private RealSampleListener mListener;
+
+ private DirectGainControl mGainController = new DirectGainControl(15.0f, 0.1f, 35.0f, 0.3f);
+
+ private int mFrequencyAdjustmentRequested = 0;
+ private int mFrequencyCorrection = 0;
+ private int mFrequencyCorrectionMaximum = 3000;
+ private boolean mResetFrequencyTracker = false;
+ private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor;
+ private Listener mFrequencyChangeListener;
+
+ /**
+ * C4FM Symbol Filter
+ *
+ * Sample Gain values - the gain of incoming sample values will critically
+ * impact the performance of this filter. Gain should be adjusted to
+ * optimize the value of mSymbolSpread toward a value of 2.0. If the value
+ * is less than 2.0, then increase gain. If the value is over 2.0, then
+ * decrease gain.
+ *
+ * When preceded by the RealAutomaticGainFilter class, an optimal gain
+ * setting for this filter is 15.4 and that yields a symbol spread that
+ * is centered on 2.0, ranging 1.92 to 2.08.
+ */
+ public C4FMSymbolFilter(int frequencyCorrectionMaximum)
+ {
+ mFrequencyCorrectionMaximum = frequencyCorrectionMaximum;
+ }
+
+ public void dispose()
+ {
+ mGainController = null;
// mFrequencyCorrectionControl = null;
- mListener = null;
- }
-
- @Override
- public void receive( RealBuffer buffer )
- {
- for( float sample: buffer.getSamples() )
- {
- receive( sample );
- }
+ mListener = null;
+ }
+
+ @Override
+ public void receive(RealBuffer buffer)
+ {
+ for(float sample : buffer.getSamples())
+ {
+ receive(sample);
+ }
/* If a frequency correction was requested during the processing of this
- * buffer, we'll apply the change and it will be reflected in the next
+ * buffer, we'll apply the change and it will be reflected in the next
* arriving buffer. Reset the lock on frequency correction and reset
* the internal frequency correction tracker */
- if( mFrequencyAdjustmentRequested != 0 )
- {
- int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested;
-
- if( correction > mFrequencyCorrectionMaximum )
- {
- correction = mFrequencyCorrectionMaximum;
- }
- else if( correction < -mFrequencyCorrectionMaximum )
- {
- correction = -mFrequencyCorrectionMaximum;
- }
+ if(mFrequencyAdjustmentRequested != 0)
+ {
+ int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested;
- broadcast( new FrequencyChangeEvent(Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) );
+ if(correction > mFrequencyCorrectionMaximum)
+ {
+ correction = mFrequencyCorrectionMaximum;
+ }
+ else if(correction < -mFrequencyCorrectionMaximum)
+ {
+ correction = -mFrequencyCorrectionMaximum;
+ }
- mFrequencyAdjustmentRequested = 0;
- }
- }
+ broadcast(SourceEvent.channelFrequencyCorrectionRequest(correction));
- public void receive( float sample )
+ mFrequencyAdjustmentRequested = 0;
+ }
+ }
+
+ public void receive(float sample)
{
- sample = mGainController.correct( sample );
-
- if( mResetFrequencyTracker )
- {
- mCoarseFrequencyCorrection = 0.0f;
- mFineFrequencyCorrection = 0.0f;
-
- broadcast( new FrequencyChangeEvent(
- Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, 0 ));
-
- mResetFrequencyTracker = false;
- }
-
- mSymbolClock += mSymbolTime;
-
- mHistory[ mHistoryLast++ ] = sample;
-
- mHistoryLast %= NUMBER_FILTER_TAPS;
-
- if( mSymbolClock > 1.0f )
- {
- mSymbolClock -= 1.0f;
-
- int imu = (int)Math.floor( 0.5 +
- ( (float)NUMBER_FILTER_STEPS * ( mSymbolClock / mSymbolTime ) ) );
-
- if( imu >= NUMBER_FILTER_STEPS )
- {
- imu = NUMBER_FILTER_STEPS - 1;
- }
+ sample = mGainController.correct(sample);
- int imu_p1 = imu + 1;
+ if(mResetFrequencyTracker)
+ {
+ mCoarseFrequencyCorrection = 0.0f;
+ mFineFrequencyCorrection = 0.0f;
- int j = mHistoryLast;
-
- double interp = 0.0;
- double interp_p1 = 0.0;
-
- for( int i = 0; i < NUMBER_FILTER_TAPS; i++ )
- {
- interp += TAPS[ imu][ i ] * mHistory[ j ];
- interp_p1 += TAPS[ imu_p1 ][ i ] * mHistory[ j ];
-
- j = ( j + 1 ) % NUMBER_FILTER_TAPS;
- }
+ broadcast(SourceEvent.channelFrequencyCorrectionRequest(0l));
+
+ mResetFrequencyTracker = false;
+ }
+
+ mSymbolClock += mSymbolTime;
+
+ mHistory[mHistoryLast++] = sample;
+
+ mHistoryLast %= NUMBER_FILTER_TAPS;
+
+ if(mSymbolClock > 1.0f)
+ {
+ mSymbolClock -= 1.0f;
+
+ int imu = (int)Math.floor(0.5 +
+ ((float)NUMBER_FILTER_STEPS * (mSymbolClock / mSymbolTime)));
+
+ if(imu >= NUMBER_FILTER_STEPS)
+ {
+ imu = NUMBER_FILTER_STEPS - 1;
+ }
+
+ int imu_p1 = imu + 1;
+
+ int j = mHistoryLast;
+
+ double interp = 0.0;
+ double interp_p1 = 0.0;
+
+ for(int i = 0; i < NUMBER_FILTER_TAPS; i++)
+ {
+ interp += TAPS[imu][i] * mHistory[j];
+ interp_p1 += TAPS[imu_p1][i] * mHistory[j];
+
+ j = (j + 1) % NUMBER_FILTER_TAPS;
+ }
/* Output symbol will be interpolated value corrected for symbol
* spread and frequency offset */
- interp -= mFineFrequencyCorrection;
- interp_p1 -= mFineFrequencyCorrection;
+ interp -= mFineFrequencyCorrection;
+ interp_p1 -= mFineFrequencyCorrection;
/* Correct output for symbol deviation (spread) */
- float output = (float)( 2.0 * interp / mSymbolSpread );
+ float output = (float)(2.0 * interp / mSymbolSpread);
/* Detect received symbol error: basically use a hard decision and
* subtract off expected position nominal symbol level which will be
* +/- 0.5 * symbol spread and +/- 1.5 symbol spread. Remember that
* nominal symbol spread will be 2.0 */
- double symbolError;
-
- if( interp < -mSymbolSpread )
- {
+ double symbolError;
+
+ if(interp < -mSymbolSpread)
+ {
/* symbol is -3: Expected at -1.5 * symbol spread */
- symbolError = interp + ( 1.5 * mSymbolSpread );
- mSymbolSpread -= ( symbolError * 0.5 * K_SYMBOL_SPREAD );
- }
- else if( interp < 0.0 )
- {
+ symbolError = interp + (1.5 * mSymbolSpread);
+ mSymbolSpread -= (symbolError * 0.5 * K_SYMBOL_SPREAD);
+ }
+ else if(interp < 0.0)
+ {
/* symbol is -1: Expected at -0.5 * symbol_spread */
- symbolError = interp + (0.5 * mSymbolSpread );
- mSymbolSpread -= ( symbolError * K_SYMBOL_SPREAD );
- }
- else if( interp < mSymbolSpread )
- {
+ symbolError = interp + (0.5 * mSymbolSpread);
+ mSymbolSpread -= (symbolError * K_SYMBOL_SPREAD);
+ }
+ else if(interp < mSymbolSpread)
+ {
/* symbol is +1: Expected at +0.5 * symbol_spread */
- symbolError = interp - ( 0.5 * mSymbolSpread );
- mSymbolSpread += ( symbolError * K_SYMBOL_SPREAD );
- }
- else
- {
+ symbolError = interp - (0.5 * mSymbolSpread);
+ mSymbolSpread += (symbolError * K_SYMBOL_SPREAD);
+ }
+ else
+ {
/* symbol is +3: Expected at +1.5 * symbol_spread */
- symbolError = interp - ( 1.5 * mSymbolSpread );
- mSymbolSpread += ( symbolError * 0.5 * K_SYMBOL_SPREAD );
- }
+ symbolError = interp - (1.5 * mSymbolSpread);
+ mSymbolSpread += (symbolError * 0.5 * K_SYMBOL_SPREAD);
+ }
/* Symbol clock tracking loop adjustment */
- if( interp_p1 < interp )
- {
- mSymbolClock += symbolError * K_SYMBOL_TIMING;
- }
- else
- {
- mSymbolClock -= symbolError * K_SYMBOL_TIMING;
- }
-
- if( mSymbolSpread < SYMBOL_SPREAD_MIN )
- {
- mGainController.increase();
-
- mSymbolSpread = SYMBOL_SPREAD_MIN;
- }
- else if( mSymbolSpread > SYMBOL_SPREAD_MAX )
- {
- mGainController.decrease();
+ if(interp_p1 < interp)
+ {
+ mSymbolClock += symbolError * K_SYMBOL_TIMING;
+ }
+ else
+ {
+ mSymbolClock -= symbolError * K_SYMBOL_TIMING;
+ }
- mSymbolSpread = SYMBOL_SPREAD_MAX;
- }
+ if(mSymbolSpread < SYMBOL_SPREAD_MIN)
+ {
+ mGainController.increase();
- mCoarseFrequencyCorrection += ( ( mFineFrequencyCorrection -
- mCoarseFrequencyCorrection ) * K_COARSE_FREQUENCY );
-
- mFineFrequencyCorrection += ( symbolError * K_FINE_FREQUENCY );
+ mSymbolSpread = SYMBOL_SPREAD_MIN;
+ }
+ else if(mSymbolSpread > SYMBOL_SPREAD_MAX)
+ {
+ mGainController.decrease();
+
+ mSymbolSpread = SYMBOL_SPREAD_MAX;
+ }
+
+ mCoarseFrequencyCorrection += ((mFineFrequencyCorrection -
+ mCoarseFrequencyCorrection) * K_COARSE_FREQUENCY);
+
+ mFineFrequencyCorrection += (symbolError * K_FINE_FREQUENCY);
/* Queue a frequency adjustment (once per buffer) as needed */
- if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_THRESHOLD )
- {
- mFrequencyAdjustmentRequested =
- 500 * ( mCoarseFrequencyCorrection > 0 ? 1 : -1 );
- }
-
- if( mSymbolSpreadTap != null )
- {
- mSymbolSpreadTap.receive( mSymbolSpread );
- }
+ if(Math.abs(mCoarseFrequencyCorrection) > COARSE_FREQUENCY_THRESHOLD)
+ {
+ mFrequencyAdjustmentRequested =
+ 500 * (mCoarseFrequencyCorrection > 0 ? 1 : -1);
+ }
+
+ if(mSymbolSpreadTap != null)
+ {
+ mSymbolSpreadTap.receive(mSymbolSpread);
+ }
/* dispatch the interpolated value to the listener */
- if( mListener != null )
- {
- mListener.receive( output );
- }
- }
+ if(mListener != null)
+ {
+ mListener.receive(output);
+ }
+ }
}
- @Override
- public void setListener( RealSampleListener listener )
+ @Override
+ public void setListener(RealSampleListener listener)
{
- mListener = listener;
+ mListener = listener;
}
- @Override
- public void removeListener( RealSampleListener listener )
+ @Override
+ public void removeListener(RealSampleListener listener)
{
- mListener = null;
+ mListener = null;
}
-
- @Override
+
+ @Override
public List getTapGroups()
{
- if( mAvailableTaps == null )
- {
- mAvailableTaps = new ArrayList<>();
+ if(mAvailableTaps == null)
+ {
+ mAvailableTaps = new ArrayList<>();
/*
* Since the target is 2.0, we subtract 2.0 from the value so that
* a good value is plotted right on the zero center line
*/
- TapGroup group = new TapGroup( "C4FM Symbol Filter" );
-
- group.add(
- new AdditiveFloatTap( INSTRUMENT_SYMBOL_SPREAD, 0, 0.1f, -2.0f ) );
-
- mAvailableTaps.add( group );
- }
-
- return mAvailableTaps;
+ TapGroup group = new TapGroup("C4FM Symbol Filter");
+
+ group.add(
+ new AdditiveFloatTap(INSTRUMENT_SYMBOL_SPREAD, 0, 0.1f, -2.0f));
+
+ mAvailableTaps.add(group);
+ }
+
+ return mAvailableTaps;
}
- @Override
- public void registerTap( Tap tap )
+ @Override
+ public void registerTap(Tap tap)
{
- if( tap.getName().contentEquals( INSTRUMENT_SYMBOL_SPREAD ) )
- {
- mSymbolSpreadTap = (FloatTap)tap;
- }
+ if(tap.getName().contentEquals(INSTRUMENT_SYMBOL_SPREAD))
+ {
+ mSymbolSpreadTap = (FloatTap)tap;
+ }
}
- @Override
- public void unregisterTap( Tap tap )
+ @Override
+ public void unregisterTap(Tap tap)
{
- if( tap.getName().contentEquals( INSTRUMENT_SYMBOL_SPREAD ) )
- {
- mSymbolSpreadTap = null;
- }
+ if(tap.getName().contentEquals(INSTRUMENT_SYMBOL_SPREAD))
+ {
+ mSymbolSpreadTap = null;
+ }
}
/**
* Broadcasts a frequency change event to the registered listener
+ *
* @param event
*/
- public void broadcast( FrequencyChangeEvent event )
- {
- if( mFrequencyChangeListener != null )
- {
- mFrequencyChangeListener.receive( event );
- }
- }
+ public void broadcast(SourceEvent event)
+ {
+ if(mFrequencyChangeListener != null)
+ {
+ mFrequencyChangeListener.receive(event);
+ }
+ }
/**
* Sets the listener for frequency change events.
+ *
* @param listener
*/
- public void setFrequencyChangeListener( Listener listener )
- {
- mFrequencyChangeListener = listener;
- }
-
- @Override
- public Listener getFrequencyChangeListener()
- {
- if( mFrequencyCorrectionProcessor == null )
- {
- mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor();
- }
-
- return mFrequencyCorrectionProcessor;
- }
+ public void setFrequencyChangeListener(Listener listener)
+ {
+ mFrequencyChangeListener = listener;
+ }
- /**
- * Receives notifications that the channel frequency correction has been
- * applied and updates internal tracking value.
- */
- public class FrequencyCorrectionProcessor implements Listener
- {
- @Override
- public void receive( FrequencyChangeEvent event )
- {
- switch( event.getEvent() )
- {
- case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
- mFrequencyCorrection = event.getValue().intValue();
+ @Override
+ public Listener getSourceEventListener()
+ {
+ if(mFrequencyCorrectionProcessor == null)
+ {
+ mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor();
+ }
- //Reset internal frequency tracking
- mCoarseFrequencyCorrection = 0.0f;
- mFineFrequencyCorrection = 0.0f;
- break;
- case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
- case NOTIFICATION_SAMPLE_RATE_CHANGE:
- mResetFrequencyTracker = true;
- break;
- default:
- break;
- }
- }
- }
+ return mFrequencyCorrectionProcessor;
+ }
+
+ /**
+ * Receives notifications that the channel frequency correction has been
+ * applied and updates internal tracking value.
+ */
+ public class FrequencyCorrectionProcessor implements Listener
+ {
+ @Override
+ public void receive(SourceEvent event)
+ {
+ switch(event.getEvent())
+ {
+ case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
+ mFrequencyCorrection = event.getValue().intValue();
+
+ //Reset internal frequency tracking
+ mCoarseFrequencyCorrection = 0.0f;
+ mFineFrequencyCorrection = 0.0f;
+ break;
+ case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
+ case NOTIFICATION_SAMPLE_RATE_CHANGE:
+ mResetFrequencyTracker = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java
index 38e3fdfc..2f08eb4f 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java
@@ -4,10 +4,10 @@ import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.instrument.Instrumentable;
import io.github.dsheirer.module.decode.Decoder;
import io.github.dsheirer.module.decode.DecoderType;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProvider;
+import io.github.dsheirer.source.ISourceEventListener;
+import io.github.dsheirer.source.ISourceEventProvider;
-public abstract class P25Decoder extends Decoder implements IFrequencyChangeListener, IFrequencyChangeProvider, Instrumentable
+public abstract class P25Decoder extends Decoder implements ISourceEventListener, ISourceEventProvider, Instrumentable
{
private P25MessageProcessor mMessageProcessor;
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java
index f73c4b05..d2331b60 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderState.java
@@ -142,7 +142,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class P25DecoderState extends DecoderState
{
@@ -223,11 +222,6 @@ public class P25DecoderState extends DecoderState
return DecoderType.P25_PHASE1;
}
- @Override
- public void stop()
- {
- }
-
/**
* Performs a full reset to prepare this object for reuse on a new channel
*/
@@ -241,6 +235,12 @@ public class P25DecoderState extends DecoderState
mSystem = null;
}
+ @Override
+ public void init()
+ {
+
+ }
+
/**
* Resets any temporal state details
*/
@@ -259,14 +259,6 @@ public class P25DecoderState extends DecoderState
mCurrentCallEvent = null;
}
- /**
- * Performs any initialization operations to prepare for use
- */
- @Override
- public void init()
- {
- }
-
/**
* Indicates if there a call event exists for the specified channel and
* from and to talkgroup identifiers.
@@ -3625,7 +3617,7 @@ public class P25DecoderState extends DecoderState
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
//Change the default (45-second) traffic channel timeout to 1 second
if(mChannelType == ChannelType.TRAFFIC)
@@ -3633,4 +3625,10 @@ public class P25DecoderState extends DecoderState
broadcast(new ChangeChannelTimeoutEvent(this, ChannelType.TRAFFIC, 1000));
}
}
+
+ @Override
+ public void stop()
+ {
+
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java
index 13512400..c4d714e2 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java
@@ -26,13 +26,12 @@ import io.github.dsheirer.instrument.tap.stream.FloatTap;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.IFilteredRealBufferListener;
import io.github.dsheirer.sample.real.RealBuffer;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
+import io.github.dsheirer.source.SourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener
{
@@ -60,7 +59,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
* infrastructure and subscriber identities that will be included in each
* decoded message
*/
- public P25_C4FMDecoder(AliasList aliasList, int frequencyCorrectionMaximum )
+ public P25_C4FMDecoder( AliasList aliasList, int frequencyCorrectionMaximum )
{
super( aliasList );
@@ -193,28 +192,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
@Override
- public void reset()
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void stop()
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void setFrequencyChangeListener( Listener listener )
+ public void setSourceEventListener(Listener listener )
{
if( mSymbolFilter != null )
{
@@ -223,7 +201,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
@Override
- public void removeFrequencyChangeListener()
+ public void removeSourceEventListener()
{
if( mSymbolFilter != null )
{
@@ -232,11 +210,11 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
@Override
- public Listener getFrequencyChangeListener()
+ public Listener getSourceEventListener()
{
if( mSymbolFilter != null )
{
- return mSymbolFilter.getFrequencyChangeListener();
+ return mSymbolFilter.getSourceEventListener();
}
return null;
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25_LSMDecoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25_LSMDecoder.java
index 806b20e6..9ec1ec00 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25_LSMDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25_LSMDecoder.java
@@ -34,13 +34,12 @@ import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexBuffer;
import io.github.dsheirer.sample.complex.ComplexBufferToStreamConverter;
import io.github.dsheirer.sample.complex.IComplexBufferListener;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
+import io.github.dsheirer.source.SourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
{
@@ -67,7 +66,7 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
super( aliasList );
mBasebandFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass(
- 48000, 7250, 8000, 60, WindowType.HANNING, true ), 1.0f );
+ 48000, 7250, 8000, 60, WindowType.HANN, true ), 1.0f );
mBasebandFilter.setListener( mStreamConverter );
@@ -106,29 +105,6 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
mMessageFramer = null;
}
- @Override
- public void setFrequencyChangeListener(Listener listener)
- {
- }
-
- @Override
- public void removeFrequencyChangeListener()
- {
- }
-
- @Override
- public Listener getFrequencyChangeListener()
- {
- return new Listener()
- {
- @Override
- public void receive(FrequencyChangeEvent frequencyChangeEvent)
- {
- //Ignored
- }
- };
- }
-
@Override
public Listener getComplexBufferListener()
{
@@ -237,23 +213,20 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
}
@Override
- public void reset()
+ public Listener getSourceEventListener()
{
- // TODO Auto-generated method stub
-
+ return null;
}
@Override
- public void start( ScheduledExecutorService executor )
+ public void setSourceEventListener(Listener listener)
{
- // TODO Auto-generated method stub
-
+
}
@Override
- public void stop()
+ public void removeSourceEventListener()
{
- // TODO Auto-generated method stub
-
+
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java
index f94f4b9b..2c34b25d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java
@@ -20,8 +20,6 @@ import jmbe.iface.AudioConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ScheduledExecutorService;
-
public class P25AudioModule extends Module implements Listener, IAudioPacketProvider, IMessageListener,
ISquelchStateListener
{
@@ -71,7 +69,7 @@ public class P25AudioModule extends Module implements Listener, IAudioP
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java
index 72ba45a2..5311eb1d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java
@@ -33,7 +33,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
public class PassportDecoder extends Decoder
implements IUnFilteredRealBufferListener, Instrumentable
@@ -108,14 +107,4 @@ public class PassportDecoder extends Decoder
{
mPassportMessageFramer.reset();
}
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
-
- @Override
- public void stop()
- {
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java
index 1174224e..805278ec 100644
--- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java
@@ -40,7 +40,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class PassportDecoderState extends DecoderState
{
@@ -76,21 +75,6 @@ public class PassportDecoderState extends DecoderState
return DecoderType.PASSPORT;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void init()
- {
- }
-
- @Override
- public void stop()
- {
- }
-
private void logTalkgroup(String talkgroup)
{
if(mTalkgroupsFirstHeard.contains(talkgroup))
@@ -519,6 +503,24 @@ public class PassportDecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
private void resetState()
{
mToTalkgroupAttribute.reset();
diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java
index 9ccae831..18174720 100644
--- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200Decoder.java
@@ -40,7 +40,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
/**
* TAIT 1200 - 1200 baud 2FSK decoder
@@ -189,25 +188,4 @@ public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListe
{
return DecoderType.TAIT_1200;
}
-
- @Override
- public void reset()
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void start( ScheduledExecutorService executor )
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void stop()
- {
- // TODO Auto-generated method stub
-
- }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java
index 44367d23..e82526bc 100644
--- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java
@@ -38,7 +38,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
-import java.util.concurrent.ScheduledExecutorService;
public class Tait1200DecoderState extends DecoderState
{
@@ -65,16 +64,6 @@ public class Tait1200DecoderState extends DecoderState
return DecoderType.TAIT_1200;
}
- @Override
- public void start(ScheduledExecutorService executor)
- {
- }
-
- @Override
- public void stop()
- {
- }
-
@Override
public void reset()
{
@@ -83,6 +72,24 @@ public class Tait1200DecoderState extends DecoderState
resetState();
}
+ @Override
+ public void start()
+ {
+
+ }
+
+ @Override
+ public void stop()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+
+ }
+
private void resetState()
{
mFromAttribute.reset();
@@ -176,12 +183,6 @@ public class Tait1200DecoderState extends DecoderState
return sb.toString();
}
- @Override
- public void init()
- {
- /* No initialization required */
- }
-
/**
* Responds to reset events issued by the channel state
*/
diff --git a/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java b/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java
index b52cd0eb..0cbfdf7a 100644
--- a/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java
+++ b/src/main/java/io/github/dsheirer/module/demodulate/am/AMDemodulatorModule.java
@@ -30,95 +30,81 @@ import io.github.dsheirer.sample.complex.IComplexBufferListener;
import io.github.dsheirer.sample.real.IFilteredRealBufferProvider;
import io.github.dsheirer.sample.real.RealBuffer;
-import java.util.concurrent.ScheduledExecutorService;
-
public class AMDemodulatorModule extends Module
- implements IComplexBufferListener, IFilteredRealBufferProvider
+ implements IComplexBufferListener, IFilteredRealBufferProvider
{
- private static final int SAMPLE_RATE = 48000;
-
- private ComplexFIRFilter_CB_CB mIQFilter;
- private AMDemodulator_CB mDemodulator;
- private RealFIRFilter_RB_RB mLowPassFilter;
+ private static final int SAMPLE_RATE = 48000;
+
+ private ComplexFIRFilter_CB_CB mIQFilter;
+ private AMDemodulator_CB mDemodulator;
+ private RealFIRFilter_RB_RB mLowPassFilter;
private AutomaticGainControl_RB mAGC;
- /**
- * FM Demodulator with integrated DC removal and automatic frequency
- * correction control.
- *
- * @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.
- *
- * @param maxFrequencyCorrection - defines the maximum frequency +/- that
- * the correction controller can adjust from the center tuned frequency.
- * Set this value to 0 for no frequency correction control.
- */
- public AMDemodulatorModule()
- {
- mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass(
- SAMPLE_RATE, 5000, 73, WindowType.HAMMING ), 1.0f );
-
- mDemodulator = new AMDemodulator_CB( 500.0f );
- mIQFilter.setListener( mDemodulator );
+ /**
+ * AM Demodulator Module
+ */
+ public AMDemodulatorModule()
+ {
+ mIQFilter = new ComplexFIRFilter_CB_CB(FilterFactory.getLowPass(
+ SAMPLE_RATE, 5000, 73, WindowType.HAMMING), 1.0f);
- mLowPassFilter = new RealFIRFilter_RB_RB(
- FilterFactory.getLowPass( 48000, 3000, 31, WindowType.COSINE ), 1.0f );
- mDemodulator.setListener( mLowPassFilter );
-
- mAGC = new AutomaticGainControl_RB();
- mLowPassFilter.setListener( mAGC );
- }
+ mDemodulator = new AMDemodulator_CB(500.0f);
+ mIQFilter.setListener(mDemodulator);
- @Override
- public Listener getComplexBufferListener()
- {
- return mIQFilter;
- }
+ mLowPassFilter = new RealFIRFilter_RB_RB(
+ FilterFactory.getLowPass(48000, 3000, 31, WindowType.COSINE), 1.0f);
+ mDemodulator.setListener(mLowPassFilter);
- @Override
- public void dispose()
- {
- mIQFilter.dispose();
- mIQFilter = null;
-
- mDemodulator.dispose();
- mDemodulator = null;
-
- mLowPassFilter.dispose();
- mLowPassFilter = null;
-
- mAGC.dispose();
- mAGC = null;
- }
+ mAGC = new AutomaticGainControl_RB();
+ mLowPassFilter.setListener(mAGC);
+ }
- @Override
- public void reset()
- {
- }
+ @Override
+ public Listener getComplexBufferListener()
+ {
+ return mIQFilter;
+ }
- @Override
- public void setFilteredRealBufferListener( Listener listener )
- {
- mAGC.setListener( listener );
- }
+ @Override
+ public void dispose()
+ {
+ mIQFilter.dispose();
+ mIQFilter = null;
- @Override
- public void removeFilteredRealBufferListener()
- {
- mAGC.removeListener();
- }
+ mDemodulator.dispose();
+ mDemodulator = null;
- @Override
- public void start( ScheduledExecutorService executor )
- {
- }
+ mLowPassFilter.dispose();
+ mLowPassFilter = null;
- @Override
- public void stop()
- {
- }
+ mAGC.dispose();
+ mAGC = null;
+ }
+
+ @Override
+ public void reset()
+ {
+ }
+
+ @Override
+ public void setFilteredRealBufferListener(Listener listener)
+ {
+ mAGC.setListener(listener);
+ }
+
+ @Override
+ public void removeFilteredRealBufferListener()
+ {
+ mAGC.removeListener();
+ }
+
+ @Override
+ public void start()
+ {
+ }
+
+ @Override
+ public void stop()
+ {
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/main/java/io/github/dsheirer/module/demodulate/audio/DemodulatedAudioFilterModule.java
index b583605a..5560b9dd 100644
--- a/src/main/java/io/github/dsheirer/module/demodulate/audio/DemodulatedAudioFilterModule.java
+++ b/src/main/java/io/github/dsheirer/module/demodulate/audio/DemodulatedAudioFilterModule.java
@@ -30,8 +30,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ScheduledExecutorService;
-
public class DemodulatedAudioFilterModule extends Module implements IUnFilteredRealBufferListener, IFilteredRealBufferProvider
{
private final static Logger mLog = LoggerFactory.getLogger(DemodulatedAudioFilterModule.class);
@@ -53,7 +51,7 @@ public class DemodulatedAudioFilterModule extends Module implements IUnFilteredR
//TODO: change this to a band pass filter
mBandPassFilter = new RealFIRFilter_RB_RB(FilterFactory.getLowPass(48000, pass, stop, 60,
- WindowType.HANNING, true), 1.0f);
+ WindowType.HANN, true), 1.0f);
mDCFilter.setListener(mBandPassFilter);
}
@@ -97,7 +95,7 @@ public class DemodulatedAudioFilterModule extends Module implements IUnFilteredR
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
}
diff --git a/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java b/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java
index e94bffd4..7a06160a 100644
--- a/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java
+++ b/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java
@@ -30,8 +30,6 @@ import io.github.dsheirer.sample.real.RealBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.concurrent.ScheduledExecutorService;
-
public class FMDemodulatorModule extends Module implements IComplexBufferListener, IUnFilteredRealBufferProvider
{
private final static Logger mLog = LoggerFactory.getLogger(FMDemodulatorModule.class);
@@ -104,7 +102,7 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
}
diff --git a/src/main/java/io/github/dsheirer/module/log/EventLogger.java b/src/main/java/io/github/dsheirer/module/log/EventLogger.java
index b4099cde..2785d365 100644
--- a/src/main/java/io/github/dsheirer/module/log/EventLogger.java
+++ b/src/main/java/io/github/dsheirer/module/log/EventLogger.java
@@ -30,7 +30,6 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Path;
-import java.util.concurrent.ScheduledExecutorService;
public abstract class EventLogger extends Module
{
@@ -65,7 +64,7 @@ public abstract class EventLogger extends Module
public abstract String getHeader();
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
if(mLogFile == null)
{
@@ -80,6 +79,8 @@ public abstract class EventLogger extends Module
mLogFileName = sb.toString();
+ mLog.info("Creating log file:" + mLogFileName);
+
mLogFile = new OutputStreamWriter(new FileOutputStream(mLogFileName));
write(getHeader());
@@ -123,17 +124,14 @@ public abstract class EventLogger extends Module
protected void write(String eventLogEntry)
{
- if(mLogFile != null)
+ try
{
- try
- {
- mLogFile.write(eventLogEntry + "\n");
- mLogFile.flush();
- }
- catch(IOException e)
- {
- mLog.error("Error writing entry to event log file", e);
- }
+ mLogFile.write(eventLogEntry + "\n");
+ mLogFile.flush();
+ }
+ catch(IOException e)
+ {
+ mLog.error("Error writing entry to event log file", e);
}
}
}
diff --git a/src/main/java/io/github/dsheirer/record/AudioRecorder.java b/src/main/java/io/github/dsheirer/record/AudioRecorder.java
index 656b275c..b53eead8 100644
--- a/src/main/java/io/github/dsheirer/record/AudioRecorder.java
+++ b/src/main/java/io/github/dsheirer/record/AudioRecorder.java
@@ -23,6 +23,7 @@ import io.github.dsheirer.audio.IAudioPacketListener;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,7 +34,6 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -193,10 +193,8 @@ public abstract class AudioRecorder extends Module implements Listener
{
recorder = new AudioPacketWaveRecorder(filePrefix, audioPacket.getMetadata());
- recorder.start(ThreadPool.SCHEDULED);
+ recorder.start();
recorder.receive(audioPacket);
mRecorders.put(identifier, recorder);
diff --git a/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java b/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java
index ab3d806a..aa0fdad8 100644
--- a/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java
+++ b/src/main/java/io/github/dsheirer/record/wave/AudioPacketWaveRecorder.java
@@ -22,6 +22,7 @@ import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.ConversionUtils;
import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,7 +32,6 @@ import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -89,7 +89,7 @@ public class AudioPacketWaveRecorder extends Module implements Listener
{
- private final static Logger mLog =
- LoggerFactory.getLogger( ComplexBufferWaveRecorder.class );
+ private final static Logger mLog = LoggerFactory.getLogger( ComplexBufferWaveRecorder.class );
private WaveWriter mWriter;
private String mFilePrefix;
@@ -68,19 +67,19 @@ public class ComplexBufferWaveRecorder extends Module
mFilePrefix = filePrefix;
}
- public Path getFile()
- {
- return mFile;
- }
-
- public void start( ScheduledExecutorService executor )
- {
- if( mRunning.compareAndSet( false, true ) )
- {
- if( mBufferProcessor == null )
- {
- mBufferProcessor = new BufferProcessor();
- }
+ public Path getFile()
+ {
+ return mFile;
+ }
+
+ public void start()
+ {
+ if(mRunning.compareAndSet(false, true))
+ {
+ if(mBufferProcessor == null)
+ {
+ mBufferProcessor = new BufferProcessor();
+ }
try
{
@@ -95,7 +94,7 @@ public class ComplexBufferWaveRecorder extends Module
mWriter = new WaveWriter( mAudioFormat, mFile );
/* Schedule the processor to run every 500 milliseconds */
- mProcessorHandle = executor.scheduleAtFixedRate(
+ mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(
mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS );
}
catch( IOException io )
diff --git a/src/main/java/io/github/dsheirer/record/wave/RealBufferWaveRecorder.java b/src/main/java/io/github/dsheirer/record/wave/RealBufferWaveRecorder.java
index 78ee73a1..28c56db5 100644
--- a/src/main/java/io/github/dsheirer/record/wave/RealBufferWaveRecorder.java
+++ b/src/main/java/io/github/dsheirer/record/wave/RealBufferWaveRecorder.java
@@ -23,6 +23,7 @@ import io.github.dsheirer.sample.ConversionUtils;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.IFilteredRealBufferListener;
import io.github.dsheirer.sample.real.RealBuffer;
+import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,7 +33,6 @@ import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -40,8 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* WAVE audio recorder module for recording real sample buffers to a wave file
*/
-public class RealBufferWaveRecorder extends Module
- implements IFilteredRealBufferListener, Listener
+public class RealBufferWaveRecorder extends Module implements IFilteredRealBufferListener, Listener
{
private final static Logger mLog = LoggerFactory.getLogger(RealBufferWaveRecorder.class);
@@ -89,7 +88,7 @@ public class RealBufferWaveRecorder extends Module
return mFile;
}
- public void start(ScheduledExecutorService executor)
+ public void start()
{
if(mRunning.compareAndSet(false, true))
{
@@ -111,7 +110,7 @@ public class RealBufferWaveRecorder extends Module
mWriter = new WaveWriter(mAudioFormat, mFile);
/* Schedule the processor to run every 500 milliseconds */
- mProcessorHandle = executor.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
+ mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
}
catch(IOException io)
{
diff --git a/src/main/java/io/github/dsheirer/sample/Broadcaster.java b/src/main/java/io/github/dsheirer/sample/Broadcaster.java
index 92496aad..52df7104 100644
--- a/src/main/java/io/github/dsheirer/sample/Broadcaster.java
+++ b/src/main/java/io/github/dsheirer/sample/Broadcaster.java
@@ -25,62 +25,100 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public class Broadcaster implements Listener
{
- private List> mListeners = new CopyOnWriteArrayList<>();
+ private List> mListeners = new CopyOnWriteArrayList<>();
- @Override
- public void receive( T t )
+ /**
+ * Implements the Listener interface to receive an element and broadcast that element to all registered
+ * listeners.
+ *
+ * @param t element to broadcast
+ */
+ @Override
+ public void receive(T t)
{
- broadcast( t );
+ broadcast(t);
}
-
- /**
- * Clear listeners to prepare for garbage collection
- */
- public void dispose()
- {
- mListeners.clear();
- }
-
- public boolean hasListeners()
- {
- return !mListeners.isEmpty();
- }
-
- public int getListenerCount()
- {
- return mListeners.size();
- }
-
- public List> getListeners()
- {
- return mListeners;
- }
-
- public void addListener( Listener listener )
- {
- if(listener == null)
- {
- throw new IllegalArgumentException("Listener cannot be null");
- }
- mListeners.add( listener );
- }
-
- public void removeListener( Listener listener )
- {
- mListeners.remove( listener );
- }
-
- public void clear()
- {
- mListeners.clear();
- }
-
- public void broadcast( T t )
+ /**
+ * Clear listeners to prepare for garbage collection
+ */
+ public void dispose()
{
- for( Listener listener: mListeners )
- {
- listener.receive( t );
- }
+ mListeners.clear();
+ }
+
+ /**
+ * Indicates if this broadcaster has any listeners registered
+ */
+ public boolean hasListeners()
+ {
+ return !mListeners.isEmpty();
+ }
+
+ /**
+ * Indicates if the listener is currently registered with this broadcaster
+ */
+ public boolean hasListener(Listener listener)
+ {
+ return listener != null && mListeners.contains(listener);
+ }
+
+ /**
+ * The count of listeners currently registered with this broadcaster
+ */
+ public int getListenerCount()
+ {
+ return mListeners.size();
+ }
+
+ /**
+ * The list of listeners currently registered with this broadcaster
+ */
+ public List> getListeners()
+ {
+ return mListeners;
+ }
+
+ /**
+ * Registers the listener to receive elements from this broadcaster
+ *
+ * @param listener
+ */
+ public void addListener(Listener listener)
+ {
+ if(listener != null && !mListeners.contains(listener))
+ {
+ mListeners.add(listener);
+ }
+ }
+
+ /**
+ * Deregisters the listener from receiving elements from this broadcaster
+ */
+ public void removeListener(Listener listener)
+ {
+ if(listener != null && mListeners.contains(listener))
+ {
+ mListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Deregisters all listeners from this broadcaster
+ */
+ public void clear()
+ {
+ mListeners.clear();
+ }
+
+ /**
+ * Broadcasts the element to all registered listeners
+ */
+ public void broadcast(T t)
+ {
+ for(Listener listener : mListeners)
+ {
+ listener.receive(t);
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java b/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java
index 70d20728..6eb49d6d 100644
--- a/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java
+++ b/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java
@@ -19,6 +19,7 @@
package io.github.dsheirer.sample;
import io.github.dsheirer.sample.real.IOverflowListener;
+import io.github.dsheirer.source.Source;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +34,7 @@ public class OverflowableTransferQueue
public enum State {NORMAL, OVERFLOW};
private IOverflowListener mOverflowListener;
+ private Source mSourceOverflowListener;
private LinkedTransferQueue mQueue = new LinkedTransferQueue();
private AtomicInteger mCounter = new AtomicInteger();
@@ -73,6 +75,14 @@ public class OverflowableTransferQueue
}
}
+ /**
+ * Removes and returns a single element from the head of the queue or null if the queue is empty
+ */
+ public E poll()
+ {
+ return mQueue.poll();
+ }
+
/**
* Retrieves elements from the queue into the collection up to the maximum number of elements specified
*/
@@ -91,13 +101,21 @@ public class OverflowableTransferQueue
}
/**
- * Sets a listener to receive overflow state change events
+ * Sets a listener to receive overflow state change events.
*/
public void setOverflowListener(IOverflowListener listener)
{
mOverflowListener = listener;
}
+ /**
+ * Sets the source to receive overflow state change events (in addition to an IOverflow listener)
+ */
+ public void setSourceOverflowListener(Source source)
+ {
+ mSourceOverflowListener = source;
+ }
+
/**
* Toggles the overflow state and broadcast state change to listener
@@ -110,6 +128,11 @@ public class OverflowableTransferQueue
{
mOverflowListener.sourceOverflow(overflow);
}
+
+ if(mSourceOverflowListener != null)
+ {
+ mSourceOverflowListener.broadcastOverflowState(overflow);
+ }
}
}
@@ -125,4 +148,4 @@ public class OverflowableTransferQueue
mOverflow.set(false);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/sample/SampleType.java b/src/main/java/io/github/dsheirer/sample/SampleType.java
index f796ee4e..f3a497f9 100644
--- a/src/main/java/io/github/dsheirer/sample/SampleType.java
+++ b/src/main/java/io/github/dsheirer/sample/SampleType.java
@@ -2,7 +2,6 @@ package io.github.dsheirer.sample;
public enum SampleType
{
- COMPLEX, //Baseband
- REAL, //Demodulated
- COMPLEX_AND_REAL; //Baseband and Demodulated
+ COMPLEX, // I/Q
+ REAL; // Real
}
diff --git a/src/sample/complex/IComplexBufferProvider.java b/src/main/java/io/github/dsheirer/sample/complex/IComplexBufferProvider.java
similarity index 93%
rename from src/sample/complex/IComplexBufferProvider.java
rename to src/main/java/io/github/dsheirer/sample/complex/IComplexBufferProvider.java
index 480cede0..af6f93a3 100644
--- a/src/sample/complex/IComplexBufferProvider.java
+++ b/src/main/java/io/github/dsheirer/sample/complex/IComplexBufferProvider.java
@@ -16,9 +16,9 @@
* along with this program. If not, see
*
******************************************************************************/
-package sample.complex;
+package io.github.dsheirer.sample.complex;
-import sample.Listener;
+import io.github.dsheirer.sample.Listener;
public interface IComplexBufferProvider
{
diff --git a/src/main/java/io/github/dsheirer/source/ComplexSource.java b/src/main/java/io/github/dsheirer/source/ComplexSource.java
index 30add060..e23e3305 100644
--- a/src/main/java/io/github/dsheirer/source/ComplexSource.java
+++ b/src/main/java/io/github/dsheirer/source/ComplexSource.java
@@ -6,8 +6,9 @@ import io.github.dsheirer.sample.complex.ComplexBuffer;
public abstract class ComplexSource extends Source implements Provider
{
- public ComplexSource()
- {
- super( SampleType.COMPLEX );
- }
+ @Override
+ public SampleType getSampleType()
+ {
+ return SampleType.COMPLEX;
+ }
}
diff --git a/src/source/ISourceEventListener.java b/src/main/java/io/github/dsheirer/source/ISourceEventListener.java
similarity index 94%
rename from src/source/ISourceEventListener.java
rename to src/main/java/io/github/dsheirer/source/ISourceEventListener.java
index feb65ddb..6f3a2cfc 100644
--- a/src/source/ISourceEventListener.java
+++ b/src/main/java/io/github/dsheirer/source/ISourceEventListener.java
@@ -16,9 +16,9 @@
* along with this program. If not, see
*
******************************************************************************/
-package source;
+package io.github.dsheirer.source;
-import sample.Listener;
+import io.github.dsheirer.sample.Listener;
/**
diff --git a/src/source/ISourceEventProcessor.java b/src/main/java/io/github/dsheirer/source/ISourceEventProcessor.java
similarity index 96%
rename from src/source/ISourceEventProcessor.java
rename to src/main/java/io/github/dsheirer/source/ISourceEventProcessor.java
index 45f74e20..8f85e0fd 100644
--- a/src/source/ISourceEventProcessor.java
+++ b/src/main/java/io/github/dsheirer/source/ISourceEventProcessor.java
@@ -16,7 +16,7 @@
* along with this program. If not, see
*
******************************************************************************/
-package source;
+package io.github.dsheirer.source;
public interface ISourceEventProcessor
{
diff --git a/src/source/ISourceEventProvider.java b/src/main/java/io/github/dsheirer/source/ISourceEventProvider.java
similarity index 93%
rename from src/source/ISourceEventProvider.java
rename to src/main/java/io/github/dsheirer/source/ISourceEventProvider.java
index 6e6a1bed..ff247fb5 100644
--- a/src/source/ISourceEventProvider.java
+++ b/src/main/java/io/github/dsheirer/source/ISourceEventProvider.java
@@ -16,9 +16,9 @@
* along with this program. If not, see
*
******************************************************************************/
-package source;
+package io.github.dsheirer.source;
-import sample.Listener;
+import io.github.dsheirer.sample.Listener;
/**
* Provider of Source Change Events
diff --git a/src/main/java/io/github/dsheirer/source/InvalidFrequencyException.java b/src/main/java/io/github/dsheirer/source/InvalidFrequencyException.java
index 54bd8666..bc30a29a 100644
--- a/src/main/java/io/github/dsheirer/source/InvalidFrequencyException.java
+++ b/src/main/java/io/github/dsheirer/source/InvalidFrequencyException.java
@@ -54,4 +54,11 @@ public class InvalidFrequencyException extends SourceException
{
return mValidFrequency;
}
+
+ @Override
+ public String getMessage()
+ {
+ return super.getMessage() + " Invalid Frequency: " + getInvalidFrequency() +
+ " - current valid frequency:" + getValidFrequency();
+ }
}
diff --git a/src/main/java/io/github/dsheirer/source/RealSource.java b/src/main/java/io/github/dsheirer/source/RealSource.java
index 3f73e7c1..1dfc15f9 100644
--- a/src/main/java/io/github/dsheirer/source/RealSource.java
+++ b/src/main/java/io/github/dsheirer/source/RealSource.java
@@ -7,8 +7,9 @@ import io.github.dsheirer.sample.real.RealBuffer;
public abstract class RealSource extends Source implements Provider
{
- public RealSource()
- {
- super( SampleType.REAL );
- }
+ @Override
+ public SampleType getSampleType()
+ {
+ return SampleType.REAL;
+ }
}
diff --git a/src/main/java/io/github/dsheirer/source/Source.java b/src/main/java/io/github/dsheirer/source/Source.java
index 23916db2..404a2cec 100644
--- a/src/main/java/io/github/dsheirer/source/Source.java
+++ b/src/main/java/io/github/dsheirer/source/Source.java
@@ -17,42 +17,53 @@
******************************************************************************/
package io.github.dsheirer.source;
-import io.github.dsheirer.channel.heartbeat.IHeartbeatProvider;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.SampleType;
import io.github.dsheirer.sample.real.IOverflowListener;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProvider;
+import io.github.dsheirer.source.heartbeat.HeartbeatManager;
+import io.github.dsheirer.source.heartbeat.IHeartbeatProvider;
/**
* Abstract class to define the minimum functionality of a sample data provider.
*/
-public abstract class Source extends Module implements IFrequencyChangeListener, IFrequencyChangeProvider,
- IHeartbeatProvider
+public abstract class Source extends Module implements ISourceEventListener, ISourceEventProvider, IHeartbeatProvider
{
- protected SampleType mSampleType;
+ private HeartbeatManager mHeartbeatManager = new HeartbeatManager();
+
+ /**
+ * Heartbeat manager for this source
+ */
+ public HeartbeatManager getHeartbeatManager()
+ {
+ return mHeartbeatManager;
+ }
+
+
protected IOverflowListener mOverflowListener;
- public Source( SampleType sampleType )
- {
- mSampleType = sampleType;
- }
-
- public abstract int getSampleRate() throws SourceException;
-
- public abstract long getFrequency() throws SourceException;
-
+ /**
+ * Indicates the type of samples provided by this source: real or complex
+ */
+ public abstract SampleType getSampleType();
+
+ /**
+ * Sample rate provided by this source
+ *
+ * @throws SourceException if there is an issue determining the sample rate for this source
+ */
+ public abstract double getSampleRate();
+
+ /**
+ * Center frequency for this source in Hertz
+ *
+ * @throws SourceException if there is an issue in determining the center frequency for this source
+ */
+ public abstract long getFrequency();
+
+ /**
+ * Process any cleanup actions to prepare for garbage collection of this source
+ */
public abstract void dispose();
-
- public SampleType getSampleType()
- {
- return mSampleType;
- }
-
- public void setSampleType( SampleType sampleType )
- {
- mSampleType = sampleType;
- }
/**
* Registers the listener to receive overflow state changes. Use null argument to clear the listener
@@ -65,9 +76,9 @@ public abstract class Source extends Module implements IFrequencyChangeListener,
/**
* Broadcasts an overflow state
*
- * @param overflow true if overlow, false if normal
+ * @param overflow true if overflow, false if normal
*/
- protected void broadcastOverflowState(boolean overflow)
+ public void broadcastOverflowState(boolean overflow)
{
if(mOverflowListener != null)
{
diff --git a/src/source/SourceEvent.java b/src/main/java/io/github/dsheirer/source/SourceEvent.java
similarity index 99%
rename from src/source/SourceEvent.java
rename to src/main/java/io/github/dsheirer/source/SourceEvent.java
index bade0e58..8f9b2857 100644
--- a/src/source/SourceEvent.java
+++ b/src/main/java/io/github/dsheirer/source/SourceEvent.java
@@ -16,7 +16,7 @@
* along with this program. If not, see
*
******************************************************************************/
-package source;
+package io.github.dsheirer.source;
public class SourceEvent
{
diff --git a/src/source/SourceEventListenerToProcessorAdapter.java b/src/main/java/io/github/dsheirer/source/SourceEventListenerToProcessorAdapter.java
similarity index 91%
rename from src/source/SourceEventListenerToProcessorAdapter.java
rename to src/main/java/io/github/dsheirer/source/SourceEventListenerToProcessorAdapter.java
index 85f50138..958b3a4f 100644
--- a/src/source/SourceEventListenerToProcessorAdapter.java
+++ b/src/main/java/io/github/dsheirer/source/SourceEventListenerToProcessorAdapter.java
@@ -16,16 +16,15 @@
* along with this program. If not, see
*
******************************************************************************/
-package source;
+package io.github.dsheirer.source;
+import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sample.Listener;
-import source.tuner.channel.TunerChannelSource;
public class SourceEventListenerToProcessorAdapter implements Listener
{
- private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class);
+ private final static Logger mLog = LoggerFactory.getLogger(SourceEventListenerToProcessorAdapter.class);
private ISourceEventProcessor mSourceEventProcessor;
diff --git a/src/main/java/io/github/dsheirer/source/config/SourceConfigRecording.java b/src/main/java/io/github/dsheirer/source/config/SourceConfigRecording.java
index aa8717b5..3d6d8356 100644
--- a/src/main/java/io/github/dsheirer/source/config/SourceConfigRecording.java
+++ b/src/main/java/io/github/dsheirer/source/config/SourceConfigRecording.java
@@ -1,26 +1,23 @@
/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
+ * sdr-trunk
+ * Copyright (C) 2014-2017 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
+ *
******************************************************************************/
package io.github.dsheirer.source.config;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.source.SourceType;
-import io.github.dsheirer.source.tuner.TunerChannel;
-import io.github.dsheirer.source.tuner.TunerChannel.Type;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
public class SourceConfigRecording extends SourceConfiguration
{
@@ -59,11 +56,11 @@ public class SourceConfigRecording extends SourceConfiguration
{
return getRecordingAlias();
}
-
- public TunerChannel getTunerChannel()
- {
- /* Create a tuner channel with a default bandwidth of 15 kHz */
- return new TunerChannel( Type.LOCKED, mFrequency, 15000 );
- }
-
+
+ public TunerChannel getTunerChannel()
+ {
+ /* Create a tuner channel with a default bandwidth of 12.5 kHz */
+ return new TunerChannel(mFrequency, 12500);
+ }
+
}
diff --git a/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java b/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java
index a542e8c6..9e9db98e 100644
--- a/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java
+++ b/src/main/java/io/github/dsheirer/source/config/SourceConfigTuner.java
@@ -21,15 +21,14 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.source.SourceType;
-import io.github.dsheirer.source.tuner.TunerChannel;
-import io.github.dsheirer.source.tuner.TunerChannel.Type;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
import java.text.DecimalFormat;
@JsonSubTypes.Type(value = SourceConfigTuner.class, name = "sourceConfigTuner")
public class SourceConfigTuner extends SourceConfiguration
{
- private static DecimalFormat sFORMAT = new DecimalFormat("0.00000");
+ private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
private long mFrequency = 0;
private int mBandwidth = 12500;
@@ -62,12 +61,12 @@ public class SourceConfigTuner extends SourceConfiguration
@Override
public String getDescription()
{
- return sFORMAT.format((double)mFrequency / 1000000.0d) + " MHz";
+ return FREQUENCY_FORMAT.format((double)mFrequency / 1000000.0d) + " MHz";
}
@JsonIgnore
public TunerChannel getTunerChannel()
{
- return new TunerChannel(Type.LOCKED, mFrequency, mBandwidth);
+ return new TunerChannel(mFrequency, mBandwidth);
}
}
diff --git a/src/source/heartbeat/Heartbeat.java b/src/main/java/io/github/dsheirer/source/heartbeat/Heartbeat.java
similarity index 96%
rename from src/source/heartbeat/Heartbeat.java
rename to src/main/java/io/github/dsheirer/source/heartbeat/Heartbeat.java
index 993d51ee..2eef023f 100644
--- a/src/source/heartbeat/Heartbeat.java
+++ b/src/main/java/io/github/dsheirer/source/heartbeat/Heartbeat.java
@@ -16,7 +16,7 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.heartbeat;
+package io.github.dsheirer.source.heartbeat;
public class Heartbeat
{
diff --git a/src/source/heartbeat/HeartbeatManager.java b/src/main/java/io/github/dsheirer/source/heartbeat/HeartbeatManager.java
similarity index 92%
rename from src/source/heartbeat/HeartbeatManager.java
rename to src/main/java/io/github/dsheirer/source/heartbeat/HeartbeatManager.java
index f570171b..98ebfbd4 100644
--- a/src/source/heartbeat/HeartbeatManager.java
+++ b/src/main/java/io/github/dsheirer/source/heartbeat/HeartbeatManager.java
@@ -16,10 +16,10 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.heartbeat;
+package io.github.dsheirer.source.heartbeat;
-import sample.Broadcaster;
-import sample.Listener;
+import io.github.dsheirer.sample.Broadcaster;
+import io.github.dsheirer.sample.Listener;
public class HeartbeatManager
{
diff --git a/src/main/java/io/github/dsheirer/channel/heartbeat/IHeartbeatListener.java b/src/main/java/io/github/dsheirer/source/heartbeat/IHeartbeatListener.java
similarity index 95%
rename from src/main/java/io/github/dsheirer/channel/heartbeat/IHeartbeatListener.java
rename to src/main/java/io/github/dsheirer/source/heartbeat/IHeartbeatListener.java
index f3bcb319..149fd527 100644
--- a/src/main/java/io/github/dsheirer/channel/heartbeat/IHeartbeatListener.java
+++ b/src/main/java/io/github/dsheirer/source/heartbeat/IHeartbeatListener.java
@@ -16,7 +16,7 @@
* along with this program. If not, see
*
******************************************************************************/
-package io.github.dsheirer.channel.heartbeat;
+package io.github.dsheirer.source.heartbeat;
import io.github.dsheirer.sample.Listener;
diff --git a/src/source/heartbeat/IHeartbeatProvider.java b/src/main/java/io/github/dsheirer/source/heartbeat/IHeartbeatProvider.java
similarity index 95%
rename from src/source/heartbeat/IHeartbeatProvider.java
rename to src/main/java/io/github/dsheirer/source/heartbeat/IHeartbeatProvider.java
index 9941b716..076b50a2 100644
--- a/src/source/heartbeat/IHeartbeatProvider.java
+++ b/src/main/java/io/github/dsheirer/source/heartbeat/IHeartbeatProvider.java
@@ -16,7 +16,7 @@
* along with this program. If not, see
*
******************************************************************************/
-package source.heartbeat;
+package io.github.dsheirer.source.heartbeat;
public interface IHeartbeatProvider
{
diff --git a/src/main/java/io/github/dsheirer/source/mixer/RealMixerSource.java b/src/main/java/io/github/dsheirer/source/mixer/RealMixerSource.java
index 4135f790..6d6ef037 100644
--- a/src/main/java/io/github/dsheirer/source/mixer/RealMixerSource.java
+++ b/src/main/java/io/github/dsheirer/source/mixer/RealMixerSource.java
@@ -17,14 +17,11 @@
******************************************************************************/
package io.github.dsheirer.source.mixer;
-import io.github.dsheirer.channel.heartbeat.Heartbeat;
-import io.github.dsheirer.dsp.gain.AutomaticGainControl;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.adapter.ISampleAdapter;
import io.github.dsheirer.sample.real.RealBuffer;
import io.github.dsheirer.source.RealSource;
-import io.github.dsheirer.source.SourceException;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
+import io.github.dsheirer.source.SourceEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,7 +31,6 @@ import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
public class RealMixerSource extends RealSource
@@ -49,9 +45,6 @@ public class RealMixerSource extends RealSource
private AudioFormat mAudioFormat;
private int mBytesPerFrame = 0;
private ISampleAdapter mSampleAdapter;
- private AutomaticGainControl mAGC = new AutomaticGainControl();
- private Listener mHeartbeatListener;
- private static final Heartbeat HEARTBEAT = new Heartbeat();
private CopyOnWriteArrayList> mSampleListeners = new CopyOnWriteArrayList>();
@@ -76,44 +69,24 @@ public class RealMixerSource extends RealSource
mSampleAdapter = sampleAdapter;
}
- @Override
- public void setFrequencyChangeListener(Listener listener)
- {
- //Not implemented
- }
+ @Override
+ public void setSourceEventListener(Listener listener)
+ {
+ //Not implemented
+ }
- @Override
- public void removeFrequencyChangeListener()
- {
- //Not implemented
- }
+ @Override
+ public void removeSourceEventListener()
+ {
+ //Not implemented
+ }
- @Override
- public Listener getFrequencyChangeListener()
- {
- //Not implemented
- return null;
- }
-
- /**
- * Registers the listener to receive heartbeats from this source.
- *
- * @param listener to receive heartbeats
- */
- @Override
- public void setHeartbeatListener(Listener listener)
- {
- mHeartbeatListener = listener;
- }
-
- /**
- * Removes the currently registered heartbeat listener
- */
- @Override
- public void removeHeartbeatListener()
- {
- mHeartbeatListener = null;
- }
+ @Override
+ public Listener getSourceEventListener()
+ {
+ //Not implemented
+ return null;
+ }
@Override
public void reset()
@@ -121,7 +94,7 @@ public class RealMixerSource extends RealSource
}
@Override
- public void start(ScheduledExecutorService executor)
+ public void start()
{
}
@@ -176,11 +149,11 @@ public class RealMixerSource extends RealSource
}
}
- public int getSampleRate()
+ public double getSampleRate()
{
if(mTargetDataLine != null)
{
- return (int)mTargetDataLine.getFormat().getSampleRate();
+ return mTargetDataLine.getFormat().getSampleRate();
}
else
{
@@ -191,7 +164,7 @@ public class RealMixerSource extends RealSource
/**
* Returns the frequency of this source. Default is 0.
*/
- public long getFrequency() throws SourceException
+ public long getFrequency()
{
return mFrequency;
}
@@ -220,11 +193,7 @@ public class RealMixerSource extends RealSource
{
if(mRunning.compareAndSet(false, true))
{
- //Send a heartbeat every time we run
- if(mHeartbeatListener != null)
- {
- mHeartbeatListener.receive(HEARTBEAT);
- }
+ getHeartbeatManager().broadcast();
if(mTargetDataLine == null)
{
diff --git a/src/main/java/io/github/dsheirer/source/recording/Recording.java b/src/main/java/io/github/dsheirer/source/recording/Recording.java
index 2d8c74ff..1fb2495f 100644
--- a/src/main/java/io/github/dsheirer/source/recording/Recording.java
+++ b/src/main/java/io/github/dsheirer/source/recording/Recording.java
@@ -1,26 +1,37 @@
+/*******************************************************************************
+ * sdr-trunk
+ * Copyright (C) 2014-2017 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with this program.
+ * If not, see
+ *
+ ******************************************************************************/
package io.github.dsheirer.source.recording;
import io.github.dsheirer.settings.SettingsManager;
+import io.github.dsheirer.source.ISourceEventProcessor;
+import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.ITunerChannelProvider;
-import io.github.dsheirer.source.tuner.TunerChannel;
-import io.github.dsheirer.source.tuner.TunerChannelSource;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
+import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import java.io.File;
import java.util.ArrayList;
-import java.util.concurrent.RejectedExecutionException;
+import java.util.List;
-public class Recording implements Comparable,
- IFrequencyChangeProcessor,
- ITunerChannelProvider
+public class Recording implements Comparable, ISourceEventProcessor, ITunerChannelProvider
{
private RecordingConfiguration mConfiguration;
- private ArrayList mTunerChannels =
- new ArrayList();
+ private List mTunerChannels = new ArrayList();
@SuppressWarnings( "unused" )
private long mCenterFrequency;
@@ -65,8 +76,7 @@ public class Recording implements Comparable,
}
@Override
- public TunerChannelSource getChannel( TunerChannel channel )
- throws RejectedExecutionException, SourceException
+ public TunerChannelSource getChannel( TunerChannel channel )
{
// TODO Auto-generated method stub
return null;
@@ -89,9 +99,9 @@ public class Recording implements Comparable,
}
@Override
- public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException
+ public void process(SourceEvent event ) throws SourceException
{
- if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE )
+ if( event.getEvent() == SourceEvent.Event.NOTIFICATION_FREQUENCY_CHANGE )
{
long frequency = event.getValue().longValue();
@@ -103,7 +113,7 @@ public class Recording implements Comparable,
for( TunerChannelSource channel: mTunerChannels )
{
- channel.frequencyChanged( event );
+ channel.process( event );
}
}
}
diff --git a/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java b/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java
index 810b17e0..e9f280ed 100644
--- a/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java
+++ b/src/main/java/io/github/dsheirer/source/recording/RecordingSourceManager.java
@@ -22,8 +22,8 @@ import io.github.dsheirer.source.Source;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.config.SourceConfigRecording;
import io.github.dsheirer.source.config.SourceConfiguration;
-import io.github.dsheirer.source.tuner.TunerChannel;
-import io.github.dsheirer.source.tuner.TunerChannelSource;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
+import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,8 +33,7 @@ import java.util.List;
public class RecordingSourceManager
{
- private final static Logger mLog =
- LoggerFactory.getLogger( RecordingSourceManager.class );
+ private final static Logger mLog = LoggerFactory.getLogger( RecordingSourceManager.class );
private ArrayList mRecordings = new ArrayList();
@@ -50,12 +49,12 @@ public class RecordingSourceManager
* Iterates current recordings to get a tuner channel source for the frequency
* specified in the channel config's source config object
*/
- public Source getSource(SourceConfiguration config, int bandwidth )
+ public Source getSource( SourceConfiguration config, int bandwidth )
throws SourceException
{
TunerChannelSource retVal = null;
- if( config instanceof SourceConfigRecording)
+ if( config instanceof SourceConfigRecording )
{
SourceConfigRecording configRecording = (SourceConfigRecording)config;
diff --git a/src/main/java/io/github/dsheirer/source/tuner/ITunerChannelProvider.java b/src/main/java/io/github/dsheirer/source/tuner/ITunerChannelProvider.java
index 92cb95fc..550931c9 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/ITunerChannelProvider.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/ITunerChannelProvider.java
@@ -18,6 +18,8 @@
package io.github.dsheirer.source.tuner;
import io.github.dsheirer.source.SourceException;
+import io.github.dsheirer.source.tuner.channel.TunerChannel;
+import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import java.util.concurrent.RejectedExecutionException;
@@ -25,18 +27,14 @@ public interface ITunerChannelProvider
{
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
- *
- * @param frequency - desired frequency
- *
+ *
* @return - source for 48k sample rate
*/
- public abstract TunerChannelSource getChannel( TunerChannel channel )
+ public abstract TunerChannelSource getChannel(TunerChannel channel )
throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
- *
- * @param channel - previously obtained tuner channel
*/
public abstract void releaseChannel( TunerChannelSource source );
diff --git a/src/main/java/io/github/dsheirer/source/tuner/MixerTuner.java b/src/main/java/io/github/dsheirer/source/tuner/MixerTuner.java
deleted file mode 100644
index 586ef6e3..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/MixerTuner.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package io.github.dsheirer.source.tuner;
-
-import io.github.dsheirer.sample.Listener;
-import io.github.dsheirer.sample.adapter.ISampleAdapter;
-import io.github.dsheirer.sample.complex.ComplexBuffer;
-import io.github.dsheirer.sample.complex.ComplexSampleListListener;
-import io.github.dsheirer.source.mixer.ComplexMixer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.sound.sampled.TargetDataLine;
-import java.util.ArrayList;
-
-public abstract class MixerTuner extends Tuner implements Listener
-{
- private final static Logger mLog =
- LoggerFactory.getLogger( MixerTuner.class );
-
- protected MixerTunerType mMixerTunerType;
- protected ComplexMixer mComplexMixer;
- protected ArrayList mListeners =
- new ArrayList();
-
- /**
- * Wrapper class to add basic Tuner functionality to the ComplexMixer.
- * Subclasses should couple this functionality with a tuner controller class
- * to support tuning.
- */
- public MixerTuner( String name,
- TunerController tunerController,
- MixerTunerType mixerTunerType,
- TargetDataLine targetDataLine,
- ISampleAdapter sampleAdapter )
- {
- super( name, tunerController );
-
- mMixerTunerType = mixerTunerType;
-
- mComplexMixer = new ComplexMixer( targetDataLine,
- mMixerTunerType.getAudioFormat(),
- name,
- sampleAdapter,
- (Listener)this );
- }
-
- public MixerTuner( String name,
- TunerController tunerController,
- MixerTunerDataLine mixerTuner,
- ISampleAdapter sampleAdapter )
- {
- this( name,
- tunerController,
- mixerTuner.getMixerTunerType(),
- mixerTuner.getTargetDataLine(),
- sampleAdapter );
- }
-
- public void dispose()
- {
- mComplexMixer.stop();
-
- super.dispose();
- }
-
- public MixerTunerType getMixerTunerType()
- {
- return mMixerTunerType;
- }
-
- public TargetDataLine getTargetDataLine()
- {
- return mComplexMixer.getTargetDataLine();
- }
-
- /**
- * Dispatches the received complex buffers from the mComplexMixer to all
- * listeners registered on the parent Tuner class
- */
- @Override
- public void receive( ComplexBuffer buffer )
- {
- super.broadcast( buffer );
- }
-
- /**
- * Overrides the parent Tuner class listener management method to allow
- * starting the buffer processing thread
- */
- @Override
- public void addListener( Listener listener )
- {
- super.addListener( listener );
-
- /* We can call start multiple times -- it ignores additional requests */
- mComplexMixer.start();
- }
-
- /**
- * Overrides the parent Tuner class listener management method to allow
- * stopping the buffer processing thread when there are no more listeners
- */
- @Override
- public void removeListener( Listener listener )
- {
- super.removeListener( listener );
-
- if( mSampleListeners.isEmpty() )
- {
- mComplexMixer.stop();
- }
- }
-}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java
index 8b74a58d..70e02d71 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java
@@ -17,196 +17,145 @@
******************************************************************************/
package io.github.dsheirer.source.tuner;
+import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
-import io.github.dsheirer.sample.complex.ComplexBuffer;
-import io.github.dsheirer.source.SourceException;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor;
+import io.github.dsheirer.source.ISourceEventProcessor;
+import io.github.dsheirer.source.SourceEvent;
+import io.github.dsheirer.source.tuner.TunerEvent.Event;
+import io.github.dsheirer.source.tuner.manager.AbstractSourceManager;
+import io.github.dsheirer.source.tuner.manager.TunerSourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.RejectedExecutionException;
-
/**
* Tuner - provides tuner channel sources, representing a channel frequency
*/
-public abstract class Tuner implements ITunerChannelProvider
+public abstract class Tuner
{
- private final static Logger mLog = LoggerFactory.getLogger( Tuner.class );
-
- private String mName;
- private TunerController mTunerController;
- /**
- * Sample Listeners - these will typically be the DFT processor for spectral
- * display, or it will be one or more tuner channel sources
- */
- protected List> mSampleListeners =
- new CopyOnWriteArrayList<>();
-
- protected List> mTunerChangeListeners =
- new ArrayList<>();
-
- public Tuner( String name, TunerController tunerController )
- {
- mName = name;
- mTunerController = tunerController;
+ private final static Logger mLog = LoggerFactory.getLogger(Tuner.class);
- //Rebroadcast frequency and sample rate change events as tuner events
- mTunerController.addListener( new IFrequencyChangeProcessor()
- {
- @Override
- public void frequencyChanged( FrequencyChangeEvent event )
- {
- switch( event.getEvent() )
- {
- case NOTIFICATION_FREQUENCY_CHANGE:
- broadcast( new TunerEvent( Tuner.this,
- TunerEvent.Event.FREQUENCY ));
- break;
- case NOTIFICATION_SAMPLE_RATE_CHANGE:
- broadcast( new TunerEvent( Tuner.this,
- TunerEvent.Event.SAMPLE_RATE ));
- break;
- default:
- break;
- }
- }
- } );
- }
-
- public TunerController getTunerController()
- {
- return mTunerController;
- }
-
- public String toString()
- {
- return mName;
- }
-
- public void dispose()
- {
- mSampleListeners.clear();
- }
-
- public void setName( String name )
- {
- mName = name;
- }
-
- /**
- * Unique identifier for this tuner, used to lookup a tuner configuration
- * from the settings manager.
- *
- * @return - unique identifier like a serial number, or a usb bus location
- * or ip address and port. Return some form of unique identification that
- * allows this tuner to be identified from among several of the same types
- * of tuners.
- */
- public abstract String getUniqueID();
+ protected Broadcaster mTunerEventBroadcaster = new Broadcaster<>();
+ private TunerController mTunerController;
+ private String mName;
+ private AbstractSourceManager mTunerSourceManager;
- /**
- * @return - tuner class enum entry
- */
- public abstract TunerClass getTunerClass();
-
- /**
- * @return - tuner type enum entry
- */
- public abstract TunerType getTunerType();
-
- /**
- * Name of this tuner object
- * @return - string name of this tuner object
- */
- public String getName()
- {
- return mName;
- }
-
- /**
- * Sample size in bits
- */
- public abstract double getSampleSize();
-
- /**
- * Returns a tuner frequency channel source, tuned to the correct frequency
- *
- * Note: ensure the concrete implementation registers the returned
- * tuner channel source as a listener on the tuner, to receive frequency
- * change events.
- *
- * @param frequency - desired frequency
- *
- * @return - source for 48k sample rate
- */
- public abstract TunerChannelSource getChannel( TunerChannel channel )
- throws RejectedExecutionException, SourceException;
-
- /**
- * Releases the tuned channel resources
- *
- * Note: ensure the concrete implementation unregisters the tuner channel
- * source from the tuner as a frequency change listener
- *
- * @param channel - previously obtained tuner channel
- */
- public abstract void releaseChannel( TunerChannelSource source );
-
- /**
- * Registers the listener to receive complex float sample arrays
- */
- public void addListener( Listener listener )
+ /**
+ * Abstract tuner class.
+ * @param name of the tuner
+ * @param tunerController for the tuner
+ */
+ public Tuner(String name, TunerController tunerController)
{
- mSampleListeners.add( listener );
- }
+ mName = name;
+ mTunerController = tunerController;
- /**
- * Removes the registered listener
- */
- public void removeListener( Listener listener )
- {
- mSampleListeners.remove( listener );
+ mTunerSourceManager = new TunerSourceManager(mTunerController);
+
+ //Rebroadcast frequency and sample rate change events as tuner events
+ mTunerController.addListener(new ISourceEventProcessor()
+ {
+ @Override
+ public void process(SourceEvent event)
+ {
+ switch(event.getEvent())
+ {
+ case NOTIFICATION_FREQUENCY_CHANGE:
+ broadcast(new TunerEvent(Tuner.this, Event.FREQUENCY));
+ break;
+ case NOTIFICATION_SAMPLE_RATE_CHANGE:
+ broadcast(new TunerEvent(Tuner.this, Event.SAMPLE_RATE));
+ break;
+ default:
+ break;
+ }
+ }
+ });
}
/**
- * Broadcasts the samples to all registered listeners
+ * Source Manager. Provides access to registering for complex buffer samples and source event notifications.
*/
- public void broadcast( ComplexBuffer sampleBuffer )
+ public AbstractSourceManager getSourceManager()
{
- for( Listener listener: mSampleListeners )
- {
- listener.receive( sampleBuffer );
- }
+ return mTunerSourceManager;
}
- /**
- * Registers the listener
- */
- public void addTunerChangeListener( Listener listener )
+ public TunerController getTunerController()
{
- mTunerChangeListeners.add( listener );
+ return mTunerController;
}
- /**
- * Removes the registered listener
- */
- public void removeTunerChangeListener( Listener listener )
+ public String toString()
{
- mTunerChangeListeners.remove( listener );
+ return mName;
+ }
+
+ public void dispose()
+ {
+ }
+
+ public void setName(String name)
+ {
+ mName = name;
+ }
+
+ /**
+ * Unique identifier for this tuner, used to lookup a tuner configuration
+ * from the settings manager.
+ *
+ * @return - unique identifier like a serial number, or a usb bus location
+ * or ip address and port. Return some form of unique identification that
+ * allows this tuner to be identified from among several of the same types
+ * of tuners.
+ */
+ public abstract String getUniqueID();
+
+ /**
+ * @return - tuner class enum entry
+ */
+ public abstract TunerClass getTunerClass();
+
+ /**
+ * @return - tuner type enum entry
+ */
+ public abstract TunerType getTunerType();
+
+ /**
+ * Name of this tuner object
+ *
+ * @return - string name of this tuner object
+ */
+ public String getName()
+ {
+ return mName;
+ }
+
+ /**
+ * Sample size in bits
+ */
+ public abstract double getSampleSize();
+
+ /**
+ * Registers the listener
+ */
+ public void addTunerChangeListener(Listener listener)
+ {
+ mTunerEventBroadcaster.addListener(listener);
+ }
+
+ /**
+ * Removes the registered listener
+ */
+ public void removeTunerChangeListener(Listener listener)
+ {
+ mTunerEventBroadcaster.removeListener(listener);
}
/**
* Broadcasts the tuner change event
*/
- public void broadcast( TunerEvent event )
+ public void broadcast(TunerEvent tunerEvent)
{
- for( Listener listener: mTunerChangeListeners )
- {
- listener.receive( event );
- }
+ mTunerEventBroadcaster.broadcast(tunerEvent);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerChannel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerChannel.java
deleted file mode 100644
index cb17482a..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/TunerChannel.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014,2015 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package io.github.dsheirer.source.tuner;
-
-public class TunerChannel implements Comparable
-{
- private Type mType;
- private long mFrequency;
- private int mBandwidth;
-
- public TunerChannel( Type type, long frequency, int bandwidth )
- {
- mType = type;
- mFrequency = frequency;
- mBandwidth = bandwidth;
- }
-
- public Type getType()
- {
- return mType;
- }
-
- public void setType( Type type )
- {
- mType = type;
- }
-
- public long getFrequency()
- {
- return mFrequency;
- }
-
- public void setFrequency( long frequency )
- {
- mFrequency = frequency;
- }
-
- public int getBandwidth()
- {
- return mBandwidth;
- }
-
- public void setBandwidth( int bandwidth )
- {
- mBandwidth = bandwidth;
- }
-
- public long getMinFrequency()
- {
- return mFrequency - ( mBandwidth / 2 );
- }
-
- public long getMaxFrequency()
- {
- return mFrequency + ( mBandwidth / 2 );
- }
-
- /**
- * Indicates if any part of this tuner channel overlaps the specified range
- */
- public boolean overlaps( long minimum, long maximum )
- {
- long channelMin = getMinFrequency();
- long channelMax = getMaxFrequency();
-
- return ( ( minimum <= channelMin && channelMin <= maximum ) ||
- ( minimum <= channelMax && channelMax <= maximum ) ||
- ( minimum <= channelMin && channelMax <= maximum ) ||
- ( channelMin <= minimum && maximum <= channelMax ) );
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "Channel: " );
- sb.append( getMinFrequency() );
- sb.append( "-" );
- sb.append( getMaxFrequency() );
-
- return sb.toString();
- }
-
- @Override
- public int compareTo( TunerChannel otherLock )
- {
- if( this.equals( otherLock ) )
- {
- return 0;
- }
- else if( mFrequency < otherLock.getFrequency() )
- {
- return -1;
- }
- else
- {
- return 1;
- }
- }
-
- public enum Type
- {
- LOCKED, //Use for decoding channels, so that they cannot be tuned out of range
- TRAFFIC, //Like locked, but for temporary allocated traffic channels
- TUNABLE; //Tunable -- can be tuned out of range
- }
-}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerChannelProvider.java b/src/main/java/io/github/dsheirer/source/tuner/TunerChannelProvider.java
deleted file mode 100644
index e1de1a12..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/TunerChannelProvider.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.github.dsheirer.source.tuner;
-
-import io.github.dsheirer.source.SourceException;
-
-import java.util.concurrent.RejectedExecutionException;
-
-public interface TunerChannelProvider
-{
- /**
- * Returns a tuner frequency channel source, tuned to the correct frequency
- *
- * @param frequency - desired frequency
- *
- * @return - source for 48k sample rate
- */
- public abstract TunerChannelSource getChannel( TunerChannel channel )
- throws RejectedExecutionException, SourceException;
-
- /**
- * Releases the tuned channel resources
- *
- * @param channel - previously obtained tuner channel
- */
- public abstract void releaseChannel( TunerChannelSource source );
-
-
-}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java
deleted file mode 100644
index c577789e..00000000
--- a/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java
+++ /dev/null
@@ -1,572 +0,0 @@
-/*******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2017 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- ******************************************************************************/
-package io.github.dsheirer.source.tuner;
-
-import io.github.dsheirer.channel.heartbeat.Heartbeat;
-import io.github.dsheirer.dsp.filter.FilterFactory;
-import io.github.dsheirer.dsp.filter.Window.WindowType;
-import io.github.dsheirer.dsp.filter.cic.ComplexPrimeCICDecimate;
-import io.github.dsheirer.dsp.mixer.Oscillator;
-import io.github.dsheirer.sample.Buffer;
-import io.github.dsheirer.sample.Listener;
-import io.github.dsheirer.sample.OverflowableTransferQueue;
-import io.github.dsheirer.sample.complex.Complex;
-import io.github.dsheirer.sample.complex.ComplexBuffer;
-import io.github.dsheirer.sample.real.IOverflowListener;
-import io.github.dsheirer.source.ComplexSource;
-import io.github.dsheirer.source.SourceException;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
-import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeListener;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor;
-import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class TunerChannelSource extends ComplexSource implements IFrequencyChangeProcessor, Listener
-{
- private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class);
-
- //Maximum number of filled buffers for the blocking queue
- private static final int BUFFER_MAX_CAPACITY = 300;
-
- //Threshold for resetting buffer overflow condition
- private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 100;
-
- private static int CHANNEL_RATE = 48000;
- private static int CHANNEL_PASS_FREQUENCY = 12000;
- private static final Heartbeat HEARTBEAT = new Heartbeat();
-
- private OverflowableTransferQueue mBuffer;
-
- private Tuner mTuner;
- private TunerChannel mTunerChannel;
- private Oscillator mMixer;
- private ComplexPrimeCICDecimate mDecimationFilter;
- private Listener mListener;
- private Listener mHeartbeatListener;
- private IFrequencyChangeProcessor mFrequencyChangeProcessor;
- private DownstreamProcessor mDownstreamFrequencyEventProcessor = new DownstreamProcessor();
- private ScheduledFuture> mTaskHandle;
-
- private long mTunerFrequency = 0;
- private int mTunerSampleRate;
- private int mChannelFrequencyCorrection = 0;
-
- private DecimationProcessor mDecimationProcessor = new DecimationProcessor();
-
- private AtomicBoolean mRunning = new AtomicBoolean();
- private boolean mExpended = false;
-
- /**
- * Provides a Digital Drop Channel (DDC) to decimate the IQ output from a
- * tuner down to a 48 kHz IQ channel rate.
- *
- * Note: this class can only be used once (started and stopped) and a new
- * tuner channel source must be requested from the tuner once this object
- * has been stopped. This is because channels are managed dynamically and
- * center tuned frequency may have changed since this source was obtained
- * and thus the tuner might no longer be able to source this channel once it
- * has been stopped.
- *
- * @param tuner to obtain wideband IQ samples from
- * @param tunerChannel specifying the center frequency for the DDC
- * @throws RejectedExecutionException if the thread pool manager cannot
- * accept the decimation processing task
- * @throws SourceException if the tuner has an issue providing IQ samples
- */
- public TunerChannelSource(Tuner tuner, TunerChannel tunerChannel) throws RejectedExecutionException, SourceException
- {
- mTuner = tuner;
- mTunerChannel = tunerChannel;
- mTuner.getTunerController().addListener((IFrequencyChangeProcessor) this);
- mTunerFrequency = mTuner.getTunerController().getFrequency();
-
- mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
-
- /* Setup the frequency translator to the current source frequency */
- long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
-
- mMixer = new Oscillator(frequencyOffset, mTuner.getTunerController().getSampleRate());
-
- /* Fire a sample rate change event to setup the decimation chain */
- frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE,
- mTuner.getTunerController().getSampleRate()));
- }
-
- /**
- * Overrides the default source overflow listener management to delegate responsibility to the overflow buffer
- */
- @Override
- public void setOverflowListener(IOverflowListener listener)
- {
- mBuffer.setOverflowListener(listener);
- }
-
- public void start(ScheduledExecutorService executor)
- {
- if(mExpended)
- {
- throw new IllegalStateException("Attempt to re-start an expended tuner channel source. TunerChannelSource" +
- " objects can only be used once. ");
- }
-
- if(mRunning.compareAndSet(false, true))
- {
- //Broadcast current frequency and sample rate settings so all downstream components are aware
- mDownstreamFrequencyEventProcessor.broadcastCurrentFrequency();
- mDownstreamFrequencyEventProcessor.broadcastCurrentSampleRate();
-
- //Schedule the decimation task to run every 9 ms (111 iterations/second), an odd periodicity relative
- //to the inbound periodicity of 20 ms, to attempt to avoid thread queue contention
- mTaskHandle = executor.scheduleAtFixedRate(mDecimationProcessor, 0, 9, TimeUnit.MILLISECONDS);
-
- /* Finally, register to receive samples from the tuner */
- mTuner.addListener((Listener) this);
- }
- else
- {
- mLog.warn("Attempt to start() an already running tuner channel source was ignored");
- }
- }
-
- @Override
- public void reset()
- {
- }
-
- @Override
- public void stop()
- {
- if(mRunning.compareAndSet(true, false))
- {
- mTuner.releaseChannel(this);
- mDecimationProcessor.shutdown();
-
- if(mTaskHandle != null)
- {
- mTaskHandle.cancel(true);
- mTaskHandle = null;
- }
-
- mBuffer.clear();
-
- mExpended = true;
- }
- else
- {
- mLog.warn("Attempt to stop() an already stopped tuner channel source was ignored");
- }
- }
-
-
- @Override
- public void dispose()
- {
- if(!mRunning.get())
- {
- /* Tell the tuner to release/unregister our resources */
- mTuner.getTunerController().removeListener(this);
- }
- }
-
- /**
- * Changes the frequency correction value and broadcasts the change to the registered downstream listener.
- * @param correction current frequency correction value.
- */
- private void setFrequencyCorrection(int correction)
- {
- mChannelFrequencyCorrection = correction;
-
- updateMixerFrequencyOffset();
-
- mDownstreamFrequencyEventProcessor.broadcast(
- new FrequencyChangeEvent( Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, mChannelFrequencyCorrection));
- }
-
- public Tuner getTuner()
- {
- return mTuner;
- }
-
- public TunerChannel getTunerChannel()
- {
- return mTunerChannel;
- }
-
- @Override
- public void receive(ComplexBuffer buffer)
- {
- if(mRunning.get())
- {
- mBuffer.offer(buffer);
- }
- }
-
- public void setFrequencyChangeListener(IFrequencyChangeProcessor processor)
- {
- mFrequencyChangeProcessor = processor;
- }
-
- @Override
- public void setListener(Listener listener)
- {
- /* Save a pointer to the listener so that if we have to change the
- * decimation filter, we can re-add the listener */
- mListener = listener;
-
- mDecimationFilter.setListener(listener);
- }
-
- @Override
- public void removeListener(Listener listener)
- {
- mDecimationFilter.removeListener();
- }
-
- /**
- * Handler for frequency change events received from the tuner and channel
- * frequency correction events received from the channel consumer/listener
- */
- @Override
- public void frequencyChanged(FrequencyChangeEvent event) throws SourceException
- {
- // Echo the event to the registered event listener
- if(mFrequencyChangeProcessor != null)
- {
- mFrequencyChangeProcessor.frequencyChanged(event);
- }
-
- switch(event.getEvent())
- {
- case NOTIFICATION_FREQUENCY_CHANGE:
- mTunerFrequency = event.getValue().longValue();
- updateMixerFrequencyOffset();
-
- //Reset frequency correction so that downstream components can recalculate the value
- setFrequencyCorrection(0);
- break;
- case NOTIFICATION_SAMPLE_RATE_CHANGE:
- int sampleRate = event.getValue().intValue();
- setSampleRate(sampleRate);
- break;
- default:
- break;
- }
- }
-
- /**
- * Updates the sample rate to the requested value and notifies any downstream components of the change
- * @param sampleRate to set
- */
- private void setSampleRate(int sampleRate)
- {
- if(mTunerSampleRate != sampleRate)
- {
- mMixer.setSampleRate(sampleRate);
-
- /* Get new decimation filter */
- mDecimationFilter = FilterFactory.getDecimationFilter(sampleRate, CHANNEL_RATE, 1,
- CHANNEL_PASS_FREQUENCY, 60, WindowType.HAMMING);
-
- /* re-add the original output listener */
- mDecimationFilter.setListener(mListener);
-
- mTunerSampleRate = sampleRate;
-
- mDownstreamFrequencyEventProcessor.broadcastCurrentSampleRate();
- }
- }
-
- /**
- * Calculates the local mixer frequency offset from the tuned frequency,
- * channel's requested frequency, and channel frequency correction.
- */
- private void updateMixerFrequencyOffset()
- {
- long offset = mTunerFrequency - mTunerChannel.getFrequency() - mChannelFrequencyCorrection;
- mMixer.setFrequency(offset);
- }
-
- public int getSampleRate() throws SourceException
- {
- return CHANNEL_RATE;
- }
-
- public long getFrequency() throws SourceException
- {
- return mTunerChannel.getFrequency();
- }
-
- /**
- * Implements IFrequencyChangeProvider to enable this source to broadcast frequency change events to downstream
- * listeners.
- *
- * @param listener to receive downstream events
- */
- @Override
- public void setFrequencyChangeListener(Listener listener)
- {
- mDownstreamFrequencyEventProcessor.setFrequencyChangeListener(listener);
- }
-
- /**
- * Implements IFrequencyChangeProvider to remove the frequency change listener from receiving down-stream frequency
- * change events.
- */
- @Override
- public void removeFrequencyChangeListener()
- {
- mDownstreamFrequencyEventProcessor.removeFrequencyChangeListener();
- }
-
- /**
- * Implements IFrequencyChangeListener to receive frequency change events containing requests from downstream
- * listeners to change frequency values.
- * @return listener
- */
- @Override
- public Listener getFrequencyChangeListener()
- {
- return mDownstreamFrequencyEventProcessor.getFrequencyChangeListener();
- }
-
- /**
- * Registers the listener to receive heartbeats from this source.
- * @param listener to receive heartbeats
- */
- @Override
- public void setHeartbeatListener(Listener listener)
- {
- mHeartbeatListener = listener;
- }
-
- /**
- * Removes the currently registered heartbeat listener
- */
- @Override
- public void removeHeartbeatListener()
- {
- mHeartbeatListener = null;
- }
-
- /**
- * Managers frequency change requests and notifications from/to any downstream component. Downstream
- * components are those that receive samples from this tuner channel source. These downstream components will be
- * notified of any frequency or sample rate change events and will also be able to request frequency correction
- * updates.
- */
- public class DownstreamProcessor implements IFrequencyChangeListener, IFrequencyChangeProvider,
- Listener
- {
- //Listener to receive downstream events
- private Listener