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 mListener; - - /** - * Broadcasts the frequency change event to the downstream frequency change listener - * @param event to broadcast - */ - public void broadcast(FrequencyChangeEvent event) - { - if(mListener != null) - { - mListener.receive(event); - } - } - - /** - * Broadcasts the current frequency of this tuner channel source to the downstream listener - */ - public void broadcastCurrentFrequency() - { - try - { - long frequency = getFrequency(); - broadcast(new FrequencyChangeEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, frequency)); - } - catch(SourceException se) - { - mLog.error("Error obtaining frequency from tuner to broadcast downstream"); - } - } - - /** - * Broadcasts the current decimated sample rate of this tuner channel source - */ - public void broadcastCurrentSampleRate() - { - try - { - //Note: downstream sample rate is currently a fixed value -- it will change in the future - broadcast(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, getSampleRate())); - } - catch(SourceException se) - { - mLog.error("Error obtaining sample rate from tuner to broadcast downstream"); - } - } - - /** - * Sets the downstream listener to receive frequency change events from this tuner channel source - * @param listener to receive events - */ - @Override - public void setFrequencyChangeListener(Listener listener) - { - mListener = listener; - } - - /** - * Removes the downstream listener from receiving frequency change events. - */ - @Override - public void removeFrequencyChangeListener() - { - mListener = null; - } - - /** - * Listener for receiving frequency change events from downstream components - */ - @Override - public Listener getFrequencyChangeListener() - { - return this; - } - - /** - * Processes frequency change events from downstream components. - * @param event to process - */ - @Override - public void receive(FrequencyChangeEvent event) - { - switch(event.getEvent()) - { - //Frequency correction requests are the only change requests supported from downstream components - case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE: - setFrequencyCorrection(event.getValue().intValue()); - break; - } - } - } - - /** - * Decimates an inbound buffer of I/Q samples from the source down to the - * standard 48000 channel sample rate - */ - public class DecimationProcessor implements Runnable - { - private boolean mProcessing = true; - private List mSampleBuffers = new ArrayList(); - - public void shutdown() - { - mProcessing = false; - } - - @Override - public void run() - { - /* General exception handler so that any errors won't kill the - * decimation thread and cause the input buffers to fill up and - * run the program out of memory */ - try - { - if(mProcessing) - { - //Send a heartbeat every time this runs to allow downstream components to perform periodic - //state monitoring functions on this thread - if(mHeartbeatListener != null) - { - mHeartbeatListener.receive(HEARTBEAT); - } - - mBuffer.drainTo(mSampleBuffers, 20); - - for(Buffer buffer : mSampleBuffers) - { - /* Check to see if we've been shutdown */ - if(!mProcessing) - { - mBuffer.clear(); - return; - } - else - { - float[] samples = buffer.getSamples(); - - /* We make a copy of the buffer so that we don't affect - * anyone else that is using the same buffer, like other - * channels or the spectral display */ - float[] translated = new float[samples.length]; - - /* Perform frequency translation */ - for(int x = 0; x < samples.length; x += 2) - { - mMixer.rotate(); - - translated[x] = Complex.multiplyInphase( - samples[x], samples[x + 1], mMixer.inphase(), mMixer.quadrature()); - - translated[x + 1] = Complex.multiplyQuadrature( - samples[x], samples[x + 1], mMixer.inphase(), mMixer.quadrature()); - } - - if(mProcessing) - { - final ComplexPrimeCICDecimate filter = mDecimationFilter; - filter.receive(new ComplexBuffer(translated)); - } - } - } - - mSampleBuffers.clear(); - } - } - catch(Exception e) - { - /* Only log the stack trace if we're still processing */ - if(mProcessing) - { - mLog.error("Error encountered during decimation process", e); - } - } - catch(Throwable throwable) - { - mLog.error("Code error encountered during decimation process - channel thread will probably die", throwable); - } - - /* Check to see if we've been shutdown */ - if(!mProcessing) - { - mBuffer.clear(); - mSampleBuffers.clear(); - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java b/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java index c255fc4b..2086d137 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerClass.java @@ -51,7 +51,8 @@ public enum TunerClass TERRATEC_NOXON_REV2( TunerType.ELONICS_E4000, "0CCD", "00E0", "Terratec", "NOXON R2" ), TERRATEC_T_STICK_PLUS( TunerType.ELONICS_E4000, "0CCD", "00D7", "Terratec", "T Stick Plus" ), TWINTECH_UT40( TunerType.FITIPOWER_FC0013, "1B80", "D3A4", "Twintech", "UT-40" ), - ZAAPA_ZTMINDVBZP( TunerType.FITIPOWER_FC0012, "1B80", "D398", "Zaapa", "ZT-MINDVBZP" ), + ZAAPA_ZTMINDVBZP( TunerType.FITIPOWER_FC0012, "1B80", "D398", "Zaapa", "ZT-MINDVBZP" ), + TEST_TUNER(TunerType.TEST, "0", "0", "ABC Tuners Inc.", "Model XYZ"), UNKNOWN( TunerType.UNKNOWN, "0", "0", "Unknown Manufacturer", "Unknown Device" ); private TunerType mTunerType; diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java index 116459ce..0076862f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java @@ -17,13 +17,21 @@ ******************************************************************************/ 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.sample.complex.IComplexBufferProvider; +import io.github.dsheirer.source.ISourceEventListener; +import io.github.dsheirer.source.ISourceEventProcessor; +import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.SourceEventListenerToProcessorAdapter; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.channel.CICTunerChannelSource; +import io.github.dsheirer.source.tuner.channel.TunerChannel; +import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event; import io.github.dsheirer.source.tuner.frequency.FrequencyController; import io.github.dsheirer.source.tuner.frequency.FrequencyController.Tunable; -import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,15 +39,18 @@ import java.util.SortedSet; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.RejectedExecutionException; -public abstract class TunerController implements Tunable, IFrequencyChangeProcessor +public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener, + IComplexBufferProvider, Listener { private final static Logger mLog = LoggerFactory.getLogger(TunerController.class); + protected Broadcaster mSampleBroadcaster = new Broadcaster<>(); /* List of currently tuned channels being served to demod channels */ private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); protected FrequencyController mFrequencyController; private int mMiddleUnusable; private double mUsableBandwidthPercentage; + private Listener mSourceEventListener; /** * Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels @@ -57,6 +68,16 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces mFrequencyController = new FrequencyController(this, minimumFrequency, maximumFrequency, 0.0d); mMiddleUnusable = middleUnusable; mUsableBandwidthPercentage = usableBandwidth; + mSourceEventListener = new SourceEventListenerToProcessorAdapter(this); + } + + /** + * Implements the ISourceEventListener interface to receive requests from sample consumers + */ + @Override + public Listener getSourceEventListener() + { + return mSourceEventListener; } /** @@ -68,11 +89,28 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces * Responds to requests to set the frequency */ @Override - public void frequencyChanged(FrequencyChangeEvent event) throws SourceException + public void process(SourceEvent sourceEvent ) throws SourceException { - if(event.getEvent() == Event.REQUEST_FREQUENCY_CHANGE) + switch(sourceEvent.getEvent()) { - setFrequency(event.getValue().longValue()); + case REQUEST_FREQUENCY_CHANGE: + setFrequency( sourceEvent.getValue().longValue() ); + break; + case REQUEST_START_SAMPLE_STREAM: + if(sourceEvent.getSource() instanceof Listener) + { + addComplexBufferListener((Listener)sourceEvent.getSource()); + } + break; + case REQUEST_STOP_SAMPLE_STREAM: + if(sourceEvent.getSource() instanceof Listener) + { + removeComplexBufferListener((Listener)sourceEvent.getSource()); + } + break; + default: + mLog.error("Ignoring unrecognized source event: " + sourceEvent.getEvent().name() + " from [" + + sourceEvent.getSource().getClass() + "]" ); } } @@ -102,9 +140,15 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces return mFrequencyController.getFrequency(); } - public int getSampleRate() + @Override + public boolean canTune(long frequency) { - return mFrequencyController.getBandwidth(); + return mFrequencyController.canTune(frequency); + } + + public double getSampleRate() + { + return mFrequencyController.getSampleRate(); } public double getFrequencyCorrection() @@ -210,7 +254,7 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces updateLOFrequency(); } - source = new TunerChannelSource(tuner, channel); + source = new CICTunerChannelSource(tuner, channel); } catch(SourceException se) { @@ -351,11 +395,13 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces } /** - * Sets the listener to be notified any time that the tuner changes frequency or bandwidth/sample rate. + * Sets the listener to be notified any time that the tuner changes frequency + * or bandwidth/sample rate. * - * Note: this is normally used by the Tuner. Any additional listeners can be registered on the tuner. + * Note: this is normally used by the Tuner. Any additional listeners can + * be registered on the tuner. */ - public void addListener(IFrequencyChangeProcessor processor) + public void addListener( ISourceEventProcessor processor ) { mFrequencyController.addListener(processor); } @@ -363,8 +409,52 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces /** * Removes the frequency change listener */ - public void removeListener(IFrequencyChangeProcessor processor) + public void removeListener( ISourceEventProcessor processor ) { mFrequencyController.removeFrequencyChangeProcessor(processor); } -} + + /** + * Adds the listener to receive complex buffer samples + */ + @Override + public void addComplexBufferListener(Listener listener) + { + mSampleBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving complex buffer samples + */ + @Override + public void removeComplexBufferListener(Listener listener) + { + mSampleBroadcaster.removeListener(listener); + } + + /** + * Indicates if there are any complex buffer listeners registered on this controller + */ + @Override + public boolean hasComplexBufferListeners() + { + return mSampleBroadcaster.hasListeners(); + } + + /** + * Broadcasts the buffer to any registered listeners + */ + protected void broadcast(ComplexBuffer complexBuffer) + { + mSampleBroadcaster.broadcast(complexBuffer); + } + + /** + * Implements the Listener interface to receive and distribute complex buffers from subclass implementations + */ + @Override + public void receive(ComplexBuffer complexBuffer) + { + broadcast(complexBuffer); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java index 4d30bd8a..e9c6a3f3 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerEditor.java @@ -1,19 +1,17 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.tuner; @@ -29,11 +27,18 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.RowFilter; +import javax.swing.ScrollPaneConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableRowSorter; -import java.awt.*; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -54,13 +59,13 @@ public class TunerEditor extends Editor private JScrollPane mEditorScroller; private Editor mEditor = new EmptyEditor<>( "a tuner" ); private JideSplitPane mEditorSplitPane = new JideSplitPane(); - + public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) { mTunerConfigurationModel = tunerConfigurationModel; init(); } - + public void init() { setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]" ) ); @@ -69,7 +74,7 @@ public class TunerEditor extends Editor listPanel.setLayout( new MigLayout( "fill,wrap 3", "[grow,fill]", "[][][][grow,fill][]" ) ); listPanel.add( mSelectedTunerType, "span" ); - + mFrequencyControl.setEnabled( false ); listPanel.add( mFrequencyControl, "span" ); @@ -85,7 +90,7 @@ public class TunerEditor extends Editor if( !event.getValueIsAdjusting() ) { int row = mTunerConfigurationTable.getSelectedRow(); - + if( row >= 0 ) { int modelRow = mTunerConfigurationTable.convertRowIndexToModel( row ); @@ -100,8 +105,8 @@ public class TunerEditor extends Editor } ); JScrollPane tableScroller = new JScrollPane( mTunerConfigurationTable ); - tableScroller.setHorizontalScrollBarPolicy( - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); + tableScroller.setHorizontalScrollBarPolicy( + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); listPanel.add( new JLabel( "Tuner Configurations" ), "span" ); listPanel.add( tableScroller, "span" ); @@ -113,14 +118,14 @@ public class TunerEditor extends Editor public void actionPerformed( ActionEvent e ) { Tuner tuner = getItem(); - + String name = mTunerConfigurationModel .getDistinctName( tuner.getTunerType(), tuner.getUniqueID() ); - + TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration( tuner.getTunerType(), - tuner.getUniqueID(), name ); - + .getTunerConfiguration( tuner.getTunerType(), + tuner.getUniqueID(), name ); + mTunerConfigurationModel.addTunerConfiguration( config ); } } ); @@ -133,21 +138,21 @@ public class TunerEditor extends Editor @Override public void actionPerformed( ActionEvent e ) { - int choice = JOptionPane.showConfirmDialog( TunerEditor.this, - "Do you want to delete this tuner configuration?", - "Delete Tuner Configuration?", JOptionPane.YES_NO_OPTION ); - + int choice = JOptionPane.showConfirmDialog( TunerEditor.this, + "Do you want to delete this tuner configuration?", + "Delete Tuner Configuration?", JOptionPane.YES_NO_OPTION ); + if( choice == JOptionPane.YES_OPTION ) { TunerConfiguration selected = getSelectedTunerConfiguration(); - + mTunerConfigurationModel.removeTunerConfiguration( selected ); mTunerConfigurationTable.setRowSelectionInterval( 0, 0 ); } } } ); listPanel.add( mDeleteConfigurationButton ); - + mAssignConfigurationButton.setEnabled( false ); mAssignConfigurationButton.setToolTipText( "Assigns to the currently selected tuner configuration to the selected tuner" ); mAssignConfigurationButton.addActionListener( new ActionListener() @@ -156,18 +161,18 @@ public class TunerEditor extends Editor public void actionPerformed( ActionEvent e ) { TunerConfiguration selected = getSelectedTunerConfiguration(); - + mTunerConfigurationModel.assignTunerConfiguration( selected ); setTunerConfiguration( getSelectedTunerConfiguration() ); } } ); listPanel.add( mAssignConfigurationButton ); - + //Set preferred size on both scrollers to same width, so they split the space listPanel.setPreferredSize( new Dimension( 100, 80 ) ); JScrollPane listScroller = new JScrollPane( listPanel ); listScroller.setPreferredSize( new Dimension( 100, 80 ) ); - + mEditorScroller = new JScrollPane( mEditor ); mEditorScroller.setPreferredSize( new Dimension( 100, 80 ) ); @@ -177,15 +182,15 @@ public class TunerEditor extends Editor add( mEditorSplitPane ); } - + private TunerConfiguration getSelectedTunerConfiguration() { int tableRow = mTunerConfigurationTable.getSelectedRow(); - + if( tableRow >= 0 ) { int modelRow = mTunerConfigurationTable.convertRowIndexToModel( tableRow ); - + if( modelRow >= 0 ) { return mTunerConfigurationModel.getTunerConfiguration( modelRow ); @@ -194,7 +199,7 @@ public class TunerEditor extends Editor return null; } - + @Override public void save() { @@ -211,9 +216,9 @@ public class TunerEditor extends Editor previous.getTunerController().removeListener( mFrequencyControl ); mFrequencyControl.removeListener( previous.getTunerController() ); } - + super.setItem( tuner ); - + if( hasItem() ) { mSelectedTunerType.setText( tuner.getName() ); @@ -222,21 +227,21 @@ public class TunerEditor extends Editor mNewConfigurationButton.setEnabled( true ); //Change to an editor for this config's tuner type - mEditor = TunerConfigurationFactory.getEditor( getItem(), mTunerConfigurationModel ); - - mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType(), - tuner.getUniqueID() ) ); + mEditor = TunerConfigurationFactory.getEditor( getItem(), mTunerConfigurationModel ); + + mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType(), + tuner.getUniqueID() ) ); TunerConfiguration assigned = mTunerConfigurationModel .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); int modelIndex = mTunerConfigurationModel.getRowIndex( assigned ); - + if( modelIndex >= 0 ) { int viewIndex = mTunerConfigurationTable - .convertRowIndexToView( modelIndex ); - + .convertRowIndexToView( modelIndex ); + if( viewIndex >= 0 ) { mTunerConfigurationTable @@ -245,14 +250,14 @@ public class TunerEditor extends Editor } //Link frequency control to the tuner - mFrequencyControl.addListener( tuner.getTunerController() ); - - //Link tuner to frequency control - tuner.getTunerController().addListener( mFrequencyControl ); + mFrequencyControl.addListener( tuner.getTunerController() ); - //Set the displayed frequency without adjusting the tuner's frequency - mFrequencyControl.setFrequency( - tuner.getTunerController().getFrequency(), false ); + //Link tuner to frequency control + tuner.getTunerController().addListener( mFrequencyControl ); + + //Set the displayed frequency without adjusting the tuner's frequency + mFrequencyControl.setFrequency( + tuner.getTunerController().getFrequency(), false ); } else { @@ -263,15 +268,15 @@ public class TunerEditor extends Editor mRowSorter.setRowFilter( null ); mEditor = new EmptyEditor(); } - - //Swap out the editor + + //Swap out the editor int split = mEditorSplitPane.getDividerLocation( 0 ); mEditorSplitPane.remove( mEditorScroller ); mEditorScroller = new JScrollPane( mEditor ); mEditorSplitPane.add( mEditorScroller ); mEditorSplitPane.setDividerLocation( 0, split ); } - + private void setTunerConfiguration( TunerConfiguration config ) { if( config != null ) @@ -293,7 +298,7 @@ public class TunerEditor extends Editor { mAssignConfigurationButton.setEnabled( false ); } - + if( mDeleteConfigurationButton.isEnabled() ) { mDeleteConfigurationButton.setEnabled( false ); @@ -306,39 +311,39 @@ public class TunerEditor extends Editor { mDeleteConfigurationButton.setEnabled( false ); } - + if( mAssignConfigurationButton.isEnabled() ) { mAssignConfigurationButton.setEnabled( false ); } } - + mEditor.setItem( config ); } - + public class ConfigurationRowFilter extends RowFilter { private TunerType mTunerType; private String mUniqueID; - + public ConfigurationRowFilter( TunerType type, String uniqueID ) { mTunerType = type; mUniqueID = uniqueID; } - + @Override - public boolean include( javax.swing.RowFilter.Entry entry ) + public boolean include( javax.swing.RowFilter.Entry entry ) { TunerConfiguration config = entry.getModel() - .getTunerConfiguration( entry.getIdentifier() ); + .getTunerConfiguration( entry.getIdentifier() ); - return config != null && - config.getTunerType() == mTunerType && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( mUniqueID ); + return config != null && + config.getTunerType() == mTunerType && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( mUniqueID ); } } - -} + +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java b/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java index 5af7215e..74ee26b0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerManager.java @@ -246,25 +246,21 @@ public class TunerManager + "supported"); } - private TunerInitStatus initFuncubeProTuner(Device device, - DeviceDescriptor descriptor) + private TunerInitStatus initFuncubeProTuner(Device device, DeviceDescriptor descriptor) { String reason = "NOT LOADED"; - MixerTunerDataLine dataline = getMixerTunerDataLine( - TunerClass.FUNCUBE_DONGLE_PRO.getTunerType()); + MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO.getTunerType()); if(dataline != null) { - FCD1TunerController controller = - new FCD1TunerController(device, descriptor); + FCD1TunerController controller = new FCD1TunerController(dataline, device, descriptor); try { controller.init(); - FCDTuner tuner = - new FCDTuner(dataline, controller); + FCDTuner tuner = new FCDTuner(controller); return new TunerInitStatus(tuner, "LOADED"); } @@ -284,25 +280,21 @@ public class TunerManager + "loaded - " + reason); } - private TunerInitStatus initFuncubeProPlusTuner(Device device, - DeviceDescriptor descriptor) + private TunerInitStatus initFuncubeProPlusTuner(Device device, DeviceDescriptor descriptor) { String reason = "NOT LOADED"; - MixerTunerDataLine dataline = getMixerTunerDataLine( - TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType()); + MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType()); if(dataline != null) { - FCD2TunerController controller = - new FCD2TunerController(device, descriptor); + FCD2TunerController controller = new FCD2TunerController(dataline, device, descriptor); try { controller.init(); - FCDTuner tuner = - new FCDTuner(dataline, controller); + FCDTuner tuner = new FCDTuner(controller); return new TunerInitStatus(tuner, "LOADED"); } @@ -323,8 +315,7 @@ public class TunerManager + "loaded - " + reason); } - private TunerInitStatus initHackRFTuner(Device device, - DeviceDescriptor descriptor) + private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor) { try { diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java index 3b7aa297..a0735a24 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java @@ -5,6 +5,8 @@ import io.github.dsheirer.source.Source; import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.tuner.TunerEvent.Event; +import io.github.dsheirer.source.tuner.channel.TunerChannel; +import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import org.slf4j.Logger; @@ -15,282 +17,275 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.concurrent.RejectedExecutionException; public class TunerModel extends AbstractTableModel implements Listener { - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( TunerModel.class ); + private static final long serialVersionUID = 1L; - public static final int TUNER_TYPE = 0; - public static final int TUNER_ID = 1; - public static final int SAMPLE_RATE = 2; - public static final int FREQUENCY = 3; - public static final int CHANNEL_COUNT = 4; - public static final int SPECTRAL_DISPLAY_MAIN = 5; - public static final int SPECTRAL_DISPLAY_NEW = 6; - - private static final String MHZ = " MHz"; - private static final String[] COLUMNS = - { "Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Spectral", "Display" }; - - private List mTuners = new ArrayList<>(); - private List> mTunerEventListeners = new ArrayList<>(); - private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.00000" ); - private DecimalFormat mSampleRateFormat = new DecimalFormat( "0.000" ); - - private TunerConfigurationModel mTunerConfigurationModel; + private final static Logger mLog = LoggerFactory.getLogger(TunerModel.class); - public TunerModel( TunerConfigurationModel tunerConfigurationModel ) - { - mTunerConfigurationModel = tunerConfigurationModel; - } - - public TunerConfigurationModel getTunerConfigurationModel() - { - return mTunerConfigurationModel; - } + public static final int TUNER_TYPE = 0; + public static final int TUNER_ID = 1; + public static final int SAMPLE_RATE = 2; + public static final int FREQUENCY = 3; + public static final int CHANNEL_COUNT = 4; + public static final int SPECTRAL_DISPLAY_MAIN = 5; + public static final int SPECTRAL_DISPLAY_NEW = 6; - /** - * List of Tuners currently in the model - */ - public List getTuners() - { - return mTuners; - } - - public Tuner getTuner( int index ) - { - if( index < mTuners.size() ) - { - return mTuners.get( index ); - } - - return null; - } + private static final String MHZ = " MHz"; + private static final String[] COLUMNS = + {"Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Spectral", "Display"}; - public void addTuners( List Tuners ) - { - for( Tuner tuner: Tuners ) - { - addTuner( tuner ); - } - } + private List mTuners = new ArrayList<>(); + private List> mTunerEventListeners = new ArrayList<>(); + private DecimalFormat mFrequencyFormat = new DecimalFormat("0.00000"); + private DecimalFormat mSampleRateFormat = new DecimalFormat("0.000"); - /** - * Adds the Tuner to this model - */ - public void addTuner( Tuner tuner ) - { - if( !mTuners.contains( tuner ) ) - { - //Get the tuner configuration and apply it to the tuner - this - //call should always produce a tuner configuration - TunerConfiguration config = mTunerConfigurationModel - .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - - try - { - tuner.getTunerController().apply( config ); - - mTuners.add( tuner ); - - int index = mTuners.indexOf( tuner ); - - fireTableRowsInserted( index, index ); - - tuner.addTunerChangeListener( this ); - } - catch( SourceException se ) - { - mLog.error( "Couldn't apply tuner configuration to tuner - [" - + tuner.getTunerType().name() + "] with id [" - + tuner.getUniqueID() + "] - tuner will not be included" ); - } - - } - } + private TunerConfigurationModel mTunerConfigurationModel; - /** - * Removes the Tuner from this model - */ - public void removeTuner( Tuner tuner ) - { - if( mTuners.contains( tuner ) ) - { - tuner.removeTunerChangeListener( this ); - - int index = mTuners.indexOf( tuner ); - - mTuners.remove( tuner ); - - fireTableRowsDeleted( index, index ); - } - } - - public void addListener( Listener listener ) - { - mTunerEventListeners.add( listener ); - } + public TunerModel(TunerConfigurationModel tunerConfigurationModel) + { + mTunerConfigurationModel = tunerConfigurationModel; + } - public void removeListener( Listener listener ) - { - mTunerEventListeners.remove( listener ); - } + public TunerConfigurationModel getTunerConfigurationModel() + { + return mTunerConfigurationModel; + } - public void broadcast( TunerEvent event ) - { - for( Listener listener: mTunerEventListeners ) - { - listener.receive( event ); - } - } + /** + * List of Tuners currently in the model + */ + public List getTuners() + { + return mTuners; + } - /** - * Requests to display the first tuner in this model. Invoke this method - * after all listeners have registered and tuners have been added to this - * model, in order to inform the primary display to use the first tuner. - */ - public void requestFirstTunerDisplay() - { - if( mTuners.size() > 0 ) - { - broadcast( new TunerEvent( mTuners.get( 0 ), - Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) ); - } - } - - @Override - public void receive( TunerEvent event ) - { - if( event.getTuner() != null ) - { - int index = mTuners.indexOf( event.getTuner() ); - - if( index >= 0 ) - { - switch( event.getEvent() ) - { - case CHANNEL_COUNT: - fireTableCellUpdated( index, CHANNEL_COUNT ); - break; - case FREQUENCY: - fireTableCellUpdated( index, FREQUENCY ); - mTunerConfigurationModel.tunerFrequencyChanged( event.getTuner() ); - break; - case SAMPLE_RATE: - fireTableCellUpdated( index, SAMPLE_RATE ); - break; - case REQUEST_MAIN_SPECTRAL_DISPLAY: - fireTableCellUpdated( index, SPECTRAL_DISPLAY_MAIN ); - default: - break; - } - } - } - - broadcast( event ); - } + public Tuner getTuner(int index) + { + if(index < mTuners.size()) + { + return mTuners.get(index); + } - @Override - public int getRowCount() - { - return mTuners.size(); - } + return null; + } - @Override - public int getColumnCount() - { - return COLUMNS.length; - } + public void addTuners(List Tuners) + { + for(Tuner tuner : Tuners) + { + addTuner(tuner); + } + } - @Override - public Object getValueAt( int rowIndex, int columnIndex ) - { - if( rowIndex < mTuners.size() ) - { - Tuner tuner = mTuners.get( rowIndex ); + /** + * Adds the Tuner to this model + */ + public void addTuner(Tuner tuner) + { + if(!mTuners.contains(tuner)) + { + //Get the tuner configuration and apply it to the tuner - this + //call should always produce a tuner configuration + TunerConfiguration config = mTunerConfigurationModel + .getTunerConfiguration(tuner.getTunerType(), tuner.getUniqueID()); - switch( columnIndex ) - { - case TUNER_TYPE: - return tuner.getTunerType().getLabel(); - case TUNER_ID: - return tuner.getUniqueID(); - case SAMPLE_RATE: - int sampleRate = tuner.getTunerController().getSampleRate(); + try + { + tuner.getTunerController().apply(config); - return mSampleRateFormat.format( sampleRate / 1E6D ) + MHZ; - case FREQUENCY: - try - { - long frequency = tuner.getTunerController().getFrequency(); - - return mFrequencyFormat.format( frequency / 1E6D ) + MHZ; - } - catch( Exception e ) - { - return 0; - } - case CHANNEL_COUNT: - return tuner.getTunerController().getChannelCount(); - case SPECTRAL_DISPLAY_MAIN: - return "Main"; - case SPECTRAL_DISPLAY_NEW: - return "New"; - default: - break; - } - } + mTuners.add(tuner); + + int index = mTuners.indexOf(tuner); + + fireTableRowsInserted(index, index); + + tuner.addTunerChangeListener(this); + } + catch(SourceException se) + { + mLog.error("Couldn't apply tuner configuration to tuner - [" + + tuner.getTunerType().name() + "] with id [" + + tuner.getUniqueID() + "] - tuner will not be included"); + } + + } + } + + /** + * Removes the Tuner from this model + */ + public void removeTuner(Tuner tuner) + { + if(mTuners.contains(tuner)) + { + tuner.removeTunerChangeListener(this); + + int index = mTuners.indexOf(tuner); + + mTuners.remove(tuner); + + fireTableRowsDeleted(index, index); + } + } + + public void addListener(Listener listener) + { + mTunerEventListeners.add(listener); + } + + public void removeListener(Listener listener) + { + mTunerEventListeners.remove(listener); + } + + public void broadcast(TunerEvent event) + { + for(Listener listener : mTunerEventListeners) + { + listener.receive(event); + } + } + + /** + * Requests to display the first tuner in this model. Invoke this method + * after all listeners have registered and tuners have been added to this + * model, in order to inform the primary display to use the first tuner. + */ + public void requestFirstTunerDisplay() + { + if(mTuners.size() > 0) + { + broadcast(new TunerEvent(mTuners.get(0), + Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); + } + } + + @Override + public void receive(TunerEvent event) + { + if(event.getTuner() != null) + { + int index = mTuners.indexOf(event.getTuner()); + + if(index >= 0) + { + switch(event.getEvent()) + { + case CHANNEL_COUNT: + fireTableCellUpdated(index, CHANNEL_COUNT); + break; + case FREQUENCY: + fireTableCellUpdated(index, FREQUENCY); + mTunerConfigurationModel.tunerFrequencyChanged(event.getTuner()); + break; + case SAMPLE_RATE: + fireTableCellUpdated(index, SAMPLE_RATE); + break; + case REQUEST_MAIN_SPECTRAL_DISPLAY: + fireTableCellUpdated(index, SPECTRAL_DISPLAY_MAIN); + default: + break; + } + } + } + + broadcast(event); + } + + @Override + public int getRowCount() + { + return mTuners.size(); + } + + @Override + public int getColumnCount() + { + return COLUMNS.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) + { + if(rowIndex < mTuners.size()) + { + Tuner tuner = mTuners.get(rowIndex); + + switch(columnIndex) + { + case TUNER_TYPE: + return tuner.getTunerType().getLabel(); + case TUNER_ID: + return tuner.getUniqueID(); + case SAMPLE_RATE: + double sampleRate = tuner.getTunerController().getSampleRate(); + + return mSampleRateFormat.format(sampleRate / 1E6D) + MHZ; + case FREQUENCY: + try + { + long frequency = tuner.getTunerController().getFrequency(); + + return mFrequencyFormat.format(frequency / 1E6D) + MHZ; + } + catch(Exception e) + { + return 0; + } + case CHANNEL_COUNT: + return tuner.getTunerController().getChannelCount(); + case SPECTRAL_DISPLAY_MAIN: + return "Main"; + case SPECTRAL_DISPLAY_NEW: + return "New"; + default: + break; + } + } + + return null; + } + + @Override + public String getColumnName(int columnIndex) + { + return COLUMNS[columnIndex]; + } - return null; - } - - @Override - public String getColumnName( int columnIndex ) - { - return COLUMNS[ columnIndex ]; - } - /** * Iterates current tuners to get a tuner channel source for the frequency * specified in the channel config's source config object. - * + * * Returns null if no tuner can source the channel */ - public Source getSource(SourceConfigTuner config, int bandwidth ) + public Source getSource(SourceConfigTuner config, int bandwidth) { - TunerChannelSource retVal = null; - - TunerChannel tunerChannel = config.getTunerChannel(); - - tunerChannel.setBandwidth( bandwidth ); - - Iterator it = mTuners.iterator(); - - Tuner tuner; - - while( it.hasNext() && retVal == null ) - { - tuner = it.next(); - - try + TunerChannelSource retVal = null; + + TunerChannel tunerChannel = config.getTunerChannel(); + + tunerChannel.setBandwidth(bandwidth); + + Iterator it = mTuners.iterator(); + + Tuner tuner; + + while(it.hasNext() && retVal == null) + { + tuner = it.next(); + + try { - retVal = tuner.getChannel( tunerChannel ); + retVal = tuner.getSourceManager().getSource(tunerChannel); } - catch ( RejectedExecutionException ree ) - { - mLog.error( "couldn't provide tuner channel source", ree ); - } - catch ( SourceException e ) + catch(Exception e) { - mLog.error( "error obtaining channel from tuner [" + - tuner.getName() + "]", e ); + mLog.error("Error obtaining channel source from tuner [" + tuner.getName() + "]", e); } - } - - return retVal; + } + + return retVal; } - -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java b/src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java index ccb907c7..be958fdc 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerSourceEditor.java @@ -20,16 +20,16 @@ package io.github.dsheirer.source.tuner; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.gui.control.JFrequencyControl; import io.github.dsheirer.gui.editor.Editor; +import io.github.dsheirer.source.ISourceEventProcessor; +import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfiguration; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; -import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor; import net.miginfocom.swing.MigLayout; public class TunerSourceEditor extends Editor { - private static final long serialVersionUID = 1L; - private JFrequencyControl mFrequencyControl; + private static final long serialVersionUID = 1L; + private JFrequencyControl mFrequencyControl; public TunerSourceEditor() { @@ -41,10 +41,10 @@ public class TunerSourceEditor extends Editor setLayout( new MigLayout( "insets 0 0 0 0", "[left]", "" ) ); mFrequencyControl = new JFrequencyControl(); mFrequencyControl.setEnabled( false ); - mFrequencyControl.addListener( new IFrequencyChangeProcessor() + mFrequencyControl.addListener( new ISourceEventProcessor() { @Override - public void frequencyChanged( FrequencyChangeEvent event ) + public void process(SourceEvent event ) { setModified( true ); } @@ -63,16 +63,16 @@ public class TunerSourceEditor extends Editor setModified( false ); } - + @Override public void setItem( Channel item ) { super.setItem( item ); - + if( hasItem() ) { SourceConfiguration config = getItem().getSourceConfiguration(); - + mFrequencyControl.setEnabled( true ); if( config instanceof SourceConfigTuner ) @@ -92,4 +92,4 @@ public class TunerSourceEditor extends Editor setModified( false ); } } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerType.java b/src/main/java/io/github/dsheirer/source/tuner/TunerType.java index 93718fdb..6b18969d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerType.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerType.java @@ -32,6 +32,7 @@ public enum TunerType RAFAELMICRO_R820T( "R820T" ), RAFAELMICRO_R828D( "R828D" ), RTL2832_VARIOUS( "Generic" ), + TEST("Test"), UNKNOWN( "Unknown" ); private String mLabel; diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java index ba1f5ad1..e22ab6df 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTuner.java @@ -17,21 +17,13 @@ ******************************************************************************/ package io.github.dsheirer.source.tuner.airspy; -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.Tuner; -import io.github.dsheirer.source.tuner.TunerChannel; -import io.github.dsheirer.source.tuner.TunerChannelSource; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerEvent; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.airspy.AirspyTunerController.BoardID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.RejectedExecutionException; - public class AirspyTuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger( AirspyTuner.class ); @@ -40,15 +32,15 @@ public class AirspyTuner extends Tuner { super( "Airspy " + controller.getDeviceInfo().getSerialNumber(), controller ); } - + public AirspyTunerController getController() { return (AirspyTunerController)getTunerController(); } @Override - public String getUniqueID() - { + public String getUniqueID() + { try { return getController().getDeviceInfo().getSerialNumber(); @@ -57,9 +49,9 @@ public class AirspyTuner extends Tuner { mLog.error( "error getting serial number", e ); } - + return BoardID.AIRSPY.getLabel(); - } + } @Override public TunerClass getTunerClass() @@ -78,50 +70,4 @@ public class AirspyTuner extends Tuner { return 13.0; } - - @Override - public TunerChannelSource getChannel( TunerChannel channel ) - throws RejectedExecutionException, SourceException - { - //TODO: this channel has a decimated sample rate of: - // 10.0 MSps = 10,000,000 / 208 = 48076.923 - // 5.0 MSps = 5,000,000 / 104 = 48076.923 - // 2.5 MSps = 2,500,000 / 52 = 48076.923 - //Consider implementing a fractional resampler to get a correct 48 kHz - //output sample rate - - TunerChannelSource source = getController().getChannel( this, channel ); - - if( source != null ) - { - broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); - } - - return source; - } - - @Override - public void releaseChannel( TunerChannelSource source ) - { - /* Unregister for receiving samples */ - removeListener( (Listener)source ); - - /* Tell the controller to release the channel and cleanup */ - if( source != null ) - { - getController().releaseChannel( source ); - } - } - - @Override - public void addListener( Listener listener ) - { - getController().addListener( listener ); - } - - @Override - public void removeListener( Listener listener ) - { - getController().removeListener( listener ); - } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java index be1163d6..8ea4ad65 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java @@ -1,11 +1,9 @@ package io.github.dsheirer.source.tuner.airspy; -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.TunerController; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.usb.USBTransferProcessor; +import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.apache.commons.io.EndianUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,7 +60,7 @@ import java.util.List; * POSSIBILITY OF SUCH DAMAGE. */ -public class AirspyTunerController extends TunerController +public class AirspyTunerController extends USBTunerController { public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14; public static final Gain SENSITIVITY_GAIN_DEFAULT = Gain.SENSITIVITY_10; @@ -183,6 +181,14 @@ public class AirspyTunerController extends TunerController USB_TRANSFER_BUFFER_SIZE); } + + @Override + protected USBTransferProcessor getUSBTransferProcessor() + { + return mUSBTransferProcessor; + } + + /** * Claims the USB interface. If another application currently has * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting @@ -368,7 +374,7 @@ public class AirspyTunerController extends TunerController } @Override - public int getCurrentSampleRate() throws SourceException + public double getCurrentSampleRate() { return mSampleRate; } @@ -456,7 +462,7 @@ public class AirspyTunerController extends TunerController throw new UsbException("Couldnt set sample packing enabled: " + enabled); } - /* If we didn't throw an exception above, then update the sample adapter + /* If we didn't throw an exception above, then update the sample adapter * to process samples accordingly */ mSampleAdapter.setSamplePacking(enabled); } @@ -671,7 +677,7 @@ public class AirspyTunerController extends TunerController mDeviceInfo = new AirspyDeviceInformation(); } - /* Board ID */ + /* Board ID */ try { int boardID = readByte(Command.BOARD_ID_READ, 0, 0, true); @@ -683,7 +689,7 @@ public class AirspyTunerController extends TunerController mLog.error("Error reading airspy board ID", e); } - /* Version String */ + /* Version String */ try { //NOTE: libairspy is internally reading 127 bytes, however airspy_info @@ -696,8 +702,8 @@ public class AirspyTunerController extends TunerController { mLog.error("Error reading airspy version string", e); } - - /* Part ID and Serial Number */ + + /* Part ID and Serial Number */ try { //Read 6 x 32-bit integers = 24 bytes @@ -1183,34 +1189,4 @@ public class AirspyTunerController extends TunerController return mValue; } } - - /** - * Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started. - */ - public void addListener(Listener listener) - { - if(mUSBTransferProcessor != null) - { - mUSBTransferProcessor.addListener(listener); - } - else - { - mLog.error("Couldn't add IQ buffer listener to Airspy tuner - processor is null"); - } - } - - /** - * Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners. - */ - public void removeListener(Listener listener) - { - if(mUSBTransferProcessor != null) - { - mUSBTransferProcessor.removeListener(listener); - } - else - { - mLog.error("Couldn't remove IQ buffer listener from Airspy tuner - processor is null"); - } - } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java index 922f98f0..2d47f9c2 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java @@ -29,7 +29,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; -import javax.swing.*; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; @@ -42,43 +54,43 @@ import java.util.List; public class AirspyTunerEditor extends TunerConfigurationEditor { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( AirspyTunerEditor.class ); + private final static Logger mLog = + LoggerFactory.getLogger( AirspyTunerEditor.class ); - private JTextField mConfigurationName; - private JButton mTunerInfo; + private JTextField mConfigurationName; + private JButton mTunerInfo; - private JComboBox mSampleRateCombo; - private JSpinner mFrequencyCorrection; + private JComboBox mSampleRateCombo; + private JSpinner mFrequencyCorrection; - private JComboBox mGainModeCombo; + private JComboBox mGainModeCombo; - private JSlider mMasterGain; - private JLabel mMasterGainValueLabel; + private JSlider mMasterGain; + private JLabel mMasterGainValueLabel; - private JSlider mIFGain; - private JLabel mIFGainValueLabel; + private JSlider mIFGain; + private JLabel mIFGainValueLabel; - private JSlider mLNAGain; - private JLabel mLNAGainValueLabel; + private JSlider mLNAGain; + private JLabel mLNAGainValueLabel; - private JSlider mMixerGain; - private JLabel mMixerGainValueLabel; + private JSlider mMixerGain; + private JLabel mMixerGainValueLabel; - private JCheckBox mLNAAGC; - private JCheckBox mMixerAGC; + private JCheckBox mLNAAGC; + private JCheckBox mMixerAGC; - private AirspyTunerController mController; - private boolean mLoading; + private AirspyTunerController mController; + private boolean mLoading; - public AirspyTunerEditor( TunerConfigurationModel tunerConfigurationModel, - AirspyTuner tuner ) + public AirspyTunerEditor( TunerConfigurationModel tunerConfigurationModel, + AirspyTuner tuner ) { super( tunerConfigurationModel ); mController = tuner.getController(); - + init(); } @@ -88,33 +100,33 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { return (AirspyTunerConfiguration)getItem(); } - + return null; } - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][grow]" ) ); - + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][grow]" ) ); + add( new JLabel( "Airspy Tuner Configuration" ), "span,align center" ); - + mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() + mConfigurationName.addFocusListener( new FocusListener() { @Override - public void focusLost( FocusEvent e ) - { + public void focusLost( FocusEvent e ) + { save(); - } + } @Override - public void focusGained( FocusEvent e ) {} + public void focusGained( FocusEvent e ) {} } ); - + add( new JLabel( "Name:" ) ); add( mConfigurationName, "span 2" ); - + mTunerInfo = new JButton( "Tuner Info" ); mTunerInfo.setEnabled( false ); mTunerInfo.addActionListener( new ActionListener() @@ -122,22 +134,22 @@ public class AirspyTunerEditor extends TunerConfigurationEditor @Override public void actionPerformed( ActionEvent e ) { - JOptionPane.showMessageDialog( AirspyTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + JOptionPane.showMessageDialog( AirspyTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); } } ); add( mTunerInfo ); - + /** * Sample Rate */ add( new JLabel( "Sample Rate:" ) ); - + List rates = mController.getSampleRates(); - - mSampleRateCombo = new JComboBox( - new DefaultComboBoxModel( rates.toArray( - new AirspySampleRate[ rates.size() ] ) ) ); + + mSampleRateCombo = new JComboBox( + new DefaultComboBoxModel( rates.toArray( + new AirspySampleRate[ rates.size() ] ) ) ); mSampleRateCombo.setEnabled( false ); mSampleRateCombo.addActionListener( new ActionListener() { @@ -150,62 +162,62 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { mController.setSampleRate( rate ); save(); - } + } catch ( LibUsbException | UsbException | SourceException e1 ) { - JOptionPane.showMessageDialog( AirspyTunerEditor.this, + JOptionPane.showMessageDialog( AirspyTunerEditor.this, "Couldn't set sample rate to " + rate.getLabel() ); - + mLog.error( "Error setting airspy sample rate", e1 ); - } + } } } ); - + add( mSampleRateCombo ); - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step + /** + * Frequency Correction + */ + SpinnerModel model = + new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - mFrequencyCorrection.addChangeListener( new ChangeListener() - { + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { + public void stateChanged( ChangeEvent e ) + { final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try + .getModel()).getNumber().doubleValue(); + + try { mController.setFrequencyCorrection( value ); - } - catch ( SourceException e1 ) + } + catch ( SourceException e1 ) { mLog.error( "Error setting frequency correction value", e1 ); } - - save(); - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); - + + save(); + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + add( new JSeparator(), "span,growx,push" ); /** @@ -222,11 +234,11 @@ public class AirspyTunerEditor extends TunerConfigurationEditor GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); updateGainComponents( mode ); - + if( hasItem() ) { AirspyTunerConfiguration airspy = getConfiguration(); - + Gain gain = airspy.getGain(); if( mode == GainMode.CUSTOM ) @@ -247,25 +259,25 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mLNAGain.setValue( gain.getLNA() ); } } - + save(); } } ); - + add( mGainModeCombo, "wrap" ); /** * Gain */ add( new JLabel( "Master:" ) ); - - mMasterGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.GAIN_MIN, - AirspyTunerController.GAIN_MAX, - AirspyTunerController.GAIN_MIN ); + + mMasterGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.GAIN_MIN, + AirspyTunerController.GAIN_MAX, + AirspyTunerController.GAIN_MIN ); mMasterGain.setMajorTickSpacing( 1 ); mMasterGain.setPaintTicks( true ); - + mMasterGain.addChangeListener( new ChangeListener() { @Override @@ -280,31 +292,31 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mController.setGain( gain ); save(); mMasterGainValueLabel.setText( String.valueOf( gain.getValue() ) ); - } + } catch ( Exception e ) { mLog.error( "Couldn't set airspy gain to:" + gain.name(), e ); - JOptionPane.showMessageDialog( mMasterGain, "Couldn't set gain value to " + - gain.getValue() ); + JOptionPane.showMessageDialog( mMasterGain, "Couldn't set gain value to " + + gain.getValue() ); } } } ); add( mMasterGain, "span 2" ); - + mMasterGainValueLabel = new JLabel( "0" ); add( mMasterGainValueLabel ); - + add( new JLabel( "IF:" ) ); - - mIFGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.IF_GAIN_MIN, - AirspyTunerController.IF_GAIN_MAX, - AirspyTunerController.IF_GAIN_MIN ); - + + mIFGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.IF_GAIN_MIN, + AirspyTunerController.IF_GAIN_MAX, + AirspyTunerController.IF_GAIN_MIN ); + mIFGain.setMajorTickSpacing( 1 ); mIFGain.setPaintTicks( true ); - + mIFGain.addChangeListener( new ChangeListener() { @Override @@ -317,34 +329,34 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mController.setIFGain( gain ); save(); mIFGainValueLabel.setText( String.valueOf( gain ) ); - } + } catch ( Exception e ) { mLog.error( "Couldn't set airspy IF gain to:" + gain, e ); - + JOptionPane.showMessageDialog( mIFGain, "Couldn't set IF gain value to " + gain ); } } } ); add( mIFGain, "span 2" ); - + mIFGainValueLabel = new JLabel( "0" ); add( mIFGainValueLabel ); - + /** - * Mixer Gain + * Mixer Gain */ add( new JLabel( "Mixer:" ) ); - - mMixerGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.MIXER_GAIN_MIN, - AirspyTunerController.MIXER_GAIN_MAX, - AirspyTunerController.MIXER_GAIN_MIN ); - + + mMixerGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.MIXER_GAIN_MIN, + AirspyTunerController.MIXER_GAIN_MAX, + AirspyTunerController.MIXER_GAIN_MIN ); + mMixerGain.setMajorTickSpacing( 1 ); mMixerGain.setPaintTicks( true ); - + mMixerGain.addChangeListener( new ChangeListener() { @Override @@ -357,7 +369,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mController.setMixerGain( gain ); save(); mMixerGainValueLabel.setText( String.valueOf( gain ) ); - } + } catch ( Exception e ) { mLog.error( "Couldn't set airspy Mixer gain to:" + gain, e ); @@ -370,17 +382,17 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mMixerGainValueLabel = new JLabel( "0" ); add( mMixerGainValueLabel ); - + add( new JLabel( "LNA:" ) ); - - mLNAGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.LNA_GAIN_MIN, - AirspyTunerController.LNA_GAIN_MAX, - AirspyTunerController.LNA_GAIN_MIN ); - + + mLNAGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.LNA_GAIN_MIN, + AirspyTunerController.LNA_GAIN_MAX, + AirspyTunerController.LNA_GAIN_MIN ); + mLNAGain.setMajorTickSpacing( 1 ); mLNAGain.setPaintTicks( true ); - + mLNAGain.addChangeListener( new ChangeListener() { @Override @@ -393,7 +405,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mController.setLNAGain( gain ); save(); mLNAGainValueLabel.setText( String.valueOf( gain ) ); - } + } catch ( Exception e ) { mLog.error( "Couldn't set airspy LNA gain to:" + gain, e ); @@ -406,7 +418,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mLNAGainValueLabel = new JLabel( "0" ); add( mLNAGainValueLabel ); - + mMixerAGC = new JCheckBox( "Mixer AGC" ); mMixerAGC.addActionListener( new ActionListener() { @@ -417,16 +429,16 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { mController.setMixerAGC( mMixerAGC.isSelected() ); mMixerGain.setEnabled( !mMixerAGC.isSelected() ); - + save(); - } + } catch ( Exception e1 ) { mLog.error( "Error setting Mixer AGC Enabled" ); } } } ); - + add( mMixerAGC, "span 2,center" ); mLNAAGC = new JCheckBox( "LNA AGC" ); @@ -441,26 +453,26 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mLNAGain.setEnabled( !mLNAAGC.isSelected() ); save(); - } + } catch ( Exception e1 ) { mLog.error( "Error setting LNA AGC Enabled" ); } - + } } ); - + add( mLNAAGC, "span 2,center" ); - } - - /** - * Updates the enabled state of each of the gain controls according to the - * specified gain mode. The master gain control is enabled for linearity - * and sensitivity and the individual gain controls are disabled, and - * vice-versa for custom mode. - */ - private void updateGainComponents( GainMode mode ) - { + } + + /** + * Updates the enabled state of each of the gain controls according to the + * specified gain mode. The master gain control is enabled for linearity + * and sensitivity and the individual gain controls are disabled, and + * vice-versa for custom mode. + */ + private void updateGainComponents( GainMode mode ) + { switch( mode ) { case LINEARITY: @@ -471,20 +483,20 @@ public class AirspyTunerEditor extends TunerConfigurationEditor } if( mIFGain.isEnabled() ) { - mIFGain.setEnabled( false ); + mIFGain.setEnabled( false ); mIFGainValueLabel.setEnabled( false ); } if( mLNAGain.isEnabled() ) { mLNAAGC.setEnabled( false ); - mLNAGainValueLabel.setEnabled( false ); - mLNAGain.setEnabled( false ); + mLNAGainValueLabel.setEnabled( false ); + mLNAGain.setEnabled( false ); } if( mMixerGain.isEnabled() ) { - mMixerGainValueLabel.setEnabled( false ); - mMixerAGC.setEnabled( false ); - mMixerGain.setEnabled( false ); + mMixerGainValueLabel.setEnabled( false ); + mMixerAGC.setEnabled( false ); + mMixerGain.setEnabled( false ); } break; case SENSITIVITY: @@ -495,20 +507,20 @@ public class AirspyTunerEditor extends TunerConfigurationEditor } if( mIFGain.isEnabled() ) { - mIFGain.setEnabled( false ); + mIFGain.setEnabled( false ); mIFGainValueLabel.setEnabled( false ); } if( mLNAGain.isEnabled() ) { mLNAAGC.setEnabled( false ); - mLNAGainValueLabel.setEnabled( false ); - mLNAGain.setEnabled( false ); + mLNAGainValueLabel.setEnabled( false ); + mLNAGain.setEnabled( false ); } if( mMixerGain.isEnabled() ) { - mMixerGainValueLabel.setEnabled( false ); - mMixerAGC.setEnabled( false ); - mMixerGain.setEnabled( false ); + mMixerGainValueLabel.setEnabled( false ); + mMixerAGC.setEnabled( false ); + mMixerGain.setEnabled( false ); } break; case CUSTOM: @@ -519,36 +531,36 @@ public class AirspyTunerEditor extends TunerConfigurationEditor } if( !mIFGain.isEnabled() ) { - mIFGain.setEnabled( true ); + mIFGain.setEnabled( true ); mIFGainValueLabel.setEnabled( true ); } if( !mLNAGain.isEnabled() ) { mLNAAGC.setEnabled( true ); - mLNAGainValueLabel.setEnabled( true ); - mLNAGain.setEnabled( true ); + mLNAGainValueLabel.setEnabled( true ); + mLNAGain.setEnabled( true ); } if( !mMixerGain.isEnabled() ) { - mMixerGainValueLabel.setEnabled( true ); - mMixerAGC.setEnabled( true ); - mMixerGain.setEnabled( true ); + mMixerGainValueLabel.setEnabled( true ); + mMixerAGC.setEnabled( true ); + mMixerGain.setEnabled( true ); } default: break; } - } + } @Override public void save() { - if( hasItem() && !mLoading ) - { - AirspyTunerConfiguration config = getConfiguration(); - config.setName( mConfigurationName.getText() ); - config.setSampleRate( ((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate() ); + if( hasItem() && !mLoading ) + { + AirspyTunerConfiguration config = getConfiguration(); + config.setName( mConfigurationName.getText() ); + config.setSampleRate( ((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate() ); - double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); + double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); config.setFrequencyCorrection( value ); Gain gain = Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ); @@ -559,15 +571,15 @@ public class AirspyTunerEditor extends TunerConfigurationEditor config.setLNAGain( mLNAGain.getValue() ); config.setMixerAGC( mMixerAGC.isSelected() ); config.setLNAAGC( mLNAAGC.isSelected() ); - + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( config, TunerConfigurationEvent.Event.CHANGE ) ); - } + } } - + private AirspySampleRate fromValue( int value ) { List rates = mController.getSampleRates(); - + for( AirspySampleRate rate: rates ) { if( rate.getRate() == value ) @@ -580,10 +592,10 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { return rates.get( 0 ); } - + return null; } - + /** * Sets all controls to the argument enabled state */ @@ -593,52 +605,52 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { mConfigurationName.setEnabled( enabled ); } - + if( mTunerInfo.isEnabled() != enabled ) { mTunerInfo.setEnabled( enabled ); } - + if( mSampleRateCombo.isEnabled() != enabled ) { mSampleRateCombo.setEnabled( enabled ); } - + if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); } - + if( mGainModeCombo.isEnabled() != enabled ) { mGainModeCombo.setEnabled( enabled ); } - + if( mMasterGain.isEnabled() != enabled ) { mMasterGain.setEnabled( enabled ); } - + if( mIFGain.isEnabled() != enabled ) { mIFGain.setEnabled( enabled ); } - + if( mMixerGain.isEnabled() != enabled ) { mMixerGain.setEnabled( enabled ); } - + if( mMixerAGC.isEnabled() != enabled ) { mMixerAGC.setEnabled( enabled ); } - + if( mLNAGain.isEnabled() != enabled ) { mLNAGain.setEnabled( enabled ); } - + if( mLNAAGC.isEnabled() != enabled ) { mLNAAGC.setEnabled( enabled ); @@ -646,43 +658,43 @@ public class AirspyTunerEditor extends TunerConfigurationEditor } /** - * Applies the tuner configuration to the editor. If the configuration is assigned to the + * Applies the tuner configuration to the editor. If the configuration is assigned to the * currently selected tuner, then the settings are applied. */ @Override public void setItem( TunerConfiguration config ) { super.setItem( config ); - + mLoading = true; - + if( hasItem() && getItem().isAssigned() ) { AirspyTunerConfiguration airspy = getConfiguration(); - + setControlsEnabled( true ); - + mConfigurationName.setText( airspy.getName() ); AirspySampleRate rate = fromValue( airspy.getSampleRate() ); - + if( rate != null ) { mSampleRateCombo.setSelectedItem( rate ); } - + mFrequencyCorrection.setValue( airspy.getFrequencyCorrection() ); GainMode mode = Gain.getGainMode( airspy.getGain() ); //This will set the master, IF, Mixer, LNA and AGC controls mGainModeCombo.setSelectedItem( mode ); - + if( mode == GainMode.CUSTOM ) { mMasterGain.setValue( airspy.getGain().getValue() ); mIFGain.setValue( airspy.getIFGain() ); - + if( airspy.isMixerAGC() ) { mMixerAGC.setSelected( airspy.isMixerAGC() ); @@ -707,7 +719,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { mMixerAGC.setSelected( false ); mLNAAGC.setSelected( false ); - + Gain gain = airspy.getGain(); mMasterGain.setValue( gain.getValue() ); @@ -721,16 +733,16 @@ public class AirspyTunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( hasItem() ? getItem().getName() : "" ); } - + mLoading = false; } - + private String getTunerInfo() { StringBuilder sb = new StringBuilder(); AirspyDeviceInformation info = mController.getDeviceInfo(); - + sb.append( "

Airspy Tuner

" ); sb.append( "Serial: " ); sb.append( info.getSerialNumber() ); @@ -740,15 +752,15 @@ public class AirspyTunerEditor extends TunerConfigurationEditor String[] firmware = info.getVersion().split( " " ); sb.append( firmware.length > 1 ? firmware[ 0 ] : info.getVersion() ); sb.append( "
" ); - + sb.append( "Part: " ); sb.append( info.getPartNumber() ); sb.append( "
" ); - + sb.append( "Board ID: " ); sb.append( info.getBoardID().getLabel() ); sb.append( "
" ); return sb.toString(); } -} +} \ No newline at end of file diff --git a/src/source/tuner/channel/cic/CICTunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java similarity index 83% rename from src/source/tuner/channel/cic/CICTunerChannelSource.java rename to src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java index a5086ae2..0e3625bd 100644 --- a/src/source/tuner/channel/cic/CICTunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java @@ -1,43 +1,38 @@ /******************************************************************************* - * 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.cic; +package io.github.dsheirer.source.tuner.channel; -import dsp.filter.FilterFactory; -import dsp.filter.Window; -import dsp.filter.cic.ComplexPrimeCICDecimate; -import dsp.mixer.Oscillator; +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +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.source.SourceEvent; +import io.github.dsheirer.source.tuner.Tuner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Buffer; -import sample.Listener; -import sample.OverflowableTransferQueue; -import sample.complex.Complex; -import sample.complex.ComplexBuffer; -import source.SourceEvent; -import source.tuner.Tuner; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; import java.util.ArrayList; import java.util.List; -public class CICTunerChannelSource extends TunerChannelSource implements Listener +public class CICTunerChannelSource extends TunerChannelSource { private final static Logger mLog = LoggerFactory.getLogger(CICTunerChannelSource.class); @@ -79,13 +74,12 @@ public class CICTunerChannelSource extends TunerChannelSource implements Listene mBuffer.setSourceOverflowListener(this); //Setup the frequency translator to the current source frequency - mTunerFrequency = tuner.getTunerController().getFrequencyController().getFrequency(); + mTunerFrequency = tuner.getTunerController().getFrequency(); long frequencyOffset = mTunerFrequency - getTunerChannel().getFrequency(); - mFrequencyCorrectionMixer = new Oscillator(frequencyOffset, - tuner.getTunerController().getFrequencyController().getSampleRate()); + mFrequencyCorrectionMixer = new Oscillator(frequencyOffset, tuner.getTunerController().getSampleRate()); //Setup the decimation filter chain - setSampleRate(tuner.getTunerController().getFrequencyController().getSampleRate()); + setSampleRate(tuner.getTunerController().getSampleRate()); } /** @@ -138,8 +132,8 @@ public class CICTunerChannelSource extends TunerChannelSource implements Listene @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 */ + /* 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); @@ -225,4 +219,4 @@ public class CICTunerChannelSource extends TunerChannelSource implements Listene mSampleBuffers.clear(); } -} +} \ No newline at end of file diff --git a/src/source/tuner/channel/TunerChannel.java b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannel.java similarity index 98% rename from src/source/tuner/channel/TunerChannel.java rename to src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannel.java index 0fa851a0..1c2e5424 100644 --- a/src/source/tuner/channel/TunerChannel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannel.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.channel; +package io.github.dsheirer.source.tuner.channel; public class TunerChannel implements Comparable { diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelProvider.java b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelProvider.java new file mode 100644 index 00000000..bc655d34 --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelProvider.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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.tuner.channel; + +import io.github.dsheirer.source.SourceException; + +public interface TunerChannelProvider +{ + /** + * Returns a tuner frequency channel source, tuned to the correct frequency + * + * @return - source for 48k sample rate + */ + public TunerChannelSource getChannel( TunerChannel channel ) throws SourceException; + + /** + * Releases the tuned channel resources + */ + public void releaseChannel( TunerChannelSource source ); + + +} diff --git a/src/source/tuner/channel/TunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java similarity index 92% rename from src/source/tuner/channel/TunerChannelSource.java rename to src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java index fef2a0eb..96386b45 100644 --- a/src/source/tuner/channel/TunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/TunerChannelSource.java @@ -16,18 +16,18 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.channel; +package io.github.dsheirer.source.tuner.channel; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.source.ComplexSource; +import io.github.dsheirer.source.ISourceEventProcessor; +import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.SourceEventListenerToProcessorAdapter; +import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.complex.ComplexBuffer; -import source.ComplexSource; -import source.ISourceEventProcessor; -import source.SourceEvent; -import source.SourceEventListenerToProcessorAdapter; -import source.SourceException; -import util.ThreadPool; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -41,7 +41,6 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc private Listener mProducerSourceEventListener; private Listener mConsumerSourceEventListener; private BufferProcessor mBufferProcessor = new BufferProcessor(); - private boolean mExpended = false; /** * Tuner Channel Source is a Digital Drop Channel (DDC) abstract class that defines the minimum functionality @@ -117,12 +116,6 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc */ public void start() { - if(mExpended) - { - throw new IllegalStateException("Cannot start an expended tuner channel source. You must request a new" + - " tuner channel source each time. Tuner channel sources are one-use only"); - } - //Broadcast current frequency and sample rate so consumer can configure correctly broadcastConsumerSourceEvent(SourceEvent.frequencyChange(getFrequency())); broadcastConsumerSourceEvent(SourceEvent.sampleRateChange(getSampleRate())); @@ -141,8 +134,6 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc */ public void stop() { - mExpended = true; - if(mProducerSourceEventListener != null) { mProducerSourceEventListener.receive(SourceEvent.stopSampleStream(this)); @@ -155,11 +146,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc @Override public void reset() { - if(mExpended) - { - throw new IllegalStateException("Cannot reset an expended tuner channel source. You must request a new" + - " tuner channel source each time. Tuner channel sources are one-use only"); - } + //Reset is not valid for a tuner channel source - ignored } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java index d29626c1..d12cd470 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationModel.java @@ -1,19 +1,17 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.tuner.configuration; @@ -31,8 +29,7 @@ import java.util.List; public class TunerConfigurationModel extends AbstractTableModel { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( TunerConfigurationModel.class ); + private final static Logger mLog = LoggerFactory.getLogger( TunerConfigurationModel.class ); public static final int TUNER_TYPE = 0; public static final int CONFIGURATION_NAME = 1; diff --git a/src/main/java/io/github/dsheirer/source/tuner/ettus/B100Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/ettus/B100Tuner.java deleted file mode 100644 index 591b81b2..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/ettus/B100Tuner.java +++ /dev/null @@ -1,83 +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.ettus; - -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.Tuner; -import io.github.dsheirer.source.tuner.TunerChannel; -import io.github.dsheirer.source.tuner.TunerChannelSource; -import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerType; -import io.github.dsheirer.source.tuner.usb.USBTunerDevice; - -import java.util.concurrent.RejectedExecutionException; - -public class B100Tuner extends Tuner -{ - public B100Tuner( USBTunerDevice device ) - { - //TODO: this should extend a USBTuner class, so that we can push the - //complex sample listener and the frequency change listener up to that class - - super( "Ettus B100 with WBX Tuner", null ); - } - - public void dispose() - { - - } - - @Override - public TunerClass getTunerClass() - { - return TunerClass.ETTUS_USRP_B100; - } - - @Override - public TunerType getTunerType() - { - return TunerType.ETTUS_WBX; - } - - @Override - public double getSampleSize() - { - return 16.0; - } - - @Override - public TunerChannelSource getChannel( TunerChannel channel ) - throws RejectedExecutionException, SourceException - { - //TODO: pass this call to the tuner controller - return null; - } - - @Override - public void releaseChannel( TunerChannelSource source ) - { - //TODO: pass this call to the tuner controller - } - - @Override - public String getUniqueID() - { - // TODO Auto-generated method stub - return null; - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/ettus/B100TunerEditorPanel.java b/src/main/java/io/github/dsheirer/source/tuner/ettus/B100TunerEditorPanel.java deleted file mode 100644 index be2238f0..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/ettus/B100TunerEditorPanel.java +++ /dev/null @@ -1,42 +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.ettus; - -import io.github.dsheirer.settings.SettingsManager; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; - -public class B100TunerEditorPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - public B100TunerEditorPanel( B100Tuner tuner, SettingsManager settingsManager ) - { - init(); - } - - private void init() - { - setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); - - setBorder( BorderFactory.createTitledBorder( "Ettus B100 Tuner" ) ); - - add( new JLabel( "Note: SDRTrunk can only recognize the B100 at this time. It cannot use this tuner") ); - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java index 7425a6d2..954e72c0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTuner.java @@ -1,51 +1,33 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.tuner.fcd; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.adapter.ShortAdapter; -import io.github.dsheirer.sample.complex.ComplexBuffer; -import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.MixerTuner; -import io.github.dsheirer.source.tuner.MixerTunerDataLine; -import io.github.dsheirer.source.tuner.TunerChannel; -import io.github.dsheirer.source.tuner.TunerChannelSource; +import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerEvent; import io.github.dsheirer.source.tuner.TunerType; -import java.util.concurrent.RejectedExecutionException; - -public class FCDTuner extends MixerTuner +public class FCDTuner extends Tuner { - public FCDTuner( MixerTunerDataLine mixerTDL, - FCDTunerController controller ) + public FCDTuner(FCDTunerController controller) { - super( controller.getConfiguration().toString(), - controller, - mixerTDL, - new ShortAdapter() ); + super( controller.getConfiguration().toString(), controller); } public void dispose() { - //TODO: release the mixer tuner data line as well - getController().dispose(); } @@ -77,36 +59,4 @@ public class FCDTuner extends MixerTuner { return 16.0; } - - @Override - public TunerChannelSource getChannel( TunerChannel tunerChannel ) - throws RejectedExecutionException, SourceException - { - TunerChannelSource source = getController().getChannel( this, tunerChannel ); - - if( source != null ) - { - broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); - } - - return source; - } - - /** - * Releases the tuned channel so that the tuner controller can tune to - * other frequencies as needed. - */ - @Override - public void releaseChannel( TunerChannelSource source ) - { - if( source != null ) - { - /* Unregister for receiving samples */ - removeListener( (Listener)source ); - - /* Tell the controller to release the channel and cleanup */ - /* This will release the channel as a frequency change listener */ - getController().releaseChannel( source ); - } - } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java index 842335b6..db6cf1bd 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/FCDTunerController.java @@ -18,12 +18,16 @@ ******************************************************************************/ package io.github.dsheirer.source.tuner.fcd; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.adapter.ShortAdapter; +import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.mixer.ComplexMixer; +import io.github.dsheirer.source.tuner.MixerTunerDataLine; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerController; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; -import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.Device; @@ -32,6 +36,7 @@ import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; +import javax.sound.sampled.AudioFormat; import javax.usb.UsbClaimException; import javax.usb.UsbException; import java.nio.ByteBuffer; @@ -54,6 +59,8 @@ public abstract class FCDTunerController extends TunerController private DeviceHandle mDeviceHandle; private FCDConfiguration mConfiguration = new FCDConfiguration(); + protected ComplexMixer mComplexMixer; + /** * Generic FCD tuner controller - contains functionality common across both @@ -64,11 +71,8 @@ public abstract class FCDTunerController extends TunerController * @param minTunableFrequency * @param maxTunableFrequency */ - public FCDTunerController(Device device, - DeviceDescriptor descriptor, - int sampleRate, - int minTunableFrequency, - int maxTunableFrequency) + public FCDTunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor, String tunerName, + int minTunableFrequency, int maxTunableFrequency, AudioFormat audioFormat) { super(minTunableFrequency, maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); mDevice = device; @@ -76,11 +80,45 @@ public abstract class FCDTunerController extends TunerController try { - mFrequencyController.setSampleRate(sampleRate); + mFrequencyController.setSampleRate((int)audioFormat.getSampleRate()); } catch(SourceException se) { - mLog.error("Error setting sample rate to [" + sampleRate + "]", se); + mLog.error("Error setting sample rate to [" + audioFormat.getSampleRate() + "]", se); + } + + mComplexMixer = new ComplexMixer( mixerTDL.getTargetDataLine(), audioFormat, tunerName, new ShortAdapter(), + mSampleBroadcaster); + } + + /** + * Overrides the super class functionality to auto-start the complex mixer and provide samples to listeners + */ + @Override + public void addComplexBufferListener(Listener listener) + { + boolean hasExistingListeners = hasComplexBufferListeners(); + + super.addComplexBufferListener(listener); + + if(!hasExistingListeners) + { + mComplexMixer.start(); + } + } + + /** + * Overrides the super class functionality to auto-stop the complex mixer and stop sample stream when there are no + * more registered listeners + */ + @Override + public void removeComplexBufferListener(Listener listener) + { + super.removeComplexBufferListener(listener); + + if(!hasComplexBufferListeners()) + { + mComplexMixer.stop(); } } @@ -135,7 +173,7 @@ public abstract class FCDTunerController extends TunerController /** * Sample rate of the tuner */ - public abstract int getCurrentSampleRate() throws SourceException; + public abstract double getCurrentSampleRate(); /** * Tuner class @@ -402,7 +440,7 @@ public abstract class FCDTunerController extends TunerController { ByteBuffer buffer = ByteBuffer.allocateDirect(64); - /* The FCD expects little-endian formatted values */ + /* The FCD expects little-endian formatted values */ buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.put(0, command.getCommand()); @@ -538,14 +576,14 @@ public abstract class FCDTunerController extends TunerController return retVal; } - public FCD1TunerController.Block getBandBlocking() + public Block getBandBlocking() { - FCD1TunerController.Block retVal = FCD1TunerController.Block.UNKNOWN; + Block retVal = Block.UNKNOWN; switch(mMode) { case APPLICATION: - retVal = FCD1TunerController.Block.getBlock(mConfig.substring(23, 33).trim()); + retVal = Block.getBlock(mConfig.substring(23, 33).trim()); break; case BOOTLOADER: case UNKNOWN: @@ -615,4 +653,39 @@ public abstract class FCDTunerController extends TunerController return retVal; } } + + public enum Block + { + CELLULAR_BAND_BLOCKED("Blocked"), + NO_BAND_BLOCK("Unblocked"), + UNKNOWN("Unknown"); + + private String mLabel; + + private Block(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static Block getBlock(String block) + { + Block retVal = UNKNOWN; + + if(block.equalsIgnoreCase("No blk")) + { + retVal = NO_BAND_BLOCK; + } + else if(block.equalsIgnoreCase("Cell blk")) + { + retVal = CELLULAR_BAND_BLOCKED; + } + + return retVal; + } + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java index 946c8d43..3049014a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java @@ -18,6 +18,7 @@ package io.github.dsheirer.source.tuner.fcd.proV1; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.MixerTunerDataLine; import io.github.dsheirer.source.tuner.MixerTunerType; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; @@ -36,682 +37,681 @@ import java.nio.ByteOrder; public class FCD1TunerController extends FCDTunerController { - private final static Logger mLog = - LoggerFactory.getLogger( FCD1TunerController.class ); + private final static Logger mLog = LoggerFactory.getLogger(FCD1TunerController.class); - public static final int sMINIMUM_TUNABLE_FREQUENCY = 64000000; - public static final int sMAXIMUM_TUNABLE_FREQUENCY = 1700000000; - public static final int sSAMPLE_RATE = 96000; - - private double mDCCorrectionInPhase = 0.0; - private double mDCCorrectionQuadrature = 0.0; - private double mPhaseCorrection = 0.0; - private double mGainCorrection = 0.0; - private LNAGain mLNAGain; - private LNAEnhance mLNAEnhance; - private MixerGain mMixerGain; - private FCD1TunerConfiguration mTunerConfiguration; - private FCD1TunerEditor mEditor; + public static final int MINIMUM_TUNABLE_FREQUENCY = 64000000; + public static final int MAXIMUM_TUNABLE_FREQUENCY = 1700000000; + public static final int SAMPLE_RATE = 96000; - public FCD1TunerController( Device device, DeviceDescriptor descriptor ) - { - super( device, - descriptor, - (int)MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat().getSampleRate(), - sMINIMUM_TUNABLE_FREQUENCY, - sMAXIMUM_TUNABLE_FREQUENCY ); - } - - public void init() throws SourceException - { - super.init(); - - mFrequencyController.setSampleRate( sSAMPLE_RATE ); - - try - { - setFCDMode( Mode.APPLICATION ); - getPhaseAndGainCorrection(); - getDCIQCorrection(); - getLNAGainSetting(); - getLNAEnhanceSetting(); - getMixerGainSetting(); - - send( FCDCommand.APP_SET_MIXER_GAIN, 1l ); - } - catch( Exception e ) - { - e.printStackTrace(); - - throw new SourceException( "FCDTunerController error " + - "during construction", e ); - } - } - - public int getCurrentSampleRate() - { - return sSAMPLE_RATE; - } + private double mDCCorrectionInPhase = 0.0; + private double mDCCorrectionQuadrature = 0.0; + private double mPhaseCorrection = 0.0; + private double mGainCorrection = 0.0; + private LNAGain mLNAGain; + private LNAEnhance mLNAEnhance; + private MixerGain mMixerGain; + private FCD1TunerConfiguration mTunerConfiguration; + private FCD1TunerEditor mEditor; - @Override + public FCD1TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor) + { + super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO.getDisplayString(), MINIMUM_TUNABLE_FREQUENCY, + MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat()); + } + + public void init() throws SourceException + { + super.init(); + + mFrequencyController.setSampleRate(SAMPLE_RATE); + + try + { + setFCDMode(Mode.APPLICATION); + getPhaseAndGainCorrection(); + getDCIQCorrection(); + getLNAGainSetting(); + getLNAEnhanceSetting(); + getMixerGainSetting(); + + send(FCDCommand.APP_SET_MIXER_GAIN, 1l); + } + catch(Exception e) + { + e.printStackTrace(); + + throw new SourceException("FCDTunerController error " + + "during construction", e); + } + } + + public double getCurrentSampleRate() + { + return SAMPLE_RATE; + } + + @Override public TunerClass getTunerClass() { - return TunerClass.FUNCUBE_DONGLE_PRO; + return TunerClass.FUNCUBE_DONGLE_PRO; } - @Override + @Override public TunerType getTunerType() { - return TunerType.FUNCUBE_DONGLE_PRO; + return TunerType.FUNCUBE_DONGLE_PRO; } - /** - * Get the tuner configuration - * - * @return current config or null if one hasn't been applied yet - */ - public FCD1TunerConfiguration getTunerConfiguration() - { - return mTunerConfiguration; - } - - @Override - public void apply( TunerConfiguration tunerConfig ) - throws SourceException + /** + * Get the tuner configuration + * + * @return current config or null if one hasn't been applied yet + */ + public FCD1TunerConfiguration getTunerConfiguration() { - FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig; - - try - { - setFrequencyCorrection( config.getFrequencyCorrection() ); - setLNAGain( config.getLNAGain() ); - setLNAEnhance( config.getLNAEnhance() ); - setMixerGain( config.getMixerGain() ); - setDCCorrectionInPhase( config.getInphaseDCCorrection() ); - setDCCorrectionQuadrature( config.getQuadratureDCCorrection() ); - setGainCorrection( config.getGainCorrection() ); - setPhaseCorrection( config.getPhaseCorrection() ); - - //If we get to here without exception, store this config - mTunerConfiguration = config; - } - catch( Exception e) - { - //Re-cast any usb exceptions as source exceptions - throw new SourceException( "FCD 1 Controller encountered an " - + "exception while applying tuner config - " + - e.getLocalizedMessage() ); - } - - try - { - setFrequency( config.getFrequency() ); - } - catch( SourceException se ) - { - //Do nothing, we couldn't set the frequency - } - } + return mTunerConfiguration; + } - /** - * Gets the current LNA Gain value from the controller and stores it - */ - private void getLNAGainSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_LNA_GAIN ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int gain = buffer.getInt( 2 ); + @Override + public void apply(TunerConfiguration tunerConfig) + throws SourceException + { + FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig; - mLNAGain = LNAGain.valueOf( gain ); - } - - /** - * @return - current lna gain setting - */ - public LNAGain getLNAGain() - { - return mLNAGain; - } + try + { + setFrequencyCorrection(config.getFrequencyCorrection()); + setLNAGain(config.getLNAGain()); + setLNAEnhance(config.getLNAEnhance()); + setMixerGain(config.getMixerGain()); + setDCCorrectionInPhase(config.getInphaseDCCorrection()); + setDCCorrectionQuadrature(config.getQuadratureDCCorrection()); + setGainCorrection(config.getGainCorrection()); + setPhaseCorrection(config.getPhaseCorrection()); - /** - * Sets lna gain for the controller - * @param gain - * @throws UsbException - * @throws UsbClaimException - */ - public void setLNAGain( LNAGain gain ) - throws UsbException, UsbClaimException - { - send( FCDCommand.APP_SET_LNA_GAIN, gain.getSetting() ); - } - - /** - * @return - current phase correction setting - */ - public double getPhaseCorrection() - { - return mPhaseCorrection; - } + //If we get to here without exception, store this config + mTunerConfiguration = config; + } + catch(Exception e) + { + //Re-cast any usb exceptions as source exceptions + throw new SourceException("FCD 1 Controller encountered an " + + "exception while applying tuner config - " + + e.getLocalizedMessage()); + } - /** - * Sets the phase correction - * @param value - new phase correction - * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setPhaseCorrection( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "Phase correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )" ); - } - - mPhaseCorrection = value; - setPhaseAndGainCorrection(); - } + try + { + setFrequency(config.getFrequency()); + } + catch(SourceException se) + { + //Do nothing, we couldn't set the frequency + } + } - /** - * @return current gain correction setting - */ - public double getGainCorrection() - { - return mGainCorrection; - } + /** + * Gets the current LNA Gain value from the controller and stores it + */ + private void getLNAGainSetting() throws UsbClaimException, UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_GAIN); - /** - * - * @param value - gain correction value - * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setGainCorrection( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "Gain correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )" ); - } - - mGainCorrection = value; - setPhaseAndGainCorrection(); - } - - /** - * Sends the phase and gain correction values to FCD. - * - * The interface expects an argument with the SIGNED phase value in the first - * 16 bits and the UNSIGNED gain value in the second 16 bits, both in - * big-endian format. Since we're sending the argument in java little endian - * format, we place the phase in the high-order bits and the gain in the - * low-order bits, so that the send(command) function will reorder the bytes - * into big endian format, and place the arguments correctly. - * - * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle - * as values 0 to 65534 - * - * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the - * dongle as signed values -32768 to 32767 - */ - private void setPhaseAndGainCorrection() throws UsbClaimException, - UsbException - { - //UNSIGNED short gain value - masked into a long to avoid sign-extension - long gain = (long)( ( 1.0 + mGainCorrection ) * Short.MAX_VALUE ); - - //Left shift gain to place value in upper 16 bits - long longGain = Long.rotateLeft( ( 0xFFFF & gain ), 16 ); + buffer.order(ByteOrder.LITTLE_ENDIAN); - //SIGNED short phase value - short phase = (short)( mPhaseCorrection * Short.MAX_VALUE ); + int gain = buffer.getInt(2); - //Merge the results - long correction = longGain | phase; + mLNAGain = LNAGain.valueOf(gain); + } - send( FCDCommand.APP_SET_IQ_CORRECTION, correction ); - } + /** + * @return - current lna gain setting + */ + public LNAGain getLNAGain() + { + return mLNAGain; + } - /** - * Retrieves the stored phase and gain correction values from the FCD. - * - * Note: testing shows that when the dongle is unplugged, any stored phase - * and gain correction values are reset. Reading the dongle after a fresh - * plugin shows the values reset. - * - * @throws UsbClaimException - * @throws UsbException - */ - private void getPhaseAndGainCorrection() throws UsbClaimException, - UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_IQ_CORRECTION ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int correction = buffer.getInt( 2 ); - - /** - * Gain: mask the upper 16 phase bits and right shift to get the - * unsigned short value and then divide by 32768 to get the double - * value. We place the unsigned short value in an int to avoid sign - * issues. - */ - int gain = (int)( Long.rotateRight( correction & 0xFFFF0000, 16 ) ); + /** + * Sets lna gain for the controller + * + * @param gain + * @throws UsbException + * @throws UsbClaimException + */ + public void setLNAGain(LNAGain gain) + throws UsbException, UsbClaimException + { + send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting()); + } - mGainCorrection = ( (double)gain / (double)Short.MAX_VALUE ) - 1.0; + /** + * @return - current phase correction setting + */ + public double getPhaseCorrection() + { + return mPhaseCorrection; + } - /** - * Phase: mask the lower 16 bits and divide by 32768 to get the double - * value - */ - short phase = (short)( correction & 0x0000FFFF ); - - mPhaseCorrection = ( (double)phase / (double)Short.MAX_VALUE ); - } - - /** - * @return current DC correction for the I (inphase) component - */ - public double getDCCorrectionInPhase() - { - return mDCCorrectionInPhase; - } + /** + * Sets the phase correction + * + * @param value - new phase correction + * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setPhaseCorrection(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("Phase correction value [" + + value + "] outside limits ( -1.0 <> 1.0 )"); + } - /** - * Sets DC correction for the I (inphase) component and sends the new value - * to the FCD controller - * @param value - new DC correction value for the inphase component - * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setDCCorrectionInPhase( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "DC inphase correction value " - + "[" + value + "] outside limits ( 0.0 <> 1.0 )" ); - } - - mDCCorrectionInPhase = value; - setDCIQCorrection(); - } + mPhaseCorrection = value; + setPhaseAndGainCorrection(); + } - /** - * @return current DC correction for the Q (quadrature) component - */ - public double getDCCorrectionQuadrature() - { - return mDCCorrectionQuadrature; - } + /** + * @return current gain correction setting + */ + public double getGainCorrection() + { + return mGainCorrection; + } - /** - * Sets the DC correction for the Q (quadrature) component and send the - * new value to the FCD controller - * @param value - new DC correction value for the quadrature component - * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setDCCorrectionQuadrature( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "DC quadrature correction " - + "value [" + value + "] outside limits ( 0.0 <> 1.0 )" ); - } - - mDCCorrectionQuadrature = value; - setDCIQCorrection(); - } + /** + * @param value - gain correction value + * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setGainCorrection(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("Gain correction value [" + + value + "] outside limits ( -1.0 <> 1.0 )"); + } - /** - * Sends the I/Q DC offset correction values to FCD. - * - * The interface expects an argument with the signed inphase value in the - * first 16 bits and the signed quadrature value in the second 16 bits, - * both in big-endian format. - * - * Since we're sending the argument in java little endian format, we place - * the quadrature in the high-order bits and the inphase in the low-order - * bits, so that the send(command) function will reorder the bytes into big - * endian format, and place the arguments correctly. - * - * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 - * and are applied to the dongle as signed short values -32768 to 0 to 32767 - */ - private void setDCIQCorrection() throws UsbClaimException, UsbException - { - //I & Q DC offset correction values are signed short values - short inphase = (short)( mDCCorrectionInPhase * Short.MAX_VALUE ); - short quadrature = (short)( mDCCorrectionQuadrature * Short.MAX_VALUE ); + mGainCorrection = value; + setPhaseAndGainCorrection(); + } + + /** + * Sends the phase and gain correction values to FCD. + * + * The interface expects an argument with the SIGNED phase value in the first + * 16 bits and the UNSIGNED gain value in the second 16 bits, both in + * big-endian format. Since we're sending the argument in java little endian + * format, we place the phase in the high-order bits and the gain in the + * low-order bits, so that the send(command) function will reorder the bytes + * into big endian format, and place the arguments correctly. + * + * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle + * as values 0 to 65534 + * + * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the + * dongle as signed values -32768 to 32767 + */ + private void setPhaseAndGainCorrection() throws UsbClaimException, + UsbException + { + //UNSIGNED short gain value - masked into a long to avoid sign-extension + long gain = (long)((1.0 + mGainCorrection) * Short.MAX_VALUE); + + //Left shift gain to place value in upper 16 bits + long longGain = Long.rotateLeft((0xFFFF & gain), 16); + + //SIGNED short phase value + short phase = (short)(mPhaseCorrection * Short.MAX_VALUE); + + //Merge the results + long correction = longGain | phase; + + send(FCDCommand.APP_SET_IQ_CORRECTION, correction); + } + + /** + * Retrieves the stored phase and gain correction values from the FCD. + * + * Note: testing shows that when the dongle is unplugged, any stored phase + * and gain correction values are reset. Reading the dongle after a fresh + * plugin shows the values reset. + * + * @throws UsbClaimException + * @throws UsbException + */ + private void getPhaseAndGainCorrection() throws UsbClaimException, + UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_IQ_CORRECTION); + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int correction = buffer.getInt(2); + + /** + * Gain: mask the upper 16 phase bits and right shift to get the + * unsigned short value and then divide by 32768 to get the double + * value. We place the unsigned short value in an int to avoid sign + * issues. + */ + int gain = (int)(Long.rotateRight(correction & 0xFFFF0000, 16)); + + mGainCorrection = ((double)gain / (double)Short.MAX_VALUE) - 1.0; + + /** + * Phase: mask the lower 16 bits and divide by 32768 to get the double + * value + */ + short phase = (short)(correction & 0x0000FFFF); + + mPhaseCorrection = ((double)phase / (double)Short.MAX_VALUE); + } + + /** + * @return current DC correction for the I (inphase) component + */ + public double getDCCorrectionInPhase() + { + return mDCCorrectionInPhase; + } + + /** + * Sets DC correction for the I (inphase) component and sends the new value + * to the FCD controller + * + * @param value - new DC correction value for the inphase component + * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setDCCorrectionInPhase(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("DC inphase correction value " + + "[" + value + "] outside limits ( 0.0 <> 1.0 )"); + } + + mDCCorrectionInPhase = value; + setDCIQCorrection(); + } + + /** + * @return current DC correction for the Q (quadrature) component + */ + public double getDCCorrectionQuadrature() + { + return mDCCorrectionQuadrature; + } + + /** + * Sets the DC correction for the Q (quadrature) component and send the + * new value to the FCD controller + * + * @param value - new DC correction value for the quadrature component + * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setDCCorrectionQuadrature(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("DC quadrature correction " + + "value [" + value + "] outside limits ( 0.0 <> 1.0 )"); + } + + mDCCorrectionQuadrature = value; + setDCIQCorrection(); + } + + /** + * Sends the I/Q DC offset correction values to FCD. + * + * The interface expects an argument with the signed inphase value in the + * first 16 bits and the signed quadrature value in the second 16 bits, + * both in big-endian format. + * + * Since we're sending the argument in java little endian format, we place + * the quadrature in the high-order bits and the inphase in the low-order + * bits, so that the send(command) function will reorder the bytes into big + * endian format, and place the arguments correctly. + * + * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 + * and are applied to the dongle as signed short values -32768 to 0 to 32767 + */ + private void setDCIQCorrection() throws UsbClaimException, UsbException + { + //I & Q DC offset correction values are signed short values + short inphase = (short)(mDCCorrectionInPhase * Short.MAX_VALUE); + short quadrature = (short)(mDCCorrectionQuadrature * Short.MAX_VALUE); + + //Mask the shorts into longs to preserve the sign bit and prepare + //for merging to a single 32-bit value + long maskedInphase = inphase & 0x0000FFFF; + long maskedQuadrature = quadrature & 0x0000FFFF; + + //Left shift quadrature to place value in upper 16 bits + long shiftedQuadrature = Long.rotateLeft(maskedQuadrature, 16); + + //Merge the results + long correction = shiftedQuadrature | maskedInphase; - //Mask the shorts into longs to preserve the sign bit and prepare - //for merging to a single 32-bit value - long maskedInphase = inphase & 0x0000FFFF; - long maskedQuadrature = quadrature & 0x0000FFFF; - - //Left shift quadrature to place value in upper 16 bits - long shiftedQuadrature = Long.rotateLeft( maskedQuadrature, 16 ); - - //Merge the results - long correction = shiftedQuadrature | maskedInphase; - // Log.info( "FCD1: setting iq correction," + // " inphase:" + inphase + -// " masked inphase:" + maskedInphase + -// " quad:" + quadrature + -// " masked quad:" + maskedQuadrature + -// " shifted quadrature:" + shiftedQuadrature + +// " masked inphase:" + maskedInphase + +// " quad:" + quadrature + +// " masked quad:" + maskedQuadrature + +// " shifted quadrature:" + shiftedQuadrature + // " correction:" + correction ); - send( FCDCommand.APP_SET_DC_CORRECTION, correction ); - } - - private void getDCIQCorrection() throws UsbClaimException, - UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_DC_CORRECTION ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int correction = buffer.getInt( 2 ); + send(FCDCommand.APP_SET_DC_CORRECTION, correction); + } - /** - * Quadrature: mask the upper 16 bits and divide by 32768 to get the - * stored quadrature value. Cast the 16-bit value to a short to get - * the signed short value - */ - long shiftedQuadrature = correction & 0xFFFF0000; - long quadrature = Long.rotateRight( shiftedQuadrature, 16 ); + private void getDCIQCorrection() throws UsbClaimException, + UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_DC_CORRECTION); - mDCCorrectionQuadrature = ( (short)quadrature / (double)Short.MAX_VALUE ); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int correction = buffer.getInt(2); + + /** + * Quadrature: mask the upper 16 bits and divide by 32768 to get the + * stored quadrature value. Cast the 16-bit value to a short to get + * the signed short value + */ + long shiftedQuadrature = correction & 0xFFFF0000; + long quadrature = Long.rotateRight(shiftedQuadrature, 16); + + mDCCorrectionQuadrature = ((short)quadrature / (double)Short.MAX_VALUE); + + /** + * InPhase: mask the lower 16 bits to get the short + * value and then divide by 32768 to get the stored inphase value. + * Cast the 16-bit value to a short to get the signed short value + */ + long inphase = correction & 0x0000FFFF; + mDCCorrectionInPhase = ((short)inphase / (double)Short.MAX_VALUE); - /** - * InPhase: mask the lower 16 bits to get the short - * value and then divide by 32768 to get the stored inphase value. - * Cast the 16-bit value to a short to get the signed short value - */ - long inphase = correction & 0x0000FFFF; - mDCCorrectionInPhase = ( (short)inphase / (double)Short.MAX_VALUE ); - // Log.info( "FCD1:" + -// " correction:" + correction + -// " Q:" + quadrature + -// " I:" + inphase + -// " QCorr:" + mDCCorrectionQuadrature + +// " correction:" + correction + +// " Q:" + quadrature + +// " I:" + inphase + +// " QCorr:" + mDCCorrectionQuadrature + // " ICorr:" + mDCCorrectionInPhase ); - } - - private void getLNAEnhanceSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_LNA_ENHANCE ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int enhance = buffer.getInt( 2 ); - - mLNAEnhance = LNAEnhance.valueOf( enhance ); - } - - public LNAEnhance getLNAEnhance() - { - return mLNAEnhance; - } - - public void setLNAEnhance( LNAEnhance enhance ) - throws UsbClaimException, UsbException - { - send( FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting() ); - } - - private void getMixerGainSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_MIXER_GAIN ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int gain = buffer.getInt( 2 ); - - mMixerGain = MixerGain.valueOf( gain ); - } - - public MixerGain getMixerGain() - { - return mMixerGain; - } - - public void setMixerGain( MixerGain gain ) throws UsbClaimException, UsbException - { - send( FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting() ); - } - - public enum Block - { - CELLULAR_BAND_BLOCKED( "Blocked" ), - NO_BAND_BLOCK( "Unblocked" ), - UNKNOWN( "Unknown" ); - - private String mLabel; - - private Block( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static Block getBlock( String block ) - { - Block retVal = UNKNOWN; - - if( block.equalsIgnoreCase( "No blk" ) ) - { - retVal = NO_BAND_BLOCK; - } - else if( block.equalsIgnoreCase( "Cell blk" ) ) - { - retVal = CELLULAR_BAND_BLOCKED; - } - - return retVal; - } - } - - /** - * LNA Gain values suppported by the FCD Pro 1.0 - */ - public enum LNAGain - { - LNA_GAIN_MINUS_5_0( 0, "-5.0 dB" ), - LNA_GAIN_MINUS_2_5( 1, "-2.5 dB" ), - LNA_GAIN_PLUS_0_0( 4, "0.0 dB" ), - LNA_GAIN_PLUS_2_5( 5, "2.5 dB" ), - LNA_GAIN_PLUS_5_0( 6, "5.0 dB" ), - LNA_GAIN_PLUS_7_5( 7, "7.5 dB" ), - LNA_GAIN_PLUS_10_0( 8, "10.0 dB" ), - LNA_GAIN_PLUS_12_5( 9, "12.5 dB" ), - LNA_GAIN_PLUS_15_0( 10, "15.0 dB" ), - LNA_GAIN_PLUS_17_5( 11, "17.5 dB" ), - LNA_GAIN_PLUS_20_0( 12, "20.0 dB" ), - LNA_GAIN_PLUS_25_0( 13, "25.0 dB" ), - LNA_GAIN_PLUS_30_0( 14, "30.0 dB" ); - - private int mSetting; - private String mLabel; - - private LNAGain( int setting, String label ) - { - mSetting = setting; - mLabel = label; - } - - public static LNAGain valueOf( int setting ) - { - switch( setting ) - { - case 0: - return LNA_GAIN_MINUS_5_0; - case 1: - return LNA_GAIN_MINUS_2_5; - case 4: - return LNA_GAIN_PLUS_0_0; - case 5: - return LNA_GAIN_PLUS_2_5; - case 6: - return LNA_GAIN_PLUS_5_0; - case 7: - return LNA_GAIN_PLUS_7_5; - case 8: - return LNA_GAIN_PLUS_10_0; - case 9: - return LNA_GAIN_PLUS_12_5; - case 10: - return LNA_GAIN_PLUS_15_0; - case 11: - return LNA_GAIN_PLUS_17_5; - case 12: - return LNA_GAIN_PLUS_20_0; - case 13: - return LNA_GAIN_PLUS_25_0; - case 14: - return LNA_GAIN_PLUS_30_0; - default: - throw new IllegalArgumentException( "FCD 1.0 Tuner " - + "Controller - unrecognized LNA gain setting " - + "value [" + setting + "]" ); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } } - /** - * LNA Enhance values suppported by the FCD Pro 1.0 - */ - public enum LNAEnhance + private void getLNAEnhanceSetting() throws UsbClaimException, UsbException { - LNA_ENHANCE_OFF( 0, "Off" ), - LNA_ENHANCE_0( 1, "0" ), - LNA_ENHANCE_1( 3, "1" ), - LNA_ENHANCE_2( 5, "2" ), - LNA_ENHANCE_3( 7, "3" ); - - private int mSetting; - private String mLabel; - - private LNAEnhance( int setting, String label ) - { - mSetting = setting; - mLabel = label; - } - - public static LNAEnhance valueOf( int setting ) - { - switch( setting ) - { - case 0: - return LNA_ENHANCE_OFF; - case 1: - return LNA_ENHANCE_0; - case 3: - return LNA_ENHANCE_1; - case 5: - return LNA_ENHANCE_2; - case 7: - return LNA_ENHANCE_3; - default: - throw new IllegalArgumentException( "FCD 1.0 Tuner " - + "Controller - unrecognized LNA enhance setting " - + "value [" + setting + "]" ); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } + ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_ENHANCE); + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int enhance = buffer.getInt(2); + + mLNAEnhance = LNAEnhance.valueOf(enhance); } - /** - * Mixer Gain values suppported by the FCD Pro 1.0 - */ - public enum MixerGain + public LNAEnhance getLNAEnhance() { - MIXER_GAIN_PLUS_4_0( 0, "4.0 dB" ), - MIXER_GAIN_PLUS_12_0( 1, "12.0 dB" ); - - private int mSetting; - private String mLabel; - - private MixerGain( int setting, String label ) - { - mSetting = setting; - mLabel = label; - } - - public static MixerGain valueOf( int setting ) - { - switch( setting ) - { - case 0: - return MIXER_GAIN_PLUS_4_0; - case 1: - return MIXER_GAIN_PLUS_12_0; - default: - throw new IllegalArgumentException( "FCD 1.0 Tuner " - + "Controller - unrecognized mixer gain setting " - + "value [" + setting + "]" ); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } + return mLNAEnhance; } -} + + public void setLNAEnhance(LNAEnhance enhance) + throws UsbClaimException, UsbException + { + send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting()); + } + + private void getMixerGainSetting() throws UsbClaimException, UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_MIXER_GAIN); + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int gain = buffer.getInt(2); + + mMixerGain = MixerGain.valueOf(gain); + } + + public MixerGain getMixerGain() + { + return mMixerGain; + } + + public void setMixerGain(MixerGain gain) throws UsbClaimException, UsbException + { + send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting()); + } + + public enum Block + { + CELLULAR_BAND_BLOCKED("Blocked"), + NO_BAND_BLOCK("Unblocked"), + UNKNOWN("Unknown"); + + private String mLabel; + + private Block(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static Block getBlock(String block) + { + Block retVal = UNKNOWN; + + if(block.equalsIgnoreCase("No blk")) + { + retVal = NO_BAND_BLOCK; + } + else if(block.equalsIgnoreCase("Cell blk")) + { + retVal = CELLULAR_BAND_BLOCKED; + } + + return retVal; + } + } + + /** + * LNA Gain values suppported by the FCD Pro 1.0 + */ + public enum LNAGain + { + LNA_GAIN_MINUS_5_0(0, "-5.0 dB"), + LNA_GAIN_MINUS_2_5(1, "-2.5 dB"), + LNA_GAIN_PLUS_0_0(4, "0.0 dB"), + LNA_GAIN_PLUS_2_5(5, "2.5 dB"), + LNA_GAIN_PLUS_5_0(6, "5.0 dB"), + LNA_GAIN_PLUS_7_5(7, "7.5 dB"), + LNA_GAIN_PLUS_10_0(8, "10.0 dB"), + LNA_GAIN_PLUS_12_5(9, "12.5 dB"), + LNA_GAIN_PLUS_15_0(10, "15.0 dB"), + LNA_GAIN_PLUS_17_5(11, "17.5 dB"), + LNA_GAIN_PLUS_20_0(12, "20.0 dB"), + LNA_GAIN_PLUS_25_0(13, "25.0 dB"), + LNA_GAIN_PLUS_30_0(14, "30.0 dB"); + + private int mSetting; + private String mLabel; + + private LNAGain(int setting, String label) + { + mSetting = setting; + mLabel = label; + } + + public static LNAGain valueOf(int setting) + { + switch(setting) + { + case 0: + return LNA_GAIN_MINUS_5_0; + case 1: + return LNA_GAIN_MINUS_2_5; + case 4: + return LNA_GAIN_PLUS_0_0; + case 5: + return LNA_GAIN_PLUS_2_5; + case 6: + return LNA_GAIN_PLUS_5_0; + case 7: + return LNA_GAIN_PLUS_7_5; + case 8: + return LNA_GAIN_PLUS_10_0; + case 9: + return LNA_GAIN_PLUS_12_5; + case 10: + return LNA_GAIN_PLUS_15_0; + case 11: + return LNA_GAIN_PLUS_17_5; + case 12: + return LNA_GAIN_PLUS_20_0; + case 13: + return LNA_GAIN_PLUS_25_0; + case 14: + return LNA_GAIN_PLUS_30_0; + default: + throw new IllegalArgumentException("FCD 1.0 Tuner " + + "Controller - unrecognized LNA gain setting " + + "value [" + setting + "]"); + } + } + + public int getSetting() + { + return mSetting; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } + } + + /** + * LNA Enhance values suppported by the FCD Pro 1.0 + */ + public enum LNAEnhance + { + LNA_ENHANCE_OFF(0, "Off"), + LNA_ENHANCE_0(1, "0"), + LNA_ENHANCE_1(3, "1"), + LNA_ENHANCE_2(5, "2"), + LNA_ENHANCE_3(7, "3"); + + private int mSetting; + private String mLabel; + + private LNAEnhance(int setting, String label) + { + mSetting = setting; + mLabel = label; + } + + public static LNAEnhance valueOf(int setting) + { + switch(setting) + { + case 0: + return LNA_ENHANCE_OFF; + case 1: + return LNA_ENHANCE_0; + case 3: + return LNA_ENHANCE_1; + case 5: + return LNA_ENHANCE_2; + case 7: + return LNA_ENHANCE_3; + default: + throw new IllegalArgumentException("FCD 1.0 Tuner " + + "Controller - unrecognized LNA enhance setting " + + "value [" + setting + "]"); + } + } + + public int getSetting() + { + return mSetting; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } + } + + /** + * Mixer Gain values suppported by the FCD Pro 1.0 + */ + public enum MixerGain + { + MIXER_GAIN_PLUS_4_0(0, "4.0 dB"), + MIXER_GAIN_PLUS_12_0(1, "12.0 dB"); + + private int mSetting; + private String mLabel; + + private MixerGain(int setting, String label) + { + mSetting = setting; + mLabel = label; + } + + public static MixerGain valueOf(int setting) + { + switch(setting) + { + case 0: + return MIXER_GAIN_PLUS_4_0; + case 1: + return MIXER_GAIN_PLUS_12_0; + default: + throw new IllegalArgumentException("FCD 1.0 Tuner " + + "Controller - unrecognized mixer gain setting " + + "value [" + setting + "]"); + } + } + + public int getSetting() + { + return mSetting; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java index 1c785f29..eb8ac835 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -21,13 +21,26 @@ import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; +import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent.Event; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.fcd.FCDTuner; +import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; +import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.LNAGain; +import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.MixerGain; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.event.ActionEvent; @@ -38,30 +51,30 @@ import java.text.DecimalFormat; public class FCD1TunerEditor extends TunerConfigurationEditor { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( FCD1TunerEditor.class ); - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JSpinner mFrequencyCorrection; - private JComboBox mComboLNAGain; - private JComboBox mComboLNAEnhance; - private JComboBox mComboMixerGain; - private CorrectionSpinner mCorrectionDCI; - private CorrectionSpinner mCorrectionDCQ; - private CorrectionSpinner mCorrectionGain; - private CorrectionSpinner mCorrectionPhase; - private boolean mLoading; - private FCD1TunerController mController; + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JSpinner mFrequencyCorrection; + private JComboBox mComboLNAGain; + private JComboBox mComboLNAEnhance; + private JComboBox mComboMixerGain; + private CorrectionSpinner mCorrectionDCI; + private CorrectionSpinner mCorrectionDCQ; + private CorrectionSpinner mCorrectionGain; + private CorrectionSpinner mCorrectionPhase; + private boolean mLoading; - public FCD1TunerEditor(TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) - { - super( tunerConfigurationModel ); - mController = (FCD1TunerController)tuner.getController(); - - init(); - } + private FCD1TunerController mController; + + public FCD1TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) + { + super( tunerConfigurationModel ); + mController = (FCD1TunerController)tuner.getController(); + + init(); + } private FCD1TunerConfiguration getConfiguration() { @@ -69,33 +82,33 @@ public class FCD1TunerEditor extends TunerConfigurationEditor { return (FCD1TunerConfiguration)getItem(); } - + return null; } private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][][][][][grow]" ) ); - + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][][][][][grow]" ) ); + add( new JLabel( "FCD Pro Tuner Configuration" ), "span,align center" ); mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() + mConfigurationName.addFocusListener( new FocusListener() { @Override - public void focusLost( FocusEvent e ) - { + public void focusLost( FocusEvent e ) + { save(); - } + } @Override - public void focusGained( FocusEvent e ) {} + public void focusGained( FocusEvent e ) {} } ); - + add( new JLabel( "Name:" ) ); add( mConfigurationName, "span 2" ); - + mTunerInfo = new JButton( "Tuner Info" ); mTunerInfo.setEnabled( false ); mTunerInfo.addActionListener( new ActionListener() @@ -103,189 +116,189 @@ public class FCD1TunerEditor extends TunerConfigurationEditor @Override public void actionPerformed( ActionEvent e ) { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + JOptionPane.showMessageDialog( FCD1TunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); } } ); add( mTunerInfo ); - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - mFrequencyCorrection.addChangeListener( new ChangeListener() - { + mFrequencyCorrection.addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { + public void stateChanged( ChangeEvent e ) + { double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); + .getModel()).getNumber().doubleValue(); try - { - mController.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner " - + "Controller - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDonglePro Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection, "wrap" ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner " + + "Controller - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "FuncubeDonglePro Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); add( new JLabel( "Gain" ), "span,align center" ); - mComboLNAGain = new JComboBox( FCD1TunerController.LNAGain.values() ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain setting." ); - mComboLNAGain.addActionListener( new ActionListener() - { + mComboLNAGain = new JComboBox( LNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain setting." ); + mComboLNAGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { - FCD1TunerController.LNAGain gain = (FCD1TunerController.LNAGain)mComboLNAGain.getSelectedItem(); - - + public void actionPerformed( ActionEvent arg0 ) + { + LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); + + try - { + { mController.setLNAGain( gain ); save(); - } - catch ( Exception e ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner Controller" - + " - error setting LNA gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - error setting gain [" + gain.toString() + "]", e ); - } - } - } ); - - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); - - /** - * LNA Enhance - */ - mComboLNAEnhance = new JComboBox( FCD1TunerController.LNAEnhance.values() ); - mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. Default value is OFF" ); + } + catch ( Exception e ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner Controller" + + " - error setting LNA gain [" + gain.toString() + "]" ); - mComboLNAEnhance.addActionListener( new ActionListener() - { + mLog.error( "FuncubeDonglePro Controller - error setting gain [" + gain.toString() + "]", e ); + } + } + } ); + + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + /** + * LNA Enhance + */ + mComboLNAEnhance = new JComboBox( LNAEnhance.values() ); + mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. Default value is OFF" ); + + mComboLNAEnhance.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { - FCD1TunerController.LNAEnhance enhance = (FCD1TunerController.LNAEnhance)mComboLNAEnhance.getSelectedItem(); + public void actionPerformed( ActionEvent arg0 ) + { + LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); try - { + { mController.setLNAEnhance( enhance ); save(); - } - catch ( Exception e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner" - + " error setting LNA enhance gain [" + enhance.toString() + "]" ); - - mLog.error( "FCDPro - error setting LNA enhance [" + enhance.toString() + "]", e1 ); - } - } - } ); - - add( new JLabel( "Enhance:" ) ); - add( mComboLNAEnhance ); - - /** - * Mixer Gain - */ - mComboMixerGain = new JComboBox( FCD1TunerController.MixerGain.values() ); - mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); - + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner" + + " error setting LNA enhance gain [" + enhance.toString() + "]" ); - mComboMixerGain.addActionListener( new ActionListener() - { + mLog.error( "FCDPro - error setting LNA enhance [" + enhance.toString() + "]", e1 ); + } + } + } ); + + add( new JLabel( "Enhance:" ) ); + add( mComboLNAEnhance ); + + /** + * Mixer Gain + */ + mComboMixerGain = new JComboBox( MixerGain.values() ); + mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); + + + mComboMixerGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { - FCD1TunerController.MixerGain gain = (FCD1TunerController.MixerGain)mComboMixerGain.getSelectedItem(); - + public void actionPerformed( ActionEvent arg0 ) + { + MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); + try - { + { mController.setMixerGain( gain ); save(); - } - catch ( Exception e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error setting" - + " mixer gain [" + gain.toString() + "]" ); - - mLog.error( "FCDPro - error setting mixer gain [" + gain.toString() + "]", e1 ); - } - } - } ); - - add( new JLabel( "Mixer:" ) ); - add( mComboMixerGain, "wrap" ); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error setting" + + " mixer gain [" + gain.toString() + "]" ); - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + mLog.error( "FCDPro - error setting mixer gain [" + gain.toString() + "]", e1 ); + } + } + } ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); add( new JLabel( "Correction" ), "span,align center" ); - /** - * Inphase DC Correction - */ - mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, 0.0, 0.00001, 5 ); - mCorrectionDCI.setEnabled( false ); - mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "DC Inphase:" ) ); - add( mCorrectionDCI ); + /** + * Inphase DC Correction + */ + mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, 0.0, 0.00001, 5 ); + mCorrectionDCI.setEnabled( false ); + mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " + + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "DC Inphase:" ) ); + add( mCorrectionDCI ); - /** - * Quadrature DC Correction - */ - mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, 0.0, 0.00001, 5 ); - mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "DC Quadrature:" ) ); - add( mCorrectionDCQ ); + /** + * Quadrature DC Correction + */ + mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, 0.0, 0.00001, 5 ); + mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " + + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "DC Quadrature:" ) ); + add( mCorrectionDCQ ); - /** - * Gain Correction - */ - mCorrectionGain = new CorrectionSpinner( Correction.GAIN, 0.0, 0.00001, 5 ); - mCorrectionGain.setToolTipText( "Gain Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "Gain:" ) ); - add( mCorrectionGain ); + /** + * Gain Correction + */ + mCorrectionGain = new CorrectionSpinner( Correction.GAIN, 0.0, 0.00001, 5 ); + mCorrectionGain.setToolTipText( "Gain Correction: valid values are " + + "-1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "Gain:" ) ); + add( mCorrectionGain ); - /** - * Phase Correction - */ - mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, 0.0, 0.00001, 5 ); - mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "Phase:" ) ); - add( mCorrectionPhase ); - } + /** + * Phase Correction + */ + mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, 0.0, 0.00001, 5 ); + mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " + + "-1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "Phase:" ) ); + add( mCorrectionPhase ); + } /** * Sets each of the tuner configuration controls to the enabled argument state @@ -296,12 +309,12 @@ public class FCD1TunerEditor extends TunerConfigurationEditor { mConfigurationName.setEnabled( enabled ); } - + if( mTunerInfo.isEnabled() != enabled ) { mTunerInfo.setEnabled( enabled ); } - + if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); @@ -311,32 +324,32 @@ public class FCD1TunerEditor extends TunerConfigurationEditor { mComboLNAEnhance.setEnabled( enabled ); } - + if( mComboLNAGain.isEnabled() != enabled ) { mComboLNAGain.setEnabled( enabled ); } - + if( mComboMixerGain.isEnabled() != enabled ) { mComboMixerGain.setEnabled( enabled ); } - + if( mCorrectionDCI.isEnabled() != enabled ) { mCorrectionDCI.setEnabled( enabled ); } - + if( mCorrectionDCQ.isEnabled() != enabled ) { mCorrectionDCQ.setEnabled( enabled ); } - + if( mCorrectionGain.isEnabled() != enabled ) { mCorrectionGain.setEnabled( enabled ); } - + if( mCorrectionPhase.isEnabled() != enabled ) { mCorrectionPhase.setEnabled( enabled ); @@ -348,7 +361,7 @@ public class FCD1TunerEditor extends TunerConfigurationEditor StringBuilder sb = new StringBuilder(); sb.append( "

Funcube Dongle Pro Tuner

" ); - + sb.append( "USB ID: " ); sb.append( mController.getUSBID() ); sb.append( "
" ); @@ -371,37 +384,37 @@ public class FCD1TunerEditor extends TunerConfigurationEditor return sb.toString(); } - + @Override public void save() { - if( hasItem() && !mLoading ) - { - FCD1TunerConfiguration config = getConfiguration(); + if( hasItem() && !mLoading ) + { + FCD1TunerConfiguration config = getConfiguration(); - config.setName( mConfigurationName.getText() ); + config.setName( mConfigurationName.getText() ); double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - config.setLNAEnhance( (FCD1TunerController.LNAEnhance)mComboLNAEnhance.getSelectedItem() ); - config.setLNAGain( (FCD1TunerController.LNAGain)mComboLNAGain.getSelectedItem() ); - config.setMixerGain( (FCD1TunerController.MixerGain)mComboMixerGain.getSelectedItem() ); + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + config.setLNAEnhance( (LNAEnhance)mComboLNAEnhance.getSelectedItem() ); + config.setLNAGain( (LNAGain)mComboLNAGain.getSelectedItem() ); + config.setMixerGain( (MixerGain)mComboMixerGain.getSelectedItem() ); double dci = ((SpinnerNumberModel)mCorrectionDCI.getModel()).getNumber().doubleValue(); - config.setInphaseDCCorrection( dci ); - + config.setInphaseDCCorrection( dci ); + double dcq = ((SpinnerNumberModel)mCorrectionDCQ.getModel()).getNumber().doubleValue(); - config.setQuadratureDCCorrection( dcq ); - + config.setQuadratureDCCorrection( dcq ); + double gain = ((SpinnerNumberModel)mCorrectionGain.getModel()).getNumber().doubleValue(); - config.setGainCorrection( gain ); - + config.setGainCorrection( gain ); + double phase = ((SpinnerNumberModel)mCorrectionPhase.getModel()).getNumber().doubleValue(); - config.setPhaseCorrection( phase ); - - getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); - } + config.setPhaseCorrection( phase ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } } @Override @@ -415,19 +428,19 @@ public class FCD1TunerEditor extends TunerConfigurationEditor if( hasItem() ) { FCD1TunerConfiguration config = getConfiguration(); - + if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboLNAEnhance.setSelectedItem( config.getLNAEnhance() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); - mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); - mCorrectionGain.setValue( config.getGainCorrection() ); - mCorrectionPhase.setValue( config.getPhaseCorrection() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboLNAEnhance.setSelectedItem( config.getLNAEnhance() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); + mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); + mCorrectionGain.setValue( config.getGainCorrection() ); + mCorrectionPhase.setValue( config.getPhaseCorrection() ); } else { @@ -440,44 +453,44 @@ public class FCD1TunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( "" ); } - + mLoading = false; } - - public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; - - public class CorrectionSpinner extends JSpinner - { - private static final long serialVersionUID = 1L; - private static final double MIN_VALUE = -1.0d; - private static final double MAX_VALUE = 1.0d; - - private Correction mCorrectionComponent; + + public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; + + public class CorrectionSpinner extends JSpinner + { + private static final long serialVersionUID = 1L; + private static final double MIN_VALUE = -1.0d; + private static final double MAX_VALUE = 1.0d; + + private Correction mCorrectionComponent; public CorrectionSpinner( Correction component, double initialValue, double step, int decimalPlaces ) - { + { mCorrectionComponent = component; - - SpinnerModel model = new SpinnerNumberModel( initialValue, MIN_VALUE, MAX_VALUE, step ); - setModel( model ); - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( decimalPlaces ); - - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + SpinnerModel model = new SpinnerNumberModel( initialValue, MIN_VALUE, MAX_VALUE, step ); + setModel( model ); - addChangeListener( new ChangeListener() - { + JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( decimalPlaces ); + + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { - double value = + public void stateChanged( ChangeEvent e ) + { + double value = ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); try - { + { switch( mCorrectionComponent ) { case DC_INPHASE: @@ -493,20 +506,20 @@ public class FCD1TunerEditor extends TunerConfigurationEditor mController.setPhaseCorrection( value ); break; } - - save(); - } - catch ( Exception e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error " - + "applying " + mCorrectionComponent.toString() + " correction value [" - + value + "]" ); - - mLog.error( "FCDPro - error applying "+ mCorrectionComponent.toString() - + " correction value [" + value + "]", e1 ); - } - } - } ); - } - } -} + + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error " + + "applying " + mCorrectionComponent.toString() + " correction value [" + + value + "]" ); + + mLog.error( "FCDPro - error applying "+ mCorrectionComponent.toString() + + " correction value [" + value + "]", e1 ); + } + } + } ); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java index 5efbc669..c8e71140 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -19,6 +19,7 @@ package io.github.dsheirer.source.tuner.fcd.proplusV2; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.MixerTunerDataLine; import io.github.dsheirer.source.tuner.MixerTunerType; import io.github.dsheirer.source.tuner.TunerClass; import io.github.dsheirer.source.tuner.TunerType; @@ -41,11 +42,10 @@ public class FCD2TunerController extends FCDTunerController public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000; public static final int SAMPLE_RATE = 192000; - public FCD2TunerController(Device device, DeviceDescriptor descriptor) + public FCD2TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor) { - super(device, descriptor, - (int)MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getAudioFormat().getSampleRate(), - MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY); + super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getDisplayString(), + MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat()); } public void init() throws SourceException @@ -64,7 +64,7 @@ public class FCD2TunerController extends FCDTunerController } } - public int getCurrentSampleRate() + public double getCurrentSampleRate() { return SAMPLE_RATE; } @@ -200,39 +200,4 @@ public class FCD2TunerController extends FCDTunerController mLog.error("error setting IQ correction to [" + value + "]", e); } } - - public enum Block - { - CELLULAR_BAND_BLOCKED("Blocked"), - NO_BAND_BLOCK("Unblocked"), - UNKNOWN("Unknown"); - - private String mLabel; - - private Block(String label) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static Block getBlock(String block) - { - Block retVal = UNKNOWN; - - if(block.equalsIgnoreCase("No blk")) - { - retVal = NO_BAND_BLOCK; - } - else if(block.equalsIgnoreCase("Cell blk")) - { - retVal = CELLULAR_BAND_BLOCKED; - } - - return retVal; - } - } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java index 97db4f17..ce8d6fb7 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proplusV2/FCD2TunerEditor.java @@ -28,7 +28,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.event.ActionEvent; @@ -40,24 +49,24 @@ import java.text.DecimalFormat; public class FCD2TunerEditor extends TunerConfigurationEditor { private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerEditor.class ); - private static final long serialVersionUID = 1L; - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JSpinner mFrequencyCorrection; - private JCheckBox mLNAGain; - private JCheckBox mMixerGain; - private boolean mLoading; + private static final long serialVersionUID = 1L; - private FCD2TunerController mController; + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JSpinner mFrequencyCorrection; + private JCheckBox mLNAGain; + private JCheckBox mMixerGain; + private boolean mLoading; - public FCD2TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) - { - super( tunerConfigurationModel ); - mController = (FCD2TunerController)tuner.getController(); - - init(); - } + private FCD2TunerController mController; + + public FCD2TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) + { + super( tunerConfigurationModel ); + mController = (FCD2TunerController)tuner.getController(); + + init(); + } private FCD2TunerConfiguration getConfiguration() { @@ -65,33 +74,33 @@ public class FCD2TunerEditor extends TunerConfigurationEditor { return (FCD2TunerConfiguration)getItem(); } - + return null; } - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][grow]" ) ); - + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][grow]" ) ); + add( new JLabel( "FCD Pro+ Tuner Configuration" ), "span,align center" ); mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() + mConfigurationName.addFocusListener( new FocusListener() { @Override - public void focusLost( FocusEvent e ) - { + public void focusLost( FocusEvent e ) + { save(); - } + } @Override - public void focusGained( FocusEvent e ) {} + public void focusGained( FocusEvent e ) {} } ); - + add( new JLabel( "Name:" ) ); add( mConfigurationName, "span 2" ); - + mTunerInfo = new JButton( "Tuner Info" ); mTunerInfo.setEnabled( false ); mTunerInfo.addActionListener( new ActionListener() @@ -99,103 +108,103 @@ public class FCD2TunerEditor extends TunerConfigurationEditor @Override public void actionPerformed( ActionEvent e ) { - JOptionPane.showMessageDialog( FCD2TunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + JOptionPane.showMessageDialog( FCD2TunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); } } ); add( mTunerInfo ); - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - mFrequencyCorrection.addChangeListener( new ChangeListener() - { + mFrequencyCorrection.addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { + public void stateChanged( ChangeEvent e ) + { double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); + .getModel()).getNumber().doubleValue(); try - { - mController.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( FCD2TunerEditor.this, "FCD Pro Plus Tuner " - + "Controller - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection, "wrap" ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( FCD2TunerEditor.this, "FCD Pro Plus Tuner " + + "Controller - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); - mLNAGain = new JCheckBox( "LNA Gain" ); - mLNAGain.setEnabled( false ); - mLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent event ) - { - try - { - mController.setLNAGain( mLNAGain.isSelected() ); - save(); - } - catch( SourceException e ) - { - mLog.error( "Couldn't set LNA gain for FCD2", e ); - } - } - } ); + mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); - add( new JLabel( "" ) ); - add( mLNAGain ); + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection, "wrap" ); - /** - * Mixer Gain - */ - mMixerGain = new JCheckBox( "Mixer Gain" ); - - mMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent event ) - { - try - { - mController.setMixerGain( mMixerGain.isSelected() ); - save(); - } - catch( SourceException e ) - { - mLog.error( "Couldn't set mixer gain for FCD2", e ); - } - } - } ); + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - add( new JLabel( "" ) ); - add( mMixerGain ); - } + mLNAGain = new JCheckBox( "LNA Gain" ); + mLNAGain.setEnabled( false ); + mLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + mController.setLNAGain( mLNAGain.isSelected() ); + save(); + } + catch( SourceException e ) + { + mLog.error( "Couldn't set LNA gain for FCD2", e ); + } + } + } ); + + add( new JLabel( "" ) ); + add( mLNAGain ); + + /** + * Mixer Gain + */ + mMixerGain = new JCheckBox( "Mixer Gain" ); + + mMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + mController.setMixerGain( mMixerGain.isSelected() ); + save(); + } + catch( SourceException e ) + { + mLog.error( "Couldn't set mixer gain for FCD2", e ); + } + } + } ); + + add( new JLabel( "" ) ); + add( mMixerGain ); + } /** * Sets each of the tuner configuration controls to the enabled argument state @@ -206,12 +215,12 @@ public class FCD2TunerEditor extends TunerConfigurationEditor { mConfigurationName.setEnabled( enabled ); } - + if( mTunerInfo.isEnabled() != enabled ) { mTunerInfo.setEnabled( enabled ); } - + if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); @@ -221,7 +230,7 @@ public class FCD2TunerEditor extends TunerConfigurationEditor { mLNAGain.setEnabled( enabled ); } - + if( mMixerGain.isEnabled() != enabled ) { mMixerGain.setEnabled( enabled ); @@ -233,7 +242,7 @@ public class FCD2TunerEditor extends TunerConfigurationEditor StringBuilder sb = new StringBuilder(); sb.append( "

Funcube Dongle Pro Plus Tuner

" ); - + sb.append( "USB ID: " ); sb.append( mController.getUSBID() ); sb.append( "
" ); @@ -256,26 +265,26 @@ public class FCD2TunerEditor extends TunerConfigurationEditor return sb.toString(); } - + @Override public void save() { - if( hasItem() && !mLoading ) - { - FCD2TunerConfiguration config = getConfiguration(); + if( hasItem() && !mLoading ) + { + FCD2TunerConfiguration config = getConfiguration(); - config.setName( mConfigurationName.getText() ); + config.setName( mConfigurationName.getText() ); double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - config.setGainLNA( mLNAGain.isSelected() ); - config.setGainMixer( mMixerGain.isSelected() ); - - getTunerConfigurationModel().broadcast( + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setGainLNA( mLNAGain.isSelected() ); + config.setGainMixer( mMixerGain.isSelected() ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); - } + } } @Override @@ -289,15 +298,15 @@ public class FCD2TunerEditor extends TunerConfigurationEditor if( hasItem() ) { FCD2TunerConfiguration config = getConfiguration(); - - + + if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mLNAGain.setSelected( config.getGainLNA() ); - mMixerGain.setSelected( config.getGainMixer() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mLNAGain.setSelected( config.getGainLNA() ); + mMixerGain.setSelected( config.getGainMixer() ); } else { @@ -310,7 +319,7 @@ public class FCD2TunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( "" ); } - + mLoading = false; } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/DCTrackingFrequencyControl.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/DCTrackingFrequencyControl.java index 74c5367f..ff92b14a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/DCTrackingFrequencyControl.java +++ b/src/main/java/io/github/dsheirer/source/tuner/frequency/DCTrackingFrequencyControl.java @@ -1,129 +1,127 @@ package io.github.dsheirer.source.tuner.frequency; /******************************************************************************* - * SDR Trunk - * Copyright (C) 2016 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 + * ******************************************************************************/ import io.github.dsheirer.module.Module; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.real.RealBuffer; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event; +import io.github.dsheirer.source.ISourceEventProvider; +import io.github.dsheirer.source.SourceEvent; -import java.util.concurrent.ScheduledExecutorService; - -public class DCTrackingFrequencyControl extends Module - implements Listener, IFrequencyChangeProvider +public class DCTrackingFrequencyControl extends Module implements Listener, ISourceEventProvider { - private static float COARSE_THRESHOLD = 0.1f; - private static float LOOP_GAIN = 0.1f; - private static float COARSE_GAIN = 1000.0f; - private static float FINE_GAIN = 100.0f; - private int mErrorCorrection = 0; - private int mMaximumFrequencyCorrection = 0; - private Listener mListener; - - /** - * DC offset Automatic Frequency Control. Monitors DC bias present in an FM - * demodulated audio stream and issues automatic frequency corrections to - * maintain the DC bias close to zero. - * - * @param maximum - maximum allowable (+/-) frequency correction value. - */ - public DCTrackingFrequencyControl( int maximum ) - { - mMaximumFrequencyCorrection = maximum; - } - - public void dispose() - { - mListener = null; - } + private static float COARSE_THRESHOLD = 0.1f; + private static float LOOP_GAIN = 0.1f; + private static float COARSE_GAIN = 1000.0f; + private static float FINE_GAIN = 100.0f; + private int mErrorCorrection = 0; + private int mMaximumFrequencyCorrection = 0; + private Listener mListener; - @Override - public void receive( RealBuffer buffer ) - { - double sum = 0.0d; - - for( float sample: buffer.getSamples() ) - { - sum += sample; - } - - float mean = (float)( sum / (double)buffer.getSamples().length ); - - float adjustment = 0.0f; - - if( mean > COARSE_THRESHOLD || mean < - COARSE_THRESHOLD ) - { - adjustment = mean * COARSE_GAIN; - } - else - { - adjustment = mean * FINE_GAIN; - } + /** + * DC offset Automatic Frequency Control. Monitors DC bias present in an FM + * demodulated audio stream and issues automatic frequency corrections to + * maintain the DC bias close to zero. + * + * @param maximum - maximum allowable (+/-) frequency correction value. + */ + public DCTrackingFrequencyControl(int maximum) + { + mMaximumFrequencyCorrection = maximum; + } - setErrorCorrection( mErrorCorrection + (int)( (float)adjustment * LOOP_GAIN ) ); - } - - private void setErrorCorrection( int correction ) - { - if( mListener != null ) - { - if( correction > mMaximumFrequencyCorrection ) - { - correction = mMaximumFrequencyCorrection; - } - else if( correction < -mMaximumFrequencyCorrection ) - { - correction = -mMaximumFrequencyCorrection; - } - - if( correction != mErrorCorrection ) - { - mListener.receive( new FrequencyChangeEvent( - Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) ); - } - } - } - - @Override - public void setFrequencyChangeListener( Listener listener ) - { - mListener = listener; - } + public void dispose() + { + mListener = null; + } - @Override - public void removeFrequencyChangeListener() - { - mListener = null; - } + @Override + public void receive(RealBuffer buffer) + { + double sum = 0.0d; - @Override - public void start( ScheduledExecutorService executor ) - { - } + for(float sample : buffer.getSamples()) + { + sum += sample; + } - @Override - public void stop() - { - } + float mean = (float)(sum / (double)buffer.getSamples().length); - @Override - public void reset() - { - setErrorCorrection( 0 ); - } + float adjustment = 0.0f; + + if(mean > COARSE_THRESHOLD || mean < -COARSE_THRESHOLD) + { + adjustment = mean * COARSE_GAIN; + } + else + { + adjustment = mean * FINE_GAIN; + } + + setErrorCorrection(mErrorCorrection + (int)((float)adjustment * LOOP_GAIN)); + } + + private void setErrorCorrection(int correction) + { + if(mListener != null) + { + if(correction > mMaximumFrequencyCorrection) + { + correction = mMaximumFrequencyCorrection; + } + else if(correction < -mMaximumFrequencyCorrection) + { + correction = -mMaximumFrequencyCorrection; + } + + if(correction != mErrorCorrection) + { + mListener.receive(SourceEvent.channelFrequencyCorrectionRequest(correction)); + } + } + } + + @Override + public void setSourceEventListener(Listener listener) + { + mListener = listener; + } + + @Override + public void removeSourceEventListener() + { + mListener = null; + } + + @Override + public void start() + { + } + + @Override + public void stop() + { + } + + @Override + public void reset() + { + setErrorCorrection(0); + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyChangeEvent.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyChangeEvent.java deleted file mode 100644 index f0a9b94f..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyChangeEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.dsheirer.source.tuner.frequency; - -public class FrequencyChangeEvent -{ - public enum Event - { - NOTIFICATION_FREQUENCY_CHANGE, - NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, - NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, - NOTIFICATION_SAMPLE_RATE_CHANGE, - - REQUEST_FREQUENCY_CHANGE, - REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE; - } - - private Event mEvent; - private Number mValue; - - public FrequencyChangeEvent( Event event, Number value ) - { - mEvent = event; - mValue = value; - } - - public Event getEvent() - { - return mEvent; - } - - public Number getValue() - { - return mValue; - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyChangeProcessorWrapper.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyChangeProcessorWrapper.java deleted file mode 100644 index 4f1baec0..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyChangeProcessorWrapper.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.dsheirer.source.tuner.frequency; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.source.SourceException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class FrequencyChangeProcessorWrapper - implements IFrequencyChangeListener, Listener -{ - private final static Logger mLog = LoggerFactory.getLogger( FrequencyChangeProcessorWrapper.class ); - - private IFrequencyChangeProcessor mProcessor; - - /** - * Provides IFrequencyChangeListener wrapper for an IFrequencyChangeProcessor - */ - public FrequencyChangeProcessorWrapper( IFrequencyChangeProcessor handler ) - { - mProcessor = handler; - } - - @Override - public Listener getFrequencyChangeListener() - { - return this; - } - - @Override - public void receive( FrequencyChangeEvent event ) - { - if( mProcessor != null ) - { - try - { - mProcessor.frequencyChanged( event ); - } - catch( SourceException se ) - { - mLog.error("Error sending frequency change event to embedded processor", se); - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java index 217afa00..74d36f8f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/frequency/FrequencyController.java @@ -1,48 +1,46 @@ /******************************************************************************* - * 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.tuner.frequency; +import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.InvalidFrequencyException; +import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.List; public class FrequencyController { - private final static Logger mLog = LoggerFactory.getLogger( FrequencyController.class ); + private final static Logger mLog = LoggerFactory.getLogger(FrequencyController.class); private Tunable mTunable; private long mFrequency = 101100000; private long mTunedFrequency = 101100000; private long mMinimumFrequency; private long mMaximumFrequency; - private double mFrequencyCorrection = 0d; - private int mSampleRate = 0; - - private ArrayList mProcessors = new ArrayList<>(); - - public FrequencyController( Tunable tunable, - long minFrequency, - long maxFrequency, - double frequencyCorrection ) + private double mFrequencyCorrection = 0.0d; + private double mSampleRate = 0.0d; + + private List mProcessors = new ArrayList<>(); + + public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection) { mTunable = tunable; mMinimumFrequency = minFrequency; @@ -51,23 +49,31 @@ public class FrequencyController } /** - * Get bandwidth/sample rate in hertz + * Get bandwidth in hertz */ public int getBandwidth() { - return mSampleRate; + return (int)mSampleRate; } /** * Set sample rate in hertz */ - public void setSampleRate( int sampleRate ) throws SourceException + public void setSampleRate(int sampleRate) throws SourceException { mSampleRate = sampleRate; - + broadcastSampleRateChange(); } + /** + * Get sample rate in hertz + */ + public double getSampleRate() + { + return mSampleRate; + } + /** * Get frequency in hertz */ @@ -78,13 +84,24 @@ public class FrequencyController /** * Set frequency + * * @param frequency in hertz * @throws SourceException if tunable doesn't support tuning a corrected - * version of the requested frequency + * version of the requested frequency */ - public void setFrequency( long frequency ) throws SourceException + public void setFrequency(long frequency) throws SourceException { - setFrequency( frequency, true ); + setFrequency(frequency, true); + } + + /** + * Indicates if the specified frequency can be tuned (ie is within min/max frequency) by this controller. + * @param frequency to evaluate + * @return true if the frequency falls within the tuning range of this controller + */ + public boolean canTune(long frequency) + { + return getMinimumFrequency() <= frequency && frequency <= getMaximumFrequency(); } /** @@ -92,149 +109,147 @@ public class FrequencyController * method supports changing the frequency correction value without * broadcasting a frequency change event. */ - private void setFrequency( long frequency, boolean broadcastChange ) throws SourceException + private void setFrequency(long frequency, boolean broadcastChange) throws SourceException { - long tunedFrequency = getTunedFrequency( frequency ); - - if( tunedFrequency < mMinimumFrequency ) + long tunedFrequency = getTunedFrequency(frequency); + + if(tunedFrequency < mMinimumFrequency) { - throw new InvalidFrequencyException( "Requested frequency not valid", frequency, mMinimumFrequency ); + throw new InvalidFrequencyException("Requested frequency not valid", frequency, mMinimumFrequency); } - if( tunedFrequency > mMaximumFrequency ) + if(tunedFrequency > mMaximumFrequency) { - throw new InvalidFrequencyException( "Requested frequency not valid", frequency, mMaximumFrequency ); + throw new InvalidFrequencyException("Requested frequency not valid", frequency, mMaximumFrequency); } mFrequency = frequency; mTunedFrequency = tunedFrequency; - if( mTunable != null ) + if(mTunable != null) { - mTunable.setTunedFrequency( mTunedFrequency ); + mTunable.setTunedFrequency(mTunedFrequency); } /* Broadcast to all listeners that the frequency has changed */ - if( broadcastChange ) + if(broadcastChange) { broadcastFrequencyChange(); } } - + public long getTunedFrequency() { return mTunedFrequency; } - + public long getMinimumFrequency() { return mMinimumFrequency; } - + public long getMaximumFrequency() { return mMaximumFrequency; } - - /** - * Calculate the tuned frequency by adding frequency correction to the - * corrected frequency. - * @param correctedFrequency - */ - private long getTunedFrequency( long correctedFrequency ) - { - return (long)( (double)correctedFrequency / - ( 1.0 + ( mFrequencyCorrection / 1000000.0 ) ) ); - } - /** - * Calculate the corrected frequency by subtracting frequency correction - * from the tuned frequency. - * @param tunedFrequency - */ - @SuppressWarnings("unused") - private long getCorrectedFrequency( long tunedFrequency ) - { - return (long)( (double)tunedFrequency / - ( 1.0 - ( mFrequencyCorrection / 1000000.0 ) ) ); - } - - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection( double correction ) throws SourceException - { - mFrequencyCorrection = correction; + /** + * Calculate the tuned frequency by adding frequency correction to the + * corrected frequency. + * + * @param correctedFrequency + */ + private long getTunedFrequency(long correctedFrequency) + { + return (long)((double)correctedFrequency / + (1.0 + (mFrequencyCorrection / 1000000.0))); + } - if( mFrequency > 0 ) - { - setFrequency( mFrequency, true ); - } + /** + * Calculate the corrected frequency by subtracting frequency correction + * from the tuned frequency. + * + * @param tunedFrequency + */ + @SuppressWarnings("unused") + private long getCorrectedFrequency(long tunedFrequency) + { + return (long)((double)tunedFrequency / + (1.0 - (mFrequencyCorrection / 1000000.0))); + } + + public double getFrequencyCorrection() + { + return mFrequencyCorrection; + } + + public void setFrequencyCorrection(double correction) throws SourceException + { + mFrequencyCorrection = correction; + + if(mFrequency > 0) + { + setFrequency(mFrequency, true); + } + + broadcastFrequencyCorrectionChange(); + } - broadcastFrequencyErrorChange(); - } - /** * Adds listener to receive frequency change events */ -//TODO: rename this to addFrequencyChangeProcessor() - public void addListener( IFrequencyChangeProcessor processor ) + public void addListener(ISourceEventProcessor processor) { - if( !mProcessors.contains( processor ) ) + if(!mProcessors.contains(processor)) { - mProcessors.add( processor ); + mProcessors.add(processor); } } /** * Removes listener from receiving frequency change events */ - public void removeFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) + public void removeFrequencyChangeProcessor(ISourceEventProcessor processor) { - mProcessors.remove( processor ); + mProcessors.remove(processor); } - - - /** - * Broadcasts a change/update to the current (uncorrected) frequency or the - * bandwidth/sample rate value. - */ - protected void broadcastFrequencyChange() throws SourceException - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, mFrequency ) ); - } - - /** - * Broadcast a frequency error/correction value change - */ - protected void broadcastFrequencyErrorChange() throws SourceException - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, mFrequencyCorrection ) ); - } - - /** - * Broadcasts a sample rate change - */ - protected void broadcastSampleRateChange() throws SourceException - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mSampleRate ) ); - } - - public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) throws SourceException - { - for( IFrequencyChangeProcessor processor: mProcessors ) - { - processor.frequencyChanged( event ); - } - } - - + + + /** + * Broadcasts a change/update to the current (uncorrected) frequency or the + * bandwidth/sample rate value. + */ + protected void broadcastFrequencyChange() throws SourceException + { + broadcast(SourceEvent.frequencyChange(mFrequency)); + } + + /** + * Broadcast a frequency error/correction value change + */ + protected void broadcastFrequencyCorrectionChange() throws SourceException + { + broadcast(SourceEvent.frequencyCorrectionChange((int)mFrequencyCorrection)); + } + + /** + * Broadcasts a sample rate change + */ + protected void broadcastSampleRateChange() throws SourceException + { + broadcast(SourceEvent.sampleRateChange(mSampleRate)); + } + + public void broadcast(SourceEvent event) throws SourceException + { + for(ISourceEventProcessor processor : mProcessors) + { + processor.process(event); + } + } + + public interface Tunable { /** @@ -245,11 +260,16 @@ public class FrequencyController /** * Sets the tuned frequency of the device */ - public void setTunedFrequency( long frequency ) throws SourceException; + public void setTunedFrequency(long frequency) throws SourceException; /** * Gets the current bandwidth setting of the device */ - public int getCurrentSampleRate() throws SourceException; + public double getCurrentSampleRate() throws SourceException; + + /** + * Indicates if this tunable can tune the frequency + */ + public boolean canTune(long frequency); } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeListener.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeListener.java deleted file mode 100644 index eda2e784..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeListener.java +++ /dev/null @@ -1,34 +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.frequency; - -import io.github.dsheirer.sample.Listener; - - -/** - * Interface for broadcasting changes to frequency, bandwidth, sample rate, - * and actual sample rate values to all registered listeners. - * - * Note: broadcasted frequency should be the uncorrected, or displayable - * frequency value. All frequency correction aspects should be handled within - * the device that implements the tuning of the frequency. - */ -public interface IFrequencyChangeListener -{ - public Listener getFrequencyChangeListener(); -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeProcessor.java deleted file mode 100644 index b47c47f6..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeProcessor.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.dsheirer.source.tuner.frequency; - -import io.github.dsheirer.source.SourceException; - -public interface IFrequencyChangeProcessor -{ - public void frequencyChanged( FrequencyChangeEvent event ) throws SourceException; -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeProvider.java b/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeProvider.java deleted file mode 100644 index f3dcb6fd..00000000 --- a/src/main/java/io/github/dsheirer/source/tuner/frequency/IFrequencyChangeProvider.java +++ /dev/null @@ -1,30 +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.frequency; - -import io.github.dsheirer.sample.Listener; - -/** - * Provider of Frequency Change Events - */ -public interface IFrequencyChangeProvider -{ - void setFrequencyChangeListener( Listener listener ); - void removeFrequencyChangeListener(); -} diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java index ce79bdb7..2e1516fe 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTuner.java @@ -1,124 +1,76 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.tuner.hackrf; -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.Tuner; -import io.github.dsheirer.source.tuner.TunerChannel; -import io.github.dsheirer.source.tuner.TunerChannelSource; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerEvent; import io.github.dsheirer.source.tuner.TunerType; +import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.BoardID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.RejectedExecutionException; - public class HackRFTuner extends Tuner { - private final static Logger mLog = LoggerFactory.getLogger( HackRFTuner.class ); + private final static Logger mLog = LoggerFactory.getLogger( HackRFTuner.class ); - public HackRFTuner( HackRFTunerController controller ) throws SourceException - { - super( "HackRF", controller ); - } - - public HackRFTunerController getController() - { - return (HackRFTunerController)getTunerController(); - } + public HackRFTuner( HackRFTunerController controller ) throws SourceException + { + super( "HackRF", controller ); + } - public void dispose() - { - //TODO: dispose of something here - } - - @Override + public HackRFTunerController getController() + { + return (HackRFTunerController)getTunerController(); + } + + public void dispose() + { + } + + @Override public TunerClass getTunerClass() { - return TunerClass.HACKRF_ONE; + return TunerClass.HACKRF_ONE; } - @Override + @Override public TunerType getTunerType() { - return TunerType.HACKRF; + return TunerType.HACKRF; } - @Override - public double getSampleSize() - { - return 11.0; - } - - @Override - public TunerChannelSource getChannel(TunerChannel channel ) - throws RejectedExecutionException, SourceException + @Override + public double getSampleSize() { - TunerChannelSource source = getController().getChannel( this, channel ); - - if( source != null ) - { - broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); - } - - return source; + return 11.0; } - @Override - public void releaseChannel( TunerChannelSource source ) - { - /* Unregister for receiving samples */ - removeListener( (Listener)source ); - - /* Tell the controller to release the channel and cleanup */ - if( source != null ) - { - getController().releaseChannel( source ); - } - } - - @Override + @Override public String getUniqueID() { - try - { - return getController().getSerial().getSerialNumber(); - } - catch( Exception e ) - { - mLog.error( "error gettting serial number", e ); - } - - return HackRFTunerController.BoardID.HACKRF_ONE.getLabel(); + try + { + return getController().getSerial().getSerialNumber(); + } + catch( Exception e ) + { + mLog.error( "error gettting serial number", e ); + } + + return BoardID.HACKRF_ONE.getLabel(); } - - @Override - public void addListener( Listener listener ) - { - getController().addListener( listener ); - } - - @Override - public void removeListener( Listener listener ) - { - getController().removeListener( listener ); - } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java index fe232ec4..01773f25 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerController.java @@ -53,13 +53,11 @@ ******************************************************************************/ package io.github.dsheirer.source.tuner.hackrf; -import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.adapter.ByteSampleAdapter; -import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.TunerController; import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.usb.USBTransferProcessor; +import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.apache.commons.io.EndianUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +72,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; -public class HackRFTunerController extends TunerController +public class HackRFTunerController extends USBTunerController { private final static Logger mLog = LoggerFactory.getLogger(HackRFTunerController.class); @@ -158,6 +156,12 @@ public class HackRFTunerController extends TunerController mUSBTransferProcessor = new USBTransferProcessor(name, mDeviceHandle, mSampleAdapter, USB_TRANSFER_BUFFER_SIZE); } + @Override + protected USBTransferProcessor getUSBTransferProcessor() + { + return mUSBTransferProcessor; + } + /** * Claims the USB interface. If another application currently has * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting @@ -328,7 +332,7 @@ public class HackRFTunerController extends TunerController } @Override - public int getCurrentSampleRate() throws SourceException + public double getCurrentSampleRate() { return mSampleRate.getRate(); } @@ -522,7 +526,7 @@ public class HackRFTunerController extends TunerController write(Request.SET_SAMPLE_RATE, 0, 0, buffer); } - public int getSampleRate() + public double getSampleRate() { return mSampleRate.getRate(); } @@ -872,23 +876,4 @@ public class HackRFTunerController extends TunerController return sb.toString(); } } - - /** - * Adds a sample listener. If the buffer processing thread is - * not currently running, starts it running in a new thread. - */ - public void addListener(Listener listener) - { - mUSBTransferProcessor.addListener(listener); - } - - /** - * Removes the sample listener. If this is the last registered listener, - * shuts down the buffer processing thread. - */ - public void removeListener(Listener listener) - { - mUSBTransferProcessor.removeListener(listener); - } - -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java index 12bb1cf4..9aea1db3 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java @@ -1,19 +1,17 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.tuner.hackrf; @@ -22,11 +20,24 @@ import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; +import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; +import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; +import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; @@ -40,196 +51,196 @@ public class HackRFTunerEditor extends TunerConfigurationEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerEditor.class ); + private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerEditor.class ); private JTextField mConfigurationName; private JButton mTunerInfo; - private JComboBox mComboSampleRate; + private JComboBox mComboSampleRate; private JSpinner mFrequencyCorrection; private JToggleButton mAmplifier; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; private boolean mLoading; private HackRFTunerController mController; - public HackRFTunerEditor(TunerConfigurationModel tunerConfigurationModel, HackRFTuner tuner ) - { - super( tunerConfigurationModel ); - - mController = tuner.getController(); - - init(); - } + public HackRFTunerEditor( TunerConfigurationModel tunerConfigurationModel, HackRFTuner tuner ) + { + super( tunerConfigurationModel ); - private HackRFTunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (HackRFTunerConfiguration)getItem(); - } - - return null; - } + mController = tuner.getController(); + + init(); + } + + private HackRFTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (HackRFTunerConfiguration)getItem(); + } + + return null; + } private void init() { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][grow]" ) ); - - add( new JLabel( "HackRF Tuner Configuration" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][grow]" ) ); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override + add( new JLabel( "HackRF Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override public void focusLost( FocusEvent e ) { - save(); + save(); } - @Override + @Override public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - mComboSampleRate = new JComboBox<>( HackRFTunerController.HackRFSampleRate.values() ); - mComboSampleRate.setEnabled( false ); - mComboSampleRate.addActionListener( new ActionListener() + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() { - @Override + @Override public void actionPerformed( ActionEvent e ) { - HackRFTunerController.HackRFSampleRate sampleRate = - (HackRFTunerController.HackRFSampleRate)mComboSampleRate.getSelectedItem(); - try + JOptionPane.showMessageDialog( HackRFTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + mComboSampleRate = new JComboBox<>( HackRFSampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + HackRFSampleRate sampleRate = + (HackRFSampleRate)mComboSampleRate.getSelectedItem(); + try { - mController.setSampleRate( sampleRate ); - save(); + mController.setSampleRate( sampleRate ); + save(); } - catch ( SourceException |UsbException e2 ) + catch ( SourceException|UsbException e2 ) { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the sample rate setting [" + sampleRate.getLabel() + - "] " + e2.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply sample rate setting [" + - sampleRate.getLabel() + "]", e ); + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the sample rate setting [" + sampleRate.getLabel() + + "] " + e2.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", e ); } } } ); add( new JLabel( "Sample Rate:" ) ); add( mComboSampleRate ); - + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step + -1000.0, //min + 1000.0, //max + 0.1 ); //step mFrequencyCorrection = new JSpinner( model ); mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - mFrequencyCorrection.addChangeListener( new ChangeListener() + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() { - @Override + @Override public void stateChanged( ChangeEvent e ) { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); - try + try { - mController.setFrequencyCorrection( value ); - save(); + mController.setFrequencyCorrection( value ); + save(); } catch ( SourceException e1 ) { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply frequency correction " - + "value: " + value, e1 ); + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply frequency correction " + + "value: " + value, e1 ); } } } ); - + add( new JLabel( "PPM:" ) ); add( mFrequencyCorrection ); add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); add( new JLabel( "Gain" ) ); add( new JLabel( "" ), "span 2" ); //filler - + mAmplifier = new JToggleButton( "Amplifier" ); mAmplifier.setEnabled( false ); - mAmplifier.addActionListener( new ActionListener() + mAmplifier.addActionListener( new ActionListener() { - @Override + @Override public void actionPerformed( ActionEvent arg0 ) { - try + try { - mController.setAmplifierEnabled( mAmplifier.isSelected() ); - save(); + mController.setAmplifierEnabled( mAmplifier.isSelected() ); + save(); } catch ( UsbException e ) { - mLog.error( "couldn't enable/disable amplifier", e ); - - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "Couldn't change amplifier setting", - "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE ); + mLog.error( "couldn't enable/disable amplifier", e ); + + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "Couldn't change amplifier setting", + "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE ); } } } ); add( mAmplifier ); - - mComboLNAGain = new JComboBox( HackRFTunerController.HackRFLNAGain.values() ); + + mComboLNAGain = new JComboBox( HackRFLNAGain.values() ); mComboLNAGain.setEnabled( false ); - mComboLNAGain.addActionListener( new ActionListener() + mComboLNAGain.addActionListener( new ActionListener() { - @Override + @Override public void actionPerformed( ActionEvent arg0 ) { - try + try { - HackRFTunerController.HackRFLNAGain lnaGain = (HackRFTunerController.HackRFLNAGain)mComboLNAGain.getSelectedItem(); - - if ( lnaGain == null ) - { - lnaGain = HackRFTunerController.HackRFLNAGain.GAIN_0; - } - - mController.setLNAGain( lnaGain ); - save(); + HackRFLNAGain lnaGain = (HackRFLNAGain)mComboLNAGain.getSelectedItem(); + + if ( lnaGain == null ) + { + lnaGain = HackRFLNAGain.GAIN_0; + } + + mController.setLNAGain( lnaGain ); + save(); } catch ( UsbException e ) { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply LNA gain setting - ", e ); + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply LNA gain setting - ", e ); } } } ); @@ -237,30 +248,30 @@ public class HackRFTunerEditor extends TunerConfigurationEditor add( new JLabel( "LNA:" ) ); add( mComboLNAGain ); - mComboVGAGain = new JComboBox( HackRFTunerController.HackRFVGAGain.values() ); - mComboVGAGain.addActionListener( new ActionListener() + mComboVGAGain = new JComboBox( HackRFVGAGain.values() ); + mComboVGAGain.addActionListener( new ActionListener() { - @Override + @Override public void actionPerformed( ActionEvent arg0 ) { - try + try { - HackRFTunerController.HackRFVGAGain vgaGain = (HackRFTunerController.HackRFVGAGain)mComboVGAGain.getSelectedItem(); + HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); - if( vgaGain == null ) - { - vgaGain = HackRFTunerController.HackRFVGAGain.GAIN_4; - } - - mController.setVGAGain( vgaGain ); - save(); + if( vgaGain == null ) + { + vgaGain = HackRFVGAGain.GAIN_4; + } + + mController.setVGAGain( vgaGain ); + save(); } catch ( UsbException e ) { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the VGA gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply VGA gain setting", e ); + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the VGA gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply VGA gain setting", e ); } } } ); @@ -269,177 +280,177 @@ public class HackRFTunerEditor extends TunerConfigurationEditor add( mComboVGAGain ); } - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } if( mComboSampleRate.isEnabled() != enabled ) { - mComboSampleRate.setEnabled( enabled ); + mComboSampleRate.setEnabled( enabled ); } - + if( mAmplifier.isEnabled() != enabled ) { - mAmplifier.setEnabled( enabled ); + mAmplifier.setEnabled( enabled ); } if( mComboLNAGain.isEnabled() != enabled ) { - mComboLNAGain.setEnabled( enabled ); + mComboLNAGain.setEnabled( enabled ); } - + if( mComboVGAGain.isEnabled() != enabled ) { - mComboVGAGain.setEnabled( enabled ); + mComboVGAGain.setEnabled( enabled ); } - } + } - private String getTunerInfo() - { - HackRFTunerController.BoardID board = HackRFTunerController.BoardID.INVALID; + private String getTunerInfo() + { + HackRFTunerController.BoardID board = HackRFTunerController.BoardID.INVALID; - try + try { - board = mController.getBoardID(); + board = mController.getBoardID(); } catch ( UsbException e ) { - mLog.error( "couldn't read HackRF board identifier", e ); + mLog.error( "couldn't read HackRF board identifier", e ); } - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( "

HackRF Tuner

" ); + sb.append( "

HackRF Tuner

" ); - sb.append( "Board: " ); - sb.append( board.getLabel() ); - sb.append( "
" ); + sb.append( "Board: " ); + sb.append( board.getLabel() ); + sb.append( "
" ); - HackRFTunerController.Serial serial = null; - - try - { - serial = mController.getSerial(); - } - catch( Exception e ) - { - mLog.error( "couldn't read HackRF serial number", e ); - } - - if( serial != null ) - { - sb.append( "Serial: " ); - sb.append( serial.getSerialNumber() ); - sb.append( "
" ); - - sb.append( "Part: " ); - sb.append( serial.getPartID() ); - sb.append( "
" ); - } - else - { - sb.append( "Serial: Unknown
" ); - sb.append( "Part: Unknown
" ); - } - - String firmware = null; - - try - { - firmware = mController.getFirmwareVersion(); - } - catch( Exception e ) - { - mLog.error( "couldn't read HackRF firmware version", e ); - } - - if( firmware != null ) - { - sb.append( "Firmware: " ); - sb.append( firmware ); - sb.append( "
" ); - } - else - { - sb.append( "Firmware: Unknown
" ); - } - - return sb.toString(); - } + HackRFTunerController.Serial serial = null; - @Override - public void setItem( TunerConfiguration tunerConfiguration ) - { - super.setItem( tunerConfiguration ); + try + { + serial = mController.getSerial(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF serial number", e ); + } - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; + if( serial != null ) + { + sb.append( "Serial: " ); + sb.append( serial.getSerialNumber() ); + sb.append( "
" ); - if( hasItem() ) - { - HackRFTunerConfiguration config = getConfiguration(); - - if( tunerConfiguration.isAssigned() ) - { - setControlsEnabled( true ); - - mConfigurationName.setText( config.getName() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mAmplifier.setSelected( config.getAmplifierEnabled() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboVGAGain.setSelectedItem( config.getVGAGain() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - - mLoading = false; - } + sb.append( "Part: " ); + sb.append( serial.getPartID() ); + sb.append( "
" ); + } + else + { + sb.append( "Serial: Unknown
" ); + sb.append( "Part: Unknown
" ); + } - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - HackRFTunerConfiguration config = getConfiguration(); + String firmware = null; - config.setName( mConfigurationName.getText() ); + try + { + firmware = mController.getFirmwareVersion(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF firmware version", e ); + } - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - config.setSampleRate( (HackRFTunerController.HackRFSampleRate)mComboSampleRate.getSelectedItem() ); - config.setAmplifierEnabled( mAmplifier.isSelected() ); - config.setLNAGain( (HackRFTunerController.HackRFLNAGain)mComboLNAGain.getSelectedItem() ); - config.setVGAGain( (HackRFTunerController.HackRFVGAGain)mComboVGAGain.getSelectedItem() ); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); - } - } -} + if( firmware != null ) + { + sb.append( "Firmware: " ); + sb.append( firmware ); + sb.append( "
" ); + } + else + { + sb.append( "Firmware: Unknown
" ); + } + + return sb.toString(); + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( hasItem() ) + { + HackRFTunerConfiguration config = getConfiguration(); + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + + mConfigurationName.setText( config.getName() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mAmplifier.setSelected( config.getAmplifierEnabled() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + + mLoading = false; + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + HackRFTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + config.setSampleRate( (HackRFSampleRate)mComboSampleRate.getSelectedItem() ); + config.setAmplifierEnabled( mAmplifier.isSelected() ); + config.setLNAGain( (HackRFLNAGain)mComboLNAGain.getSelectedItem() ); + config.setVGAGain( (HackRFVGAGain)mComboVGAGain.getSelectedItem() ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/AbstractSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/AbstractSourceManager.java new file mode 100644 index 00000000..6a463feb --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/AbstractSourceManager.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * 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.tuner.manager; + +import io.github.dsheirer.sample.Broadcaster; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.sample.complex.IComplexBufferProvider; +import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.tuner.channel.TunerChannel; +import io.github.dsheirer.source.tuner.channel.TunerChannelSource; + +import java.util.List; + +/** + * Interface to define the functionality of a source manager for handling tuner channel management, complex buffer + * listeners, and source event listeners. + */ +public abstract class AbstractSourceManager implements IComplexBufferProvider, Listener +{ + protected Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); + + /** + * Indicates if this source manager can tune (ie provide a source for) the specified tuner channel + */ + public abstract boolean canTune(TunerChannel tunerChannel); + + /** + * List of tuner channels being sourced by this source manager + */ + public abstract List getChannels(); + + /** + * Obtains a source for the tuner channel or returns null if the channel cannot be sourced by this tuner. + * Note: use the canTune() method to check if a tuner channel can be sourced/tuned prior to accessing this method. + * + * @param tunerChannel for requested source + * @return tuner channel source or null + */ + public abstract TunerChannelSource getSource(TunerChannel tunerChannel); + + + /** + * Releases the source and all related resources + */ + public abstract void releaseSource(TunerChannelSource source); + + /** + * Adds the listener to receive source events from this source manager + * + * @param sourceEventListener to add + */ + public abstract void addSourceEventListener(Listener sourceEventListener); + + /** + * Removes the listener from receiving source events from this source manager + * @param sourceEventListener to remove + */ + public abstract void removeSourceEventListener(Listener sourceEventListener); + + /** + * Adds the listener to receive complex buffer samples + */ + @Override + public void addComplexBufferListener(Listener listener) + { + mComplexBufferBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving complex buffer samples + */ + @Override + public void removeComplexBufferListener(Listener listener) + { + mComplexBufferBroadcaster.removeListener(listener); + } + + /** + * Indicates if there are any complex buffer listeners registered with this source manager + */ + @Override + public boolean hasComplexBufferListeners() + { + return mComplexBufferBroadcaster.hasListeners(); + } + + /** + * Broadcasts the buffer to any registered listeners + */ + protected void broadcast(ComplexBuffer complexBuffer) + { + mComplexBufferBroadcaster.broadcast(complexBuffer); + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/TunerSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerSourceManager.java new file mode 100644 index 00000000..8b7c913d --- /dev/null +++ b/src/main/java/io/github/dsheirer/source/tuner/manager/TunerSourceManager.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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.tuner.manager; + +import io.github.dsheirer.dsp.filter.channelizer.PolyphaseChannelManager; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.source.SourceEvent; +import io.github.dsheirer.source.tuner.TunerController; +import io.github.dsheirer.source.tuner.channel.TunerChannel; +import io.github.dsheirer.source.tuner.channel.TunerChannelSource; + +import java.util.List; + +public class TunerSourceManager extends AbstractSourceManager +{ + private PolyphaseChannelManager mPolyphaseChannelManager; + private TunerController mTunerController; + + public TunerSourceManager(TunerController tunerController) + { + mTunerController = tunerController; + } + + //TODO: broadcast a tuner event each time we add a new channel to the polyphase manager + + @Override + public boolean canTune(TunerChannel tunerChannel) + { + return false; + } + + @Override + public List getChannels() + { + return null; + } + + @Override + public TunerChannelSource getSource(TunerChannel tunerChannel) + { + return null; + } + + @Override + public void releaseSource(TunerChannelSource source) + { + + } + + @Override + public void addSourceEventListener(Listener sourceEventListener) + { + + } + + @Override + public void removeSourceEventListener(Listener sourceEventListener) + { + + } + + @Override + public void receive(SourceEvent sourceEvent) + { + + } +} diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java index 7066dbcc..4f84f4e8 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832Tuner.java @@ -17,143 +17,59 @@ ******************************************************************************/ package io.github.dsheirer.source.tuner.rtl; -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.Tuner; -import io.github.dsheirer.source.tuner.TunerChannel; -import io.github.dsheirer.source.tuner.TunerChannelSource; import io.github.dsheirer.source.tuner.TunerClass; -import io.github.dsheirer.source.tuner.TunerEvent; import io.github.dsheirer.source.tuner.TunerType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import java.util.concurrent.RejectedExecutionException; public class RTL2832Tuner extends Tuner { - private final static Logger mLog = - LoggerFactory.getLogger( RTL2832Tuner.class ); + private final static Logger mLog = LoggerFactory.getLogger(RTL2832Tuner.class); - private TunerClass mTunerClass; + private TunerClass mTunerClass; - public RTL2832Tuner( TunerClass tunerClass, - RTL2832TunerController controller ) - throws SourceException - { - super( tunerClass.getVendorDeviceLabel() + "/" + - controller.getTunerType().getLabel() + " " + - controller.getUniqueID(), controller ); - - mTunerClass = tunerClass; - } - - public void dispose() - { - //TODO: dispose of something here - } - - public RTL2832TunerController getController() - { - return (RTL2832TunerController)getTunerController(); - } + public RTL2832Tuner(TunerClass tunerClass, RTL2832TunerController controller) throws SourceException + { + super(tunerClass.getVendorDeviceLabel() + "/" + controller.getTunerType().getLabel() + " " + + controller.getUniqueID(), controller); - @Override + mTunerClass = tunerClass; + } + + public void dispose() + { + } + + public RTL2832TunerController getController() + { + return (RTL2832TunerController)getTunerController(); + } + + @Override public String getUniqueID() { - return getController().getUniqueID(); + return getController().getUniqueID(); } - @Override + @Override public TunerClass getTunerClass() { - return mTunerClass; + return mTunerClass; } - @Override + @Override public TunerType getTunerType() { - return getController().getTunerType(); + return getController().getTunerType(); } - - public void setSampleRate( RTL2832TunerController.SampleRate sampleRate ) throws SourceException - { - try - { - getController().setSampleRate( sampleRate ); - } - catch( LibUsbException e ) - { - throw new SourceException( "RTL2832 Tuner - error setting " - + "sample rate", e ); - } - } - - @Override - public double getSampleSize() - { - //Note: although sample size is 8, we set it to 11 to align with the - //actual noise floor. - return 11.0; - } - public void setFrequency( int frequency ) throws SourceException - { - getController().setFrequency( frequency ); - } - - @Override - public TunerChannelSource getChannel(TunerChannel channel ) - throws RejectedExecutionException, - SourceException - { - TunerChannelSource source = null; - - try - { - source = getController().getChannel( this, channel ); - - if( source != null ) - { - broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); - } - } - catch( Exception e ) - { - mLog.error( "couldn't provide source channel", e ); - } - - return source; - } - - /** - * Releases the tuned channel so that the tuner controller can tune to - * other frequencies as needed. - */ - @Override - public void releaseChannel( TunerChannelSource source ) + @Override + public double getSampleSize() { - /* Unregister for receiving samples */ - removeListener( (Listener)source ); - - /* Tell the controller to release the channel and cleanup */ - if( source != null ) - { - getController().releaseChannel( source ); - } + //Note: although sample size is 8, we set it to 11 to align with the + //actual noise floor. + return 11.0; } - - @Override - public void addListener( Listener listener ) - { - getController().addListener( listener ); - } - - @Override - public void removeListener( Listener listener ) - { - getController().removeListener( listener ); - } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java index 7fbfe6c4..c71218a2 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/RTL2832TunerController.java @@ -26,10 +26,10 @@ import io.github.dsheirer.sample.adapter.ByteSampleAdapter; import io.github.dsheirer.sample.adapter.ISampleAdapter; import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.source.SourceException; -import io.github.dsheirer.source.tuner.TunerController; import io.github.dsheirer.source.tuner.TunerManager; import io.github.dsheirer.source.tuner.TunerType; import io.github.dsheirer.source.tuner.usb.USBTransferProcessor; +import io.github.dsheirer.source.tuner.usb.USBTunerController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.Device; @@ -46,7 +46,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; -public abstract class RTL2832TunerController extends TunerController +public abstract class RTL2832TunerController extends USBTunerController { private final static Logger mLog = LoggerFactory.getLogger(RTL2832TunerController.class); @@ -162,6 +162,12 @@ public abstract class RTL2832TunerController extends TunerController USB_TRANSFER_BUFFER_SIZE); } + @Override + protected USBTransferProcessor getUSBTransferProcessor() + { + return mUSBTransferProcessor; + } + /** * Claims the USB interface. Attempts to detach the active kernel driver * if one is currently attached. @@ -238,21 +244,21 @@ public abstract class RTL2832TunerController extends TunerController /* Set intermediate frequency to 0 Hz */ setIFFrequency(0); - /* Enable I/Q ADC Input */ + /* Enable I/Q ADC Input */ writeDemodRegister(mDeviceHandle, Page.ZERO, (short) 0x08, (short) 0xCD, 1); - - /* Enable zero-IF mode */ + + /* Enable zero-IF mode */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0xB1, (short) 0x1B, 1); - - /* Set default i/q path */ + + /* Set default i/q path */ writeDemodRegister(mDeviceHandle, Page.ZERO, (short) 0x06, @@ -271,21 +277,21 @@ public abstract class RTL2832TunerController extends TunerController long ifFrequency = ((long) TWO_TO_22_POWER * (long) frequency) / (long) mOscillatorFrequency * -1; - /* Write byte 2 (high) */ + /* Write byte 2 (high) */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x19, (short) (Long.rotateRight(ifFrequency, 16) & 0x3F), 1); - - /* Write byte 1 (middle) */ + + /* Write byte 1 (middle) */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x1A, (short) (Long.rotateRight(ifFrequency, 8) & 0xFF), 1); - - /* Write byte 0 (low) */ + + /* Write byte 0 (low) */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x1B, @@ -341,8 +347,8 @@ public abstract class RTL2832TunerController extends TunerController try { claimInterface(handle); - - /* Perform a dummy write to see if the device needs reset */ + + /* Perform a dummy write to see if the device needs reset */ boolean resetRequired = false; try @@ -392,14 +398,14 @@ public abstract class RTL2832TunerController extends TunerController } } - /* Initialize the baseband */ + /* Initialize the baseband */ initBaseband(handle); enableI2CRepeater(handle, true); boolean controlI2CRepeater = false; - /* Test for each tuner type until we find the correct one */ + /* Test for each tuner type until we find the correct one */ if(isTuner(TunerTypeCheck.E4K, handle, controlI2CRepeater)) { tunerClass = TunerType.ELONICS_E4000; @@ -448,7 +454,7 @@ public abstract class RTL2832TunerController extends TunerController { if(mUSBTransferProcessor != null) { - mUSBTransferProcessor.removeAllListeners(); + mUSBTransferProcessor.removeListener(); TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(mUSBTransferProcessor); } @@ -468,32 +474,32 @@ public abstract class RTL2832TunerController extends TunerController public static void initBaseband(DeviceHandle handle) throws LibUsbException { - /* Initialize USB */ + /* Initialize USB */ writeRegister(handle, Block.USB, Address.USB_SYSCTL.getAddress(), 0x09, 1); writeRegister(handle, Block.USB, Address.USB_EPA_MAXPKT.getAddress(), 0x0002, 2); writeRegister(handle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); - /* Power on demod */ + /* Power on demod */ writeRegister(handle, Block.SYS, Address.DEMOD_CTL_1.getAddress(), 0x22, 1); writeRegister(handle, Block.SYS, Address.DEMOD_CTL.getAddress(), 0xE8, 1); - /* Reset demod */ + /* Reset demod */ writeDemodRegister(handle, Page.ONE, (short) 0x01, 0x14, 1); //Bit 3 = soft reset writeDemodRegister(handle, Page.ONE, (short) 0x01, 0x10, 1); - - /* Disable spectrum inversion and adjacent channel rejection */ + + /* Disable spectrum inversion and adjacent channel rejection */ writeDemodRegister(handle, Page.ONE, (short) 0x15, 0x00, 1); writeDemodRegister(handle, Page.ONE, (short) 0x16, 0x0000, 2); - /* Clear DDC shift and IF frequency registers */ + /* Clear DDC shift and IF frequency registers */ writeDemodRegister(handle, Page.ONE, (short) 0x16, 0x00, 1); writeDemodRegister(handle, Page.ONE, (short) 0x17, 0x00, 1); writeDemodRegister(handle, Page.ONE, (short) 0x18, 0x00, 1); writeDemodRegister(handle, Page.ONE, (short) 0x19, 0x00, 1); writeDemodRegister(handle, Page.ONE, (short) 0x1A, 0x00, 1); writeDemodRegister(handle, Page.ONE, (short) 0x1B, 0x00, 1); - - /* Set FIR coefficients */ + + /* Set FIR coefficients */ for(int x = 0; x < sFIR_COEFFICIENTS.length; x++) { writeDemodRegister(handle, @@ -502,32 +508,32 @@ public abstract class RTL2832TunerController extends TunerController sFIR_COEFFICIENTS[x], 1); } - - /* Enable SDR mode, disable DAGC (bit 5) */ + + /* Enable SDR mode, disable DAGC (bit 5) */ writeDemodRegister(handle, Page.ZERO, (short) 0x19, 0x05, 1); - - /* Init FSM state-holding register */ + + /* Init FSM state-holding register */ writeDemodRegister(handle, Page.ONE, (short) 0x93, 0xF0, 1); writeDemodRegister(handle, Page.ONE, (short) 0x94, 0x0F, 1); - - /* Disable AGC (en_dagc, bit 0) (seems to have no effect) */ + + /* Disable AGC (en_dagc, bit 0) (seems to have no effect) */ writeDemodRegister(handle, Page.ONE, (short) 0x11, 0x00, 1); - /* Disable RF and IF AGC loop */ + /* Disable RF and IF AGC loop */ writeDemodRegister(handle, Page.ONE, (short) 0x04, 0x00, 1); - - /* Disable PID filter */ + + /* Disable PID filter */ writeDemodRegister(handle, Page.ZERO, (short) 0x61, 0x60, 1); - /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ + /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ writeDemodRegister(handle, Page.ZERO, (short) 0x06, 0x80, 1); - - /* Enable Zero-if mode (en_bbin bit), - * DC cancellation (en_dc_est), - * IQ estimation/compensation (en_iq_comp, en_iq_est) */ + + /* Enable Zero-if mode (en_bbin bit), + * DC cancellation (en_dc_est), + * IQ estimation/compensation (en_iq_comp, en_iq_est) */ writeDemodRegister(handle, Page.ONE, (short) 0xB1, 0x1B, 1); - /* Disable 4.096 MHz clock output on pin TP_CK0 */ + /* Disable 4.096 MHz clock output on pin TP_CK0 */ writeDemodRegister(handle, Page.ZERO, (short) 0x0D, 0x83, 1); } @@ -896,10 +902,10 @@ public abstract class RTL2832TunerController extends TunerController if(type == TunerTypeCheck.FC0012 || type == TunerTypeCheck.FC2580) { - /* Initialize the GPIOs */ + /* Initialize the GPIOs */ setGPIOOutput(handle, (byte) 0x20); - /* Reset tuner before probing */ + /* Reset tuner before probing */ setGPIOBit(handle, (byte) 0x20, true); setGPIOBit(handle, (byte) 0x20, false); } @@ -924,7 +930,7 @@ public abstract class RTL2832TunerController extends TunerController return false; } - public int getCurrentSampleRate() throws SourceException + public double getCurrentSampleRate() { return mSampleRate.getRate(); } @@ -942,7 +948,7 @@ public abstract class RTL2832TunerController extends TunerController SampleRate sampleRate = SampleRate.getClosest(rate); - /* If we're not currently set to this rate, set it as the current rate */ + /* If we're not currently set to this rate, set it as the current rate */ if(sampleRate.getRate() != rate) { setSampleRate(sampleRate); @@ -961,23 +967,23 @@ public abstract class RTL2832TunerController extends TunerController public void setSampleRate(SampleRate sampleRate) throws SourceException { - /* Write high-order 16-bits of sample rate ratio to demod register */ + /* Write high-order 16-bits of sample rate ratio to demod register */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x9F, sampleRate.getRatioHighBits(), 2); - - /* Write low-order 16-bits of sample rate ratio to demod register. - * Note: none of the defined rates have a low order ratio value, so we - * simply write a zero to the register */ + + /* Write low-order 16-bits of sample rate ratio to demod register. + * Note: none of the defined rates have a low order ratio value, so we + * simply write a zero to the register */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0xA1, 0, 2); - - /* Set sample rate correction to 0 */ + + /* Set sample rate correction to 0 */ setSampleRateFrequencyCorrection(0); - /* Reset the demod for the changes to take effect */ + /* Reset the demod for the changes to take effect */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x01, 0x14, 1); writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x01, 0x10, 1); - /* Apply any tuner specific sample rate filter settings */ + /* Apply any tuner specific sample rate filter settings */ setSampleRateFilters(sampleRate.getRate()); mSampleRate = sampleRate; @@ -999,7 +1005,7 @@ public abstract class RTL2832TunerController extends TunerController (short) 0x3E, (Integer.rotateRight(offset, 8) & 0xFF), 1); - /* Test to retune controller to apply frequency correction */ + /* Test to retune controller to apply frequency correction */ try { mFrequencyController.setFrequency(mFrequencyController.getFrequency()); @@ -1052,7 +1058,7 @@ public abstract class RTL2832TunerController extends TunerController try { - /* Tell the RTL-2832 to address the EEPROM */ + /* Tell the RTL-2832 to address the EEPROM */ writeRegister(handle, Block.I2C, EEPROM_ADDRESS, (byte) offset, 1); } catch(LibUsbException e) @@ -1410,13 +1416,13 @@ public abstract class RTL2832TunerController extends TunerController private int getLabel(int start) { - /* Validate length and check second byte for ETX (0x03) */ + /* Validate length and check second byte for ETX (0x03) */ if(start > 254 || mData[start + 1] != (byte) 0x03) { return 256; } - /* Get label length, including the length and ETX bytes */ + /* Get label length, including the length and ETX bytes */ int length = 0xFF & mData[start]; if(start + length > 255) @@ -1424,10 +1430,10 @@ public abstract class RTL2832TunerController extends TunerController return 256; } - /* Get the label bytes */ + /* Get the label bytes */ byte[] data = Arrays.copyOfRange(mData, start + 2, start + length); - - /* Translate the bytes as UTF-16 Little Endian and store the label */ + + /* Translate the bytes as UTF-16 Little Endian and store the label */ String label = new String(data, Charset.forName("UTF-16LE")); mLabels.add(label); @@ -1489,20 +1495,33 @@ public abstract class RTL2832TunerController extends TunerController } /** - * Adds a sample listener. If the USB transfer processor is not currently running, it will auto-start + * Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started. */ - public void addListener(Listener listener) + @Override + public void addComplexBufferListener(Listener listener) { - mUSBTransferProcessor.addListener(listener); + boolean hasExistingListeners = hasComplexBufferListeners(); + + super.addComplexBufferListener(listener); + + if(!hasExistingListeners) + { + mUSBTransferProcessor.setListener(this); + } } /** - * Removes the sample listener. If this is the last registered listener, the USB transfer processor will - * halt buffer processing + * Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners. */ - public void removeListener(Listener listener) + @Override + public void removeComplexBufferListener(Listener listener) { - mUSBTransferProcessor.removeListener(listener); + super.removeComplexBufferListener(listener); + + if(!hasComplexBufferListeners()) + { + mUSBTransferProcessor.removeListener(); + } } /** @@ -1531,4 +1550,4 @@ public abstract class RTL2832TunerController extends TunerController resetUSBBuffer(); } } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java index 90bef77c..79a6e00a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerController.java @@ -44,8 +44,8 @@ public class E4KTunerController extends RTL2832TunerController /* The local oscillator is defined by whole (integer) units of the oscillator * frequency and fractional units representing 1/65536th of the oscillator - * frequency, meaning we can only tune the local oscillator in units of - * 439.453125 hertz. */ + * frequency, meaning we can only tune the local oscillator in units of + * 439.453125 hertz. */ public static final long E4K_PLL_Y = 65536l; /* 16-bit fractional register */ public static final byte MASTER1_RESET = (byte) 0x01; public static final byte MASTER1_NORM_STBY = (byte) 0x02; @@ -191,7 +191,7 @@ public class E4KTunerController extends RTL2832TunerController try { - /* Read the contents of the 256-byte EEPROM */ + /* Read the contents of the 256-byte EEPROM */ eeprom = readEEPROM(mDeviceHandle, (short) 0, 256); } catch(Exception e) @@ -218,7 +218,7 @@ public class E4KTunerController extends RTL2832TunerController try { - /* Dummy write to test USB interface */ + /* Dummy write to test USB interface */ writeRegister(mDeviceHandle, Block.USB, Address.USB_SYSCTL.getAddress(), (short) 0x09, 1); @@ -263,7 +263,7 @@ public class E4KTunerController extends RTL2832TunerController @Override public void setSampleRateFilters(int bandwidth) throws SourceException { - /* Determine repeater state so we can restore it when done */ + /* Determine repeater state so we can restore it when done */ boolean i2CRepeaterEnabled = isI2CRepeaterEnabled(); if(!i2CRepeaterEnabled) @@ -293,7 +293,7 @@ public class E4KTunerController extends RTL2832TunerController { try { - /* Determine repeater state so we can restore it when done */ + /* Determine repeater state so we can restore it when done */ boolean i2CRepeaterEnabled = isI2CRepeaterEnabled(); if(!i2CRepeaterEnabled) @@ -313,7 +313,7 @@ public class E4KTunerController extends RTL2832TunerController int pllSetting = readE4KRegister(Register.SYNTH7, controlI2CRepeater); PLL pll = PLL.fromSetting(pllSetting); - /* Return the repeater to its previous state */ + /* Return the repeater to its previous state */ if(!i2CRepeaterEnabled) { enableI2CRepeater(mDeviceHandle, false); @@ -331,22 +331,22 @@ public class E4KTunerController extends RTL2832TunerController @Override public void setTunedFrequency(long frequency) throws SourceException { - /* Get the phase locked loop setting */ + /* Get the phase locked loop setting */ PLL pll = PLL.fromFrequency(frequency); - /* Z is an integer representing the number of scaled oscillator frequency - * increments the multiplied frequency. */ + /* Z is an integer representing the number of scaled oscillator frequency + * increments the multiplied frequency. */ byte z = (byte) (frequency / pll.getScaledOscillator()); - /* remainder is just as it describes. It is what is left over after we - * carve out scaled oscillator frequency increments (z) from the desired - * frequency. */ + /* remainder is just as it describes. It is what is left over after we + * carve out scaled oscillator frequency increments (z) from the desired + * frequency. */ int remainder = (int) (frequency - ((z & 0xFF) * pll.getScaledOscillator())); - /* X is a 16-bit representation of the remainder */ + /* X is a 16-bit representation of the remainder */ int x = (int) ((double) remainder / (double) pll.getScaledOscillator() * E4K_PLL_Y); - /* Calculate the exact (tunable) frequency and apply that to the tuner */ + /* Calculate the exact (tunable) frequency and apply that to the tuner */ long actualFrequency = calculateActualFrequency(pll, z, x); /** @@ -360,34 +360,34 @@ public class E4KTunerController extends RTL2832TunerController actualFrequency = calculateActualFrequency(pll, z, x); } - /* Apply the actual frequency */ + /* Apply the actual frequency */ try { enableI2CRepeater(mDeviceHandle, true); boolean controlI2CRepeater = false; - /* Write the PLL setting */ + /* Write the PLL setting */ writeE4KRegister(Register.SYNTH7, pll.getIndex(), controlI2CRepeater); - /* Write z (integral) value */ + /* Write z (integral) value */ writeE4KRegister(Register.SYNTH3, z, controlI2CRepeater); - /* Write the x (fractional) value high-order byte to synth4 register */ + /* Write the x (fractional) value high-order byte to synth4 register */ writeE4KRegister(Register.SYNTH4, (byte) (x & 0xFF), controlI2CRepeater); - /* Write the x (fractional) value low-order byte to synth5 register */ + /* Write the x (fractional) value low-order byte to synth5 register */ writeE4KRegister(Register.SYNTH5, (byte) ((Integer.rotateRight(x, 8)) & 0xFF), controlI2CRepeater); - /* Set the band for the new frequency */ + /* Set the band for the new frequency */ setBand(actualFrequency, controlI2CRepeater); - /* Set the filter */ + /* Set the filter */ setRFFilter(actualFrequency, controlI2CRepeater); - /* Check for PLL lock */ + /* Check for PLL lock */ int lock = readE4KRegister(Register.SYNTH1, controlI2CRepeater); if(!((lock & 0x1) == 0x1)) @@ -425,24 +425,24 @@ public class E4KTunerController extends RTL2832TunerController boolean i2CRepeaterControl = false; - /* Perform dummy read */ + /* Perform dummy read */ readE4KRegister(Register.DUMMY, i2CRepeaterControl); - /* Reset everything and clear POR indicator */ - /* NOTE: register value remains 0010 even after we write 0111 to it */ + /* Reset everything and clear POR indicator */ + /* NOTE: register value remains 0010 even after we write 0111 to it */ writeE4KRegister(Register.MASTER1, (byte) (MASTER1_RESET | MASTER1_NORM_STBY | MASTER1_POR_DET), i2CRepeaterControl); - /* Configure clock input */ + /* Configure clock input */ writeE4KRegister(Register.CLK_INP, (byte) 0x00, i2CRepeaterControl); - /* Disable clock output */ + /* Disable clock output */ writeE4KRegister(Register.REF_CLK, (byte) 0x00, i2CRepeaterControl); writeE4KRegister(Register.CLKOUT_PWDN, (byte) 0x96, i2CRepeaterControl); - /* Magic Init */ + /* Magic Init */ magicInit(i2CRepeaterControl); // /* Set common mode voltage a bit higher for more margin - 850 mv */ @@ -456,7 +456,7 @@ public class E4KTunerController extends RTL2832TunerController // writeE4KRegister( Register.DCTIME1, (byte)0x01, i2CRepeaterControl ); // writeE4KRegister( Register.DCTIME2, (byte)0x01, i2CRepeaterControl ); // - /* Set LNA Mode */ + /* Set LNA Mode */ writeE4KRegister(Register.AGC4, (byte) 0x10, i2CRepeaterControl); //High Threshold writeE4KRegister(Register.AGC5, (byte) 0x04, i2CRepeaterControl); //Low Threshold writeE4KRegister(Register.AGC6, (byte) 0x1A, i2CRepeaterControl); //LNA calibrate + loop rate @@ -496,12 +496,12 @@ public class E4KTunerController extends RTL2832TunerController // i2CRepeaterControl ); - /* Use automatic gain as a default */ + /* Use automatic gain as a default */ setLNAGain(E4KLNAGain.AUTOMATIC, i2CRepeaterControl); setMixerGain(E4KMixerGain.AUTOMATIC, i2CRepeaterControl); setEnhanceGain(E4KEnhanceGain.AUTOMATIC, i2CRepeaterControl); - /* Set IF gain stages */ + /* Set IF gain stages */ setIFStage1Gain(IFStage1Gain.GAIN_PLUS6, i2CRepeaterControl); setIFStage2Gain(IFStage2Gain.GAIN_PLUS0, i2CRepeaterControl); setIFStage3Gain(IFStage3Gain.GAIN_PLUS0, i2CRepeaterControl); @@ -509,7 +509,7 @@ public class E4KTunerController extends RTL2832TunerController setIFStage5Gain(IFStage5Gain.GAIN_PLUS9, i2CRepeaterControl); setIFStage6Gain(IFStage6Gain.GAIN_PLUS9, i2CRepeaterControl); - /* Set the most narrow filter we can possible use */ + /* Set the most narrow filter we can possible use */ setMixerFilter(MixerFilter.BW_1M9, i2CRepeaterControl); setRCFilter(RCFilter.BW_1M0, i2CRepeaterControl); setChannelFilter(ChannelFilter.BW_2M15, i2CRepeaterControl); @@ -519,7 +519,7 @@ public class E4KTunerController extends RTL2832TunerController // /* Disable DC detector */ // setDCRangeDetectorEnabled( false, i2CRepeaterControl ); - /* Disable time variant DC correction */ + /* Disable time variant DC correction */ writeMaskedE4KRegister(Register.DC5, (byte) 0x03, (byte) 0, i2CRepeaterControl); writeMaskedE4KRegister(Register.DCTIME1, (byte) 0x03, (byte) 0, i2CRepeaterControl); writeMaskedE4KRegister(Register.DCTIME2, (byte) 0x03, (byte) 0, i2CRepeaterControl); @@ -541,7 +541,7 @@ public class E4KTunerController extends RTL2832TunerController enableI2CRepeater(mDeviceHandle, true); } - /* Capture current gain settings to reapply at the end */ + /* Capture current gain settings to reapply at the end */ IFStage1Gain stage1 = getIFStage1Gain(i2CRepeaterControl); IFStage2Gain stage2 = getIFStage2Gain(i2CRepeaterControl); IFStage3Gain stage3 = getIFStage3Gain(i2CRepeaterControl); @@ -549,15 +549,15 @@ public class E4KTunerController extends RTL2832TunerController IFStage5Gain stage5 = getIFStage5Gain(i2CRepeaterControl); IFStage6Gain stage6 = getIFStage6Gain(i2CRepeaterControl); - /* Set Mixer gain to manual using current auto Mixer gain setting */ + /* Set Mixer gain to manual using current auto Mixer gain setting */ E4KMixerGain mixerGain = getMixerGain(i2CRepeaterControl); setMixerGain(mixerGain, i2CRepeaterControl); - /* Set LNA gain to manual using current auto LNA gain setting */ + /* Set LNA gain to manual using current auto LNA gain setting */ E4KLNAGain lnaGain = getLNAGain(i2CRepeaterControl); setLNAGain(lnaGain, i2CRepeaterControl); - /* Set IF Stage 2 - 6 gains to maximum */ + /* Set IF Stage 2 - 6 gains to maximum */ setIFStage2Gain(IFStage2Gain.GAIN_PLUS9, i2CRepeaterControl); setIFStage3Gain(IFStage3Gain.GAIN_PLUS9, i2CRepeaterControl); setIFStage4Gain(IFStage4Gain.GAIN_PLUS2B, i2CRepeaterControl); @@ -570,17 +570,17 @@ public class E4KTunerController extends RTL2832TunerController */ for(DCGainCombination combo : DCGainCombination.values()) { - /* Set mixer and stage1 gain values */ + /* Set mixer and stage1 gain values */ setMixerGain(combo.getMixerGain(), i2CRepeaterControl); setIFStage1Gain(combo.getIFStage1Gain(), i2CRepeaterControl); - /* Turn on the DC range detector */ + /* Turn on the DC range detector */ setDCRangeDetectorEnabled(true, i2CRepeaterControl); - /* Calibrate the DC value */ + /* Calibrate the DC value */ writeE4KRegister(Register.DC1, (byte) 0x01, i2CRepeaterControl); - /* Get the I/Q offset and range values */ + /* Get the I/Q offset and range values */ byte offsetI = (byte) (readE4KRegister(Register.DC2, i2CRepeaterControl) & 0x3F); byte offsetQ = (byte) (readE4KRegister(Register.DC3, @@ -590,7 +590,7 @@ public class E4KTunerController extends RTL2832TunerController byte rangeI = (byte) (range & 0x3); byte rangeQ = (byte) ((range >> 4) & 0x3); - /* Write the offset and range values to the lookup table */ + /* Write the offset and range values to the lookup table */ writeE4KRegister(combo.getIRegister(), (byte) (offsetI | (rangeI << 6)), i2CRepeaterControl); @@ -735,7 +735,7 @@ public class E4KTunerController extends RTL2832TunerController AGC7_MIX_GAIN_MANUAL, localI2CRepeaterControl); - /* Set the desired manual gain setting */ + /* Set the desired manual gain setting */ writeMaskedE4KRegister(E4KMixerGain.getRegister(), E4KMixerGain.getMask(), gain.getValue(), @@ -945,7 +945,7 @@ public class E4KTunerController extends RTL2832TunerController Band band = Band.fromFrequency(frequency); - /* Set the bias */ + /* Set the bias */ switch(band) { case UHF: @@ -1018,8 +1018,8 @@ public class E4KTunerController extends RTL2832TunerController } /* - * Performs magic initialization ... and that's all we need to know, I guess - */ + * Performs magic initialization ... and that's all we need to know, I guess + */ private void magicInit(boolean controlI2CRepeater) throws UsbException { if(controlI2CRepeater) @@ -1058,7 +1058,7 @@ public class E4KTunerController extends RTL2832TunerController { int temp = readE4KRegister(register, controlI2CRepeater); - /* If the register is not set to the masked value, then change it */ + /* If the register is not set to the masked value, then change it */ if((byte) (temp & mask) != value) { writeE4KRegister(register, @@ -1207,7 +1207,7 @@ public class E4KTunerController extends RTL2832TunerController } /* Returns the PLL setting with the closest frequency that is greater - * than the frequency argument */ + * than the frequency argument */ public static PLL fromFrequency(long frequency) { for(PLL pll : values()) @@ -1288,7 +1288,7 @@ public class E4KTunerController extends RTL2832TunerController M1720(14, 1710000000, 1735000000), /* Note: max frequency is currently limited by the max integer value - * and we set the max frequency for this filter to 2147 MHz */ + * and we set the max frequency for this filter to 2147 MHz */ M1750(15, 1735000000, 2147000000); private int mValue; @@ -1446,7 +1446,7 @@ public class E4KTunerController extends RTL2832TunerController } } - /* default */ + /* default */ return MixerFilter.BW_27M0; } @@ -1557,7 +1557,7 @@ public class E4KTunerController extends RTL2832TunerController } } - /* default */ + /* default */ return RCFilter.BW_21M4; } @@ -1684,7 +1684,7 @@ public class E4KTunerController extends RTL2832TunerController } } - /* default */ + /* default */ return ChannelFilter.BW_5M50; } @@ -2509,4 +2509,4 @@ public class E4KTunerController extends RTL2832TunerController return (byte) mValue; } } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java index cdc64bab..394d785d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -1,19 +1,17 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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.tuner.rtl.e4k; @@ -24,6 +22,7 @@ import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; +import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate; import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KGain; import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; @@ -33,7 +32,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; @@ -46,28 +54,28 @@ import java.text.DecimalFormat; public class E4KTunerEditor extends TunerConfigurationEditor { private final static Logger mLog = LoggerFactory.getLogger( E4KTunerEditor.class ); - - private static final long serialVersionUID = 1L; - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboEnhanceGain; - private E4KTunerController mController; - private boolean mLoading; + private static final long serialVersionUID = 1L; - public E4KTunerEditor(TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) - { - super( tunerConfigurationModel ); - mController = (E4KTunerController)tuner.getController(); - - init(); - } + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JComboBox mComboMasterGain; + private JComboBox mComboMixerGain; + private JComboBox mComboLNAGain; + private JComboBox mComboEnhanceGain; + + private E4KTunerController mController; + private boolean mLoading; + + public E4KTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) + { + super( tunerConfigurationModel ); + mController = (E4KTunerController)tuner.getController(); + + init(); + } private E4KTunerConfiguration getConfiguration() { @@ -75,15 +83,15 @@ public class E4KTunerEditor extends TunerConfigurationEditor { return (E4KTunerConfiguration)getItem(); } - + return null; } - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][grow]" ) ); - + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][grow]" ) ); + add( new JLabel( "E4000 Tuner Configuration" ), "span,align center" ); /** @@ -92,19 +100,19 @@ public class E4KTunerEditor extends TunerConfigurationEditor add( new JLabel( "Name:" ) ); mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() + mConfigurationName.addFocusListener( new FocusListener() { @Override - public void focusLost( FocusEvent e ) - { + public void focusLost( FocusEvent e ) + { save(); - } + } @Override - public void focusGained( FocusEvent e ) {} + public void focusGained( FocusEvent e ) {} } ); - + add( mConfigurationName, "span 2" ); - + mTunerInfo = new JButton( "Tuner Info" ); mTunerInfo.setEnabled( false ); mTunerInfo.addActionListener( new ActionListener() @@ -112,223 +120,223 @@ public class E4KTunerEditor extends TunerConfigurationEditor @Override public void actionPerformed( ActionEvent e ) { - JOptionPane.showMessageDialog( E4KTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + JOptionPane.showMessageDialog( E4KTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); } } ); add( mTunerInfo ); - mComboSampleRate = new JComboBox<>( RTL2832TunerController.SampleRate.values() ); - mComboSampleRate.setEnabled( false ); - mComboSampleRate.addActionListener( new ActionListener() - { + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent e ) - { - RTL2832TunerController.SampleRate sampleRate = (RTL2832TunerController.SampleRate)mComboSampleRate.getSelectedItem(); - + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = (SampleRate)mComboSampleRate.getSelectedItem(); + try - { + { mController.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, - "E4K Tuner Controller - couldn't apply the sample rate setting [" + - sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply sample rate setting [" + - sampleRate.getLabel() + "]", eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, + "E4K Tuner Controller - couldn't apply the sample rate setting [" + + sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage() ); - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step + mLog.error( "E4K Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step - mFrequencyCorrection.addChangeListener( new ChangeListener() - { + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { + public void stateChanged( ChangeEvent e ) + { final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); + .getModel()).getNumber().doubleValue(); try - { - mController.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply frequency correction value: " + value + e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply frequency correction value:" - + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply frequency correction value: " + value + e1.getLocalizedMessage() ); - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - add( new JLabel( "Gain" ), "wrap" ); - - mComboMasterGain = new JComboBox( E4KGain.values() ); - mComboMasterGain.setEnabled( false ); - mComboMasterGain.addActionListener( new ActionListener() - { + mLog.error( "E4K Tuner Controller - couldn't apply frequency correction value:" + + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ), "wrap" ); + + mComboMasterGain = new JComboBox( E4KGain.values() ); + mComboMasterGain.setEnabled( false ); + mComboMasterGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { + { E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == E4KGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( mController.getMixerGain( true ) ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( mController.getLNAGain( true ) ); - mComboLNAGain.setEnabled( true ); - mComboEnhanceGain.setSelectedItem( mController.getEnhanceGain( true ) ); - mComboEnhanceGain.setEnabled( true ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + if( gain == E4KGain.MANUAL ) + { + mComboMixerGain.setSelectedItem( mController.getMixerGain( true ) ); + mComboMixerGain.setEnabled( true ); - mComboEnhanceGain.setEnabled( false ); - mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); - } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply gain setting", e ); - } - } - } ); - - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
independent control of " - + "Mixer, LNA and Enhance gain
" - + "settings, or one of the individual gain settings for
" - + "semi-manual gain control" ); - add( new JLabel( "Master:" ) ); - add( mComboMasterGain ); + mComboLNAGain.setSelectedItem( mController.getLNAGain( true ) ); + mComboLNAGain.setEnabled( true ); - mComboMixerGain = new JComboBox( E4KMixerGain.values() ); - mComboMixerGain.addActionListener( new ActionListener() - { + mComboEnhanceGain.setSelectedItem( mController.getEnhanceGain( true ) ); + mComboEnhanceGain.setEnabled( true ); + } + else + { + mComboMixerGain.setEnabled( false ); + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + + mComboEnhanceGain.setEnabled( false ); + mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply gain setting", e ); + } + } + } ); + + mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " + + "gain, MANUAL to enable
independent control of " + + "Mixer, LNA and Enhance gain
" + + "settings, or one of the individual gain settings for
" + + "semi-manual gain control" ); + add( new JLabel( "Master:" ) ); + add( mComboMasterGain ); + + mComboMixerGain = new JComboBox( E4KMixerGain.values() ); + mComboMixerGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { + { E4KMixerGain mixerGain = (E4KMixerGain)mComboMixerGain.getSelectedItem(); mController.setMixerGain( mixerGain, true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply mixer gain setting", e ); - } - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); - add( new JLabel( "Mixer:" ) ); - add( mComboMixerGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage() ); - mComboLNAGain = new JComboBox( E4KLNAGain.values() ); - mComboLNAGain.addActionListener( new ActionListener() - { + mLog.error( "E4K Tuner Controller - couldn't apply mixer gain setting", e ); + } + } + } ); + mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + + "to MASTER to enable adjustment" ); + mComboMixerGain.setEnabled( false ); + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain ); + + mComboLNAGain = new JComboBox( E4KLNAGain.values() ); + mComboLNAGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { + { E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); mController.setLNAGain( lnaGain, true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply LNA gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); - mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); - mComboEnhanceGain.addActionListener( new ActionListener() - { + mLog.error( "E4K Tuner Controller - couldn't apply LNA gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboLNAGain.setEnabled( false ); + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); + mComboEnhanceGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { + { E4KEnhanceGain enhance = (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); mController.setEnhanceGain( enhance, true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the enhance gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply enhance gain setting", e ); - } - } - } ); - mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboEnhanceGain.setEnabled( false ); - add( new JLabel( "Enhance:" ) ); - add( mComboEnhanceGain ); - } - + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the enhance gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply enhance gain setting", e ); + } + } + } ); + mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboEnhanceGain.setEnabled( false ); + add( new JLabel( "Enhance:" ) ); + add( mComboEnhanceGain ); + } + /** * Sets each of the tuner configuration controls to the enabled argument state */ @@ -338,51 +346,51 @@ public class E4KTunerEditor extends TunerConfigurationEditor { mConfigurationName.setEnabled( enabled ); } - + if( mTunerInfo.isEnabled() != enabled ) { mTunerInfo.setEnabled( enabled ); } - + if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); } - if( mComboSampleRate.isEnabled() != enabled ) - { - mComboSampleRate.setEnabled( enabled ); - } + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } - if( mComboMasterGain.isEnabled() != enabled ) - { - mComboMasterGain.setEnabled( enabled ); - } + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } - if( mComboMixerGain.isEnabled() != enabled ) - { - mComboMixerGain.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboEnhanceGain.isEnabled() != enabled ) - { - mComboEnhanceGain.setEnabled( enabled ); - } + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboEnhanceGain.isEnabled() != enabled ) + { + mComboEnhanceGain.setEnabled( enabled ); + } } - + private String getTunerInfo() { StringBuilder sb = new StringBuilder(); RTL2832TunerController.Descriptor descriptor = mController.getDescriptor(); - + sb.append( "

RTL-2832 with E4000 Tuner

" ); - + if( descriptor == null ) { sb.append( "No EEPROM Descriptor Available" ); @@ -422,27 +430,27 @@ public class E4KTunerEditor extends TunerConfigurationEditor @Override public void save() { - if( hasItem() && !mLoading ) - { - E4KTunerConfiguration config = getConfiguration(); + if( hasItem() && !mLoading ) + { + E4KTunerConfiguration config = getConfiguration(); - config.setName( mConfigurationName.getText() ); + config.setName( mConfigurationName.getText() ); - config.setSampleRate( (RTL2832TunerController.SampleRate)mComboSampleRate.getSelectedItem() ); + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); - config.setMasterGain( (E4KGain)mComboMasterGain.getSelectedItem() ); - config.setMixerGain( (E4KMixerGain)mComboMixerGain.getSelectedItem() ); + config.setMasterGain( (E4KGain)mComboMasterGain.getSelectedItem() ); + config.setMixerGain( (E4KMixerGain)mComboMixerGain.getSelectedItem() ); config.setLNAGain( (E4KLNAGain)mComboLNAGain.getSelectedItem() ); config.setEnhanceGain( (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem() ); - + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( - getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); - } - + getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); + } + } @Override @@ -456,18 +464,18 @@ public class E4KTunerEditor extends TunerConfigurationEditor if( hasItem() ) { E4KTunerConfiguration config = getConfiguration(); - + if( tunerConfiguration.isAssigned() ) { - setControlsEnabled( true ); + setControlsEnabled( true ); mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mComboMasterGain.setSelectedItem( config.getMasterGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboEnhanceGain.setSelectedItem( config.getEnhanceGain() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboEnhanceGain.setSelectedItem( config.getEnhanceGain() ); } else { @@ -480,7 +488,7 @@ public class E4KTunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( "" ); } - + mLoading = false; } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java index bda2e908..c440ba04 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014-2017 Dennis Sheirer * * Java port of librtlsdr * @@ -32,14 +32,13 @@ import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import org.usb4java.LibUsbException; -import javax.swing.*; +import javax.swing.JPanel; import javax.usb.UsbException; import java.nio.ByteBuffer; public class R820TTunerController extends RTL2832TunerController { - private final static Logger mLog = - LoggerFactory.getLogger(R820TTunerController.class); + private final static Logger mLog = LoggerFactory.getLogger(R820TTunerController.class); public static final long MIN_FREQUENCY = 3180000; public static final long MAX_FREQUENCY = 1782030000; @@ -188,14 +187,14 @@ public class R820TTunerController extends RTL2832TunerController /* Set intermediate frequency to R820T IF frequency */ setIFFrequency(R820T_IF_FREQUENCY); - /* Enable spectrum inversion */ + /* Enable spectrum inversion */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x15, (short) 0x01, 1); - - /* Set default i/q path */ + + /* Set default i/q path */ writeDemodRegister(mDeviceHandle, Page.ZERO, (short) 0x06, @@ -215,23 +214,23 @@ public class R820TTunerController extends RTL2832TunerController { FrequencyRange range = FrequencyRange.getRangeForFrequency(frequency); - /* Set open drain */ + /* Set open drain */ writeR820TRegister(Register.DRAIN, range.getOpenDrain(), controlI2C); - /* RF_MUX, Polymux */ + /* RF_MUX, Polymux */ writeR820TRegister(Register.RF_POLY_MUX, range.getRFMuxPolyMux(), controlI2C); - - /* TF Band */ + + /* TF Band */ writeR820TRegister(Register.TF_BAND, range.getTFC(), controlI2C); - - /* XTAL CAP & Drive */ + + /* XTAL CAP & Drive */ writeR820TRegister(Register.PLL_XTAL_CAPACITOR_AND_DRIVE, range.getXTALHighCap0P(), controlI2C); - - /* Register 8 - what is it? */ + + /* Register 8 - what is it? */ writeR820TRegister(Register.UNKNOWN_REGISTER_8, (byte) 0x00, controlI2C); - - /* Register 9 - what is it? */ + + /* Register 9 - what is it? */ writeR820TRegister(Register.UNKNOWN_REGISTER_9, (byte) 0x00, controlI2C); } @@ -240,7 +239,7 @@ public class R820TTunerController extends RTL2832TunerController */ public void init() throws SourceException { - /* Initialize the super class to open and claim the usb interface*/ + /* Initialize the super class to open and claim the usb interface*/ super.init(); try @@ -266,24 +265,24 @@ public class R820TTunerController extends RTL2832TunerController */ public void initTuner(boolean controlI2C) throws UsbException { - /* Disable zero IF mode */ + /* Disable zero IF mode */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0xB1, (short) 0x1A, 1); - - /* Only enable in-phase ADC input */ + + /* Only enable in-phase ADC input */ writeDemodRegister(mDeviceHandle, Page.ZERO, (short) 0x08, (short) 0x4D, 1); - - /* Set intermediate frequency to R820T IF frequency (3.57 MHz) */ + + /* Set intermediate frequency to R820T IF frequency (3.57 MHz) */ setIFFrequency(R820T_IF_FREQUENCY); - /* Enable spectrum inversion */ + /* Enable spectrum inversion */ writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x15, @@ -303,12 +302,12 @@ public class R820TTunerController extends RTL2832TunerController */ private void setTVStandard(boolean controlI2C) throws UsbException { - /* Init Flag & Xtal check Result */ + /* Init Flag & Xtal check Result */ writeR820TRegister(Register.XTAL_CHECK, (byte) 0x00, controlI2C); - - /* Set version */ + + /* Set version */ writeR820TRegister(Register.VERSION, VERSION, controlI2C); - + /* LT Gain Test */ writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C); @@ -326,7 +325,7 @@ public class R820TTunerController extends RTL2832TunerController writeR820TRegister(Register.PLL_XTAL_CAPACITOR, (byte) 0x00, controlI2C); setPLL(56000 * 1000, controlI2C); - + /* Start trigger */ writeR820TRegister(Register.CALIBRATION_TRIGGER, (byte) 0x10, controlI2C); @@ -349,36 +348,36 @@ public class R820TTunerController extends RTL2832TunerController { calibrationCode = 0; } - - /* Write calibration code */ + + /* Write calibration code */ byte filt_q = 0x10; writeR820TRegister(Register.FILTER_CALIBRATION_CODE, (byte) (calibrationCode | filt_q), controlI2C); - /* Set BW, Filter gain & HP Corner */ + /* Set BW, Filter gain & HP Corner */ writeR820TRegister(Register.BANDWIDTH_FILTER_GAIN_HIGHPASS_FILTER_CORNER, (byte) 0x6B, controlI2C); - /* Set Image_R */ + /* Set Image_R */ writeR820TRegister(Register.IMAGE_REVERSE, (byte) 0x00, controlI2C); - - /* Set filter_3db, V6MHz */ + + /* Set filter_3db, V6MHz */ writeR820TRegister(Register.FILTER_GAIN, (byte) 0x10, controlI2C); - /* Channel filter extension */ + /* Channel filter extension */ writeR820TRegister(Register.CHANNEL_FILTER_EXTENSION, (byte) 0x60, controlI2C); - /* Loop through */ + /* Loop through */ writeR820TRegister(Register.LOOP_THROUGH, (byte) 0x00, controlI2C); - /* Loop through attenuation */ + /* Loop through attenuation */ writeR820TRegister(Register.LOOP_THROUGH_ATTENUATION, (byte) 0x00, controlI2C); - - /* Filter extension widest */ + + /* Filter extension widest */ writeR820TRegister(Register.FILTER_EXTENSION_WIDEST, (byte) 0x00, controlI2C); - /* RF poly filter current */ + /* RF poly filter current */ writeR820TRegister(Register.RF_POLY_FILTER_CURRENT, (byte) 0x60, controlI2C); } @@ -404,7 +403,7 @@ public class R820TTunerController extends RTL2832TunerController private void systemFrequencySelect(long frequency, boolean controlI2C) throws UsbException { - /* LNA top? */ + /* LNA top? */ writeR820TRegister(Register.LNA_TOP2, (byte) 0xE5, controlI2C); byte mixer_top; @@ -431,8 +430,8 @@ public class R820TTunerController extends RTL2832TunerController writeR820TRegister(Register.LNA_VTH_L, (byte) 0x53, controlI2C); writeR820TRegister(Register.MIXER_VTH_L, (byte) 0x75, controlI2C); - - /* Air-In only for Astrometa */ + + /* Air-In only for Astrometa */ writeR820TRegister(Register.AIR_CABLE1_INPUT_SELECTOR, (byte) 0x00, controlI2C); writeR820TRegister(Register.CABLE2_INPUT_SELECTOR, (byte) 0x00, controlI2C); @@ -443,7 +442,7 @@ public class R820TTunerController extends RTL2832TunerController writeR820TRegister(Register.FILTER_CURRENT, (byte) 0x40, controlI2C); - /* if( type != TUNER_ANALOG_TV ) ... */ + /* if( type != TUNER_ANALOG_TV ) ... */ writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C); @@ -457,10 +456,10 @@ public class R820TTunerController extends RTL2832TunerController writeR820TRegister(Register.MIXER_TOP2, mixer_top, controlI2C); - /* LNA discharge current */ + /* LNA discharge current */ writeR820TRegister(Register.LNA_DISCHARGE_CURRENT, (byte) 0x14, controlI2C); - - /* AGC clock 1 khz, external det1 cap 1u */ + + /* AGC clock 1 khz, external det1 cap 1u */ writeR820TRegister(Register.AGC_CLOCK, (byte) 0x20, controlI2C); } @@ -474,7 +473,7 @@ public class R820TTunerController extends RTL2832TunerController */ private void setPLL(long frequency, boolean controlI2C) throws UsbException { - /* Set reference divider to 0 */ + /* Set reference divider to 0 */ writeR820TRegister(Register.REFERENCE_DIVIDER_2, (byte) 0x00, controlI2C); /* Set PLL autotune to 128kHz */ @@ -525,7 +524,7 @@ public class R820TTunerController extends RTL2832TunerController * settings */ if(!isPLLLocked(controlI2C)) { - /* Increase VCO current */ + /* Increase VCO current */ writeR820TRegister(Register.VCO_CURRENT, (byte) 0x60, controlI2C); if(!isPLLLocked(controlI2C)) @@ -612,8 +611,8 @@ public class R820TTunerController extends RTL2832TunerController mShadowRegister[register.getRegister()] = value; -// Log.info( "R820T writing register " + -// String.format( "%02X", register.getRegister() ) + " value " + +// Log.info( "R820T writing register " + +// String.format( "%02X", register.getRegister() ) + " value " + // String.format( "%02X", value ) ); } @@ -1293,4 +1292,4 @@ public class R820TTunerController extends RTL2832TunerController "] must be in the range 0 - 31"); } } -} +} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java index 6558271f..43fa49d2 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -24,12 +24,26 @@ import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner; import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController; +import io.github.dsheirer.source.tuner.rtl.RTL2832TunerController.SampleRate; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; +import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; -import javax.swing.*; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; @@ -44,61 +58,61 @@ public class R820TTunerEditor extends TunerConfigurationEditor private final static Logger mLog = LoggerFactory.getLogger( R820TTunerEditor.class ); private static final long serialVersionUID = 1L; - private static final R820TTunerController.R820TGain DEFAULT_GAIN = R820TTunerController.R820TGain.GAIN_279; - - private R820TTunerController mController; + private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - private boolean mLoading; + private R820TTunerController mController; + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JComboBox mComboMasterGain; + private JComboBox mComboMixerGain; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; + private boolean mLoading; + + public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) + { + super( tunerConfigurationModel ); + mController = (R820TTunerController)tuner.getController(); + + init(); + } - public R820TTunerEditor(TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) - { - super( tunerConfigurationModel ); - mController = (R820TTunerController)tuner.getController(); - - init(); - } - private R820TTunerConfiguration getConfiguration() { if( hasItem() ) { return (R820TTunerConfiguration)getItem(); } - + return null; } private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][grow]" ) ); - + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][grow]" ) ); + add( new JLabel( "R820T Tuner Configuration" ), "span,align center" ); mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() + mConfigurationName.addFocusListener( new FocusListener() { @Override - public void focusLost( FocusEvent e ) - { + public void focusLost( FocusEvent e ) + { save(); - } + } @Override - public void focusGained( FocusEvent e ) {} + public void focusGained( FocusEvent e ) {} } ); - + add( new JLabel( "Name:" ) ); add( mConfigurationName, "span 2" ); - + mTunerInfo = new JButton( "Tuner Info" ); mTunerInfo.setEnabled( false ); mTunerInfo.addActionListener( new ActionListener() @@ -106,279 +120,279 @@ public class R820TTunerEditor extends TunerConfigurationEditor @Override public void actionPerformed( ActionEvent e ) { - JOptionPane.showMessageDialog( R820TTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + JOptionPane.showMessageDialog( R820TTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); } } ); add( mTunerInfo ); - - mComboSampleRate = new JComboBox<>( RTL2832TunerController.SampleRate.values() ); - mComboSampleRate.setEnabled( false ); - mComboSampleRate.addActionListener( new ActionListener() - { + + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent e ) - { - RTL2832TunerController.SampleRate sampleRate = - (RTL2832TunerController.SampleRate)mComboSampleRate.getSelectedItem(); + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = + (SampleRate)mComboSampleRate.getSelectedItem(); try - { + { mController.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the sample " + + "rate setting [" + sampleRate.getLabel() + "] " + + eSampleRate.getLocalizedMessage() ); - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step + mLog.error( "R820T Tuner Controller - couldn't apply sample " + + "rate setting [" + sampleRate.getLabel() + "]", + eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); + /** + * Frequency Correction + */ + SpinnerModel model = + new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); - mFrequencyCorrection.addChangeListener( new ChangeListener() - { + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { + public void stateChanged( ChangeEvent e ) + { final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try + .getModel()).getNumber().doubleValue(); + + try { mController.setFrequencyCorrection( value ); - } - catch ( SourceException e1 ) + } + catch ( SourceException e1 ) { mLog.error( "Error setting frequency correction value", e1 ); } - - save(); - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - - /** - * Gain Controls - */ - add( new JLabel( "Gain" ), "wrap" ); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox( R820TTunerController.R820TGain.values() ); - mComboMasterGain.setEnabled( false ); - mComboMasterGain.addActionListener( new ActionListener() - { + save(); + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + + /** + * Gain Controls + */ + add( new JLabel( "Gain" ), "wrap" ); + + /* Master Gain Control */ + mComboMasterGain = new JComboBox( R820TGain.values() ); + mComboMasterGain.setEnabled( false ); + mComboMasterGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { - R820TTunerController.R820TGain gain = (R820TTunerController.R820TGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (R820TTunerController.R820TGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == R820TTunerController.R820TGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - mComboLNAGain.setEnabled( true ); + { + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - mComboVGAGain.setEnabled( true ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + mController.setGain( (R820TGain)mComboMasterGain.getSelectedItem(), true ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + if( gain == R820TGain.MANUAL ) + { + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + mComboMixerGain.setEnabled( true ); - mComboVGAGain.setEnabled( false ); - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - } + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + mComboLNAGain.setEnabled( true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "gain setting - ", e ); - } - } - } ); - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
independent control of " - + "Mixer, LNA and Enhance gain
" - + "settings, or one of the individual gain settings for
" - + "semi-manual gain control" ); - add( new JLabel( "Master:" ) ); - add( mComboMasterGain ); + mComboVGAGain.setSelectedItem( gain.getVGAGain() ); + mComboVGAGain.setEnabled( true ); + } + else + { + mComboMixerGain.setEnabled( false ); + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox( R820TTunerController.R820TMixerGain.values() ); - mComboMixerGain.setEnabled( false ); - mComboMixerGain.addActionListener( new ActionListener() - { + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + + mComboVGAGain.setEnabled( false ); + mComboVGAGain.setSelectedItem( gain.getVGAGain() ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the gain " + + "setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply " + + "gain setting - ", e ); + } + } + } ); + mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " + + "gain, MANUAL to enable
independent control of " + + "Mixer, LNA and Enhance gain
" + + "settings, or one of the individual gain settings for
" + + "semi-manual gain control" ); + add( new JLabel( "Master:" ) ); + add( mComboMasterGain ); + + /* Mixer Gain Control */ + mComboMixerGain = new JComboBox( R820TMixerGain.values() ); + mComboMixerGain.setEnabled( false ); + mComboMixerGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { - R820TTunerController.R820TMixerGain mixerGain = - (R820TTunerController.R820TMixerGain)mComboMixerGain.getSelectedItem(); - + { + R820TMixerGain mixerGain = + (R820TMixerGain)mComboMixerGain.getSelectedItem(); + if( mixerGain == null ) { mixerGain = DEFAULT_GAIN.getMixerGain(); } - + if( mComboMixerGain.isEnabled() ) { mController.setMixerGain( mixerGain, true ); } - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply mixer " - + "gain setting - ", e ); - } - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - - add( new JLabel( "Mixer:" ) ); - add( mComboMixerGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the mixer " + + "gain setting - " + e.getLocalizedMessage() ); - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( R820TTunerController.R820TLNAGain.values() ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.addActionListener( new ActionListener() - { + mLog.error( "R820T Tuner Controller - couldn't apply mixer " + + "gain setting - ", e ); + } + } + } ); + mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain ); + + /* LNA Gain Control */ + mComboLNAGain = new JComboBox( R820TLNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { - R820TTunerController.R820TLNAGain lnaGain = - (R820TTunerController.R820TLNAGain)mComboLNAGain.getSelectedItem(); - + { + R820TLNAGain lnaGain = + (R820TLNAGain)mComboLNAGain.getSelectedItem(); + if ( lnaGain == null ) { lnaGain = DEFAULT_GAIN.getLNAGain(); } - + if( mComboLNAGain.isEnabled() ) { mController.setLNAGain( lnaGain, true ); } - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the LNA " + + "gain setting - " + e.getLocalizedMessage() ); - /* VGA Gain Control */ - mComboVGAGain = new JComboBox( R820TTunerController.R820TVGAGain.values() ); - mComboVGAGain.setEnabled( false ); - mComboVGAGain.addActionListener( new ActionListener() - { + mLog.error( "R820T Tuner Controller - couldn't apply LNA " + + "gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + /* VGA Gain Control */ + mComboVGAGain = new JComboBox( R820TVGAGain.values() ); + mComboVGAGain.setEnabled( false ); + mComboVGAGain.addActionListener( new ActionListener() + { @Override - public void actionPerformed( ActionEvent arg0 ) - { + public void actionPerformed( ActionEvent arg0 ) + { try - { - R820TTunerController.R820TVGAGain vgaGain = - (R820TTunerController.R820TVGAGain)mComboVGAGain.getSelectedItem(); + { + R820TVGAGain vgaGain = + (R820TVGAGain)mComboVGAGain.getSelectedItem(); if( vgaGain == null ) { vgaGain = DEFAULT_GAIN.getVGAGain(); } - + if( mComboVGAGain.isEnabled() ) { mController.setVGAGain( vgaGain, true ); } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the VGA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply VGA " - + "gain setting", e ); - } - } - } ); - mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - add( new JLabel( "VGA:" ) ); - add( mComboVGAGain ); - } - + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the VGA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply VGA " + + "gain setting", e ); + } + } + } ); + mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + add( new JLabel( "VGA:" ) ); + add( mComboVGAGain ); + } + /** * Sets each of the tuner configuration controls to the enabled argument state */ @@ -388,41 +402,41 @@ public class R820TTunerEditor extends TunerConfigurationEditor { mConfigurationName.setEnabled( enabled ); } - + if( mTunerInfo.isEnabled() != enabled ) { mTunerInfo.setEnabled( enabled ); } - + if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); } - if( mComboSampleRate.isEnabled() != enabled ) - { - mComboSampleRate.setEnabled( enabled ); - } + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } - if( mComboMasterGain.isEnabled() != enabled ) - { - mComboMasterGain.setEnabled( enabled ); - } + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } - if( mComboMixerGain.isEnabled() != enabled ) - { - mComboMixerGain.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboVGAGain.isEnabled() != enabled ) - { - mComboVGAGain.setEnabled( enabled ); - } + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboVGAGain.isEnabled() != enabled ) + { + mComboVGAGain.setEnabled( enabled ); + } } private String getTunerInfo() @@ -430,9 +444,9 @@ public class R820TTunerEditor extends TunerConfigurationEditor StringBuilder sb = new StringBuilder(); RTL2832TunerController.Descriptor descriptor = mController.getDescriptor(); - + sb.append( "

RTL-2832 with R820T Tuner

" ); - + if( descriptor == null ) { sb.append( "No EEPROM Descriptor Available" ); @@ -468,37 +482,37 @@ public class R820TTunerEditor extends TunerConfigurationEditor return sb.toString(); } - + @Override public void save() { - if( hasItem() && !mLoading ) - { - R820TTunerConfiguration config = getConfiguration(); + if( hasItem() && !mLoading ) + { + R820TTunerConfiguration config = getConfiguration(); - config.setName( mConfigurationName.getText() ); + config.setName( mConfigurationName.getText() ); double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - config.setSampleRate( (RTL2832TunerController.SampleRate)mComboSampleRate.getSelectedItem() ); - - R820TTunerController.R820TGain gain = (R820TTunerController.R820TGain)mComboMasterGain.getSelectedItem(); + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); + + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); config.setMasterGain( gain ); - - R820TTunerController.R820TMixerGain mixerGain = (R820TTunerController.R820TMixerGain)mComboMixerGain.getSelectedItem(); + + R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); config.setMixerGain( mixerGain ); - - R820TTunerController.R820TLNAGain lnaGain = (R820TTunerController.R820TLNAGain)mComboLNAGain.getSelectedItem(); + + R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); config.setLNAGain( lnaGain ); - R820TTunerController.R820TVGAGain vgaGain = (R820TTunerController.R820TVGAGain)mComboVGAGain.getSelectedItem(); + R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); config.setVGAGain( vgaGain ); - - getTunerConfigurationModel().broadcast( + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); - } + } } @Override @@ -508,21 +522,21 @@ public class R820TTunerEditor extends TunerConfigurationEditor //Toggle loading so that we don't fire a change event and schedule a settings file save mLoading = true; - + if( hasItem() ) { R820TTunerConfiguration config = getConfiguration(); - + if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mComboMasterGain.setSelectedItem( config.getMasterGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboVGAGain.setSelectedItem( config.getVGAGain() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); } else { @@ -535,7 +549,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( "" ); } - + mLoading = false; } -} +} \ No newline at end of file diff --git a/src/source/tuner/test/SampleGenerator.java b/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java similarity index 96% rename from src/source/tuner/test/SampleGenerator.java rename to src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java index 15796caf..b08403eb 100644 --- a/src/source/tuner/test/SampleGenerator.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java @@ -16,14 +16,14 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.test; +package io.github.dsheirer.source.tuner.test; -import dsp.mixer.LowPhaseNoiseOscillator; +import io.github.dsheirer.dsp.mixer.LowPhaseNoiseOscillator; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.complex.ComplexBuffer; -import util.ThreadPool; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; diff --git a/src/source/tuner/test/TestTuner.java b/src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java similarity index 91% rename from src/source/tuner/test/TestTuner.java rename to src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java index f43e646b..fa918b56 100644 --- a/src/source/tuner/test/TestTuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/TestTuner.java @@ -16,13 +16,13 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.test; +package io.github.dsheirer.source.tuner.test; +import io.github.dsheirer.source.tuner.Tuner; +import io.github.dsheirer.source.tuner.TunerClass; +import io.github.dsheirer.source.tuner.TunerType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import source.tuner.Tuner; -import source.tuner.TunerClass; -import source.tuner.TunerType; /** * Testing tuner that implements an internal oscillator to output a unity gain tone at a specified frequency offset diff --git a/src/source/tuner/test/TestTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java similarity index 93% rename from src/source/tuner/test/TestTunerController.java rename to src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java index 98ff7387..0c94dd5d 100644 --- a/src/source/tuner/test/TestTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java @@ -16,15 +16,15 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.test; +package io.github.dsheirer.source.tuner.test; +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.TunerController; +import io.github.dsheirer.source.tuner.configuration.TunerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.complex.ComplexBuffer; -import source.SourceException; -import source.tuner.TunerController; -import source.tuner.configuration.TunerConfiguration; public class TestTunerController extends TunerController { diff --git a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java index 3331c184..b1bf58a0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTransferProcessor.java @@ -18,7 +18,6 @@ ******************************************************************************/ package io.github.dsheirer.source.tuner.usb; -import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.OverflowableTransferQueue; import io.github.dsheirer.sample.adapter.ISampleAdapter; @@ -69,10 +68,10 @@ public class USBTransferProcessor implements TransferCallback //Byte array transfer buffers size in bytes private int mBufferSize; - private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private OverflowableTransferQueue mFilledBuffers; private LinkedTransferQueue mAvailableTransfers = new LinkedTransferQueue<>(); private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); + private Listener mComplexBufferListener; private AtomicBoolean mRunning = new AtomicBoolean(); @@ -233,36 +232,26 @@ public class USBTransferProcessor implements TransferCallback } /** - * Indicates if there are complex buffer listeners registered with this processor + * Sets the listener and auto-starts the buffer processor */ - public boolean hasListeners() + public void setListener(Listener listener) { - return mComplexBufferBroadcaster.hasListeners(); - } - - public void addListener(Listener listener) - { - mComplexBufferBroadcaster.addListener(listener); - + mComplexBufferListener = listener; start(); } - public void removeListener(Listener listener) + /** + * Auto-stops the buffer processor and removes the listener + */ + public void removeListener() { - mComplexBufferBroadcaster.removeListener(listener); - - if(!hasListeners()) + if(mComplexBufferListener != null) { stop(); + mComplexBufferListener = null; } } - public void removeAllListeners() - { - mComplexBufferBroadcaster.clear(); - stop(); - } - /** * Prepares (allocates) a set of transfer buffers for use in transferring data from the USB device via the bulk * interface. Since we're using direct allocation (native), buffers are retained and reused across multiple @@ -424,7 +413,10 @@ public class USBTransferProcessor implements TransferCallback { float[] complexSamples = mSampleAdapter.convert(buffer); - mComplexBufferBroadcaster.broadcast(new ComplexBuffer(complexSamples)); + if(mComplexBufferListener != null) + { + mComplexBufferListener.receive(new ComplexBuffer(complexSamples)); + } } } catch(Exception e) diff --git a/src/source/tuner/usb/USBTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java similarity index 92% rename from src/source/tuner/usb/USBTunerController.java rename to src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java index f731783b..19d60729 100644 --- a/src/source/tuner/usb/USBTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/usb/USBTunerController.java @@ -16,11 +16,11 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner.usb; +package io.github.dsheirer.source.tuner.usb; -import sample.Listener; -import sample.complex.ComplexBuffer; -import source.tuner.TunerController; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.source.tuner.TunerController; public abstract class USBTunerController extends TunerController { diff --git a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java index 8ece68b6..e219d85b 100644 --- a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java +++ b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java @@ -18,14 +18,13 @@ ******************************************************************************/ package io.github.dsheirer.source.wave; -import io.github.dsheirer.channel.heartbeat.Heartbeat; import io.github.dsheirer.sample.ConversionUtils; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.source.ComplexSource; import io.github.dsheirer.source.IControllableFileSource; import io.github.dsheirer.source.IFrameLocationListener; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; +import io.github.dsheirer.source.SourceEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,12 +35,10 @@ import javax.sound.sampled.UnsupportedAudioFileException; import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.concurrent.ScheduledExecutorService; public class ComplexWaveSource extends ComplexSource implements IControllableFileSource { - private final static Logger mLog = - LoggerFactory.getLogger(ComplexWaveSource.class); + private final static Logger mLog = LoggerFactory.getLogger(ComplexWaveSource.class); private IFrameLocationListener mFrameLocationListener; private int mBytesPerFrame; @@ -57,49 +54,33 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil } @Override - public void setFrequencyChangeListener(Listener listener) + public void setSourceEventListener(Listener listener) { //Not implemented } @Override - public void removeFrequencyChangeListener() + public void removeSourceEventListener() { //Not implemented } @Override - public Listener getFrequencyChangeListener() + public Listener getSourceEventListener() { //Not implemented return null; } - /** - * Not implemented - */ - @Override - public void setHeartbeatListener(Listener listener) - { - } - - /** - * Not implemented - */ - @Override - public void removeHeartbeatListener() - { - } - @Override public void reset() { stop(); - start(null); + start(); } @Override - public void start(ScheduledExecutorService executor) + public void start() { try { @@ -132,11 +113,11 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil } @Override - public int getSampleRate() + public double getSampleRate() { if(mInputStream != null) { - return (int)mInputStream.getFormat().getSampleRate(); + return mInputStream.getFormat().getSampleRate(); } return 0; diff --git a/src/main/java/io/github/dsheirer/source/wave/RealWaveSource.java b/src/main/java/io/github/dsheirer/source/wave/RealWaveSource.java index b41042b8..8a94520c 100644 --- a/src/main/java/io/github/dsheirer/source/wave/RealWaveSource.java +++ b/src/main/java/io/github/dsheirer/source/wave/RealWaveSource.java @@ -17,14 +17,13 @@ ******************************************************************************/ package io.github.dsheirer.source.wave; -import io.github.dsheirer.channel.heartbeat.Heartbeat; import io.github.dsheirer.sample.ConversionUtils; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.real.RealBuffer; import io.github.dsheirer.source.IControllableFileSource; import io.github.dsheirer.source.IFrameLocationListener; import io.github.dsheirer.source.RealSource; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; +import io.github.dsheirer.source.SourceEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +34,6 @@ import javax.sound.sampled.UnsupportedAudioFileException; import java.io.File; import java.io.IOException; import java.util.Arrays; -import java.util.concurrent.ScheduledExecutorService; public class RealWaveSource extends RealSource implements IControllableFileSource, AutoCloseable { @@ -54,52 +52,35 @@ public class RealWaveSource extends RealSource implements IControllableFileSourc mFile = file; } - @Override - public void setFrequencyChangeListener(Listener listener) - { - //Not implemented - } + @Override + public void setSourceEventListener(Listener listener) + { + //Not implemented + } - @Override - public void removeFrequencyChangeListener() - { - //Not implemented - } - - @Override - public Listener getFrequencyChangeListener() - { - //Not implemented - return null; - } - - /** - * Not implemented - */ - @Override - public void setHeartbeatListener(Listener listener) - { - } - - /** - * Not implemented - */ - @Override - public void removeHeartbeatListener() - { - } + @Override + public void removeSourceEventListener() + { + //Not implemented + } + @Override + public Listener getSourceEventListener() + { + //Not implemented + return null; + } @Override public void reset() { stop(); - start(null); + start(); } @Override - public void start(ScheduledExecutorService executor) + public void start() { try { @@ -129,16 +110,15 @@ public class RealWaveSource extends RealSource implements IControllableFileSourc @Override public long getFrameCount() throws IOException { - // TODO Auto-generated method stub return 0; } @Override - public int getSampleRate() + public double getSampleRate() { if(mInputStream != null) { - return (int)mInputStream.getFormat().getSampleRate(); + return mInputStream.getFormat().getSampleRate(); } return 0; diff --git a/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java b/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java index 7b7bf9d0..08a4e355 100644 --- a/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java @@ -1,3 +1,18 @@ +/******************************************************************************* + * 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.spectrum; import io.github.dsheirer.controller.channel.Channel; @@ -18,8 +33,7 @@ import io.github.dsheirer.settings.ColorSettingMenuItem; import io.github.dsheirer.settings.Setting; import io.github.dsheirer.settings.SettingChangeListener; import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event; +import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.spectrum.converter.DFTResultsConverter; import io.github.dsheirer.spectrum.converter.RealDecibelConverter; import io.github.dsheirer.spectrum.menu.AveragingItem; @@ -30,8 +44,13 @@ import io.github.dsheirer.spectrum.menu.SmoothingItem; import io.github.dsheirer.spectrum.menu.SmoothingTypeItem; import net.miginfocom.swing.MigLayout; -import javax.swing.*; -import java.awt.*; +import javax.swing.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JSeparator; +import javax.swing.SwingUtilities; +import java.awt.Component; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; @@ -39,446 +58,456 @@ import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.concurrent.atomic.AtomicBoolean; -public class ChannelSpectrumPanel extends JPanel - implements ChannelEventListener, - Listener, - SettingChangeListener, - SpectralDisplayAdjuster +public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener, + Listener, SettingChangeListener, SpectralDisplayAdjuster { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.REAL ); + private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.REAL); private DFTResultsConverter mDFTConverter = new RealDecibelConverter(); private JLayeredPane mLayeredPane; private SpectrumPanel mSpectrumPanel; private ChannelOverlayPanel mOverlayPanel; - + private Channel mCurrentChannel; - + private int mSampleBufferSize = 2400; - private HalfBandFilter_RB_RB mDecimatingFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - + private HalfBandFilter_RB_RB mDecimatingFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true); + private AtomicBoolean mEnabled = new AtomicBoolean(); - + private SettingsManager mSettingsManager; private ChannelProcessingManager mChannelProcessingManager; - public ChannelSpectrumPanel( SettingsManager settingsManager, - ChannelProcessingManager channelProcessingManager ) + public ChannelSpectrumPanel(SettingsManager settingsManager, + ChannelProcessingManager channelProcessingManager) { - mSettingsManager = settingsManager; - mChannelProcessingManager = channelProcessingManager; - - if( mSettingsManager != null ) - { - mSettingsManager.addListener( this ); - } - - mSpectrumPanel = new SpectrumPanel( mSettingsManager ); - mSpectrumPanel.setAveraging( 1 ); + mSettingsManager = settingsManager; + mChannelProcessingManager = channelProcessingManager; - mOverlayPanel = new ChannelOverlayPanel( mSettingsManager ); - mDFTProcessor.addConverter( mDFTConverter ); - mDFTConverter.addListener( mSpectrumPanel ); + if(mSettingsManager != null) + { + mSettingsManager.addListener(this); + } - /* Set the DFTProcessor to the decimated 24kHz sample rate */ - mDFTProcessor.frequencyChanged( - new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, 24000 ) ); - - initGui(); + mSpectrumPanel = new SpectrumPanel(mSettingsManager); + mSpectrumPanel.setAveraging(1); + + mOverlayPanel = new ChannelOverlayPanel(mSettingsManager); + mDFTProcessor.addConverter(mDFTConverter); + mDFTConverter.addListener(mSpectrumPanel); + + /* Set the DFTProcessor to the decimated 24kHz sample rate */ + mDFTProcessor.process(SourceEvent.sampleRateChange(24000.0)); + + initGui(); } - + public void dispose() { - setEnabled( false ); - - mDFTProcessor.dispose(); + setEnabled(false); - if( mSettingsManager != null ) - { - mSettingsManager.removeListener( this ); - } - - mSettingsManager = null; - mCurrentChannel = null; - mDFTProcessor = null; - mSpectrumPanel = null; + mDFTProcessor.dispose(); + + if(mSettingsManager != null) + { + mSettingsManager.removeListener(this); + } + + mSettingsManager = null; + mCurrentChannel = null; + mDFTProcessor = null; + mSpectrumPanel = null; } - - public void setFrameRate( int framesPerSecond ) + + public void setFrameRate(int framesPerSecond) { - mSampleBufferSize = (int)( 48000 / framesPerSecond ); - - mDFTProcessor.setFrameRate( framesPerSecond ); + mSampleBufferSize = (int)(48000 / framesPerSecond); + + mDFTProcessor.setFrameRate(framesPerSecond); } - + private void initGui() { - setLayout( new MigLayout( "insets 0 0 0 0 ", - "[grow,fill]", - "[grow,fill]") ); - - mLayeredPane = new JLayeredPane(); - mLayeredPane.addComponentListener( new ResizeListener() ); - - MouseEventProcessor mouser = new MouseEventProcessor(); - - mOverlayPanel.addMouseListener( mouser ); - mOverlayPanel.addMouseMotionListener( mouser ); - - mLayeredPane.add( mSpectrumPanel, new Integer( 0 ), 0 ); - mLayeredPane.add( mOverlayPanel, new Integer( 1 ), 0 ); + setLayout(new MigLayout("insets 0 0 0 0 ", + "[grow,fill]", + "[grow,fill]")); - add( mLayeredPane ); + mLayeredPane = new JLayeredPane(); + mLayeredPane.addComponentListener(new ResizeListener()); + + MouseEventProcessor mouser = new MouseEventProcessor(); + + mOverlayPanel.addMouseListener(mouser); + mOverlayPanel.addMouseMotionListener(mouser); + + mLayeredPane.add(mSpectrumPanel, new Integer(0), 0); + mLayeredPane.add(mOverlayPanel, new Integer(1), 0); + + add(mLayeredPane); } - - public void setEnabled( boolean enabled ) + + public void setEnabled(boolean enabled) { - if( enabled && mEnabled.compareAndSet( false, true ) ) - { - start(); - } - else if( !enabled && mEnabled.compareAndSet( true, false ) ) - { - stop(); - } + if(enabled && mEnabled.compareAndSet(false, true)) + { + start(); + } + else if(!enabled && mEnabled.compareAndSet(true, false)) + { + stop(); + } } @Override - @SuppressWarnings( "incomplete-switch" ) - public void channelChanged( ChannelEvent event ) + @SuppressWarnings("incomplete-switch") + public void channelChanged(ChannelEvent event) { - switch( event.getEvent() ) - { - case NOTIFICATION_SELECTION_CHANGE: - //ChannelSelectionManager ensures that only 1 channel can be - //selected and any previously selected channel will be first - //deselected before we get a new selection event - if( event.getChannel().isSelected() ) - { - if( mCurrentChannel != null ) - { - stop(); - mCurrentChannel = null; - } - - mCurrentChannel = event.getChannel(); - - if( mEnabled.get() ) - { - start(); - } - } - else - { - stop(); - mCurrentChannel = null; - } - break; - case NOTIFICATION_PROCESSING_STOP: - if( event.getChannel() == mCurrentChannel ) - { - if( mEnabled.get() ) - { - stop(); - } - - mCurrentChannel = null; - } - break; - } - } - - private void start() - { - if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.getEnabled() ) - { - ProcessingChain processingChain = mChannelProcessingManager - .getProcessingChain( mCurrentChannel ); - - if( processingChain != null ) - { - processingChain.addRealBufferListener( this ); - - mDFTProcessor.start(); - } - } - } - - private void stop() - { - if( mCurrentChannel != null && mCurrentChannel.getEnabled() ) - { - ProcessingChain processingChain = mChannelProcessingManager - .getProcessingChain( mCurrentChannel ); - - if( processingChain != null ) - { - processingChain.removeRealBufferListener( this ); - } - } - - mDFTProcessor.stop(); - - mSpectrumPanel.clearSpectrum(); - } - - @Override - public void settingChanged( Setting setting ) - { - if( mSpectrumPanel != null ) - { - mSpectrumPanel.settingChanged( setting ); - } - if( mOverlayPanel != null ) - { - mOverlayPanel.settingChanged( setting ); - } - } - - @Override - public void settingDeleted( Setting setting ) - { - if( mSpectrumPanel != null ) - { - mSpectrumPanel.settingDeleted( setting ); - } - - if( mOverlayPanel != null ) - { - mOverlayPanel.settingDeleted( setting ); - } - } - - @Override - public void receive( RealBuffer buffer ) - { - RealBuffer decimated = mDecimatingFilter.filter( buffer ); - - //Hack: we're placing real samples in a complex buffer that the DFT - //processor is expecting. - mDFTProcessor.receive( new ComplexBuffer( decimated.getSamples() ) ); - } - - /** - * Monitors the sizing of the layered pane and resizes the spectrum and - * channel panels whenever the layered pane is resized - */ - public class ResizeListener implements ComponentListener - { - @Override - public void componentResized( ComponentEvent e ) + switch(event.getEvent()) { - Component c = e.getComponent(); - - mSpectrumPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() ); - mOverlayPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() ); + case NOTIFICATION_SELECTION_CHANGE: + //ChannelSelectionManager ensures that only 1 channel can be + //selected and any previously selected channel will be first + //deselected before we get a new selection event + if(event.getChannel().isSelected()) + { + if(mCurrentChannel != null) + { + stop(); + mCurrentChannel = null; + } + + mCurrentChannel = event.getChannel(); + + if(mEnabled.get()) + { + start(); + } + } + else + { + stop(); + mCurrentChannel = null; + } + break; + case NOTIFICATION_PROCESSING_STOP: + if(event.getChannel() == mCurrentChannel) + { + if(mEnabled.get()) + { + stop(); + } + + mCurrentChannel = null; + } + break; + } + } + + private void start() + { + if(mEnabled.get() && mCurrentChannel != null && mCurrentChannel.getEnabled()) + { + ProcessingChain processingChain = mChannelProcessingManager + .getProcessingChain(mCurrentChannel); + + if(processingChain != null) + { + processingChain.addRealBufferListener(this); + + mDFTProcessor.start(); + } + } + } + + private void stop() + { + if(mCurrentChannel != null && mCurrentChannel.getEnabled()) + { + ProcessingChain processingChain = mChannelProcessingManager + .getProcessingChain(mCurrentChannel); + + if(processingChain != null) + { + processingChain.removeRealBufferListener(this); + } } - @Override - public void componentHidden( ComponentEvent arg0 ) {} - @Override - public void componentMoved( ComponentEvent arg0 ) {} - @Override - public void componentShown( ComponentEvent arg0 ) {} - } - - /** - * Mouse event handler for the channel panel. - */ - public class MouseEventProcessor implements MouseMotionListener, MouseListener - { - @Override - public void mouseMoved( MouseEvent event ) + mDFTProcessor.stop(); + + mSpectrumPanel.clearSpectrum(); + } + + @Override + public void settingChanged(Setting setting) + { + if(mSpectrumPanel != null) { - update( event ); + mSpectrumPanel.settingChanged(setting); } - @Override - public void mouseDragged( MouseEvent event ) - { - update( event ); - } - - private void update( MouseEvent event ) - { - if( event.getComponent() == mOverlayPanel ) - { - mOverlayPanel.setCursorLocation( event.getPoint() ); - } - } - - @Override - public void mouseEntered( MouseEvent e ) - { - if( e.getComponent() == mOverlayPanel ) - { - mOverlayPanel.setCursorVisible( true ); - } - } - - @Override - public void mouseExited( MouseEvent e ) - { - mOverlayPanel.setCursorVisible( false ); - } - - /** - * Displays the context menu. - */ - @Override - public void mouseClicked( MouseEvent event ) + if(mOverlayPanel != null) { - if( SwingUtilities.isRightMouseButton( event ) ) - { - JPopupMenu contextMenu = new JPopupMenu(); - - /** - * Color Menus - */ - JMenu colorMenu = new JMenu( "Color" ); + mOverlayPanel.settingChanged(setting); + } + } - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_CURSOR ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_LINE ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_BACKGROUND ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_GRADIENT_BOTTOM ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_GRADIENT_TOP ) ); - - contextMenu.add( colorMenu ); - - /** - * Display items: fft and frame rate - */ - JMenu displayMenu = new JMenu( "Display" ); - contextMenu.add( displayMenu ); - - /** - * Averaging menu - */ - JMenu averagingMenu = new JMenu( "Averaging" ); - averagingMenu.add( - new AveragingItem( ChannelSpectrumPanel.this, 2 ) ); - displayMenu.add( averagingMenu ); - - /** - * FFT width - */ - JMenu fftWidthMenu = new JMenu( "FFT Width" ); - displayMenu.add( fftWidthMenu ); - - for( DFTSize width: DFTSize.values() ) - { - fftWidthMenu.add( new DFTSizeItem( mDFTProcessor, width ) ); - } - - /** - * DFT Processor Frame Rate - */ - JMenu frameRateMenu = new JMenu( "Frame Rate" ); - displayMenu.add( frameRateMenu ); - - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 14 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 16 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 18 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 20 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 25 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 30 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 40 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 50 ) ); - - /** - * FFT Window Type - */ - JMenu fftWindowType = new JMenu( "Window Type" ); - displayMenu.add( fftWindowType ); - - for( WindowType type: WindowType.values() ) - { - fftWindowType.add( - new FFTWindowTypeItem( mDFTProcessor, type ) ); - } - - /** - * Smoothing menu - */ - JMenu smoothingMenu = new JMenu( "Smoothing" ); - - if( mSpectrumPanel.getSmoothingType() != SmoothingType.NONE ) - { - smoothingMenu.add( new SmoothingItem( ChannelSpectrumPanel.this, 5 ) ); - smoothingMenu.add( new JSeparator() ); - } - - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.GAUSSIAN ) ); - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.TRIANGLE ) ); - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.RECTANGLE ) ); - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.NONE ) ); - - displayMenu.add( smoothingMenu ); - - if( contextMenu != null ) - { - contextMenu.show( mOverlayPanel, - event.getX(), - event.getY() ); - } - } + @Override + public void settingDeleted(Setting setting) + { + if(mSpectrumPanel != null) + { + mSpectrumPanel.settingDeleted(setting); } - @Override - public void mousePressed( MouseEvent e ) {} - @Override - public void mouseReleased( MouseEvent e ) {} - } + if(mOverlayPanel != null) + { + mOverlayPanel.settingDeleted(setting); + } + } - @Override + @Override + public void receive(RealBuffer buffer) + { + RealBuffer decimated = mDecimatingFilter.filter(buffer); + + //Hack: we're placing real samples in a complex buffer that the DFT + //processor is expecting. + mDFTProcessor.receive(new ComplexBuffer(decimated.getSamples())); + } + + /** + * Monitors the sizing of the layered pane and resizes the spectrum and + * channel panels whenever the layered pane is resized + */ + public class ResizeListener implements ComponentListener + { + @Override + public void componentResized(ComponentEvent e) + { + Component c = e.getComponent(); + + mSpectrumPanel.setBounds(0, 0, c.getWidth(), c.getHeight()); + mOverlayPanel.setBounds(0, 0, c.getWidth(), c.getHeight()); + } + + @Override + public void componentHidden(ComponentEvent arg0) + { + } + + @Override + public void componentMoved(ComponentEvent arg0) + { + } + + @Override + public void componentShown(ComponentEvent arg0) + { + } + } + + /** + * Mouse event handler for the channel panel. + */ + public class MouseEventProcessor implements MouseMotionListener, MouseListener + { + @Override + public void mouseMoved(MouseEvent event) + { + update(event); + } + + @Override + public void mouseDragged(MouseEvent event) + { + update(event); + } + + private void update(MouseEvent event) + { + if(event.getComponent() == mOverlayPanel) + { + mOverlayPanel.setCursorLocation(event.getPoint()); + } + } + + @Override + public void mouseEntered(MouseEvent e) + { + if(e.getComponent() == mOverlayPanel) + { + mOverlayPanel.setCursorVisible(true); + } + } + + @Override + public void mouseExited(MouseEvent e) + { + mOverlayPanel.setCursorVisible(false); + } + + /** + * Displays the context menu. + */ + @Override + public void mouseClicked(MouseEvent event) + { + if(SwingUtilities.isRightMouseButton(event)) + { + JPopupMenu contextMenu = new JPopupMenu(); + + /** + * Color Menus + */ + JMenu colorMenu = new JMenu("Color"); + + colorMenu.add(new ColorSettingMenuItem(mSettingsManager, + ColorSettingName.SPECTRUM_CURSOR)); + + colorMenu.add(new ColorSettingMenuItem(mSettingsManager, + ColorSettingName.SPECTRUM_LINE)); + + colorMenu.add(new ColorSettingMenuItem(mSettingsManager, + ColorSettingName.SPECTRUM_BACKGROUND)); + + colorMenu.add(new ColorSettingMenuItem(mSettingsManager, + ColorSettingName.SPECTRUM_GRADIENT_BOTTOM)); + + colorMenu.add(new ColorSettingMenuItem(mSettingsManager, + ColorSettingName.SPECTRUM_GRADIENT_TOP)); + + contextMenu.add(colorMenu); + + /** + * Display items: fft and frame rate + */ + JMenu displayMenu = new JMenu("Display"); + contextMenu.add(displayMenu); + + /** + * Averaging menu + */ + JMenu averagingMenu = new JMenu("Averaging"); + averagingMenu.add( + new AveragingItem(ChannelSpectrumPanel.this, 2)); + displayMenu.add(averagingMenu); + + /** + * FFT width + */ + JMenu fftWidthMenu = new JMenu("FFT Width"); + displayMenu.add(fftWidthMenu); + + for(DFTSize width : DFTSize.values()) + { + fftWidthMenu.add(new DFTSizeItem(mDFTProcessor, width)); + } + + /** + * DFT Processor Frame Rate + */ + JMenu frameRateMenu = new JMenu("Frame Rate"); + displayMenu.add(frameRateMenu); + + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 14)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 16)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 18)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 20)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 25)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 30)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 40)); + frameRateMenu.add(new FrameRateItem(mDFTProcessor, 50)); + + /** + * FFT Window Type + */ + JMenu fftWindowType = new JMenu("Window Type"); + displayMenu.add(fftWindowType); + + for(WindowType type : WindowType.values()) + { + fftWindowType.add( + new FFTWindowTypeItem(mDFTProcessor, type)); + } + + /** + * Smoothing menu + */ + JMenu smoothingMenu = new JMenu("Smoothing"); + + if(mSpectrumPanel.getSmoothingType() != SmoothingType.NONE) + { + smoothingMenu.add(new SmoothingItem(ChannelSpectrumPanel.this, 5)); + smoothingMenu.add(new JSeparator()); + } + + smoothingMenu.add(new SmoothingTypeItem(ChannelSpectrumPanel.this, SmoothingType.GAUSSIAN)); + smoothingMenu.add(new SmoothingTypeItem(ChannelSpectrumPanel.this, SmoothingType.TRIANGLE)); + smoothingMenu.add(new SmoothingTypeItem(ChannelSpectrumPanel.this, SmoothingType.RECTANGLE)); + smoothingMenu.add(new SmoothingTypeItem(ChannelSpectrumPanel.this, SmoothingType.NONE)); + + displayMenu.add(smoothingMenu); + + if(contextMenu != null) + { + contextMenu.show(mOverlayPanel, + event.getX(), + event.getY()); + } + } + } + + @Override + public void mousePressed(MouseEvent e) + { + } + + @Override + public void mouseReleased(MouseEvent e) + { + } + } + + @Override public int getAveraging() { - return mSpectrumPanel.getAveraging(); + return mSpectrumPanel.getAveraging(); } - @Override - public void setAveraging( int averaging ) + @Override + public void setAveraging(int averaging) { - mSpectrumPanel.setAveraging( averaging ); + mSpectrumPanel.setAveraging(averaging); } - public void setSampleSize( double sampleSize ) - { - mSpectrumPanel.setSampleSize( sampleSize ); - } + public void setSampleSize(double sampleSize) + { + mSpectrumPanel.setSampleSize(sampleSize); + } - @Override - public int getSmoothing() - { - return mSpectrumPanel.getSmoothing(); - } + @Override + public int getSmoothing() + { + return mSpectrumPanel.getSmoothing(); + } - @Override - public void setSmoothing( int smoothing ) - { - mSpectrumPanel.setSmoothing( smoothing ); - } + @Override + public void setSmoothing(int smoothing) + { + mSpectrumPanel.setSmoothing(smoothing); + } - @Override - public SmoothingType getSmoothingType() - { - return mSpectrumPanel.getSmoothingType(); - } + @Override + public SmoothingType getSmoothingType() + { + return mSpectrumPanel.getSmoothingType(); + } - @Override - public void setSmoothingType( SmoothingType type ) - { - mSpectrumPanel.setSmoothingType( type ); - } + @Override + public void setSmoothingType(SmoothingType type) + { + mSpectrumPanel.setSmoothingType(type); + } } diff --git a/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java b/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java index b69628be..d2bf9205 100644 --- a/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java +++ b/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.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.spectrum; @@ -25,8 +22,8 @@ import io.github.dsheirer.sample.Buffer; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.SampleType; import io.github.dsheirer.sample.complex.ComplexBuffer; -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.spectrum.converter.DFTResultsConverter; import io.github.dsheirer.util.ThreadPool; import org.jtransforms.fft.FloatFFT_1D; @@ -46,7 +43,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * Processes both complex samples or float samples and dispatches a float array * of DFT results, using configurable fft size and output dispatch timelines. */ -public class DFTProcessor implements Listener, IFrequencyChangeProcessor, IDFTWidthChangeProcessor +public class DFTProcessor implements Listener, ISourceEventProcessor, IDFTWidthChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger(DFTProcessor.class); @@ -70,7 +67,7 @@ public class DFTProcessor implements Listener, IFrequencyChangePr /* The Cosine and Hanning windows seem to offer the best spectral display * with minimal bin leakage/smearing */ - private WindowType mWindowType = Window.WindowType.HANNING; + private WindowType mWindowType = WindowType.BLACKMAN_HARRIS_7; private FloatFFT_1D mFFT = new FloatFFT_1D(mDFTSize.getSize()); @@ -452,7 +449,7 @@ public class DFTProcessor implements Listener, IFrequencyChangePr } @Override - public void frequencyChanged(FrequencyChangeEvent event) + public void process(SourceEvent event) { switch(event.getEvent()) { diff --git a/src/main/java/io/github/dsheirer/spectrum/OverlayPanel.java b/src/main/java/io/github/dsheirer/spectrum/OverlayPanel.java index d1317e11..37b607df 100644 --- a/src/main/java/io/github/dsheirer/spectrum/OverlayPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/OverlayPanel.java @@ -28,14 +28,22 @@ import io.github.dsheirer.settings.ColorSetting.ColorSettingName; import io.github.dsheirer.settings.Setting; import io.github.dsheirer.settings.SettingChangeListener; import io.github.dsheirer.settings.SettingsManager; -import io.github.dsheirer.source.tuner.TunerChannel; -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.channel.TunerChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.awt.Stroke; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.geom.Line2D; @@ -44,7 +52,7 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.concurrent.CopyOnWriteArrayList; -public class OverlayPanel extends JPanel implements ChannelEventListener, IFrequencyChangeProcessor, +public class OverlayPanel extends JPanel implements ChannelEventListener, ISourceEventProcessor, SettingChangeListener { private static final long serialVersionUID = 1L; @@ -635,7 +643,7 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, IFrequ * Frequency change event handler */ @Override - public void frequencyChanged(FrequencyChangeEvent event) + public void process(SourceEvent event) { switch(event.getEvent()) { diff --git a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java index 6ade9244..c9719429 100644 --- a/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/SpectralDisplayPanel.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.spectrum; @@ -33,10 +30,9 @@ import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.settings.ColorSetting.ColorSettingName; import io.github.dsheirer.settings.ColorSettingMenuItem; import io.github.dsheirer.settings.SettingsManager; +import io.github.dsheirer.source.ISourceEventProcessor; +import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.Tuner; -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.spectrum.OverlayPanel.ChannelDisplay; import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter; import io.github.dsheirer.spectrum.converter.DFTResultsConverter; @@ -50,11 +46,25 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.JSplitPane; +import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.MouseInputAdapter; -import java.awt.*; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; @@ -65,7 +75,7 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Hashtable; -public class SpectralDisplayPanel extends JPanel implements Listener, IFrequencyChangeProcessor, IDFTWidthChangeProcessor +public class SpectralDisplayPanel extends JPanel implements Listener, ISourceEventProcessor, IDFTWidthChangeProcessor { private static final long serialVersionUID = 1L; @@ -223,7 +233,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener) mDFTProcessor); + mTuner.getSourceManager().addComplexBufferListener(mDFTProcessor); mSpectrumPanel.setSampleSize(mTuner.getSampleSize()); //Fire frequency and sample rate change events so that the spectrum //and overlay panels can synchronize - frequencyChanged(new FrequencyChangeEvent( - Event.NOTIFICATION_FREQUENCY_CHANGE, - mTuner.getTunerController().getFrequency())); + process(SourceEvent.frequencyChange(mTuner.getTunerController().getFrequency())); - frequencyChanged(new FrequencyChangeEvent( - Event.NOTIFICATION_SAMPLE_RATE_CHANGE, - mTuner.getTunerController().getSampleRate())); + process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate())); } } @@ -452,7 +458,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener) mDFTProcessor); + mTuner.getTunerController().removeComplexBufferListener(mDFTProcessor); mTuner = null; } } @@ -508,7 +514,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener - ******************************************************************************/ - -package module; - -import java.util.concurrent.ScheduledExecutorService; - -public abstract class Module -{ - /** - * Defines the basic component level class for all processing, demodulation - * and decoding components that can operate within a processing chain. - * - * Modules can optionally implement any of the following interfaces: - * - * IAudioPacketListener consumes audio packets - * IAudioPacketProvider produces audio packets - * ICallEventListener consumes call events - * ICallEventProvider provides call events - * IChannelEventListener consumes channel events - * IChannelEventProvider provides channel events (reset, selection, start, etc) - * IComplexBufferListener consumes complex samples and normally - * produces demodulated real sample buffers - * IFrequencyCorrectionController provides tuned frequency error corrections - * IMessageProvider produces messages - * IRealBufferListener consumes demodulated real sample buffers - * IRealBufferListener produces demodulated real sample buffers - * ISquelchStateListener consumes squelch states - * ISquelchStateProvider provides squelch states - */ - public Module() - { - } - - /** - * Initialize or reset all internal states to default - prepare to start - * processing or resume processing, potentially with a different source. - * - * This method is invoked after constructing a module, and following any stop() - * calls and before any start() calls. This allows the module to prior to - * starting a module and starting it again processing a newly tuned channel, - * like a traffic channel. - */ - public abstract void reset(); - - /** - * Start processing. - */ - public abstract void start(); - - /** - * Stop processing - */ - public abstract void stop(); - - /** - * Dispose of all resources and listeners and prepare for garbage collection - */ - public abstract void dispose(); - -} diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java deleted file mode 100644 index fb4a8722..00000000 --- a/src/module/ProcessingChain.java +++ /dev/null @@ -1,821 +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 module; - -import audio.AudioPacket; -import audio.IAudioPacketListener; -import audio.IAudioPacketProvider; -import audio.squelch.ISquelchStateListener; -import audio.squelch.ISquelchStateProvider; -import audio.squelch.SquelchState; -import source.heartbeat.Heartbeat; -import source.heartbeat.IHeartbeatListener; -import source.heartbeat.IHeartbeatProvider; -import channel.metadata.AttributeChangeRequest; -import channel.metadata.IAttributeChangeRequestListener; -import channel.metadata.IAttributeChangeRequestProvider; -import channel.state.ChannelState; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.IDecoderStateEventListener; -import channel.state.IDecoderStateEventProvider; -import controller.channel.Channel.ChannelType; -import controller.channel.ChannelEvent; -import controller.channel.IChannelEventListener; -import controller.channel.IChannelEventProvider; -import message.IMessageListener; -import message.IMessageProvider; -import message.Message; -import module.decode.event.CallEvent; -import module.decode.event.CallEventModel; -import module.decode.event.ICallEventListener; -import module.decode.event.ICallEventProvider; -import module.decode.event.MessageActivityModel; -import module.log.EventLogger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import record.wave.ComplexBufferWaveRecorder; -import record.wave.RealBufferWaveRecorder; -import sample.Broadcaster; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferListener; -import sample.real.IFilteredRealBufferListener; -import sample.real.IFilteredRealBufferProvider; -import sample.real.IUnFilteredRealBufferListener; -import sample.real.IUnFilteredRealBufferProvider; -import sample.real.RealBuffer; -import source.ComplexSource; -import source.RealSource; -import source.Source; -import source.ISourceEventListener; -import source.ISourceEventProvider; -import source.SourceEvent; -import util.ThreadPool; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Processing chain provides a framework for connecting a complex or real sample - * source to a set of one primary decoder and zero or more auxiliary decoders. - * All decoded messages and call events produced by the decoders and the decoder - * call states are aggregated by the various broadcasters. You can register - * listeners to receive aggregated messages, call events, and audio packets. - * - * Normal setup sequence: - * - * 1) Add one or more modules - * 2) Register listeners to receive messages, call events, audio, etc. - * 3) Add a valid source - * 4) Invoke the start() method to start processing. - * 5) Invoke the stop() method to stop processing. - * - * Optional: if you want to reuse the processing chain with a new sample source, - * invoke the following method sequence: stop(), setSource(), start() - */ -public class ProcessingChain implements IChannelEventListener -{ - private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class); - - private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); - private Broadcaster mAudioPacketBroadcaster = new Broadcaster<>(); - private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); - private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); - private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); - private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); - private Broadcaster mHeartbeatBroadcaster = new Broadcaster<>(); - private Broadcaster mFrequencyChangeEventBroadcaster = new Broadcaster<>(); - private Broadcaster mMessageBroadcaster = new Broadcaster<>(); - private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>(); - private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); - private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); - - private AtomicBoolean mRunning = new AtomicBoolean(); - - protected Source mSource; - private List mModules = new ArrayList<>(); - private CallEventModel mCallEventModel; - private ChannelState mChannelState; - private MessageActivityModel mMessageActivityModel; - - /** - * Creates a processing chain for managing a set of modules - * - * @param channelType - */ - public ProcessingChain(ChannelType channelType) - { - mChannelState = new ChannelState(channelType); - addModule(mChannelState); - - mCallEventModel = new CallEventModel(); - addCallEventListener(mCallEventModel); - } - - public CallEventModel getCallEventModel() - { - return mCallEventModel; - } - - public ChannelState getChannelState() - { - return mChannelState; - } - - public MessageActivityModel getMessageActivityModel() - { - return mMessageActivityModel; - } - - public void setMessageActivityModel(MessageActivityModel model) - { - mMessageActivityModel = model; - - addMessageListener(mMessageActivityModel); - } - - public void dispose() - { - stop(); - - for(Module module : mModules) - { - module.dispose(); - } - - mModules.clear(); - - mAudioPacketBroadcaster.dispose(); - mCallEventBroadcaster.dispose(); - mChannelEventBroadcaster.dispose(); - mComplexBufferBroadcaster.dispose(); - mMessageBroadcaster.dispose(); - mFilteredRealBufferBroadcaster.dispose(); - mSquelchStateBroadcaster.dispose(); - } - - /** - * Indicates if this processing chain is currently receiving samples from - * a source and sending those samples to the decoders. - */ - public boolean isProcessing() - { - return mRunning.get(); - } - - /** - * Indicates if this chain currently has a valid sample source. - */ - public boolean hasSource() - { - return mSource != null; - } - - /** - * Applies a sample source to this processing chain. Processing won't - * start until the start() method is invoked. - * - * @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 - { - if(isProcessing()) - { - throw new IllegalStateException("Processing chain is currently processing. Invoke stop() on the " + - "processing chain before applying a new sample source"); - } - - mSource = source; - - addModule(mSource); - } - - /** - * List of current modules for this processing chain - */ - public List getModules() - { - return mModules; - } - - /** - * List of decoder states for this processing chain - */ - public List getDecoderStates() - { - List decoderStates = new ArrayList<>(); - - for(Module module : mModules) - { - if(module instanceof DecoderState) - { - decoderStates.add((DecoderState) module); - } - } - - return decoderStates; - } - - /** - * Adds the list of modules to this processing chain - */ - public void addModules(List modules) - { - for(Module module : modules) - { - addModule(module); - } - } - - /** - * Adds a module to the processing chain. Each module is tested for the - * interfaces that it supports and is registered or receives a listener - * to consume or produce the supported interface data type. All elements - * and events that are produced by any module are automatically routed to - * all other components that support the corresponding listener interface. - * - * At least one module should consume complex samples and either produce - * decoded messages and/or audio, or produce decoded real sample buffers - * for all other modules to consume. - * - * @param module - processing module, demodulator, decoder, source, state - * machine, etc. - */ - public void addModule(Module module) - { - mModules.add(module); - - registerListeners(module); - registerProviders(module); - } - - /** - * Removes the module from the processing chain and deregisters the module as a listener and - * as a provider - */ - public void removeModule(Module module) - { - unregisterListeners(module); - unregisterProviders(module); - - mModules.remove(module); - } - - /** - * Registers the module as a listener to each of the broadcasters that - * provide the data interface(s) supported by the module. - */ - private void registerListeners(Module module) - { - if(module instanceof IAttributeChangeRequestListener) - { - mAttributeChangeRequestBroadcaster.addListener(((IAttributeChangeRequestListener)module).getAttributeChangeRequestListener()); - } - - if(module instanceof IAudioPacketListener) - { - mAudioPacketBroadcaster.addListener(((IAudioPacketListener) module).getAudioPacketListener()); - } - - if(module instanceof ICallEventListener) - { - mCallEventBroadcaster.addListener(((ICallEventListener) module).getCallEventListener()); - } - - if(module instanceof IChannelEventListener) - { - mChannelEventBroadcaster.addListener(((IChannelEventListener) module).getChannelEventListener()); - } - - if(module instanceof IComplexBufferListener) - { - mComplexBufferBroadcaster.addListener(((IComplexBufferListener) module).getComplexBufferListener()); - } - - if(module instanceof IDecoderStateEventListener) - { - mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener()); - } - - if(module instanceof ISourceEventListener) - { - Listener listener = ((ISourceEventListener)module).getSourceEventListener(); - - if(listener != null) - { - mFrequencyChangeEventBroadcaster.addListener(listener); - } - } - - if(module instanceof IHeartbeatListener) - { - mHeartbeatBroadcaster.addListener(((IHeartbeatListener)module).getHeartbeatListener()); - } - - if(module instanceof IMessageListener) - { - mMessageBroadcaster.addListener(((IMessageListener) module).getMessageListener()); - } - - if(module instanceof IFilteredRealBufferListener) - { - mFilteredRealBufferBroadcaster.addListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener()); - } - - if(module instanceof ISquelchStateListener) - { - mSquelchStateBroadcaster.addListener(((ISquelchStateListener) module).getSquelchStateListener()); - } - - if(module instanceof IUnFilteredRealBufferListener) - { - mUnFilteredRealBufferBroadcaster.addListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener()); - } - } - - /** - * Registers the module as a listener to each of the broadcasters that - * provide the data interface(s) supported by the module. - */ - private void unregisterListeners(Module module) - { - if(module instanceof IAttributeChangeRequestListener) - { - mAttributeChangeRequestBroadcaster.removeListener(((IAttributeChangeRequestListener) module).getAttributeChangeRequestListener()); - } - - if(module instanceof IAudioPacketListener) - { - mAudioPacketBroadcaster.removeListener(((IAudioPacketListener) module).getAudioPacketListener()); - } - - if(module instanceof ICallEventListener) - { - mCallEventBroadcaster.removeListener(((ICallEventListener) module).getCallEventListener()); - } - - if(module instanceof IChannelEventListener) - { - mChannelEventBroadcaster.removeListener(((IChannelEventListener) module).getChannelEventListener()); - } - - if(module instanceof IComplexBufferListener) - { - mComplexBufferBroadcaster.removeListener(((IComplexBufferListener) module).getComplexBufferListener()); - } - - if(module instanceof IDecoderStateEventListener) - { - mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener()); - } - - if(module instanceof ISourceEventListener) - { - mFrequencyChangeEventBroadcaster.removeListener(((ISourceEventListener) module).getSourceEventListener()); - } - - if(module instanceof IHeartbeatListener) - { - mHeartbeatBroadcaster.removeListener(((IHeartbeatListener)module).getHeartbeatListener()); - } - - if(module instanceof IMessageListener) - { - mMessageBroadcaster.removeListener(((IMessageListener) module).getMessageListener()); - } - - if(module instanceof IFilteredRealBufferListener) - { - mFilteredRealBufferBroadcaster.removeListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener()); - } - - if(module instanceof ISquelchStateListener) - { - mSquelchStateBroadcaster.removeListener(((ISquelchStateListener) module).getSquelchStateListener()); - } - - if(module instanceof IUnFilteredRealBufferListener) - { - mUnFilteredRealBufferBroadcaster.removeListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener()); - } - } - - /** - * Registers the broadcaster(s) as listeners to the module for each - * provider interface that is supported by the module. - */ - private void registerProviders(Module module) - { - if(module instanceof IAttributeChangeRequestProvider) - { - ((IAttributeChangeRequestProvider)module).setAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster); - } - - if(module instanceof IAudioPacketProvider) - { - ((IAudioPacketProvider) module).setAudioPacketListener(mAudioPacketBroadcaster); - } - - if(module instanceof ICallEventProvider) - { - ((ICallEventProvider) module).addCallEventListener(mCallEventBroadcaster); - } - - if(module instanceof IChannelEventProvider) - { - ((IChannelEventProvider) module).setChannelEventListener(mChannelEventBroadcaster); - } - - if(module instanceof IDecoderStateEventProvider) - { - ((IDecoderStateEventProvider) module).setDecoderStateListener(mDecoderStateEventBroadcaster); - } - - if(module instanceof IHeartbeatProvider) - { - ((IHeartbeatProvider)module).getHeartbeatManager().addHeartbeatListener(mHeartbeatBroadcaster); - } - - if(module instanceof IMessageProvider) - { - ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); - } - - if(module instanceof IFilteredRealBufferProvider) - { - ((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster); - } - - if(module instanceof IMessageProvider) - { - ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); - } - - if(module instanceof ISourceEventProvider) - { - ((ISourceEventProvider) module).setSourceEventListener(mFrequencyChangeEventBroadcaster); - } - - if(module instanceof ISquelchStateProvider) - { - ((ISquelchStateProvider) module).setSquelchStateListener(mSquelchStateBroadcaster); - } - - if(module instanceof IUnFilteredRealBufferProvider) - { - ((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(mUnFilteredRealBufferBroadcaster); - } - } - - /** - * Unregisters the broadcaster(s) as listeners to the module for each - * provider interface that is supported by the module. - */ - private void unregisterProviders(Module module) - { - if(module instanceof IAttributeChangeRequestProvider) - { - ((IAttributeChangeRequestProvider)module).removeAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster); - } - - if(module instanceof IAudioPacketProvider) - { - ((IAudioPacketProvider) module).setAudioPacketListener(null); - } - - if(module instanceof ICallEventProvider) - { - ((ICallEventProvider) module).removeCallEventListener(mCallEventBroadcaster); - } - - if(module instanceof IChannelEventProvider) - { - ((IChannelEventProvider) module).setChannelEventListener(null); - } - - if(module instanceof IDecoderStateEventProvider) - { - ((IDecoderStateEventProvider) module).setDecoderStateListener(null); - } - - if(module instanceof IHeartbeatProvider) - { - ((IHeartbeatProvider)module).getHeartbeatManager().removeHeartbeatListener(mHeartbeatBroadcaster); - } - - if(module instanceof IMessageProvider) - { - ((IMessageProvider) module).setMessageListener(null); - } - - if(module instanceof IFilteredRealBufferProvider) - { - ((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); - } - - if(module instanceof IUnFilteredRealBufferProvider) - { - ((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(null); - } - } - - /** - * Starts processing if the chain has a valid source. Invocations on an - * already started chain have no effect. - */ - public void start() - { - if(mRunning.compareAndSet(false, true)) - { - if(mSource != null) - { - /* Reset each of the modules */ - for(Module module : mModules) - { - module.reset(); - } - - //Setup the channel state to monitor source overflow conditions - mSource.setOverflowListener(mChannelState); - - /* Register with the source to receive sample data. Setup a - * timer task to process the buffer queues 50 times a second - * (every 20 ms) */ - switch(mSource.getSampleType()) - { - case COMPLEX: - ((ComplexSource) mSource).setListener(mComplexBufferBroadcaster); - break; - case REAL: - ((RealSource) mSource).setListener(mFilteredRealBufferBroadcaster); - break; - default: - throw new IllegalArgumentException("Unrecognized source " - + "sample type - cannot start processing chain"); - } - - /* Start each of the modules */ - for(Module module : mModules) - { - try - { - module.start(); - } - catch(Exception e) - { - mLog.error("Error starting module", e); - } - } - } - else - { - mLog.error("Source is null on start()"); - } - } - } - - /** - * Stops processing if the chain is currently processing. Invocations on an already stopped chain have no effect. - */ - public void stop() - { - if(mRunning.compareAndSet(true, false)) - { - /* Stop each of the modules */ - for(Module module : mModules) - { - module.stop(); - } - - if(mSource != null) - { - removeModule(mSource); - - mSource.setOverflowListener(null); - - switch(mSource.getSampleType()) - { - case COMPLEX: - ((ComplexSource) mSource).removeListener(mComplexBufferBroadcaster); - break; - case REAL: - ((RealSource) mSource).removeListener(mFilteredRealBufferBroadcaster); - break; - default: - throw new IllegalArgumentException("Unrecognized source sample type - cannot start processing " + - "chain"); - } - - /* Release the source */ - mSource.dispose(); - mSource = null; - } - } - } - - /** - * Removes any logging modules that are currently registered with this processing chain - */ - public void removeEventLoggingModules() - { - List eventLoggingModules = new ArrayList<>(); - - for(Module module : mModules) - { - if(module instanceof EventLogger) - { - eventLoggingModules.add(module); - } - } - - for(Module eventLoggingModule : eventLoggingModules) - { - removeModule(eventLoggingModule); - } - } - - /** - * Removes any recording modules that are currently registered with this processing chain - */ - public void removeRecordingModules() - { - List recordingModules = new ArrayList<>(); - - for(Module module : mModules) - { - if(module instanceof RealBufferWaveRecorder || module instanceof ComplexBufferWaveRecorder) - { - recordingModules.add(module); - } - } - - for(Module recordingModule : recordingModules) - { - removeModule(recordingModule); - } - } - - /** - * Adds the listener to receive audio packets from all modules. - */ - public void addAudioPacketListener(Listener listener) - { - mAudioPacketBroadcaster.addListener(listener); - } - - public void removeAudioPacketListener(Listener listener) - { - mAudioPacketBroadcaster.removeListener(listener); - } - - /** - * Adds the listener to receive call events from all modules. - */ - public void addCallEventListener(Listener listener) - { - mCallEventBroadcaster.addListener(listener); - } - - public void removeCallEventListener(Listener listener) - { - mCallEventBroadcaster.removeListener(listener); - } - - /** - * Adds the listener to receive call events from all modules. - */ - public void addChannelEventListener(Listener listener) - { - mChannelEventBroadcaster.addListener(listener); - } - - public void removeChannelEventListener(Listener listener) - { - mChannelEventBroadcaster.removeListener(listener); - } - - /** - * Adds the listener to receive decoder state events from decoder modules - */ - public void addDecoderStateEventListener(Listener listener) - { - mDecoderStateEventBroadcaster.addListener(listener); - } - - public void removeDecoderStateEventListener(Listener listener) - { - mDecoderStateEventBroadcaster.removeListener(listener); - } - - public Listener getDecoderStateEventListener() - { - return mDecoderStateEventBroadcaster; - } - - /** - * Adds the listener to receive decoded messages from all decoders. - */ - public void addMessageListener(Listener listener) - { - mMessageBroadcaster.addListener(listener); - } - - /** - * Adds the list of listeners to receive decoded messages from all decoders. - */ - public void addMessageListeners(List> listeners) - { - for(Listener listener : listeners) - { - mMessageBroadcaster.addListener(listener); - } - } - - public void removeMessageListener(Listener listener) - { - mMessageBroadcaster.removeListener(listener); - } - - /** - * Adds the listener to receive frequency change events from the processing chain - * @param listener to receive events - */ - public void addFrequencyChangeListener(Listener listener) - { - mFrequencyChangeEventBroadcaster.addListener(listener); - } - - /** - * Removes the listener from receiving frequency change events - * @param listener to remove - */ - public void removeFrequencyChangeListener(Listener listener) - { - mFrequencyChangeEventBroadcaster.removeListener(listener); - } - - /** - * Adds the listener to receive call events from all modules. - */ - public void addSquelchStateListener(Listener listener) - { - mSquelchStateBroadcaster.addListener(listener); - } - - public void removeSquelchStateListener(Listener listener) - { - mSquelchStateBroadcaster.removeListener(listener); - } - - public void addRealBufferListener(Listener listener) - { - mFilteredRealBufferBroadcaster.addListener(listener); - } - - public void removeRealBufferListener(Listener listener) - { - mFilteredRealBufferBroadcaster.removeListener(listener); - } - - @Override - public Listener getChannelEventListener() - { - return mChannelEventBroadcaster; - } -} diff --git a/src/module/decode/Decoder.java b/src/module/decode/Decoder.java deleted file mode 100644 index 3a029035..00000000 --- a/src/module/decode/Decoder.java +++ /dev/null @@ -1,100 +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 module.decode; - -import message.IMessageProvider; -import message.Message; -import module.Module; -import sample.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; - - /** - * Decoder - parent class for all decoders, demodulators and components. - */ - public Decoder() - { - } - - @Override - public void dispose() - { - mMessageListener = null; - } - - /** - * Identifies the decoder type (ie protocol) - */ - public abstract DecoderType getDecoderType(); - - - /** - * Adds a listener for receiving decoded messages from this decoder - */ - @Override - public void setMessageListener( Listener listener ) - { - mMessageListener = listener; - } - - /** - * Removes the listener from receiving decoded messages from all attached - * decoders - */ - @Override - public void removeMessageListener() - { - mMessageListener = null; - } - - /** - * Broadcasts the message to the registered message listener - */ - public void broadcast( Message message ) - { - if( mMessageListener != null ) - { - mMessageListener.receive( message ); - } - } - - @Override - public void receive( Message message ) - { - broadcast( message ); - } - - @Override - public void reset() - { - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } -} diff --git a/src/module/decode/am/AMDecoder.java b/src/module/decode/am/AMDecoder.java deleted file mode 100644 index 29c5b52d..00000000 --- a/src/module/decode/am/AMDecoder.java +++ /dev/null @@ -1,53 +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 module.decode.am; - -import module.decode.DecoderType; -import module.decode.PrimaryDecoder; -import module.decode.config.DecodeConfiguration; - -public class AMDecoder extends PrimaryDecoder -{ - public AMDecoder(DecodeConfiguration config) - { - super(config); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.AM; - } - - @Override - public void reset() - { - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } -} diff --git a/src/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java deleted file mode 100644 index c27d4a97..00000000 --- a/src/module/decode/fleetsync2/Fleetsync2Decoder.java +++ /dev/null @@ -1,177 +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 module.decode.fleetsync2; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatBufferTap; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import module.decode.Decoder; -import module.decode.DecoderType; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.filter.Filters; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.fsk.FSK2Decoder; -import dsp.fsk.FSK2Decoder.Output; - -/** - * Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate - * floating point samples and output fully framed Fleetsync II messages - */ -public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferListener, - Instrumentable -{ - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ - private static final int sDECIMATED_SAMPLE_RATE = 24000; - - /* Baud or Symbol Rate */ - private static final int sSYMBOL_RATE = 1200; - - /* Message length - 5 x REVS + 16 x SYNC + 8 x 64Bit Blocks */ - private static final int sMESSAGE_LENGTH = 537; - - /* Instrumentation Taps */ - private ArrayList mAvailableTaps; - private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = - "Tap Point: Bandpass Filter > < FSK2 Decoder"; - private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = - "Tap Point: FSK2 Decoder > < Message Framer"; - - private FSK2Decoder mFSKDecoder; - private HalfBandFilter_RB_RB mDecimationFilter; - private RealFIRFilter_RB_RB mBandPassFilter; - private MessageFramer mMessageFramer; - private Fleetsync2MessageProcessor mMessageProcessor; - - public Fleetsync2Decoder( AliasList aliasList ) - { - mDecimationFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - - mBandPassFilter = new RealFIRFilter_RB_RB( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); - - mDecimationFilter.setListener( mBandPassFilter ); - - mFSKDecoder = new FSK2Decoder( sDECIMATED_SAMPLE_RATE, - sSYMBOL_RATE, Output.INVERTED ); - mBandPassFilter.setListener( mFSKDecoder ); - - mMessageFramer = new MessageFramer( - SyncPattern.FLEETSYNC2.getPattern(), sMESSAGE_LENGTH ); - mFSKDecoder.setListener( mMessageFramer ); - - mMessageProcessor = new Fleetsync2MessageProcessor( aliasList ); - mMessageFramer.addMessageListener( mMessageProcessor ); - - mMessageProcessor.setMessageListener( this ); - } - - public void dispose() - { - super.dispose(); - - mBandPassFilter.dispose(); - mDecimationFilter.dispose(); - mFSKDecoder.dispose(); - mMessageFramer.dispose(); - mMessageProcessor.dispose(); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.FLEETSYNC2; - } - - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - TapGroup group = new TapGroup( "Fleetsync 2 Decoder" ); - - group.add( new FloatBufferTap( - INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); - group.add( new BinaryTap( - INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER, 0, 0.025f ) ); - - mAvailableTaps.add( group ); - - mAvailableTaps.addAll( mFSKDecoder.getTapGroups() ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - mFSKDecoder.registerTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatBufferTap bpTap = (FloatBufferTap)tap; - mBandPassFilter.setListener( bpTap ); - bpTap.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER: - BinaryTap decoderTap = (BinaryTap)tap; - mFSKDecoder.setListener( decoderTap ); - decoderTap.setListener( mMessageFramer ); - break; - } - } - - @Override - public void unregisterTap( Tap tap ) - { - mFSKDecoder.unregisterTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - mBandPassFilter.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER: - mFSKDecoder.setListener( mMessageFramer ); - break; - } - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mDecimationFilter; - } -} diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java deleted file mode 100644 index d72346e1..00000000 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ /dev/null @@ -1,301 +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 module.decode.fleetsync2; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import util.StringUtils; - -import java.util.Iterator; -import java.util.TreeSet; -import java.util.concurrent.ScheduledExecutorService; - -public class Fleetsync2DecoderState extends DecoderState -{ - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); - - private AliasedStringAttributeMonitor mFromAttribute; - private AliasedStringAttributeMonitor mToAttribute; - private String mMessage; - private String mMessageType; - private long mFrequency; - - public Fleetsync2DecoderState(AliasList aliasList) - { - super(aliasList); - - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.FLEETSYNC); - mToAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.FLEETSYNC); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.FLEETSYNC2; - } - - @Override - public void dispose() - { - super.dispose(); - } - - /** - * Resets the overall decoder state and clears any accumulated event details - */ - @Override - public void reset() - { - mIdents.clear(); - mEmergencyIdents.clear(); - - resetState(); - } - - /** - * Resets this decoder state - */ - public void resetState() - { - mFromAttribute.reset(); - mToAttribute.reset(); - mMessage = null; - mMessageType = null; - } - - @Override - public void receive(Message message) - { - if(message instanceof FleetsyncMessage) - { - FleetsyncMessage fleetsync = (FleetsyncMessage) message; - - if(fleetsync.isValid()) - { - State state = State.CALL; - - mFromAttribute.process(fleetsync.getFromID()); - mIdents.add(fleetsync.getFromID()); - - FleetsyncMessageType type = fleetsync.getMessageType(); - - if(type != FleetsyncMessageType.ANI) - { - mToAttribute.process(fleetsync.getToID()); - mIdents.add(fleetsync.getToID()); - } - - setMessageType(type.getLabel()); - - switch(type) - { - case GPS: - setMessage(fleetsync.getGPSLocation()); - state = State.DATA; - break; - case STATUS: - StringBuilder sb = new StringBuilder(); - sb.append(fleetsync.getStatus()); - - Alias status = fleetsync.getStatusAlias(); - - if(status != null) - { - sb.append("/"); - sb.append(status.getName()); - } - - setMessage(sb.toString()); - state = State.DATA; - break; - case EMERGENCY: - case LONE_WORKER_EMERGENCY: - mEmergencyIdents.add(fleetsync.getFromID()); - state = State.DATA; - break; - default: - break; - } - - FleetsyncCallEvent fsCallEvent = - FleetsyncCallEvent.getFleetsync2Event(fleetsync, mFrequency); - - fsCallEvent.setAliasList(getAliasList()); - - broadcast(fsCallEvent); - - /* Broadcast decode event so that the channel state will - * kick in and reset everything after a short delay */ - broadcast(new DecoderStateEvent(this, Event.DECODE, state)); - } - } - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, mMessage)); - } - } - - public String getMessageType() - { - return "Fleetsync " + mMessageType; - } - - public void setMessageType(String type) - { - if(!StringUtils.isEqual(mMessageType, type)) - { - mMessageType = type; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, getMessageType())); - } - } - - /** - * Responds to reset events issued by the channel state - */ - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - break; - default: - break; - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("=============================\n"); - sb.append("Decoder:\tFleetsync II\n\n"); - sb.append("Fleetsync Idents\n"); - - if(mIdents.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mIdents.iterator(); - - while(it.hasNext()) - { - String ident = it.next(); - - sb.append(" "); - sb.append(formatIdent(ident)); - - if(hasAliasList()) - { - Alias alias = getAliasList().getFleetsyncAlias(ident); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nFleetsync Emergency Activations\n"); - - if(mEmergencyIdents.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while(it.hasNext()) - { - String ident = it.next(); - - sb.append(" "); - sb.append(formatIdent(ident)); - - if(hasAliasList()) - { - Alias alias = getAliasList().getFleetsyncAlias(ident); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - - sb.append("\n"); - } - } - } - - return sb.toString(); - } - - public static String formatIdent(String ident) - { - StringBuilder sb = new StringBuilder(); - - if(ident.length() == 7) - { - sb.append(ident.substring(0, 3)); - sb.append("-"); - sb.append(ident.substring(3, 7)); - - return sb.toString(); - } - else - { - return ident; - } - } -} diff --git a/src/module/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java deleted file mode 100644 index 2e936984..00000000 --- a/src/module/decode/lj1200/LJ1200Decoder.java +++ /dev/null @@ -1,195 +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 module.decode.lj1200; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatBufferTap; -import instrument.tap.stream.FloatTap; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import module.decode.Decoder; -import module.decode.DecoderType; -import sample.Broadcaster; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; -import sample.real.RealSampleListener; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.filter.Filters; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.fsk.FSK2Decoder; -import dsp.fsk.FSK2Decoder.Output; - -/** - * LJ1200 - 1200 baud 2FSK decoder - */ -public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListener, - Instrumentable -{ - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ - private static final int DECIMATED_SAMPLE_RATE = 24000; - - /* Baud or Symbol Rate */ - private static final int SYMBOL_RATE = 1200; - - /* Message length - 16-bit sync plus 64 bit message */ - private static final int MESSAGE_LENGTH = 80; - - /* Instrumentation Taps */ - private ArrayList mAvailableTaps; - private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = - "Tap Point: Bandpass Filter > < FSK2 Decoder"; - private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = - "Tap Point: FSK2 Decoder > < Message Framer"; - - private FSK2Decoder mFSKDecoder; - private Broadcaster mFSKDecoderBroadcaster = - new Broadcaster(); - - private HalfBandFilter_RB_RB mDecimationFilter; - private RealFIRFilter_RB_RB mBandPassFilter; - private MessageFramer mTowerMessageFramer; - private MessageFramer mTransponderMessageFramer; - private LJ1200MessageProcessor mMessageProcessor; - - public LJ1200Decoder( AliasList aliasList ) - { - mDecimationFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - - mBandPassFilter = new RealFIRFilter_RB_RB( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); - mDecimationFilter.setListener( mBandPassFilter ); - - mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE, - SYMBOL_RATE, Output.INVERTED ); - mBandPassFilter.setListener( mFSKDecoder ); - - mFSKDecoder.setListener( mFSKDecoderBroadcaster ); - - mTowerMessageFramer = new MessageFramer( - SyncPattern.LJ1200.getPattern(), MESSAGE_LENGTH ); - mTransponderMessageFramer = new MessageFramer( - SyncPattern.LJ1200_TRANSPONDER.getPattern(), MESSAGE_LENGTH ); - mFSKDecoderBroadcaster.addListener( mTowerMessageFramer ); - mFSKDecoderBroadcaster.addListener( mTransponderMessageFramer ); - - mMessageProcessor = new LJ1200MessageProcessor( aliasList ); - mTowerMessageFramer.addMessageListener( mMessageProcessor ); - mTransponderMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.setMessageListener( this ); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.LJ_1200; - } - - public void dispose() - { - super.dispose(); - - mDecimationFilter.dispose(); - mBandPassFilter.dispose(); - mFSKDecoder.dispose(); - mTowerMessageFramer.dispose(); - mMessageProcessor.dispose(); - } - - /** - * Returns a float listener interface for connecting this decoder to a - * float stream provider - */ - public RealSampleListener getRealReceiver() - { - return (RealSampleListener)mDecimationFilter; - } - - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - TapGroup group = new TapGroup( "LJ-1200 Decoder" ); - - group.add( new FloatTap( - INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); - group.add( new BinaryTap( - INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER, 0, 0.025f ) ); - - mAvailableTaps.add( group ); - mAvailableTaps.addAll( mFSKDecoder.getTapGroups() ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - mFSKDecoder.registerTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatBufferTap bpTap = (FloatBufferTap)tap; - mBandPassFilter.setListener( bpTap ); - bpTap.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER: - BinaryTap decoderTap = (BinaryTap)tap; - mFSKDecoder.setListener( decoderTap ); - decoderTap.setListener( mTowerMessageFramer ); - break; - } - } - - @Override - public void unregisterTap( Tap tap ) - { - mFSKDecoder.unregisterTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - mBandPassFilter.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER: - mFSKDecoder.setListener( mTowerMessageFramer ); - break; - } - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mDecimationFilter; - } -} diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java deleted file mode 100644 index a5cac1b8..00000000 --- a/src/module/decode/lj1200/LJ1200DecoderState.java +++ /dev/null @@ -1,138 +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 module.decode.lj1200; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import util.StringUtils; - -import java.util.ArrayList; -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 -{ - private final static Logger mLog = LoggerFactory.getLogger(LJ1200DecoderState.class); - - private Set mAddresses = new TreeSet(); - - private AliasedStringAttributeMonitor mAddressAttribute; - - public LJ1200DecoderState(AliasList aliasList) - { - super(aliasList); - - mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.LJ_1200; - } - - @Override - public void receive(Message message) - { - if(message instanceof LJ1200Message) - { - LJ1200Message lj = (LJ1200Message) message; - - if(lj.isValid()) - { - String address = lj.getAddress(); - - mAddressAttribute.process(address); - mAddresses.add(address); - - broadcast(LJ1200CallEvent.getLJ1200Event(lj)); - - broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); - } - } - else if(message instanceof LJ1200TransponderMessage) - { - - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("=============================\n"); - sb.append("Decoder:\tLJ-1200I\n\n"); - - if(!mAddresses.isEmpty()) - { - sb.append("Transponder Addresses:\n"); - - List addresses = new ArrayList(mAddresses); - - Collections.sort(addresses); - - for(String address : addresses) - { - sb.append("\t"); - sb.append(address); - sb.append("\n"); - } - } - - return sb.toString(); - } - - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - if(event.getEvent() == Event.RESET) - { - resetState(); - } - } - - private void resetState() - { - mAddressAttribute.reset(); - } - - @Override - public void reset() - { - mAddresses.clear(); - resetState(); - } -} diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java deleted file mode 100644 index bf1fbcd4..00000000 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ /dev/null @@ -1,123 +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 module.decode.ltrnet; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import message.MessageDirection; -import module.decode.Decoder; -import module.decode.DecoderType; -import sample.Listener; -import sample.real.IUnFilteredRealBufferListener; -import sample.real.RealBuffer; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.fsk.LTRFSKDecoder; - -public class LTRNetDecoder extends Decoder - implements IUnFilteredRealBufferListener, Instrumentable -{ - public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; - private LTRFSKDecoder mLTRFSKDecoder; - private MessageFramer mLTRMessageFramer; - private LTRNetMessageProcessor mLTRMessageProcessor; - - private List mAvailableTaps; - - /** - * LTR-Net Decoder. Decodes unfiltered (e.g. demodulated but with no DC or - * audio filtering) samples and produces LTR-Net messages. - */ - public LTRNetDecoder( DecodeConfigLTRNet config, AliasList aliasList ) - { - mLTRFSKDecoder = new LTRFSKDecoder(); - - - if( config.getMessageDirection() == MessageDirection.OSW ) - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), - LTR_STANDARD_MESSAGE_LENGTH ); - } - else - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), - LTR_STANDARD_MESSAGE_LENGTH ); - } - - mLTRFSKDecoder.addListener( mLTRMessageFramer ); - - mLTRMessageProcessor = new LTRNetMessageProcessor( - config.getMessageDirection(), aliasList ); - - mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - - mLTRMessageProcessor.setMessageListener( this ); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.LTR_NET; - } - - @Override - public Listener getUnFilteredRealBufferListener() - { - return mLTRFSKDecoder; - } - - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - mAvailableTaps.addAll( mLTRFSKDecoder.getTapGroups() ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - mLTRFSKDecoder.registerTap( tap ); - } - - @Override - public void unregisterTap( Tap tap ) - { - mLTRFSKDecoder.unregisterTap( tap ); - } - - @Override - public void reset() - { - mLTRMessageFramer.reset(); - } -} diff --git a/src/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java deleted file mode 100644 index 544302bc..00000000 --- a/src/module/decode/ltrnet/LTRNetDecoderState.java +++ /dev/null @@ -1,783 +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 module.decode.ltrnet; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedIntegerAttributeMonitor; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import module.decode.event.CallEvent; -import module.decode.event.CallEvent.CallEventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import util.StringUtils; - -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 -{ - private final static Logger mLog = LoggerFactory.getLogger(LTRNetDecoderState.class); - - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mTalkgroupsFirstHeard = new TreeSet(); - private TreeSet mESNs = new TreeSet(); - private TreeSet mUniqueIDs = new TreeSet(); - private TreeSet mNeighborIDs = new TreeSet(); - private TreeSet mSiteIDs = new TreeSet(); - private HashMap mReceiveFrequencies = - new HashMap(); - private HashMap mTransmitFrequencies = - new HashMap(); - private HashMap mActiveCalls = new HashMap(); - - private AliasedStringAttributeMonitor mToAttribute; - private AliasedIntegerAttributeMonitor mFromUIDAttribute; - private AliasedStringAttributeMonitor mESNAttribute; - private String mMessage; - private String mMessageType; - private int mChannelNumber; - private long mFrequency = 0; - - public LTRNetDecoderState(AliasList aliasList) - { - super(aliasList); - - mToAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mToAttribute.addIllegalValue("0-00-000"); - - mFromUIDAttribute = new AliasedIntegerAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.LTR_NET_UID); - - mESNAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.ESN); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.LTR_NET; - } - - @Override - public void receive(Message message) - { - if(message.isValid()) - { - State state = State.IDLE; - - if(message instanceof LTRNetOSWMessage) - { - LTRNetOSWMessage ltr = (LTRNetOSWMessage) message; - - switch(ltr.getMessageType()) - { - case CA_ENDD: - if(mChannelNumber == 0) - { - setChannelNumber(ltr.getChannel()); - } - - /* Process FCC Station ID Events */ - if(ltr.getGroup() == 254) - { - if(mCurrentCallEvent == null || - mCurrentCallEvent.getCallEventType() != - CallEventType.STATION_ID) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEventType.STATION_ID) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(mFrequency) - .to(ltr.getTalkgroupID()) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); - } - else - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); - } - } - else - { - processCallEndMessage(ltr); - } - break; - case CA_STRT: - if(mChannelNumber == 0) - { - setChannelNumber(ltr.getChannel()); - } - - /* If the call event channel matches our current channel - * then it's a call, otherwise it's a call detect. */ - if(ltr.getChannel() == mChannelNumber) - { - processCallMessage(ltr); - } - else - { - processCallDetectMessage(ltr); - } - - break; - case SY_IDLE: - if(mChannelNumber != ltr.getChannel()) - { - setChannelNumber(ltr.getChannel()); - } - break; - case MA_CHNH: - break; - case MA_CHNL: - break; - case FQ_RXHI: - case FQ_RXLO: - if(ltr.getFrequency() > 0) - { - mReceiveFrequencies.put(ltr.getHomeRepeater(), ltr.getFrequency()); - } - break; - case FQ_TXHI: - case FQ_TXLO: - if(ltr.getFrequency() > 0) - { - mTransmitFrequencies.put(ltr.getHomeRepeater(), ltr.getFrequency()); - } - break; - case ID_NBOR: - String neighborID = ltr.getNeighborID(); - - if(neighborID != null) - { - mNeighborIDs.add(neighborID); - } - break; - case ID_UNIQ: - state = State.DATA; - - int uniqueID = ltr.getRadioUniqueID(); - - if(uniqueID != LTRNetOSWMessage.INT_NULL_VALUE) - { - mUniqueIDs.add(uniqueID); - } - - if(getCurrentLTRCallEvent() == null) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(mFrequency) - .from(String.valueOf(uniqueID)) - .build(); - } - else - { - mCurrentCallEvent.setFromID( - String.valueOf(ltr.getRadioUniqueID())); - mCurrentCallEvent.setDetails("Unique ID"); - - broadcast(mCurrentCallEvent); - } - - mFromUIDAttribute.process(uniqueID); - break; - case ID_SITE: - String siteID = ltr.getSiteID(); - - if(siteID != null) - { - mSiteIDs.add(siteID); - } - break; - default: - break; - } - } - else if(message instanceof LTRNetISWMessage) - { - LTRNetISWMessage ltr = ((LTRNetISWMessage) message); - - switch(ltr.getMessageType()) - { - case CA_STRT: - processCallMessage(ltr); - break; - case CA_ENDD: - processCallEndMessage(ltr); - break; - case ID_ESNH: - case ID_ESNL: - state = State.DATA; - - String esn = ltr.getESN(); - - if(!esn.contains("xxxx")) - { - mESNs.add(ltr.getESN()); - } - - setMessageType("ESN"); - mESNAttribute.process(ltr.getESN()); - - broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEventType.REGISTER_ESN) - .aliasList(getAliasList()) - .details("ESN:" + ltr.getESN()) - .frequency(mFrequency) - .from(ltr.getESN()) - .build(); - - broadcast(mCurrentCallEvent); - } - - break; - case ID_UNIQ: - state = State.DATA; - - int uniqueid = ltr.getRadioUniqueID(); - - if(uniqueid != LTRNetISWMessage.INT_NULL_VALUE) - { - mUniqueIDs.add(uniqueid); - - setMessageType("REGISTER UID"); - mFromUIDAttribute.process(uniqueid); - - if(getCurrentLTRCallEvent() == null) - { - mCurrentCallEvent = new LTRCallEvent.Builder( - DecoderType.LTR_NET, CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(mFrequency) - .from(String.valueOf(uniqueid)) - .build(); - } - else - { - mCurrentCallEvent.setFromID( - String.valueOf(ltr.getRadioUniqueID())); - mCurrentCallEvent.setDetails("Unique ID"); - - broadcast(mCurrentCallEvent); - } - } - break; - default: - break; - } - } - } - } - - public LTRCallEvent getCurrentLTRCallEvent() - { - if(mCurrentCallEvent != null) - { - return (LTRCallEvent) mCurrentCallEvent; - } - - return null; - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("Activity Summary\n"); - sb.append("Decoder:\tLTR-Net\n\n"); - - if(mSiteIDs.isEmpty()) - { - sb.append("Site:\tUnknown\n"); - } - else - { - Iterator it = mSiteIDs.iterator(); - - while(it.hasNext()) - { - sb.append("Site:\t"); - - String siteID = it.next(); - - sb.append(siteID); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(String.valueOf(siteID)); - - if(siteAlias != null) - { - sb.append(" "); - sb.append(siteAlias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nLCNs (transmit | receive)\n"); - - if(mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty()) - { - sb.append(" None\n"); - } - else - { - for(int x = 1; x < 21; x++) - { - long rcv = 0; - - if(mReceiveFrequencies.containsKey(x)) - { - rcv = mReceiveFrequencies.get(x); - } - - long xmt = 0; - - if(mTransmitFrequencies.containsKey(x)) - { - xmt = mTransmitFrequencies.get(x); - } - - if(rcv > 0 || xmt > 0) - { - if(x < 10) - { - sb.append(" "); - } - - sb.append(x); - sb.append(": "); - - if(xmt == 0) - { - sb.append("---.-----"); - } - else - { - sb.append(mDecimalFormatter.format((double) xmt / 1E6d)); - } - sb.append(" | "); - - if(rcv == 0) - { - sb.append("---.-----"); - } - else - { - sb.append(mDecimalFormatter.format((double) rcv / 1E6d)); - } - - if(x == mChannelNumber) - { - sb.append(" **"); - } - - sb.append("\n"); - } - } - } - - sb.append("\nTalkgroups\n"); - - if(mTalkgroups.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while(it.hasNext()) - { - String tgid = it.next(); - - sb.append(" "); - sb.append(tgid); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(tgid); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nRadio Unique IDs\n"); - - if(mUniqueIDs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mUniqueIDs.iterator(); - - while(it.hasNext()) - { - int uid = it.next(); - - sb.append(" "); - sb.append(uid); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getUniqueID(uid); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nESNs\n"); - - if(mESNs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mESNs.iterator(); - - while(it.hasNext()) - { - String esn = it.next(); - - sb.append(" "); - sb.append(esn); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getESNAlias(esn); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nNeighbor Sites\n"); - - if(mNeighborIDs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mNeighborIDs.iterator(); - - while(it.hasNext()) - { - String neighbor = it.next(); - - sb.append(" "); - sb.append(neighbor); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getSiteID(String.valueOf(neighbor)); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - return sb.toString(); - } - - /** - * Call Detect - current channel messages indicate a call on another channel - */ - private void processCallDetectMessage(LTRNetMessage message) - { - if(!mActiveCalls.containsKey(message.getChannel()) || - !mActiveCalls.get(message.getChannel()).contentEquals(message.getTalkgroupID())) - { - mActiveCalls.put(message.getChannel(), message.getTalkgroupID()); - - int channel = message.getChannel(); - - long frequency = 0; - - if(mTransmitFrequencies.containsKey(channel)) - { - frequency = mTransmitFrequencies.get(channel); - } - - broadcast(new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(String.valueOf(message.getChannel())) - .frequency(frequency) - .to(message.getTalkgroupID()) - .build()); - } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); - } - - /** - * Indicates if the talkgroup is different than the talkgroup specified in - * the current call event - */ - private boolean isDifferentTalkgroup(String talkgroup) - { - return talkgroup != null && - mCurrentCallEvent != null && - mCurrentCallEvent.getToID() != null && - !mCurrentCallEvent.getToID().contentEquals(talkgroup); - } - - private boolean isValidTalkgroup(String talkgroup) - { - return talkgroup != null && !talkgroup.matches("0-00-000"); - } - - private void processCallMessage(LTRNetMessage message) - { - int group = message.getGroup(); - - /* Process call registration */ - if(group == 253) - { - setMessageType("REGISTER"); - broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); - } - /* Process call */ - else - { - String talkgroup = message.getTalkgroupID(); - - if(isValidTalkgroup(talkgroup)) - { - mToAttribute.process(talkgroup); - - final LTRCallEvent current = getCurrentLTRCallEvent(); - - //If this is a new call or the talkgroup is different from the current call, create a new call event - if(current == null || isDifferentTalkgroup(talkgroup)) - { - /* Invalidate the current call */ - if(current != null) - { - current.setValid(false); - broadcast(current); - } - - /* A talkgroup must be seen at least once before it will be added - * to the mTalkgroups list that is used in the activity summary, - * so that we don't pollute the summary with one-off error talkgroups */ - if(mTalkgroupsFirstHeard.contains(talkgroup)) - { - mTalkgroups.add(talkgroup); - } - else - { - mTalkgroupsFirstHeard.add(talkgroup); - } - - CallEvent callEvent = new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(message.getChannel())) - .frequency(mFrequency) - .to(message.getTalkgroupID()) - .build(); - - broadcast(callEvent); - - mCurrentCallEvent = callEvent; - - broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); - } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); - } - } - } - - private void processCallEndMessage(LTRNetMessage message) - { - if(mCurrentCallEvent != null && - mCurrentCallEvent.getCallEventType() == CallEventType.CALL) - { - mCurrentCallEvent.setEnd(System.currentTimeMillis()); - broadcast(mCurrentCallEvent); - } - - mCurrentCallEvent = null; - - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - } - - /** - * Performs a full reset - */ - public void reset() - { - mChannelNumber = 0; - - mActiveCalls.clear(); - mESNs.clear(); - mNeighborIDs.clear(); - mReceiveFrequencies.clear(); - mSiteIDs.clear(); - mTalkgroups.clear(); - mTransmitFrequencies.clear(); - mUniqueIDs.clear(); - - resetState(); - } - - /** - * Resets the decoder state after a call or other decode event - */ - private void resetState() - { - if(mCurrentCallEvent != null && mCurrentCallEvent - .getCallEventType() == CallEventType.CALL) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - } - - mCurrentCallEvent = null; - - mToAttribute.reset(); - mESNAttribute.reset(); - mFromUIDAttribute.reset(); - mMessage = null; - mMessageType = null; - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, mMessageType)); - } - } - - public String getMessageType() - { - return mMessageType; - } - - private void setMessageType(String messageType) - { - if(!StringUtils.isEqual(mMessageType, messageType)) - { - mMessageType = messageType; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, mMessageType)); - } - } - - public boolean hasChannelNumber() - { - return mChannelNumber != 0; - } - - public int getChannelNumber() - { - return mChannelNumber; - } - - public void setChannelNumber(int channelNumber) - { - if(mChannelNumber != channelNumber) - { - mChannelNumber = channelNumber; - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, - "LCN:" + getChannelNumber())); - } - } - - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - break; - default: - break; - } - } -} diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java deleted file mode 100644 index 40b355c5..00000000 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ /dev/null @@ -1,87 +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 module.decode.ltrstandard; - -import java.util.concurrent.ScheduledExecutorService; - -import message.MessageDirection; -import module.decode.Decoder; -import module.decode.DecoderType; -import sample.Listener; -import sample.real.IUnFilteredRealBufferListener; -import sample.real.RealBuffer; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.fsk.LTRFSKDecoder; - -public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBufferListener -{ - public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; - - private LTRFSKDecoder mLTRFSKDecoder; - private MessageFramer mLTRMessageFramer; - private LTRStandardMessageProcessor mLTRMessageProcessor; - - /** - * LTR Decoder. Decodes unfiltered (e.g. demodulated but with no DC or - * audio filtering) samples and produces LTR Standard messages. - */ - public LTRStandardDecoder( AliasList aliasList, - MessageDirection direction ) - { - mLTRFSKDecoder = new LTRFSKDecoder(); - - if( direction == MessageDirection.OSW ) - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), - LTR_STANDARD_MESSAGE_LENGTH ); - } - else - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), - LTR_STANDARD_MESSAGE_LENGTH ); - } - - mLTRFSKDecoder.addListener( mLTRMessageFramer ); - - mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); - mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.setMessageListener( this ); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.LTR_STANDARD; - } - - @Override - public Listener getUnFilteredRealBufferListener() - { - return mLTRFSKDecoder; - } - - @Override - public void reset() - { - mLTRMessageFramer.reset(); - } -} diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java deleted file mode 100644 index 1693b2b6..00000000 --- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java +++ /dev/null @@ -1,466 +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 module.decode.ltrstandard; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import module.decode.event.CallEvent.CallEventType; -import module.decode.ltrnet.LTRCallEvent; -import module.decode.ltrstandard.message.CallEndMessage; -import module.decode.ltrstandard.message.CallMessage; -import module.decode.ltrstandard.message.IdleMessage; -import module.decode.ltrstandard.message.LTRStandardMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import util.StringUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -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 -{ - private final static Logger mLog = - LoggerFactory.getLogger(LTRStandardDecoderState.class); - - private Map mActiveCalls = new HashMap<>(); - private Set mTalkgroupsFirstHeard = new HashSet<>(); - private Set mTalkgroups = new TreeSet<>(); - - private AliasedStringAttributeMonitor mTalkgroupAttribute; - private long mFrequency; - private LCNTracker mLCNTracker = new LCNTracker(); - - public LTRStandardDecoderState(AliasList aliasList) - { - super(aliasList); - - mTalkgroupAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.LTR_STANDARD; - } - - @Override - public void receive(Message message) - { - if(message.isValid() && message instanceof LTRStandardMessage) - { - switch(((LTRStandardMessage) message).getMessageType()) - { - case CA_STRT: - CallMessage start = (CallMessage) message; - - int channel = start.getChannel(); - - setChannelNumber(channel); - - mLCNTracker.processFreeChannel(start.getFree()); - - //Only process calls on this LCN, or call detects for - //talkgroups that are homed on this LCN - if(mLCNTracker.isValidChannel(channel) && - (mLCNTracker.isCurrentChannel(channel) || - mLCNTracker.isCurrentChannel(start.getHomeRepeater()))) - { - LTRCallEvent event = mActiveCalls.get(channel); - - if(event == null || !event.isMatchingTalkgroup(start.getToID())) - { - //Check for different talkgroup - if(event != null) - { - event.end(); - mActiveCalls.remove(channel); - } - - boolean current = mLCNTracker.isCurrentChannel(channel); - - event = new LTRCallEvent.Builder( - DecoderType.LTR_STANDARD, - current ? CallEventType.CALL : CallEventType.CALL_DETECT) - .to(start.getToID()) - .aliasList(getAliasList()) - .channel(start.getChannelFormatted()) - .frequency(current ? mFrequency : 0) - .build(); - - mActiveCalls.put(channel, event); - - broadcast(event); - - if(mLCNTracker.isCurrentChannel(channel)) - { - String talkgroup = start.getToID(); - mTalkgroupAttribute.process(talkgroup); - processTalkgroup(talkgroup); - } - } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); - } - break; - case CA_ENDD: - CallEndMessage end = (CallEndMessage) message; - - //Home channel is 31 for call end -- use the free channel - //as the call end channel - int repeater = end.getFree(); - - setChannelNumber(repeater); - - if(mLCNTracker.isCurrentChannel(repeater)) - { - String talkgroup = end.getToID(); - mTalkgroupAttribute.process(talkgroup); - processTalkgroup(talkgroup); - - LTRCallEvent event = mActiveCalls.remove(repeater); - - if(event != null) - { - event.end(); - broadcast(event); - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - } - } - break; - case SY_IDLE: - IdleMessage idle = (IdleMessage) message; - - int lcn = idle.getChannel(); - - mLCNTracker.processCallChannel(lcn); - - break; - case UN_KNWN: - default: - break; - } - } - } - - private void processTalkgroup(String talkgroup) - { - if(mTalkgroupsFirstHeard.contains(talkgroup)) - { - mTalkgroups.add(talkgroup); - } - else - { - mTalkgroupsFirstHeard.add(talkgroup); - } - } - - /** - * Performs a full reset - */ - public void reset() - { - mActiveCalls.clear(); - mTalkgroupsFirstHeard.clear(); - mTalkgroups.clear(); - mLCNTracker.reset(); - - resetState(); - } - - /** - * Performs a temporal reset following a call or other decode event - */ - private void resetState() - { - for(Integer key : mActiveCalls.keySet()) - { - LTRCallEvent event = mActiveCalls.get(key); - - if(event != null) - { - event.end(); - - broadcast(event); - } - } - - mActiveCalls.clear(); - - mTalkgroupAttribute.reset(); - } - - public boolean hasChannelNumber() - { - return mLCNTracker.getCurrentChannel() != 0; - } - - public int getChannelNumber() - { - return mLCNTracker.getCurrentChannel(); - } - - private void setChannelNumber(int channel) - { - int original = mLCNTracker.getCurrentChannel(); - - mLCNTracker.processCallChannel(channel); - - if(mLCNTracker.getCurrentChannel() != original) - { - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, - "LCN:" + mLCNTracker.getCurrentChannel())); - } - } - - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - break; - default: - break; - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("Activity Summary\n\n"); - - sb.append("Decoder:\tLTR-Standard\n"); - - sb.append("Monitored LCN: "); - - if(hasChannelNumber()) - { - sb.append(getChannelNumber()); - } - else - { - sb.append("*Insufficient Data*"); - } - - sb.append("\n"); - - sb.append("Active LCNs:\t"); - - List lcns = mLCNTracker.getActiveLCNs(); - - if(lcns.size() > 0) - { - sb.append(mLCNTracker.getActiveLCNs()); - } - else - { - sb.append("*Insufficient Data*"); - } - sb.append("\n\n"); - - sb.append("Talkgroups\n"); - - if(mTalkgroups.isEmpty()) - { - sb.append(" None\n"); - } - else - { - for(String talkgroup : mTalkgroups) - { - sb.append(" "); - sb.append(talkgroup); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(talkgroup); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - - } - } - - return sb.toString(); - } - - /** - * Tracks the set of call and free channels for a system in order to - * dynamically determine the current LCN and minimize false triggers from - * bogus decoded LTR messages. Tracks the number of occurances of each - * LCN and uses a dynamic threshold to determine LCN validity. - */ - public class LCNTracker - { - private static final int DEFAULT_COUNT = 10; - private int[] mCallLCNCounts; - private int[] mFreeLCNCounts; - private int mCallHighestCount; - private int mFreeHighestCount; - private int mCurrentLCN; - - public LCNTracker() - { - reset(); - } - - public void logStatistics() - { - for(int x = 1; x <= 20; x++) - { - mLog.debug("Call " + x + ": " + mCallLCNCounts[x]); - } - mLog.debug("Call Highest Count: " + mCallHighestCount); - for(int x = 1; x <= 20; x++) - { - mLog.debug("Free " + x + ": " + mFreeLCNCounts[x]); - } - mLog.debug("Free Highest Count: " + mFreeHighestCount); - - mLog.debug("Current LCN: " + mCurrentLCN); - } - - public void reset() - { - //Dim channel arrays to 21 -- ignore the 0 index - mCallLCNCounts = new int[21]; - mFreeLCNCounts = new int[21]; - mCallHighestCount = 3; - mFreeHighestCount = DEFAULT_COUNT; - mCurrentLCN = 0; - } - - public boolean isCurrentChannel(int channel) - { - if(mCurrentLCN == 0) - { - return true; - } - else - { - return channel == mCurrentLCN; - } - } - - public int getCurrentChannel() - { - return mCurrentLCN; - } - - /** - * Indicates if the channel is valid based on the observed current and - * free channels reported by the system. Tracks the count of each - * reported free channel the highest free channel count is used as a - * threshold where a channel is valid when its count exceeds 20% of the - * highest free channel count. A channel is also valid when it is - * the currently monitored channel. - */ - public boolean isValidChannel(int channel) - { - if(isCurrentChannel(channel)) - { - return true; - } - - int count = mFreeLCNCounts[channel]; - - int threshold = (int) ((double) mFreeHighestCount * 0.2); - - return count >= threshold; - } - - public void processCallChannel(int channel) - { - if(1 <= channel && channel <= 20) - { - mCallLCNCounts[channel]++; - - if(mCallLCNCounts[channel] > mCallHighestCount) - { - mCallHighestCount = mCallLCNCounts[channel]; - mCurrentLCN = channel; - } - } - } - - public void processFreeChannel(int channel) - { - if(1 <= channel && channel <= 20) - { - mFreeLCNCounts[channel]++; - - if(mFreeLCNCounts[channel] > mFreeHighestCount) - { - mFreeHighestCount = mFreeLCNCounts[channel]; - } - } - } - - public List getActiveLCNs() - { - List active = new ArrayList<>(); - - if(mFreeHighestCount > DEFAULT_COUNT) - { - for(int x = 1; x <= 20; x++) - { - if(mCurrentLCN != 0 && isCurrentChannel(x)) - { - active.add(x); - } - else if(isValidChannel(x)) - { - active.add(x); - } - } - } - - return active; - } - } -} \ No newline at end of file diff --git a/src/module/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java deleted file mode 100644 index 2bd89fba..00000000 --- a/src/module/decode/mdc1200/MDCDecoder.java +++ /dev/null @@ -1,205 +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 module.decode.mdc1200; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatBufferTap; -import instrument.tap.stream.FloatTap; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import module.decode.Decoder; -import module.decode.DecoderType; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.NRZDecoder; -import dsp.filter.Filters; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.fsk.FSK2Decoder; -import dsp.fsk.FSK2Decoder.Output; - -/** - * MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate - * complex or floating point samples and output fully framed MDC1200 messages - */ -public class MDCDecoder extends Decoder implements IFilteredRealBufferListener, - Instrumentable -{ - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ - private static final int sDECIMATED_SAMPLE_RATE = 24000; - - /* Baud or Symbol Rate */ - private static final int sSYMBOL_RATE = 1200; - - /* Message length */ - private static final int sMESSAGE_LENGTH = 304; - - /* Instrumentation Taps */ - private ArrayList mAvailableTaps; - private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = - "Tap Point: Bandpass Filter > < FSK2 Decoder"; - private static final String INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER = - "Tap Point: FSK2 Decoder > < NRZI Decoder"; - private static final String INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER = - "Tap Point: NRZ Decoder > < Message Framer"; - - private FSK2Decoder mFSKDecoder; - private HalfBandFilter_RB_RB mDecimationFilter; - private RealFIRFilter_RB_RB mBandPassFilter; - private NRZDecoder mNRZDecoder; - private MessageFramer mMessageFramer; - private MDCMessageProcessor mMessageProcessor; - - public MDCDecoder( AliasList aliasList ) - { - /* Decimation filter: 48000 / 2 = 24000 */ - mDecimationFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - - /* Bandpass filter - predefined */ - mBandPassFilter = new RealFIRFilter_RB_RB( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); - mDecimationFilter.setListener( mBandPassFilter ); - - /* 2FSK Decoder */ - mFSKDecoder = new FSK2Decoder( sDECIMATED_SAMPLE_RATE, - sSYMBOL_RATE, Output.NORMAL ); - mBandPassFilter.setListener( mFSKDecoder ); - - /* NRZ Decoder */ - mNRZDecoder = new NRZDecoder( NRZDecoder.MODE_INVERTED ); - mFSKDecoder.setListener( mNRZDecoder ); - - /* Message Framer */ - mMessageFramer = new MessageFramer( SyncPattern.MDC1200.getPattern(), - sMESSAGE_LENGTH ); - mNRZDecoder.setListener( mMessageFramer ); - - /* Message Processor */ - mMessageProcessor = new MDCMessageProcessor( aliasList ); - mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( this ); - } - - public void dispose() - { - super.dispose(); - - mDecimationFilter.dispose(); - mBandPassFilter.dispose(); - mFSKDecoder.dispose(); - mNRZDecoder.dispose(); - mMessageFramer.dispose(); - mMessageProcessor.dispose(); - } - - /* Instrumentation */ - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - TapGroup group = new TapGroup( "MDC-1200 Decoder" ); - - group.add( - new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); - group.add( - new BinaryTap( INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER, 0, 0.025f ) ); - group.add( - new BinaryTap( INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER, 0, 0.025f ) ); - - mAvailableTaps.add( group ); - mAvailableTaps.addAll( mFSKDecoder.getTapGroups() ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - mFSKDecoder.registerTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - /* wire the tap between the bandpass filter and the demod */ - FloatBufferTap floatTap = (FloatBufferTap)tap; - mBandPassFilter.setListener( floatTap ); - floatTap.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER: - /* wire between FSK2 decoder and NRZ decoder */ - BinaryTap binTap = (BinaryTap)tap; - mFSKDecoder.setListener( binTap ); - binTap.setListener( mNRZDecoder ); - break; - case INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER: - /* wire between demod and framer */ - BinaryTap binInvTap = (BinaryTap)tap; - mNRZDecoder.setListener( binInvTap ); - binInvTap.setListener( mMessageFramer ); - break; - default: - } - } - - @Override - public void unregisterTap( Tap tap ) - { - mFSKDecoder.unregisterTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - mBandPassFilter.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER: - mFSKDecoder.setListener( mNRZDecoder ); - break; - case INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER: - mNRZDecoder.setListener( mMessageFramer ); - break; - default: - } - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.MDC1200; - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mDecimationFilter; - } -} diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java deleted file mode 100644 index a70c8f0f..00000000 --- a/src/module/decode/mdc1200/MDCDecoderState.java +++ /dev/null @@ -1,253 +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 module.decode.mdc1200; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import util.StringUtils; - -import java.util.Iterator; -import java.util.TreeSet; -import java.util.concurrent.ScheduledExecutorService; - -public class MDCDecoderState extends DecoderState -{ - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); - - private AliasedStringAttributeMonitor mFromAttribute; - private String mMessage; - private String mMessageType; - - public MDCDecoderState(AliasList aliasList) - { - super(aliasList); - - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MDC1200); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.MDC1200; - } - - @Override - public void reset() - { - mIdents.clear(); - mEmergencyIdents.clear(); - - resetState(); - } - - private void resetState() - { - mFromAttribute.reset(); - mMessage = null; - mMessageType = null; - } - - @Override - public void receive(Message message) - { - if(message instanceof MDCMessage) - { - MDCMessage mdc = (MDCMessage) message; - - mIdents.add(mdc.getUnitID()); - - if(mdc.isEmergency()) - { - mEmergencyIdents.add(mdc.getUnitID()); - } - - mFromAttribute.process(mdc.getFromID()); - - MDCMessageType type = mdc.getMessageType(); - - setMessageType(type.getLabel()); - - StringBuilder sb = new StringBuilder(); - - sb.append("OPCODE "); - sb.append(String.valueOf(mdc.getOpcode())); - - if(mdc.isBOT()) - { - sb.append(" TYPE:BOT"); - } - - if(mdc.isEOT()) - { - sb.append(" TYPE:EOT"); - } - - setMessage(sb.toString()); - - MDCCallEvent event = MDCCallEvent.getMDCCallEvent(mdc); - event.setAliasList(getAliasList()); - broadcast(event); - - switch(type) - { - case ANI: - broadcast(new DecoderStateEvent(this, Event.DECODE, State.CALL)); - break; - case ACKNOWLEDGE: - case EMERGENCY: - case PAGING: - case STATUS: - default: - broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); - break; - } - - } - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, mMessage)); - } - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType(String type) - { - if(!StringUtils.isEqual(mMessageType, type)) - { - mMessageType = type; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, "MDC:" + mMessageType)); - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("=============================\n"); - sb.append("Decoder:\tMDC-1200\n\n"); - sb.append("MDC-1200 Idents\n"); - - if(mIdents.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mIdents.iterator(); - - while(it.hasNext()) - { - String ident = it.next(); - - sb.append(" "); - sb.append(ident); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMDC1200Alias(ident); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("MDC-1200 Emergency Idents\n"); - - if(mEmergencyIdents.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while(it.hasNext()) - { - String ident = it.next(); - - sb.append(" "); - sb.append(ident); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMDC1200Alias(ident); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - return sb.toString(); - } - - /** - * Responds to reset events issued by the channel state - */ - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - default: - break; - } - } -} diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java deleted file mode 100644 index 228f8dc9..00000000 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ /dev/null @@ -1,300 +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 module.decode.mpt1327; - -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.filter.FilterFactory; -import dsp.filter.Filters; -import dsp.filter.fir.FIRFilterSpecification; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.fsk.FSK2Decoder; -import dsp.fsk.FSK2Decoder.Output; -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatBufferTap; -import instrument.tap.stream.FloatTap; -import module.decode.Decoder; -import module.decode.DecoderType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Broadcaster; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; - -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 - * complex or floating point samples and output fully framed MPT1327 control and - * traffic messages. - */ -public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable -{ - private final static Logger mLog = LoggerFactory.getLogger(MPT1327Decoder.class); - - private static final FIRFilterSpecification HIGH_PASS_SPECIFICATION = FIRFilterSpecification.highPassBuilder() - .sampleRate( 24000 ) - .stopBandCutoff( 800 ) - .stopBandAmplitude( 0.0 ) - .stopBandRipple( 0.03 ) - .passBandStart( 1000 ) - .passBandAmplitude( 1.0 ) - .passBandRipple( 0.08 ) - .build(); - - private static float[] HIGHPASS_FILTER; - - static - { - try - { - HIGHPASS_FILTER = FilterFactory.getTaps(HIGH_PASS_SPECIFICATION); - } - catch(Exception e) - { - mLog.error("Couldn't design MPT1327 highpass filter"); - } - } - - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ - private static final int sDECIMATED_SAMPLE_RATE = 24000; - - /* Baud or Symbol Rate */ - private static final int sSYMBOL_RATE = 1200; - - /* Message length -- longest possible message is: - * 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */ - private static final int sMESSAGE_LENGTH = 350; - - /* Instrumentation Taps */ - private static final String INSTRUMENT_HB1_FILTER_TO_HIGH_PASS = - "Tap Point: Half-band Decimation Filter > < Low Pass Filter"; - private static final String INSTRUMENT_HIGH_PASS_TO_DECODER = - "Tap Point: Low Pass Filter > < Decoder"; - private static final String INSTRUMENT_DECODER_TO_FRAMER = - "Tap Point: Decoder > < Sync Detect/Message Framer"; - private List mAvailableTaps; - - private HalfBandFilter_RB_RB mDecimationFilter; - private RealFIRFilter_RB_RB mHighPassFilter; - private FSK2Decoder mFSKDecoder; - private Broadcaster mSymbolBroadcaster; - private MessageFramer mControlMessageFramer; - private MessageFramer mTrafficMessageFramer; - private MPT1327MessageProcessor mMessageProcessor; - - public MPT1327Decoder(AliasList aliasList, Sync sync) - { - /** - * Normal: 2FSK Decoder with inverted output - * French: 2FSK Decoder with normal output - */ - if(sync == Sync.NORMAL) - { - mFSKDecoder = new FSK2Decoder(24000, 1200, Output.INVERTED); - } - else if(sync == Sync.FRENCH) - { - mFSKDecoder = new FSK2Decoder(24000, 1200, Output.NORMAL); - } - else - { - throw new IllegalArgumentException("MPT1327 Decoder - unrecognized Sync type"); - } - - /* Decimation filter - 48000 / 2 = 24000 output */ - mDecimationFilter = new HalfBandFilter_RB_RB(Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true); - - //High-pass filter the audio to remove any DC component or CTCSS tones. The audio has already been low-pass - //filtered by the AudioDemodulation filter cutoff of 3400 Hz. - mHighPassFilter = new RealFIRFilter_RB_RB(HIGHPASS_FILTER, 1.0f); - mDecimationFilter.setListener(mHighPassFilter); - - mHighPassFilter.setListener(mFSKDecoder); - - mSymbolBroadcaster = new Broadcaster(); - mFSKDecoder.setListener(mSymbolBroadcaster); - - /* Message framer for control channel messages */ - mControlMessageFramer = new MessageFramer(sync.getControlSyncPattern().getPattern(), sMESSAGE_LENGTH); - mSymbolBroadcaster.addListener(mControlMessageFramer); - - /* Message framer for traffic channel massages */ - mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), sMESSAGE_LENGTH); - mSymbolBroadcaster.addListener(mTrafficMessageFramer); - - /* Fully decoded and framed messages processor */ - mMessageProcessor = new MPT1327MessageProcessor(aliasList); - mMessageProcessor.setMessageListener(this); - mControlMessageFramer.addMessageListener(mMessageProcessor); - mTrafficMessageFramer.addMessageListener(mMessageProcessor); - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mDecimationFilter; - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.MPT1327; - } - - /** - * Cleanup method - */ - public void dispose() - { - super.dispose(); - - mSymbolBroadcaster.dispose(); - - mControlMessageFramer.dispose(); - - mFSKDecoder.dispose(); - - mDecimationFilter.dispose(); - - mMessageProcessor.dispose(); - - mTrafficMessageFramer.dispose(); - } - - /* Instrumentation Taps */ - @Override - public List getTapGroups() - { - if(mAvailableTaps == null) - { - mAvailableTaps = new ArrayList(); - - TapGroup group = new TapGroup("MPT-1327 Decoder"); - - group.add(new FloatTap(INSTRUMENT_HB1_FILTER_TO_HIGH_PASS, 0, 0.5f)); - group.add(new FloatTap(INSTRUMENT_HIGH_PASS_TO_DECODER, 0, 0.5f)); - group.add(new BinaryTap(INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f)); - - mAvailableTaps.add(group); - - /* Add the taps from the FSK decoder */ - mAvailableTaps.addAll(mFSKDecoder.getTapGroups()); - } - - return mAvailableTaps; - } - - @Override - public void registerTap(Tap tap) - { - /* Send request to decoder */ - mFSKDecoder.registerTap(tap); - - switch(tap.getName()) - { - case INSTRUMENT_HB1_FILTER_TO_HIGH_PASS: - FloatBufferTap hb1Tap = (FloatBufferTap) tap; - mDecimationFilter.setListener(hb1Tap); - hb1Tap.setListener(mHighPassFilter); - break; - case INSTRUMENT_HIGH_PASS_TO_DECODER: - FloatBufferTap lowTap = (FloatBufferTap) tap; - mHighPassFilter.setListener(lowTap); - lowTap.setListener(mFSKDecoder); - break; - case INSTRUMENT_DECODER_TO_FRAMER: - BinaryTap decoderTap = (BinaryTap) tap; - mFSKDecoder.setListener(decoderTap); - decoderTap.setListener(mSymbolBroadcaster); - break; - } - } - - @Override - public void unregisterTap(Tap tap) - { - mFSKDecoder.unregisterTap(tap); - - switch(tap.getName()) - { - case INSTRUMENT_HB1_FILTER_TO_HIGH_PASS: - mDecimationFilter.setListener(mHighPassFilter); - break; - case INSTRUMENT_HIGH_PASS_TO_DECODER: - mHighPassFilter.setListener(mFSKDecoder); - break; - case INSTRUMENT_DECODER_TO_FRAMER: - mFSKDecoder.setListener(mSymbolBroadcaster); - break; - } - } - - public enum Sync - { - NORMAL("Normal", SyncPattern.MPT1327_CONTROL, SyncPattern.MPT1327_TRAFFIC), - FRENCH("French", SyncPattern.MPT1327_CONTROL_FRENCH, SyncPattern.MPT1327_TRAFFIC_FRENCH); - - private String mLabel; - private SyncPattern mControlSyncPattern; - private SyncPattern mTrafficSyncPattern; - - private Sync(String label, SyncPattern controlPattern, SyncPattern trafficPattern) - { - mLabel = label; - mControlSyncPattern = controlPattern; - mTrafficSyncPattern = trafficPattern; - } - - public String getLabel() - { - return mLabel; - } - - public SyncPattern getControlSyncPattern() - { - return mControlSyncPattern; - } - - public SyncPattern getTrafficSyncPattern() - { - return mTrafficSyncPattern; - } - - public String toString() - { - return getLabel(); - } - } - - @Override - public void reset() - { - mControlMessageFramer.reset(); - mTrafficMessageFramer.reset(); - } -} diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java deleted file mode 100644 index 940e1496..00000000 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ /dev/null @@ -1,541 +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 module.decode.mpt1327; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.ChangeChannelTimeoutEvent; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import channel.traffic.TrafficChannelAllocationEvent; -import controller.channel.Channel.ChannelType; -import controller.channel.map.ChannelMap; -import message.Message; -import module.decode.DecoderType; -import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEvent; -import module.decode.event.CallEvent.CallEventType; -import module.decode.mpt1327.MPT1327Message.IdentType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import util.StringUtils; - -import java.util.ArrayList; -import java.util.Collections; -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 -{ - private final static Logger mLog = LoggerFactory.getLogger(MPT1327DecoderState.class); - - private TreeSet mIdents = new TreeSet(); - private HashMap> mGroups = new HashMap>(); - - private String mSite; - private AliasedStringAttributeMonitor mFromAttribute; - private AliasedStringAttributeMonitor mToAttribute; - - private int mChannelNumber; - private ChannelType mChannelType; - private ChannelMap mChannelMap; - - private long mFrequency = 0; - private long mCallTimeout; - - public MPT1327DecoderState(AliasList aliasList, ChannelMap channelMap, ChannelType channelType, long callTimeout) - { - super(aliasList); - - mChannelMap = channelMap; - mChannelType = channelType; - mCallTimeout = callTimeout; - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MPT1327); - mToAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MPT1327); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.MPT1327; - } - - public ChannelType getChannelType() - { - return mChannelType; - } - - public void dispose() - { - super.dispose(); - } - - @Override - public void receive(Message message) - { - if(message.isValid()) - { - if(message instanceof MPT1327Message) - { - MPT1327Message mpt = ((MPT1327Message) message); - - switch(mpt.getMessageType()) - { - case ACK: - mIdents.add(mpt.getFromID()); - - IdentType identType = mpt.getIdent1Type(); - - if(identType == IdentType.REGI) - { - broadcast(new MPT1327CallEvent.Builder(CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details("REGISTERED ON NETWORK") - .frequency(mFrequency) - .from(mpt.getToID()) - .to(mpt.getFromID()) - .build()); - } - else - { - broadcast(new MPT1327CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details("ACK " + identType.getLabel()) - .frequency(mFrequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) - .build()); - } - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - break; - case ACKI: - mIdents.add(mpt.getFromID()); - mIdents.add(mpt.getToID()); - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - break; - case AHYC: - mIdents.add(mpt.getToID()); - - broadcast(new MPT1327CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details(((MPT1327Message) message).getRequestString()) - .frequency(mFrequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) - .build()); - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - break; - case AHYQ: - broadcast(new MPT1327CallEvent.Builder(CallEventType.STATUS) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details(mpt.getStatusMessage()) - .frequency(mFrequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) - .build()); - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - break; - case ALH: - setSite(mpt.getSiteID()); - broadcast(new DecoderStateEvent(this, Event.START, State.CONTROL)); - break; - case GTC: - String from = mpt.getFromID(); - String to = mpt.getToID(); - - if(from != null) - { - mIdents.add(from); - } - - if(to != null) - { - mIdents.add(to); - } - - /* Capture the idents that talk to each group */ - if(from != null && to != null) - { - if(mGroups.containsKey(to)) - { - ArrayList talkgroups = mGroups.get(to); - - if(!talkgroups.contains(from)) - { - talkgroups.add(from); - } - } - else - { - ArrayList talkgroups = new ArrayList(); - talkgroups.add(from); - - mGroups.put(to, talkgroups); - } - } - - int channel = mpt.getChannel(); - - long frequency = 0; - - if(getChannelMap() != null) - { - frequency = getChannelMap().getFrequency(channel); - } - - CallEvent event = new MPT1327CallEvent.Builder(CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(channel)) - .details("GTC") - .frequency(frequency) - .from(mpt.getFromID()) - .to(mpt.getToID()) - .build(); - - broadcast(new TrafficChannelAllocationEvent(this, event)); - break; - case HEAD_PLUS1: - case HEAD_PLUS2: - case HEAD_PLUS3: - case HEAD_PLUS4: - broadcast(new MPT1327CallEvent.Builder(CallEventType.SDM) - .aliasList(getAliasList()) - .details(mpt.getMessage()) - .from(mpt.getFromID()) - .to(mpt.getToID()) - .build()); - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - break; - - /* Traffic Channel Events */ - case CLEAR: - mChannelNumber = mpt.getChannel(); - - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - break; - case MAINT: - if(mChannelType == ChannelType.STANDARD) - { - /** - * When we receive a MAINT message and we're configured - * as a standard channel, we need to apply the call - * timeout specified by the user. Otherwise we'll - * be using the shorter default call timeout - */ - broadcast(new ChangeChannelTimeoutEvent(this, - mChannelType, mCallTimeout)); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new MPT1327CallEvent.Builder(CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .details("MONITORED TRAFFIC CHANNEL") - .frequency(mFrequency) - .to(mpt.getToID()) - .build(); - - broadcast(mCurrentCallEvent); - } - - mToAttribute.process(mpt.getToID()); - - broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); - } - break; - default: - break; - } - } - } - } - - public void reset() - { - mIdents.clear(); - - resetState(); - } - - private void resetState() - { - mFromAttribute.reset(); - mToAttribute.reset(); - - /** - * If this is a standard channel, reset the fade timeout to the default - * timeout. Once processing is underway, if we get a MAINT message, - * this indicates we're processing a traffic channel as a standard - * channel, so we'll issue a different call timeout then. - */ - if(mChannelType == ChannelType.STANDARD) - { - broadcast(new ChangeChannelTimeoutEvent(this, mChannelType, - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000)); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - mCurrentCallEvent = null; - } - } - } - - public String getSite() - { - return mSite; - } - - /** - * Set the site number. - */ - public void setSite(String site) - { - if(!StringUtils.isEqual(mSite, site)) - { - mSite = site; - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_1, "SITE:" + getSite(), - getSiteAlias())); - } - } - - public Alias getSiteAlias() - { - if(hasAliasList()) - { - return getAliasList().getSiteID(mSite); - } - - return null; - } - - public ChannelMap getChannelMap() - { - return mChannelMap; - } - - public void setChannelMap(ChannelMap channelMap) - { - mChannelMap = channelMap; - } - - public int getChannelNumber() - { - return mChannelNumber; - } - - /** - * Set the channel number. This is used primarily for traffic channels since - * the channel will already have been identified prior to the traffic - * channel being created. - */ - public void setChannelNumber(int channel) - { - if(mChannelNumber != channel) - { - mChannelNumber = channel; - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, String.valueOf(mChannelNumber))); - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("Activity Summary - Decoder: MPT-1327\n"); - sb.append(DIVIDER1); - sb.append("Site: "); - - if(mSite != null) - { - sb.append(mSite); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(mSite); - - if(siteAlias != null) - { - sb.append(" ").append(siteAlias.getName()).append("\n"); - } - } - } - else - { - sb.append("Unknown\n"); - } - - sb.append(DIVIDER2).append("Talkgroups: "); - - if(mGroups.isEmpty()) - { - sb.append("None\n"); - } - else - { - List talkgroups = new ArrayList(mGroups.keySet()); - Collections.sort(talkgroups); - - for(String talkgroup : talkgroups) - { - sb.append("\n ").append(talkgroup); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMPT1327Alias(talkgroup); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - } - - sb.append("\n"); - - ArrayList members = mGroups.get(talkgroup); - Collections.sort(members); - - for(String member : members) - { - sb.append(" >"); - sb.append(member); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMPT1327Alias(member); - - if(alias != null) - { - sb.append(" "); - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - } - - sb.append(DIVIDER2).append("All Idents: "); - - if(mIdents.isEmpty()) - { - sb.append("None\n"); - } - else - { - sb.append("\n"); - - Iterator it = mIdents.iterator(); - - while(it.hasNext()) - { - String ident = it.next(); - - sb.append(""); - sb.append(ident); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMPT1327Alias(ident); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - return sb.toString(); - } - - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, event.getFrequency())); - break; - case TRAFFIC_CHANNEL_ALLOCATION: - if(event.getSource() != MPT1327DecoderState.this) - { - if(event instanceof TrafficChannelAllocationEvent) - { - TrafficChannelAllocationEvent allocationEvent = - (TrafficChannelAllocationEvent) event; - - String channel = allocationEvent.getCallEvent().getChannel(); - - if(channel != null) - { - try - { - setChannelNumber(Integer.valueOf(channel)); - } - catch(Exception e) - { - //Do nothing, we couldn't parse the channel number - } - } - - mFrequency = allocationEvent.getCallEvent().getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, - allocationEvent.getCallEvent().getFrequency())); - - mFromAttribute.process(allocationEvent.getCallEvent().getFromID()); - mToAttribute.process(allocationEvent.getCallEvent().getToID()); - } - } - break; - default: - break; - } - } -} \ No newline at end of file diff --git a/src/module/decode/nbfm/NBFMDecoder.java b/src/module/decode/nbfm/NBFMDecoder.java deleted file mode 100644 index dfe83545..00000000 --- a/src/module/decode/nbfm/NBFMDecoder.java +++ /dev/null @@ -1,52 +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 module.decode.nbfm; - -import module.decode.DecoderType; -import module.decode.PrimaryDecoder; -import module.decode.config.DecodeConfiguration; - -public class NBFMDecoder extends PrimaryDecoder -{ - public NBFMDecoder( DecodeConfiguration config ) - { - super( config ); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.NBFM; - } - - @Override - public void reset() - { - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } -} diff --git a/src/module/decode/p25/C4FMSymbolFilter.java b/src/module/decode/p25/C4FMSymbolFilter.java deleted file mode 100644 index 70a772e0..00000000 --- a/src/module/decode/p25/C4FMSymbolFilter.java +++ /dev/null @@ -1,508 +0,0 @@ -package module.decode.p25; - -import dsp.gain.DirectGainControl; -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.AdditiveFloatTap; -import instrument.tap.stream.FloatTap; -import sample.Listener; -import sample.real.RealBuffer; -import sample.real.RealSampleListener; -import sample.real.RealSampleProvider; -import source.ISourceEventListener; -import source.SourceEvent; - -import java.util.ArrayList; -import java.util.List; - -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 - }; - - /* Instrumentation Taps */ - 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% - - /* 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 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); - } - - /* 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 - * 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; - } - - broadcast(SourceEvent.channelFrequencyCorrectionRequest(correction)); - - mFrequencyAdjustmentRequested = 0; - } - } - - public void receive(float sample) - { - sample = mGainController.correct(sample); - - if(mResetFrequencyTracker) - { - mCoarseFrequencyCorrection = 0.0f; - mFineFrequencyCorrection = 0.0f; - - 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; - - /* Correct output for symbol deviation (spread) */ - float output = (float)(2.0 * interp / mSymbolSpread); - - /* Detect received symbol error: basically use a hard decision and - * subtract off expected position nominal symbol level which will be - * +/- 0.5 * symbol spread and +/- 1.5 symbol spread. Remember that - * nominal symbol spread will be 2.0 */ - double symbolError; - - if(interp < -mSymbolSpread) - { - /* symbol is -3: Expected at -1.5 * symbol spread */ - symbolError = interp + (1.5 * mSymbolSpread); - mSymbolSpread -= (symbolError * 0.5 * K_SYMBOL_SPREAD); - } - else if(interp < 0.0) - { - /* symbol is -1: Expected at -0.5 * symbol_spread */ - symbolError = interp + (0.5 * mSymbolSpread); - mSymbolSpread -= (symbolError * K_SYMBOL_SPREAD); - } - else if(interp < mSymbolSpread) - { - /* symbol is +1: Expected at +0.5 * symbol_spread */ - symbolError = interp - (0.5 * mSymbolSpread); - mSymbolSpread += (symbolError * K_SYMBOL_SPREAD); - } - else - { - /* symbol is +3: Expected at +1.5 * symbol_spread */ - symbolError = interp - (1.5 * mSymbolSpread); - mSymbolSpread += (symbolError * 0.5 * K_SYMBOL_SPREAD); - } - - /* Symbol clock tracking loop adjustment */ - if(interp_p1 < interp) - { - mSymbolClock += symbolError * K_SYMBOL_TIMING; - } - else - { - mSymbolClock -= symbolError * K_SYMBOL_TIMING; - } - - if(mSymbolSpread < SYMBOL_SPREAD_MIN) - { - mGainController.increase(); - - 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); - } - - /* dispatch the interpolated value to the listener */ - if(mListener != null) - { - mListener.receive(output); - } - } - } - - @Override - public void setListener(RealSampleListener listener) - { - mListener = listener; - } - - @Override - public void removeListener(RealSampleListener listener) - { - mListener = null; - } - - @Override - public List getTapGroups() - { - 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; - } - - @Override - public void registerTap(Tap tap) - { - if(tap.getName().contentEquals(INSTRUMENT_SYMBOL_SPREAD)) - { - mSymbolSpreadTap = (FloatTap)tap; - } - } - - @Override - public void unregisterTap(Tap tap) - { - if(tap.getName().contentEquals(INSTRUMENT_SYMBOL_SPREAD)) - { - mSymbolSpreadTap = null; - } - } - - /** - * Broadcasts a frequency change event to the registered listener - * - * @param 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 getSourceEventListener() - { - if(mFrequencyCorrectionProcessor == null) - { - mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor(); - } - - 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/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java deleted file mode 100644 index 79ab8f7b..00000000 --- a/src/module/decode/p25/P25Decoder.java +++ /dev/null @@ -1,70 +0,0 @@ -package module.decode.p25; - -import alias.AliasList; -import instrument.Instrumentable; -import module.decode.Decoder; -import module.decode.DecoderType; -import source.ISourceEventListener; -import source.ISourceEventProvider; - -public abstract class P25Decoder extends Decoder implements ISourceEventListener, ISourceEventProvider, Instrumentable -{ - private P25MessageProcessor mMessageProcessor; - - public P25Decoder( AliasList aliasList ) - { - mMessageProcessor = new P25MessageProcessor( aliasList ); - mMessageProcessor.setMessageListener( this ); - } - - public void dispose() - { - super.dispose(); - - mMessageProcessor.dispose(); - mMessageProcessor = null; - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.P25_PHASE1; - } - - protected P25MessageProcessor getMessageProcessor() - { - return mMessageProcessor; - } - - public abstract Modulation getModulation(); - - public enum Modulation - { - CQPSK( "Simulcast (LSM)", "LSM" ), - C4FM( "Normal (C4FM)", "C4FM" ); - - private String mLabel; - private String mShortLabel; - - private Modulation( String label, String shortLabel ) - { - mLabel = label; - mShortLabel = shortLabel; - } - - public String getLabel() - { - return mLabel; - } - - public String getShortLabel() - { - return mShortLabel; - } - - public String toString() - { - return getLabel(); - } - } -} diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java deleted file mode 100644 index 2a84255b..00000000 --- a/src/module/decode/p25/P25DecoderState.java +++ /dev/null @@ -1,3622 +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 module.decode.p25; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.ChangeChannelTimeoutEvent; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import channel.traffic.TrafficChannelAllocationEvent; -import controller.channel.Channel.ChannelType; -import message.Message; -import module.decode.DecoderType; -import module.decode.event.CallEvent.CallEventType; -import module.decode.p25.P25Decoder.Modulation; -import module.decode.p25.message.IAdjacentSite; -import module.decode.p25.message.IBandIdentifier; -import module.decode.p25.message.P25Message; -import module.decode.p25.message.hdu.HDUMessage; -import module.decode.p25.message.ldu.LDU1Message; -import module.decode.p25.message.ldu.LDUMessage; -import module.decode.p25.message.ldu.lc.CallTermination; -import module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser; -import module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser; -import module.decode.p25.message.pdu.PDUMessage; -import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import module.decode.p25.message.pdu.confirmed.PDUTypeUnknown; -import module.decode.p25.message.pdu.confirmed.PacketData; -import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; -import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; -import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; -import module.decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; -import module.decode.p25.message.pdu.confirmed.SNDCPUserData; -import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import module.decode.p25.message.pdu.osp.control.CallAlertExtended; -import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import module.decode.p25.message.pdu.osp.control.StatusQueryExtended; -import module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; -import module.decode.p25.message.tdu.TDUMessage; -import module.decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcast; -import module.decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcastExplicit; -import module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdate; -import module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdateExplicit; -import module.decode.p25.message.tdu.lc.NetworkStatusBroadcast; -import module.decode.p25.message.tdu.lc.NetworkStatusBroadcastExplicit; -import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast; -import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; -import module.decode.p25.message.tdu.lc.TDULinkControlMessage; -import module.decode.p25.message.tsbk.TSBKMessage; -import module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; -import module.decode.p25.message.tsbk.motorola.PatchGroupAdd; -import module.decode.p25.message.tsbk.motorola.PatchGroupDelete; -import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; -import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; -import module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import module.decode.p25.message.tsbk.osp.control.CallAlert; -import module.decode.p25.message.tsbk.osp.control.DenyResponse; -import module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; -import module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; -import module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import module.decode.p25.message.tsbk.osp.control.IdentifierUpdate; -import module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import module.decode.p25.message.tsbk.osp.control.MessageUpdate; -import module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import module.decode.p25.message.tsbk.osp.control.QueuedResponse; -import module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import module.decode.p25.message.tsbk.osp.control.StatusQuery; -import module.decode.p25.message.tsbk.osp.control.StatusUpdate; -import module.decode.p25.message.tsbk.osp.control.SystemService; -import module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; -import module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; -import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; -import module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; -import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import module.decode.p25.reference.IPProtocol; -import module.decode.p25.reference.LinkControlOpcode; -import module.decode.p25.reference.Response; -import module.decode.p25.reference.Vendor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -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 -{ - private final static Logger mLog = LoggerFactory.getLogger(P25DecoderState.class); - private static final DecimalFormat mFrequencyFormatter = - new DecimalFormat("0.000000"); - - - private module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast mNetworkStatus; - private NetworkStatusBroadcastExtended mNetworkStatusExtended; - private ProtectionParameterBroadcast mProtectionParameterBroadcast; - private RFSSStatusBroadcast mRFSSStatusMessage; - private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; - private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; - - private Set mSecondaryControlChannels = - new TreeSet<>(); - - private Map mBands = new HashMap<>(); - private Map mRegistrations = new HashMap<>(); - private Map mNeighborMap = new HashMap<>(); - - private String mLastCommandEventID; - private String mLastPageEventID; - private String mLastQueryEventID; - private String mLastRegistrationEventID; - private String mLastResponseEventID; - - //TODO: create multi-attribute monitor for NAC and System - private String mNAC; - private String mSystem; - private AliasedStringAttributeMonitor mSiteAttributeMonitor; - private AliasedStringAttributeMonitor mFromTalkgroupMonitor; - private AliasedStringAttributeMonitor mToTalkgroupMonitor; - private String mCurrentChannel = "CURRENT"; - private long mCurrentChannelFrequency = 0; - - private ChannelType mChannelType; - private Modulation mModulation; - private boolean mIgnoreDataCalls; - private boolean mControlChannelShutdownLogged; - - private P25CallEvent mCurrentCallEvent; - private List mCallDetectTalkgroups = new ArrayList<>(); - private Map mChannelCallMap = new HashMap<>(); - private PatchGroupManager mPatchGroupManager; - - public P25DecoderState(AliasList aliasList, - ChannelType channelType, - Modulation modulation, - boolean ignoreDataCalls) - { - super(aliasList); - mChannelType = channelType; - mModulation = modulation; - mIgnoreDataCalls = ignoreDataCalls; - - mPatchGroupManager = new PatchGroupManager(aliasList, getCallEventBroadcaster()); - mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE); - mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mFromTalkgroupMonitor.addIllegalValue("000000"); - mToTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mToTalkgroupMonitor.addIllegalValue("0000"); - mToTalkgroupMonitor.addIllegalValue("000000"); - } - - public Modulation getModulation() - { - return mModulation; - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.P25_PHASE1; - } - - /** - * Performs a full reset to prepare this object for reuse on a new channel - */ - @Override - public void reset() - { - resetState(); - - mNAC = null; - mSiteAttributeMonitor.reset(); - mSystem = null; - } - - /** - * Resets any temporal state details - */ - private void resetState() - { - mFromTalkgroupMonitor.reset(); - mToTalkgroupMonitor.reset(); - - mCallDetectTalkgroups.clear(); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - } - - mCurrentCallEvent = null; - } - - /** - * Indicates if there a call event exists for the specified channel and - * from and to talkgroup identifiers. - * - * @param channel - channel number - * @param from user id - * @param to talkgroup or user id - * @return true if there is a call event - */ - public boolean hasCallEvent(String channel, String from, String to) - { - boolean hasEvent = false; - - if(mChannelCallMap.containsKey(channel)) - { - P25CallEvent event = mChannelCallMap.get(channel); - - if(to != null && - event.getToID() != null && - to.contentEquals(event.getToID())) - { - if(from != null) - { - if(event.getFromID() == null) - { - hasEvent = true; - } - else if(from.contentEquals(event.getFromID())) - { - hasEvent = true; - } - } - else - { - hasEvent = true; - } - } - } - - return hasEvent; - } - - /** - * Adds the channel and event to the current channel call map. If an entry - * already exists, terminates the event and broadcasts the update. - * - * @param event to place in the map - */ - public void registerCallEvent(P25CallEvent event) - { - String channel = event.getChannel(); - - if(mChannelCallMap.containsKey(event.getChannel())) - { - P25CallEvent previousEvent = mChannelCallMap.remove(event.getChannel()); - - previousEvent.end(); - - broadcast(previousEvent); - } - - mChannelCallMap.put(event.getChannel(), event); - } - - private void updateCallEvent(String channel, String from, String to) - { - P25CallEvent event = mChannelCallMap.get(channel); - - if(event != null && - to != null && - event.getToID() != null && - event.getToID().contentEquals(to)) - { - if(event.getFromID() == null && - from != null && - !from.contentEquals("0000") && - !from.contentEquals("000000")) - { - event.setFromID(from); - broadcast(event); - } - } - } - - /** - * Primary message processing method. - */ - public void receive(Message message) - { - if(message instanceof P25Message) - { - updateNAC(((P25Message)message).getNAC()); - - /* Voice Vocoder messages */ - if(message instanceof LDUMessage) - { - processLDU((LDUMessage)message); - } - - /* Trunking Signalling Messages */ - else if(message instanceof TSBKMessage) - { - processTSBK((TSBKMessage)message); - } - - /* Terminator Data Unit with Link Control Message */ - else if(message instanceof TDULinkControlMessage) - { - processTDULC((TDULinkControlMessage)message); - } - - /* Packet Data Unit Messages */ - else if(message instanceof PDUMessage) - { - processPDU((PDUMessage)message); - } - - /* Header Data Unit Message - preceeds voice LDUs */ - else if(message instanceof HDUMessage) - { - processHDU((HDUMessage)message); - } - - /* Terminator Data Unit, or default message if CRC failed */ - else if(message instanceof TDUMessage) - { - processTDU((TDUMessage)message); - } - } - } - - /** - * Terminator Data Unit - */ - private void processTDU(TDUMessage tdu) - { - } - - /** - * Header Data Unit - first message in a call sequence. - */ - private void processHDU(HDUMessage hdu) - { - if(hdu.isValid()) - { - broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); - - String to = hdu.getToID(); - - mToTalkgroupMonitor.process(to); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details((hdu.isEncryptedAudio() ? "ENCRYPTED " : "")) - .frequency(mCurrentChannelFrequency) - .to(to) - .build(); - - broadcast(mCurrentCallEvent); - } - - } - } - - /** - * Terminator Data Unit with Link Control - transmitted multiple times at - * beginning or end of call sequence and includes embedded link control messages - */ - private void processTDULC(TDULinkControlMessage tdulc) - { - if(tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION) - { - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - mCurrentCallEvent = null; - } - } - else - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); - } - - switch(tdulc.getOpcode()) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if(tdulc instanceof AdjacentSiteStatusBroadcast) - { - IAdjacentSite ias = (IAdjacentSite)tdulc; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - if(tdulc instanceof AdjacentSiteStatusBroadcastExplicit) - { - IAdjacentSite ias = (IAdjacentSite)tdulc; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - break; - case CALL_ALERT: - if(tdulc instanceof module.decode.p25.message.tdu.lc.CallAlert) - { - module.decode.p25.message.tdu.lc.CallAlert ca = - (module.decode.p25.message.tdu.lc.CallAlert)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getSourceAddress()) - .to(ca.getTargetAddress()) - .details("CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case CALL_TERMINATION_OR_CANCELLATION: - /* This opcode as handled at the beginning of the method */ - break; - case CHANNEL_IDENTIFIER_UPDATE: - //TODO: does the activity summary need this message? - - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - //TODO: does the activity summary need this message? - - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case EXTENDED_FUNCTION_COMMAND: - if(tdulc instanceof module.decode.p25.message.tdu.lc.ExtendedFunctionCommand) - { - module.decode.p25.message.tdu.lc.ExtendedFunctionCommand efc = - (module.decode.p25.message.tdu.lc.ExtendedFunctionCommand)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(efc.getTargetAddress()) - .details("FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case GROUP_AFFILIATION_QUERY: - if(tdulc instanceof module.decode.p25.message.tdu.lc.GroupAffiliationQuery) - { - module.decode.p25.message.tdu.lc.GroupAffiliationQuery gaq = - (module.decode.p25.message.tdu.lc.GroupAffiliationQuery)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION QUERY") - .from(gaq.getSourceAddress()) - .to(gaq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE: - /* Used only on trunked systems on the outbound channel, to - * reflect user activity on other channels. We process this - * as a call detect */ - if(tdulc instanceof GroupVoiceChannelUpdate) - { - GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; - - String groupA = gvcu.getGroupAddressA(); - - if(!mCallDetectTalkgroups.contains(groupA)) - { - broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelA()) - .details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyA()) - .to(groupA) - .build()); - - mCallDetectTalkgroups.add(groupA); - } - - String groupB = gvcu.getGroupAddressB(); - - if(!mCallDetectTalkgroups.contains(groupB)) - { - broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelB()) - .details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyB()) - .to(groupB) - .build()); - - mCallDetectTalkgroups.add(groupB); - } - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - /* Reflects other call activity on the system: CALL DETECT */ - if(mChannelType == ChannelType.STANDARD && - tdulc instanceof GroupVoiceChannelUpdateExplicit) - { - GroupVoiceChannelUpdateExplicit gvcue = - (GroupVoiceChannelUpdateExplicit)tdulc; - - String group = gvcue.getGroupAddress(); - - if(!mCallDetectTalkgroups.contains(group)) - { - broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcue.getTransmitChannel()) - .details((gvcue.isEncrypted() ? "ENCRYPTED" : "")) - .frequency(gvcue.getDownlinkFrequency()) - .to(group) - .build()); - - mCallDetectTalkgroups.add(group); - } - } - break; - case GROUP_VOICE_CHANNEL_USER: - /* Used on a traffic channel to reflect current call entities */ - if(tdulc instanceof module.decode.p25.message.tdu.lc.GroupVoiceChannelUser) - { - module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser = - (module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; - - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); - - String from = gvcuser.getSourceAddress(); - String to = gvcuser.getGroupAddress(); - - mFromTalkgroupMonitor.process(from); - mToTalkgroupMonitor.process(to); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("TDULC GROUP VOICE CHANNEL USER" + - (gvcuser.isEncrypted() ? "ENCRYPTED " : "") + - (gvcuser.isEmergency() ? "EMERGENCY " : "")) - .frequency(mCurrentChannelFrequency) - .from(from) - .to(to) - .build(); - - broadcast(mCurrentCallEvent); - } - } - break; - case MESSAGE_UPDATE: - if(tdulc instanceof module.decode.p25.message.tdu.lc.MessageUpdate) - { - module.decode.p25.message.tdu.lc.MessageUpdate mu = - (module.decode.p25.message.tdu.lc.MessageUpdate)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.SDM) - .aliasList(getAliasList()) - .from(mu.getSourceAddress()) - .to(mu.getTargetAddress()) - .details("MSG: " + mu.getShortDataMessage()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case NETWORK_STATUS_BROADCAST: - if(tdulc instanceof NetworkStatusBroadcast) - { - updateSystem(((NetworkStatusBroadcast)tdulc).getSystem()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if(tdulc instanceof NetworkStatusBroadcastExplicit) - { - updateSystem(((NetworkStatusBroadcastExplicit)tdulc).getSystem()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case PROTECTION_PARAMETER_BROADCAST: - if(tdulc instanceof module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast) - { - module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast ppb = - (module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(ppb.getTargetAddress()) - .details("ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case RFSS_STATUS_BROADCAST: - if(tdulc instanceof module.decode.p25.message.tdu.lc.RFSSStatusBroadcast) - { - module.decode.p25.message.tdu.lc.RFSSStatusBroadcast rfsssb = - (module.decode.p25.message.tdu.lc.RFSSStatusBroadcast)tdulc; - - updateSystem(rfsssb.getSystem()); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - mSiteAttributeMonitor.process(site); - - if(mCurrentChannel == null || - !mCurrentChannel.contentEquals(rfsssb.getChannel())) - { - mCurrentChannel = rfsssb.getChannel(); - mCurrentChannelFrequency = rfsssb.getDownlinkFrequency(); - } - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if(tdulc instanceof module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit) - { - module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit)tdulc; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - mSiteAttributeMonitor.process(site); - - if(mCurrentChannel == null || - !mCurrentChannel.contentEquals(rfsssbe.getTransmitChannel())) - { - mCurrentChannel = rfsssbe.getTransmitChannel(); - mCurrentChannelFrequency = rfsssbe.getDownlinkFrequency(); - } - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if(tdulc instanceof SecondaryControlChannelBroadcast) - { - SecondaryControlChannelBroadcast sccb = - (SecondaryControlChannelBroadcast)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if(tdulc instanceof SecondaryControlChannelBroadcastExplicit) - { - SecondaryControlChannelBroadcastExplicit sccb = - (SecondaryControlChannelBroadcastExplicit)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case STATUS_QUERY: - if(tdulc instanceof module.decode.p25.message.tdu.lc.StatusQuery) - { - module.decode.p25.message.tdu.lc.StatusQuery sq = - (module.decode.p25.message.tdu.lc.StatusQuery)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceAddress()) - .to(sq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case STATUS_UPDATE: - if(tdulc instanceof module.decode.p25.message.tdu.lc.StatusUpdate) - { - module.decode.p25.message.tdu.lc.StatusUpdate su = - (module.decode.p25.message.tdu.lc.StatusUpdate)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.STATUS) - .aliasList(getAliasList()) - .details("STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus()) - .from(su.getSourceAddress()) - .to(su.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if(tdulc instanceof module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest) - { - module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest tiar = - (module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(tiar.getTelephoneNumber()) - .to(tiar.getTargetAddress()) - .details("TELEPHONE CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - if(mChannelType == ChannelType.STANDARD && - tdulc instanceof module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser) - { - module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser tivcu = - (module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc; - - String to = tivcu.getAddress(); - - mToTalkgroupMonitor.process(to); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details((tivcu.isEncrypted() ? "ENCRYPTED" : "") + - (tivcu.isEmergency() ? " EMERGENCY" : "")) - .frequency(mCurrentChannelFrequency) - .to(to) - .build(); - - broadcast(mCurrentCallEvent); - } - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if(tdulc instanceof module.decode.p25.message.tdu.lc.UnitAuthenticationCommand) - { - module.decode.p25.message.tdu.lc.UnitAuthenticationCommand uac = - (module.decode.p25.message.tdu.lc.UnitAuthenticationCommand)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(uac.getCompleteTargetAddress()) - .details("AUTHENTICATE") - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case UNIT_REGISTRATION_COMMAND: - if(tdulc instanceof module.decode.p25.message.tdu.lc.UnitRegistrationCommand) - { - module.decode.p25.message.tdu.lc.UnitRegistrationCommand urc = - (module.decode.p25.message.tdu.lc.UnitRegistrationCommand)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(urc.getCompleteTargetAddress()) - .details("REGISTER") - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if(tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest) - { - module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest uuar = - (module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest)tdulc; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(uuar.getSourceAddress()) - .to(uuar.getTargetAddress()) - .details("UNIT TO UNIT CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(tdulc); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - /* Used on traffic channels to indicate the current call entities */ - if(tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser) - { - module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser uuvcu = - (module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; - - String from = uuvcu.getSourceAddress(); - mFromTalkgroupMonitor.process(from); - - String to = uuvcu.getTargetAddress(); - mToTalkgroupMonitor.process(to); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details((uuvcu.isEncrypted() ? "ENCRYPTED " : "") + - (uuvcu.isEmergency() ? "EMERGENCY " : "")) - .frequency(mCurrentChannelFrequency) - .from(from) - .to(to) - .build(); - } - } - break; - default: - break; - } - } - - /** - * Log optional vendor format messages that we don't yet support so that we - * can understand those messages and eventually add support. - */ - private void logAlternateVendorMessage(P25Message message) - { -// if( message.isValid() ) -// { -// mLog.info( "PLEASE NOTIFY DEVELOPER - UNRECOGNIZED P25 VENDOR FORMAT -" -// + " DUID:" + message.getDUID() -// + " MSG:" + message.getBinaryMessage() -// + " CLASS:" + message.getClass() ); -// } - } - - /** - * Processes LDU voice frame messages. Sends continuation events to keep - * the channel state synchronized and processes the embedded link control - * messages to capture/broadcast peripheral events like paging. - */ - private void processLDU(LDUMessage ldu) - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL)); - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .frequency(mCurrentChannelFrequency) - .build(); - - broadcast(mCurrentCallEvent); - } - - if(ldu instanceof LDU1Message) - { - switch(((LDU1Message)ldu).getOpcode()) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if(ldu instanceof module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast) - { - IAdjacentSite ias = (IAdjacentSite)ldu; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - else - { - logAlternateVendorMessage(ldu); - } - - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - if(ldu instanceof module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcastExplicit) - { - IAdjacentSite ias = (IAdjacentSite)ldu; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case CALL_ALERT: - if(ldu instanceof module.decode.p25.message.ldu.lc.CallAlert) - { - module.decode.p25.message.ldu.lc.CallAlert ca = - (module.decode.p25.message.ldu.lc.CallAlert)ldu; - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getSourceAddress()) - .to(ca.getTargetAddress()) - .details("CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case CALL_TERMINATION_OR_CANCELLATION: - broadcast(new DecoderStateEvent(this, Event.END, State.FADE)); - mCurrentCallEvent = null; - - if(!(ldu instanceof CallTermination)) - { - logAlternateVendorMessage(ldu); - } - break; - case CHANNEL_IDENTIFIER_UPDATE: - //TODO: do we need this for the activity summary? - - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - //TODO: do we need this for the activity summary? - - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case EXTENDED_FUNCTION_COMMAND: - if(ldu instanceof module.decode.p25.message.ldu.lc.ExtendedFunctionCommand) - { - module.decode.p25.message.ldu.lc.ExtendedFunctionCommand efc = - (module.decode.p25.message.ldu.lc.ExtendedFunctionCommand)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(efc.getTargetAddress()) - .details("FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_AFFILIATION_QUERY: - if(mChannelType == ChannelType.STANDARD && - ldu instanceof module.decode.p25.message.ldu.lc.GroupAffiliationQuery) - { - module.decode.p25.message.ldu.lc.GroupAffiliationQuery gaq = - (module.decode.p25.message.ldu.lc.GroupAffiliationQuery)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION QUERY") - .from(gaq.getSourceAddress()) - .to(gaq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE: - if(ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate) - { - module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = - (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; - - String userA = gvcu.getGroupAddressA(); - String userB = gvcu.getGroupAddressB(); - - if(mChannelType == ChannelType.STANDARD) - { - if(!mCallDetectTalkgroups.contains(userA)) - { - broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelA()) - .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyA()) - .to(userA) - .build()); - - mCallDetectTalkgroups.add(userA); - } - - if(userB != null && - !userB.contentEquals("0000") && - !mCallDetectTalkgroups.contains(userB)) - { - broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcu.getChannelB()) - .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) - .frequency(gvcu.getDownlinkFrequencyB()) - .to(userB) - .build()); - - mCallDetectTalkgroups.add(userB); - } - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - if(ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit) - { - module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = - (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; - - String group = gvcue.getGroupAddress(); - - if(mChannelType == ChannelType.STANDARD) - { - if(!mCallDetectTalkgroups.contains(group)) - { - broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(gvcue.getTransmitChannel()) - .details((gvcue.isEncryptedLinkControlWord() ? "ENCRYPTED" : "")) - .frequency(gvcue.getDownlinkFrequency()) - .to(group) - .build()); - - mCallDetectTalkgroups.add(group); - } - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case GROUP_VOICE_CHANNEL_USER: - /* Indicates the current user of the current channel */ - if(ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser) - { - module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = - (module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; - - mFromTalkgroupMonitor.process(gvcuser.getSourceAddress()); - - mToTalkgroupMonitor.process(gvcuser.getGroupAddress()); - - if(mChannelType == ChannelType.STANDARD) - { - if(mCurrentCallEvent.getCallEventType() != CallEventType.GROUP_CALL) - { - mCurrentCallEvent.setCallEventType(CallEventType.GROUP_CALL); - broadcast(mCurrentCallEvent); - } - - if(mCurrentCallEvent.getDetails() == null) - { - mCurrentCallEvent.setDetails( - (gvcuser.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + - (gvcuser.isEmergency() ? "EMERGENCY " : "")); - - broadcast(mCurrentCallEvent); - } - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case MESSAGE_UPDATE: - if(ldu instanceof module.decode.p25.message.ldu.lc.MessageUpdate) - { - module.decode.p25.message.ldu.lc.MessageUpdate mu = - (module.decode.p25.message.ldu.lc.MessageUpdate)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.SDM) - .aliasList(getAliasList()) - .from(mu.getSourceAddress()) - .to(mu.getTargetAddress()) - .details("MSG: " + mu.getShortDataMessage()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case NETWORK_STATUS_BROADCAST: - if(ldu instanceof module.decode.p25.message.ldu.lc.NetworkStatusBroadcast) - { - updateSystem(((module.decode.p25.message.ldu.lc.NetworkStatusBroadcast)ldu).getSystem()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if(ldu instanceof module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit) - { - updateSystem(((module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit)ldu).getSystem()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case PROTECTION_PARAMETER_BROADCAST: - if(ldu instanceof module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast) - { - module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast ppb = - (module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(ppb.getTargetAddress()) - .details("ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case RFSS_STATUS_BROADCAST: - if(ldu instanceof module.decode.p25.message.ldu.lc.RFSSStatusBroadcast) - { - module.decode.p25.message.ldu.lc.RFSSStatusBroadcast rfsssb = - (module.decode.p25.message.ldu.lc.RFSSStatusBroadcast)ldu; - - updateSystem(rfsssb.getSystem()); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if(ldu instanceof module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit) - { - module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit)ldu; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if(ldu instanceof module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast) - { - module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast sccb = - (module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if(ldu instanceof module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit) - { - module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit sccb = - (module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - mSiteAttributeMonitor.process(site); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case STATUS_QUERY: - if(ldu instanceof module.decode.p25.message.ldu.lc.StatusQuery) - { - module.decode.p25.message.ldu.lc.StatusQuery sq = - (module.decode.p25.message.ldu.lc.StatusQuery)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceAddress()) - .to(sq.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case STATUS_UPDATE: - if(ldu instanceof module.decode.p25.message.ldu.lc.StatusUpdate) - { - module.decode.p25.message.ldu.lc.StatusUpdate su = - (module.decode.p25.message.ldu.lc.StatusUpdate)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.STATUS) - .aliasList(getAliasList()) - .details("STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus()) - .from(su.getSourceAddress()) - .to(su.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if(ldu instanceof module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest) - { - module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest tiar = - (module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(tiar.getTelephoneNumber()) - .to(tiar.getTargetAddress()) - .details("TELEPHONE CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - /* Indicates the current user of the current channel */ - if(mChannelType == ChannelType.STANDARD && - ldu instanceof TelephoneInterconnectVoiceChannelUser) - { - TelephoneInterconnectVoiceChannelUser tivcu = - (TelephoneInterconnectVoiceChannelUser)ldu; - - mToTalkgroupMonitor.process(tivcu.getAddress()); - - if(mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT) - { - mCurrentCallEvent.setCallEventType(CallEventType.TELEPHONE_INTERCONNECT); - broadcast(mCurrentCallEvent); - } - - if(mCurrentCallEvent.getDetails() == null) - { - mCurrentCallEvent.setDetails( - (tivcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + - (tivcu.isEmergency() ? "EMERGENCY " : "")); - - broadcast(mCurrentCallEvent); - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if(ldu instanceof module.decode.p25.message.ldu.lc.UnitAuthenticationCommand) - { - module.decode.p25.message.ldu.lc.UnitAuthenticationCommand uac = - (module.decode.p25.message.ldu.lc.UnitAuthenticationCommand)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(uac.getCompleteTargetAddress()) - .details("AUTHENTICATE") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_REGISTRATION_COMMAND: - if(ldu instanceof module.decode.p25.message.ldu.lc.UnitRegistrationCommand) - { - module.decode.p25.message.ldu.lc.UnitRegistrationCommand urc = - (module.decode.p25.message.ldu.lc.UnitRegistrationCommand)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .to(urc.getCompleteTargetAddress()) - .details("REGISTER") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if(ldu instanceof module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest) - { - module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest uuar = - (module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest)ldu; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(uuar.getSourceAddress()) - .to(uuar.getTargetAddress()) - .details("UNIT TO UNIT CALL ALERT") - .build()); - } - else - { - logAlternateVendorMessage(ldu); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - if(mChannelType == ChannelType.STANDARD && - ldu instanceof UnitToUnitVoiceChannelUser) - { - UnitToUnitVoiceChannelUser uuvcu = - (UnitToUnitVoiceChannelUser)ldu; - - mFromTalkgroupMonitor.process(uuvcu.getSourceAddress()); - - mToTalkgroupMonitor.process(uuvcu.getTargetAddress()); - - if(mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL) - { - mCurrentCallEvent.setCallEventType(CallEventType.UNIT_TO_UNIT_CALL); - broadcast(mCurrentCallEvent); - } - - if(mCurrentCallEvent.getDetails() == null) - { - mCurrentCallEvent.setDetails( - (uuvcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") + - (uuvcu.isEmergency() ? "EMERGENCY " : "")); - - broadcast(mCurrentCallEvent); - } - } - else - { - logAlternateVendorMessage(ldu); - } - break; - default: - break; - } - } - } - - /** - * Process a Trunking Signalling Block message - */ - private void processTSBK(TSBKMessage tsbk) - { - /* Trunking Signalling Block Messages - indicates Control Channel */ - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - - if(tsbk.getVendor() == Vendor.STANDARD) - { - switch(tsbk.getOpcode()) - { - case ADJACENT_STATUS_BROADCAST: - if(tsbk instanceof AdjacentStatusBroadcast) - { - IAdjacentSite ias = (IAdjacentSite)tsbk; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - break; - case ACKNOWLEDGE_RESPONSE: - processTSBKResponse(tsbk); - break; - case AUTHENTICATION_COMMAND: - processTSBKCommand(tsbk); - break; - case CALL_ALERT: - processTSBKPage(tsbk); - break; - case DENY_RESPONSE: - processTSBKResponse(tsbk); - break; - case EXTENDED_FUNCTION_COMMAND: - processTSBKCommand(tsbk); - break; - case GROUP_AFFILIATION_QUERY: - processTSBKQuery(tsbk); - break; - case GROUP_AFFILIATION_RESPONSE: - processTSBKResponse(tsbk); - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - processTSBKDataChannelAnnouncement(tsbk); - break; - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - processTSBKChannelGrant(tsbk); - break; - case IDENTIFIER_UPDATE_NON_VUHF: - case IDENTIFIER_UPDATE_VHF_UHF_BANDS: - case IDENTIFIER_UPDATE_TDMA: - IdentifierUpdate iu = (IdentifierUpdate)tsbk; - - if(!mBands.containsKey(iu.getIdentifier())) - { - mBands.put(iu.getIdentifier(), iu); - } - break; - case LOCATION_REGISTRATION_RESPONSE: - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - processTSBKResponse(tsbk); - break; - case MESSAGE_UPDATE: - processTSBKMessage(tsbk); - break; - case NETWORK_STATUS_BROADCAST: - mNetworkStatus = (module.decode.p25.message.tsbk.osp - .control.NetworkStatusBroadcast)tsbk; - break; - case PROTECTION_PARAMETER_UPDATE: - processTSBKResponse(tsbk); - break; - case QUEUED_RESPONSE: - processTSBKResponse(tsbk); - break; - case RADIO_UNIT_MONITOR_COMMAND: - processTSBKCommand(tsbk); - break; - case RFSS_STATUS_BROADCAST: - processTSBKRFSSStatus((RFSSStatusBroadcast)tsbk); - break; - case ROAMING_ADDRESS_COMMAND: - processTSBKCommand(tsbk); - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sccb = - (module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast)tsbk; - - if(sccb.getDownlinkFrequency1() > 0) - { - mSecondaryControlChannels.add(sccb); - } - break; - case SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)tsbk; - break; - case SNDCP_DATA_CHANNEL_GRANT: - processTSBKChannelGrant(tsbk); - break; - case STATUS_QUERY: - processTSBKQuery(tsbk); - break; - case STATUS_UPDATE: - processTSBKResponse(tsbk); - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - case UNIT_TO_UNIT_ANSWER_REQUEST: - processTSBKPage(tsbk); - break; - case UNIT_REGISTRATION_COMMAND: - processTSBKCommand(tsbk); - break; - case UNIT_REGISTRATION_RESPONSE: - processTSBKResponse(tsbk); - break; - default: - break; - } - } - else if(tsbk.getVendor() == Vendor.MOTOROLA) - { - processMotorolaTSBK((MotorolaTSBKMessage)tsbk); - } - } - - /** - * Process a Packet Data Unit message - */ - private void processPDU(PDUMessage pdu) - { - if(pdu instanceof PacketData || pdu instanceof PDUTypeUnknown) - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); - } - else if(pdu instanceof PDUConfirmedMessage) - { - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); - - PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; - - switch(pduc.getPDUType()) - { - case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPActivateTDSContextAccept satca = - (SNDCPActivateTDSContextAccept)pduc; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("ACTIVATE SNDCP USE IP:" + satca.getIPAddress()) - .frequency(mCurrentChannelFrequency) - .to(satca.getLogicalLinkID()) - .build()); - break; - case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: - SNDCPActivateTDSContextReject satcr = - (SNDCPActivateTDSContextReject)pduc; - - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("REJECT: SNDCP CONTEXT ACTIVATION " - + "REASON:" + satcr.getReason().getLabel()) - .frequency(mCurrentChannelFrequency) - .to(satcr.getLogicalLinkID()) - .build()); - break; - case SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPActivateTDSContextRequest satcreq = - (SNDCPActivateTDSContextRequest)pduc; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("REQUEST SNDCP USE IP:" + satcreq.getIPAddress()) - .frequency(mCurrentChannelFrequency) - .from(satcreq.getLogicalLinkID()) - .build()); - break; - case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPDeactivateTDSContext sdtca = - (SNDCPDeactivateTDSContext)pduc; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("ACCEPT DEACTIVATE SNDCP CONTEXT") - .frequency(mCurrentChannelFrequency) - .from(sdtca.getLogicalLinkID()) - .build()); - break; - case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPDeactivateTDSContext sdtcreq = - (SNDCPDeactivateTDSContext)pduc; - - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("REQUEST DEACTIVATE SNDCP CONTEXT") - .frequency(mCurrentChannelFrequency) - .from(sdtcreq.getLogicalLinkID()) - .build()); - break; - case SNDCP_RF_CONFIRMED_DATA: - if(pduc instanceof SNDCPUserData) - { - SNDCPUserData sud = (SNDCPUserData)pduc; - - StringBuilder sbFrom = new StringBuilder(); - StringBuilder sbTo = new StringBuilder(); - - sbFrom.append(sud.getSourceIPAddress()); - sbTo.append(sud.getDestinationIPAddress()); - - if(sud.getIPProtocol() == IPProtocol.UDP) - { - sbFrom.append(":"); - sbFrom.append(sud.getUDPSourcePort()); - sbTo.append(":"); - sbTo.append(sud.getUDPDestinationPort()); - } - - broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); - - broadcast(new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(mCurrentChannel) - .details("DATA: " + sud.getPayload() + - " RADIO IP:" + sbTo.toString()) - .frequency(mCurrentChannelFrequency) - .from(sbFrom.toString()) - .to(pduc.getLogicalLinkID()) - .build()); - } - else - { - mLog.error("Error - expected SNDCPUserData instance but class was: " + pduc.getClass() + - " and PDU Type:" + pduc.getPDUType().name()); - } - break; - case SNDCP_RF_UNCONFIRMED_DATA: - break; - default: -// mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); - break; - } - } - else - { - /* These are alternate trunking control blocks in PDU format */ - broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL)); - - switch(pdu.getOpcode()) - { - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - processPDUChannelGrant(pdu); - break; - case ADJACENT_STATUS_BROADCAST: - if(pdu instanceof AdjacentStatusBroadcastExtended) - { - IAdjacentSite ias = (IAdjacentSite)pdu; - - mNeighborMap.put(ias.getUniqueID(), ias); - - updateSystem(ias.getSystemID()); - } - break; - case CALL_ALERT: - if(pdu instanceof CallAlertExtended) - { - CallAlertExtended ca = (CallAlertExtended)pdu; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getWACN() + "-" + ca.getSystemID() + "-" + - ca.getSourceID()) - .to(ca.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case GROUP_AFFILIATION_QUERY: - if(pdu instanceof GroupAffiliationQueryExtended) - { - GroupAffiliationQueryExtended gaqe = - (GroupAffiliationQueryExtended)pdu; - - if(mLastQueryEventID == null || !gaqe.getTargetAddress() - .contentEquals(mLastQueryEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION") - .from(gaqe.getWACN() + "-" + gaqe.getSystemID() + - "-" + gaqe.getSourceID()) - .to(gaqe.getTargetAddress()) - .build()); - - mLastQueryEventID = gaqe.getToID(); - } - } - break; - case GROUP_AFFILIATION_RESPONSE: - if(pdu instanceof GroupAffiliationResponseExtended) - { - GroupAffiliationResponseExtended gar = - (GroupAffiliationResponseExtended)pdu; - - if(mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("AFFILIATION:" + gar.getResponse().name() + - " FOR GROUP:" + gar.getGroupWACN() + "-" + - gar.getGroupSystemID() + "-" + - gar.getGroupID() + " ANNOUNCEMENT GROUP:" + - gar.getAnnouncementGroupID()) - .from(gar.getSourceWACN() + "-" + - gar.getSourceSystemID() + "-" + - gar.getSourceID()) - .to(gar.getTargetAddress()) - .build()); - - mLastResponseEventID = gar.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case MESSAGE_UPDATE: - if(pdu instanceof MessageUpdateExtended) - { - MessageUpdateExtended mu = (MessageUpdateExtended)pdu; - - broadcast(new P25CallEvent.Builder(CallEventType.SDM) - .aliasList(getAliasList()) - .details("MESSAGE: " + mu.getMessage()) - .from(mu.getSourceWACN() + "-" + mu.getSourceSystemID() + - "-" + mu.getSourceID()) - .to(mu.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case NETWORK_STATUS_BROADCAST: - if(pdu instanceof NetworkStatusBroadcastExtended) - { - mNetworkStatusExtended = (NetworkStatusBroadcastExtended)pdu; - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case PROTECTION_PARAMETER_BROADCAST: - if(pdu instanceof ProtectionParameterBroadcast) - { - mProtectionParameterBroadcast = - (ProtectionParameterBroadcast)pdu; - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case RFSS_STATUS_BROADCAST: - if(pdu instanceof RFSSStatusBroadcastExtended) - { - mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)pdu; - - updateNAC(mRFSSStatusMessageExtended.getNAC()); - updateSystem(mRFSSStatusMessageExtended.getSystemID()); - mSiteAttributeMonitor.process(mRFSSStatusMessageExtended.getRFSubsystemID() + - "-" + mRFSSStatusMessageExtended.getSiteID()); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case ROAMING_ADDRESS_UPDATE: - if(pdu instanceof RoamingAddressUpdateExtended) - { - RoamingAddressUpdateExtended raue = - (RoamingAddressUpdateExtended)pdu; - - StringBuilder sb = new StringBuilder(); - sb.append("ROAMING ADDRESS STACK A:"); - sb.append(raue.getWACNA() + "-" + raue.getSystemIDA()); - - if(raue.isFormat2()) - { - sb.append(" B:"); - sb.append(raue.getWACNB() + "-" + raue.getSystemIDB()); - sb.append(" C:"); - sb.append(raue.getWACNC() + "-" + raue.getSystemIDC()); - sb.append(" D:"); - sb.append(raue.getWACND() + "-" + raue.getSystemIDD()); - } - - if(raue.isFormat3()) - { - sb.append(" E:"); - sb.append(raue.getWACNE() + "-" + raue.getSystemIDE()); - sb.append(" F:"); - sb.append(raue.getWACNF() + "-" + raue.getSystemIDF()); - sb.append(" G:"); - sb.append(raue.getWACNG() + "-" + raue.getSystemIDG()); - } - - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details(sb.toString()) - .from(raue.getSourceID()) - .to(raue.getTargetAddress()) - .build()); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case STATUS_QUERY: - if(pdu instanceof StatusQueryExtended) - { - StatusQueryExtended sq = (StatusQueryExtended)pdu; - - if(mLastQueryEventID == null || !sq.getTargetAddress() - .contentEquals(mLastQueryEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceWACN() + "-" + - sq.getSourceSystemID() + "-" + - sq.getSourceID()) - .to(sq.getTargetAddress()) - .build()); - - mLastQueryEventID = sq.getToID(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case STATUS_UPDATE: - if(pdu instanceof StatusUpdateExtended) - { - StatusUpdateExtended su = (StatusUpdateExtended)pdu; - - if(mLastResponseEventID == null || !mLastResponseEventID - .contentEquals(su.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus()) - .from(su.getSourceWACN() + "-" + - su.getSourceSystemID() + "-" + - su.getSourceID()) - .to(su.getTargetAddress()) - .build()); - - mLastResponseEventID = su.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case UNIT_REGISTRATION_RESPONSE: - if(pdu instanceof UnitRegistrationResponseExtended) - { - UnitRegistrationResponseExtended urr = - (UnitRegistrationResponseExtended)pdu; - - if(urr.getResponse() == Response.ACCEPT) - { - mRegistrations.put(urr.getAssignedSourceAddress(), - System.currentTimeMillis()); - } - - if(mLastRegistrationEventID == null || !mLastRegistrationEventID - .contentEquals(urr.getAssignedSourceAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.REGISTER) - .aliasList(getAliasList()) - .details("REGISTRATION:" + urr.getResponse().name() + - " FOR EXTERNAL SYSTEM ADDRESS: " + - urr.getWACN() + "-" + - urr.getSystemID() + "-" + - urr.getSourceAddress() + - " SOURCE ID: " + urr.getSourceID()) - .from(urr.getAssignedSourceAddress()) - .build()); - - mLastRegistrationEventID = urr.getAssignedSourceAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if(pdu instanceof UnitToUnitAnswerRequestExplicit) - { - UnitToUnitAnswerRequestExplicit utuare = - (UnitToUnitAnswerRequestExplicit)pdu; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(utuare.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .details((utuare.isEmergency() ? "EMERGENCY" : "")) - .from(utuare.getWACN() + "-" + - utuare.getSystemID() + "-" + - utuare.getSourceID()) - .to(utuare.getTargetAddress()) - .build()); - - mLastPageEventID = utuare.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - default: - break; - } - } - } - - private void processPDUChannelGrant(PDUMessage pdu) - { - String channel = null; - String from = null; - String to = null; - - switch(pdu.getOpcode()) - { - case GROUP_DATA_CHANNEL_GRANT: - if(pdu instanceof GroupDataChannelGrantExtended) - { - GroupDataChannelGrantExtended gdcge = - (GroupDataChannelGrantExtended)pdu; - - channel = gdcge.getTransmitChannel(); - from = gdcge.getSourceAddress(); - to = gdcge.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((gdcge.isEncrypted() ? "ENCRYPTED" : "") + - (gdcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - } - else - { - logAlternateVendorMessage(pdu); - } - - if(!mIgnoreDataCalls) - { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - if(pdu instanceof GroupVoiceChannelGrantExplicit) - { - GroupVoiceChannelGrantExplicit gvcge = - (GroupVoiceChannelGrantExplicit)pdu; - - channel = gvcge.getTransmitChannel(); - from = gvcge.getSourceAddress(); - to = gvcge.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((gvcge.isEncrypted() ? "ENCRYPTED" : "") + - (gvcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(gvcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - if(pdu instanceof IndividualDataChannelGrantExtended) - { - IndividualDataChannelGrantExtended idcge = - (IndividualDataChannelGrantExtended)pdu; - - channel = idcge.getTransmitChannel(); - from = idcge.getSourceWACN() + "-" + - idcge.getSourceSystemID() + "-" + - idcge.getSourceAddress(); - to = idcge.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((idcge.isEncrypted() ? "ENCRYPTED" : "") + - (idcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(idcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - - if(!mIgnoreDataCalls) - { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectChannelGrantExplicit ticge = - (TelephoneInterconnectChannelGrantExplicit)pdu; - - channel = ticge.getTransmitChannel(); - - //We don't know if the subscriber is calling or being called, so - //we use the same address in both from/to fields - from = ticge.getAddress(); - to = ticge.getAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(channel) - .details((ticge.isEncrypted() ? "ENCRYPTED" : "") + - (ticge.isEmergency() ? " EMERGENCY" : "") + - " CALL TIMER:" + ticge.getCallTimer()) - .frequency(ticge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - if(pdu instanceof UnitToUnitVoiceChannelGrantExtended) - { - UnitToUnitVoiceChannelGrantExtended uuvcge = - (UnitToUnitVoiceChannelGrantExtended)pdu; - - channel = uuvcge.getTransmitChannel(); - from = uuvcge.getSourceWACN() + "-" + - uuvcge.getSourceSystemID() + "-" + - uuvcge.getSourceID(); - to = uuvcge.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((uuvcge.isEncrypted() ? "ENCRYPTED" : "") + - (uuvcge.isEmergency() ? " EMERGENCY" : "")) - .frequency(uuvcge.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - else - { - logAlternateVendorMessage(pdu); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - if(pdu instanceof UnitToUnitVoiceChannelGrantUpdateExtended) - { - UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = - (UnitToUnitVoiceChannelGrantUpdateExtended)pdu; - - channel = uuvcgue.getTransmitChannel(); - from = uuvcgue.getSourceWACN() + "-" + - uuvcgue.getSourceSystemID() + "-" + - uuvcgue.getSourceID(); - to = uuvcgue.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - P25CallEvent callEvent = new P25CallEvent.Builder(CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((uuvcgue.isEncrypted() ? "ENCRYPTED" : "") + - (uuvcgue.isEmergency() ? " EMERGENCY" : "")) - .frequency(uuvcgue.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(callEvent); - broadcast(callEvent); - } - - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - - } - else - { - logAlternateVendorMessage(pdu); - } - break; - default: - break; - } - } - - private void processTSBKCommand(TSBKMessage message) - { - switch(message.getOpcode()) - { - case AUTHENTICATION_COMMAND: - AuthenticationCommand ac = (AuthenticationCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(ac.getFullTargetID())) - { - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .details("AUTHENTICATE") - .to(ac.getWACN() + "-" + ac.getSystemID() + "-" + - ac.getTargetID()) - .build()); - - mLastCommandEventID = ac.getFullTargetID(); - } - break; - case EXTENDED_FUNCTION_COMMAND: - ExtendedFunctionCommand efc = (ExtendedFunctionCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(efc.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.FUNCTION) - .aliasList(getAliasList()) - .details("EXTENDED FUNCTION: " + - efc.getExtendedFunction().getLabel()) - .from(efc.getSourceAddress()) - .to(efc.getTargetAddress()) - .build()); - - mLastCommandEventID = efc.getTargetAddress(); - } - break; - case RADIO_UNIT_MONITOR_COMMAND: - RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(rumc.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .details("RADIO UNIT MONITOR") - .from(rumc.getSourceAddress()) - .to(rumc.getTargetAddress()) - .build()); - - mLastCommandEventID = rumc.getTargetAddress(); - } - break; - case ROAMING_ADDRESS_COMMAND: - RoamingAddressCommand rac = (RoamingAddressCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(rac.getTargetID())) - { - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .details(rac.getStackOperation().name() + - " ROAMING ADDRESS " + rac.getWACN() + "-" + - rac.getSystemID()) - .to(rac.getTargetID()) - .build()); - - mLastCommandEventID = rac.getTargetID(); - } - break; - case UNIT_REGISTRATION_COMMAND: - UnitRegistrationCommand urc = (UnitRegistrationCommand)message; - - if(mLastCommandEventID == null || !mLastCommandEventID - .contentEquals(urc.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.COMMAND) - .aliasList(getAliasList()) - .details("REGISTER") - .from(urc.getSourceAddress()) - .to(urc.getTargetAddress()) - .build()); - - mLastCommandEventID = urc.getTargetAddress(); - } - break; - default: - break; - } - } - - private void processTSBKMessage(TSBKMessage message) - { - MessageUpdate mu = (MessageUpdate)message; - - broadcast(new P25CallEvent.Builder(CallEventType.SDM) - .aliasList(getAliasList()) - .details("MESSAGE: " + mu.getMessage()) - .from(mu.getSourceAddress()) - .to(mu.getTargetAddress()) - .build()); - } - - private void processTSBKQuery(TSBKMessage message) - { - switch(message.getOpcode()) - { - case GROUP_AFFILIATION_QUERY: - GroupAffiliationQuery gaq = (GroupAffiliationQuery)message; - - if(mLastQueryEventID == null || !mLastQueryEventID - .contentEquals(gaq.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("GROUP AFFILIATION") - .from(gaq.getSourceAddress()) - .to(gaq.getTargetAddress()) - .build()); - } - break; - case STATUS_QUERY: - StatusQuery sq = (StatusQuery)message; - - if(mLastQueryEventID == null || !mLastQueryEventID - .contentEquals(sq.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.QUERY) - .aliasList(getAliasList()) - .details("STATUS QUERY") - .from(sq.getSourceAddress()) - .to(sq.getTargetAddress()) - .build()); - } - break; - default: - break; - } - } - - private void processTSBKResponse(TSBKMessage message) - { - switch(message.getOpcode()) - { - case ACKNOWLEDGE_RESPONSE: - AcknowledgeResponse ar = (AcknowledgeResponse)message; - - if(mLastResponseEventID == null || !ar.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - String to = ar.getTargetAddress(); - - if(ar.hasAdditionalInformation() && ar.hasExtendedAddress()) - { - to = ar.getWACN() + "-" + ar.getSystemID() + "-" + - ar.getTargetAddress(); - } - - String from = null; - - if(ar.hasAdditionalInformation() && !ar.hasExtendedAddress()) - { - from = ar.getFromID(); - } - - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("ACKNOWLEDGE") - .from(from) - .to(to) - .build()); - - mLastResponseEventID = ar.getTargetAddress(); - } - break; - case DENY_RESPONSE: - DenyResponse dr = (DenyResponse)message; - - if(mLastResponseEventID == null || !dr.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("DENY REASON: " + dr.getReason().name() + - " REQUESTED: " + dr.getServiceType().name()) - .from(dr.getSourceAddress()) - .to(dr.getTargetAddress()) - .build()); - - mLastResponseEventID = dr.getTargetAddress(); - } - break; - case GROUP_AFFILIATION_RESPONSE: - GroupAffiliationResponse gar = (GroupAffiliationResponse)message; - - if(mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("AFFILIATION:" + gar.getResponse().name() + - " FOR " + gar.getAffiliationScope() + - " GROUP:" + gar.getGroupAddress() + - " ANNOUNCEMENT GROUP:" + - gar.getAnnouncementGroupAddress()) - .to(gar.getTargetAddress()) - .build()); - - mLastResponseEventID = gar.getTargetAddress(); - } - break; - case LOCATION_REGISTRATION_RESPONSE: - LocationRegistrationResponse lrr = - (LocationRegistrationResponse)message; - - if(lrr.getResponse() == Response.ACCEPT) - { - mRegistrations.put(lrr.getTargetAddress(), - System.currentTimeMillis()); - } - - if(mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals(lrr.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.REGISTER) - .aliasList(getAliasList()) - .details("REGISTRATION:" + lrr.getResponse().name() + - " SITE: " + lrr.getRFSSID() + "-" + - lrr.getSiteID()) - .from(lrr.getTargetAddress()) - .to(lrr.getGroupAddress()) - .build()); - - mLastRegistrationEventID = lrr.getTargetAddress(); - } - break; - case PROTECTION_PARAMETER_UPDATE: - ProtectionParameterUpdate ppu = (ProtectionParameterUpdate)message; - - if(mLastResponseEventID == null || !ppu.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("USE ENCRYPTION ALGORITHM:" + - ppu.getAlgorithm().name() + " KEY:" + - ppu.getKeyID()) - .to(ppu.getTargetAddress()) - .build()); - - mLastResponseEventID = ppu.getTargetAddress(); - } - break; - case QUEUED_RESPONSE: - QueuedResponse qr = (QueuedResponse)message; - - if(mLastResponseEventID == null || !qr.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("QUEUED REASON: " + qr.getReason().name() + - " REQUESTED: " + qr.getServiceType().name()) - .from(qr.getSourceAddress()) - .to(qr.getTargetAddress()) - .build()); - - mLastResponseEventID = qr.getTargetAddress(); - } - break; - case STATUS_UPDATE: - StatusUpdate su = (StatusUpdate)message; - - if(mLastResponseEventID == null || !su.getTargetAddress() - .contentEquals(mLastResponseEventID)) - { - broadcast(new P25CallEvent.Builder(CallEventType.RESPONSE) - .aliasList(getAliasList()) - .details("STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus()) - .from(su.getSourceAddress()) - .to(su.getTargetAddress()) - .build()); - - mLastResponseEventID = su.getTargetAddress(); - } - - break; - case UNIT_REGISTRATION_RESPONSE: - UnitRegistrationResponse urr = (UnitRegistrationResponse)message; - - if(urr.getResponse() == Response.ACCEPT) - { - mRegistrations.put(urr.getSourceAddress(), - System.currentTimeMillis()); - } - - if(mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals(urr.getSourceAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.REGISTER) - .aliasList(getAliasList()) - .details("REGISTRATION:" + urr.getResponse().name() + - " SYSTEM: " + urr.getSystemID() + - " SOURCE ID: " + urr.getSourceID()) - .from(urr.getSourceAddress()) - .build()); - - mLastRegistrationEventID = urr.getSourceAddress(); - } - break; - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - UnitDeregistrationAcknowledge udr = - (UnitDeregistrationAcknowledge)message; - - if(mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals(udr.getSourceID())) - { - - broadcast(new P25CallEvent.Builder(CallEventType.DEREGISTER) - .aliasList(getAliasList()) - .from(udr.getSourceID()) - .build()); - - List keysToRemove = new ArrayList(); - - /* Remove this radio from the registrations set */ - for(String key : mRegistrations.keySet()) - { - if(key.startsWith(udr.getSourceID())) - { - keysToRemove.add(key); - } - } - - for(String key : keysToRemove) - { - mRegistrations.remove(key); - } - - mLastRegistrationEventID = udr.getSourceID(); - } - break; - default: - break; - } - } - - private void processTSBKRFSSStatus(RFSSStatusBroadcast message) - { - mRFSSStatusMessage = message; - - updateNAC(message.getNAC()); - - updateSystem(message.getSystemID()); - - mSiteAttributeMonitor.process(message.getRFSubsystemID() + "-" + message.getSiteID()); - } - - private void processTSBKDataChannelAnnouncement(TSBKMessage message) - { - switch(message.getOpcode()) - { - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - GroupDataChannelAnnouncement gdca = (GroupDataChannelAnnouncement)message; - - broadcast(new P25CallEvent.Builder(CallEventType.ANNOUNCEMENT) - .aliasList(getAliasList()) - .channel(gdca.getChannel1()) - .details((gdca.isEncrypted() ? "ENCRYPTED" : "") + - (gdca.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdca.getDownlinkFrequency1()) - .to(gdca.getGroupAddress1()) - .build()); - - if(gdca.hasChannelNumber2()) - { - broadcast(new P25CallEvent.Builder(CallEventType.ANNOUNCEMENT) - .aliasList(getAliasList()) - .channel(gdca.getChannel2()) - .details((gdca.isEncrypted() ? "ENCRYPTED" : "") + - (gdca.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdca.getDownlinkFrequency2()) - .to(gdca.getGroupAddress2()) - .build()); - } - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - GroupDataChannelAnnouncementExplicit gdcae = - (GroupDataChannelAnnouncementExplicit)message; - - broadcast(new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(gdcae.getTransmitChannel()) - .details((gdcae.isEncrypted() ? "ENCRYPTED" : "") + - (gdcae.isEmergency() ? " EMERGENCY" : "")) - .frequency(gdcae.getDownlinkFrequency()) - .to(gdcae.getGroupAddress()) - .build()); - break; - default: - break; - } - } - - /** - * Process Motorola vendor-specific Trunking Signaling Block messages - */ - private void processMotorolaTSBK(MotorolaTSBKMessage tsbk) - { - String channel; - String from; - String to; - - switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode()) - { - case PATCH_GROUP_CHANNEL_GRANT: - //Cleanup patch groups - auto-expire any patch groups before we allocate a channel - mPatchGroupManager.cleanupPatchGroups(); - - PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk; - - channel = pgvcg.getChannel(); - from = pgvcg.getSourceAddress(); - to = pgvcg.getPatchGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(pgvcg.isTDMAChannel() ? "TDMA " : ""); - details.append(pgvcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(pgvcg.getPriority()); - details.append(pgvcg.isEncryptedChannel() ? " ENCRYPTED" : ""); - details.append(" ").append(pgvcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(pgvcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!pgvcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case PATCH_GROUP_CHANNEL_GRANT_UPDATE: - //Cleanup patch groups - auto-expire any patch groups before we allocate a channel - mPatchGroupManager.cleanupPatchGroups(); - - PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk; - - channel = gvcgu.getChannel1(); - to = gvcgu.getPatchGroupAddress1(); - - if(hasCallEvent(channel, null, to)) - { - updateCallEvent(channel, null, to); - } - else - { - P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details((gvcgu.isTDMAChannel1() ? "TDMA " : "") + - (gvcgu.isEncrypted() ? "ENCRYPTED " : "")) - .frequency(gvcgu.getDownlinkFrequency1()) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgu.isTDMAChannel1()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - - String channel2 = gvcgu.getChannel2(); - String to2 = gvcgu.getPatchGroupAddress2(); - - if(hasCallEvent(channel2, null, to2)) - { - updateCallEvent(channel2, null, to2); - } - else - { - P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel2) - .details((gvcgu.isTDMAChannel2() ? "TDMA " : "") + - (gvcgu.isEncrypted() ? "ENCRYPTED " : "")) - .frequency(gvcgu.getDownlinkFrequency2()) - .to(to2) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgu.isTDMAChannel2()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel2))); - } - - break; - case PATCH_GROUP_ADD: - PatchGroupAdd pga = (PatchGroupAdd)tsbk; - mPatchGroupManager.updatePatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups()); - break; - case PATCH_GROUP_DELETE: - PatchGroupDelete pgd = (PatchGroupDelete)tsbk; - mPatchGroupManager.removePatchGroup(pgd.getPatchGroupAddress()); - break; - case CCH_PLANNED_SHUTDOWN: - if(!mControlChannelShutdownLogged) - { - broadcast(new P25CallEvent.Builder(CallEventType.NOTIFICATION) - .details("PLANNED CONTROL CHANNEL SHUTDOWN") - .build()); - - mControlChannelShutdownLogged = true; - } - break; - } - } - - /** - * Process a traffic channel allocation message - */ - private void processTSBKChannelGrant(TSBKMessage message) - { - //Cleanup patch groups - auto-expire any patch groups before we allocate a channel - mPatchGroupManager.cleanupPatchGroups(); - - String channel = null; - String from = null; - String to = null; - - switch(message.getOpcode()) - { - case GROUP_DATA_CHANNEL_GRANT: - GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; - - channel = gdcg.getChannel(); - from = gdcg.getSourceAddress(); - to = gdcg.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gdcg.isTDMAChannel() ? "TDMA " : ""); - details.append(gdcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gdcg.getPriority()).append(" "); - details.append(gdcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gdcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(gdcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!mIgnoreDataCalls && !gdcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; - - channel = gvcg.getChannel(); - from = gvcg.getSourceAddress(); - to = gvcg.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcg.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcg.getPriority()).append(" "); - details.append(gvcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(gvcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)message; - - channel = gvcgu.getChannel1(); - from = null; - to = gvcgu.getGroupAddress1(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcgu.getPriority()).append(" "); - details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcgu.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(gvcgu.getChannel1()) - .details(details.toString()) - .frequency(gvcgu.getDownlinkFrequency1()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgu.isTDMAChannel1()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - - if(gvcgu.hasChannelNumber2()) - { - channel = gvcgu.getChannel2(); - to = gvcgu.getGroupAddress2(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcgu.getPriority()).append(" "); - details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcgu.getSessionMode().name()); - - P25CallEvent event2 = new P25CallEvent.Builder(CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(gvcgu.getChannel2()) - .details(details.toString()) - .frequency(gvcgu.getDownlinkFrequency2()) - .to(gvcgu.getGroupAddress2()) - .build(); - - registerCallEvent(event2); - broadcast(event2); - } - - if(!gvcgu.isTDMAChannel2()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - GroupVoiceChannelGrantUpdateExplicit gvcgue = - (GroupVoiceChannelGrantUpdateExplicit)message; - - channel = gvcgue.getTransmitChannelIdentifier() + "-" + - gvcgue.getTransmitChannelNumber(); - from = null; - to = gvcgue.getGroupAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(gvcgue.isTDMAChannel() ? "TDMA " : ""); - details.append(gvcgue.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(gvcgue.getPriority()).append(" "); - details.append(gvcgue.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(gvcgue.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(gvcgue.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!gvcgue.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; - - channel = idcg.getChannel(); - from = idcg.getSourceAddress(); - to = idcg.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(idcg.isTDMAChannel() ? "TDMA " : ""); - details.append(idcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(idcg.getPriority()).append(" "); - details.append(idcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(idcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(idcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!mIgnoreDataCalls && !idcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case SNDCP_DATA_CHANNEL_GRANT: - SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; - - channel = sdcg.getTransmitChannel(); - from = null; - to = sdcg.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(sdcg.isTDMAChannel() ? "TDMA " : ""); - details.append(sdcg.isEmergency() ? "EMERGENCY " : ""); - details.append(sdcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(sdcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(sdcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!mIgnoreDataCalls && !sdcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectVoiceChannelGrant tivcg = (TelephoneInterconnectVoiceChannelGrant)message; - - channel = tivcg.getChannel(); - from = null; - /* Address is ambiguous and could mean either source or target, - * so we'll place the value in the to field */ - to = tivcg.getAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(tivcg.isTDMAChannel() ? "TDMA " : ""); - details.append(tivcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(tivcg.getPriority()).append(" "); - details.append(tivcg.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(tivcg.getSessionMode().name()).append(" "); - details.append("CALL TIMER:").append(tivcg.getCallTimer()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(tivcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!tivcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = (TelephoneInterconnectVoiceChannelGrantUpdate)message; - - channel = tivcgu.getChannelIdentifier() + "-" + tivcgu.getChannelNumber(); - from = null; - - /* Address is ambiguous and could mean either source or target, - * so we'll place the value in the to field */ - to = tivcgu.getAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(tivcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(tivcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(tivcgu.getPriority()).append(" "); - details.append(tivcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(tivcgu.getSessionMode().name()).append(" "); - details.append("CALL TIMER:").append(tivcgu.getCallTimer()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEventType.TELEPHONE_INTERCONNECT) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(tivcgu.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!tivcgu.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - UnitToUnitVoiceChannelGrant uuvcg = (UnitToUnitVoiceChannelGrant)message; - - channel = uuvcg.getChannelIdentifier() + "-" + uuvcg.getChannelNumber(); - from = uuvcg.getSourceAddress(); - to = uuvcg.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(uuvcg.isTDMAChannel() ? "TDMA " : ""); - details.append(uuvcg.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(uuvcg.getPriority()).append(" "); - details.append(uuvcg.isEncryptedChannel() ? " ENCRYPTED " : ""); - details.append(uuvcg.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(uuvcg.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!uuvcg.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - UnitToUnitVoiceChannelGrantUpdate uuvcgu = (UnitToUnitVoiceChannelGrantUpdate)message; - - channel = uuvcgu.getChannelIdentifier() + "-" + uuvcgu.getChannelNumber(); - from = uuvcgu.getSourceAddress(); - to = uuvcgu.getTargetAddress(); - - if(hasCallEvent(channel, from, to)) - { - updateCallEvent(channel, from, to); - } - else - { - StringBuilder details = new StringBuilder(); - details.append(uuvcgu.isTDMAChannel() ? "TDMA " : ""); - details.append(uuvcgu.isEmergency() ? "EMERGENCY " : ""); - details.append("PRI:").append(uuvcgu.getPriority()).append(" "); - details.append(uuvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); - details.append(uuvcgu.getSessionMode().name()); - - P25CallEvent event = new P25CallEvent.Builder( - CallEventType.UNIT_TO_UNIT_CALL) - .aliasList(getAliasList()) - .channel(channel) - .details(details.toString()) - .frequency(uuvcgu.getDownlinkFrequency()) - .from(from) - .to(to) - .build(); - - registerCallEvent(event); - broadcast(event); - } - - if(!uuvcgu.isTDMAChannel()) - { - broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); - } - break; - default: - break; - } - } - - private void updateSystem(String system) - { - if(mSystem == null || (system != null && !mSystem.contentEquals(system))) - { - mSystem = system; - broadcastSystemAndNACUpdate(); - } - } - - /** - * Broadcasts an update to the NAC - */ - private void updateNAC(String nac) - { - if(mNAC == null || (nac != null && !mNAC.contentEquals(nac))) - { - mNAC = nac; - broadcastSystemAndNACUpdate(); - } - } - - private void broadcastSystemAndNACUpdate() - { - String label = String.format("SYS:%s NAC:%s", mSystem, mNAC); - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_1, label)); - } - - /** - * Process a unit paging event message - */ - private void processTSBKPage(TSBKMessage message) - { - switch(message.getOpcode()) - { - case CALL_ALERT: - CallAlert ca = (CallAlert)message; - - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .from(ca.getSourceID()) - .to(ca.getTargetAddress()) - .build()); - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(utuar.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .details((utuar.isEmergency() ? "EMERGENCY" : "")) - .from(utuar.getSourceAddress()) - .to(utuar.getTargetAddress()) - .build()); - - mLastPageEventID = utuar.getTargetAddress(); - } - break; - case SNDCP_DATA_PAGE_REQUEST: - SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)message; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(sdpr.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .details("SNDCP DATA DAC: " + - sdpr.getDataAccessControl() + - " NSAPI:" + sdpr.getNSAPI()) - .to(sdpr.getTargetAddress()) - .build()); - - mLastPageEventID = sdpr.getTargetAddress(); - } - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - TelephoneInterconnectAnswerRequest tiar = - (TelephoneInterconnectAnswerRequest)message; - - if(mLastPageEventID == null || !mLastPageEventID - .contentEquals(tiar.getTargetAddress())) - { - broadcast(new P25CallEvent.Builder(CallEventType.PAGE) - .aliasList(getAliasList()) - .details(("TELEPHONE INTERCONNECT")) - .from(tiar.getTelephoneNumber()) - .to(tiar.getTargetAddress()) - .build()); - - mLastPageEventID = tiar.getTargetAddress(); - } - break; - default: - break; - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("Activity Summary - Decoder:P25 ").append(getModulation().getLabel()).append("\n"); - sb.append(DIVIDER1); - sb.append("SITE "); - - if(mNetworkStatus != null) - { - sb.append("NAC:" + mNetworkStatus.getNAC()); - sb.append("h WACN:" + mNetworkStatus.getWACN()); - sb.append("h SYS:" + mNetworkStatus.getSystemID()); - sb.append("h [").append(mNetworkStatus.getNetworkCallsign()).append("]"); - sb.append(" LRA:" + mNetworkStatus.getLocationRegistrationArea()); - } - else if(mNetworkStatusExtended != null) - { - sb.append("NAC:" + mNetworkStatusExtended.getNAC()); - sb.append("h WACN:" + mNetworkStatusExtended.getWACN()); - sb.append("h SYS:" + mNetworkStatusExtended.getSystemID()); - sb.append("h [").append(mNetworkStatusExtended.getNetworkCallsign()).append("]"); - sb.append(" LRA:" + mNetworkStatusExtended.getLocationRegistrationArea()); - } - - String site = null; - - if(mRFSSStatusMessage != null) - { - site = mRFSSStatusMessage.getRFSubsystemID() + "-" + mRFSSStatusMessage.getSiteID(); - } - else if(mRFSSStatusMessageExtended != null) - { - site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + mRFSSStatusMessageExtended.getSiteID(); - } - - sb.append("h RFSS-SITE:").append(site).append("h"); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(site); - - if(siteAlias != null) - { - sb.append(" " + siteAlias.getName()); - } - } - - sb.append("\n").append(DIVIDER2); - - if(mNetworkStatus != null) - { - sb.append("SERVICES: " + SystemService.toString(mNetworkStatus.getSystemServiceClass())).append("\n"); - sb.append(DIVIDER2); - sb.append("PCCH DOWNLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatus.getDownlinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) - .append("] UPLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatus.getUplinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) - .append("]\n"); - } - else if(mNetworkStatusExtended != null) - { - sb.append("\nSERVICES:").append(SystemService.toString(mNetworkStatusExtended.getSystemServiceClass())) - .append("\n"); - sb.append(DIVIDER2); - sb.append("PCCH DOWNLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatusExtended.getTransmitIdentifier()).append("-") - .append(mNetworkStatusExtended.getTransmitChannel()).append("] UPLINK ") - .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d)) - .append(" [").append(mNetworkStatusExtended.getReceiveIdentifier()).append("-") - .append(mNetworkStatusExtended.getReceiveChannel()).append("]\n"); - } - - if(mSecondaryControlChannels.isEmpty()) - { - sb.append("SCCH: NONE\n"); - } - else - { - for(module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sec : mSecondaryControlChannels) - { - sb.append("SCCH DOWNLINK ") - .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency1() / 1E6d)) - .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("] UPLINK ") - .append(mFrequencyFormatter.format((double)sec.getUplinkFrequency1() / 1E6d)) - .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("]"); - - if(sec.hasChannel2()) - { - sb.append(" SCCH 2 DOWNLINK ") - .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency2() / 1E6d)) - .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()) - .append("] UPLINK ").append(mFrequencyFormatter.format((double)sec.getUplinkFrequency2() / 1E6d)) - .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()).append("]"); - } - - sb.append("\n"); - } - } - - - if(mSNDCPDataChannel != null) - { - sb.append("SNDCP DOWNLINK ") - .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D)) - .append(" [").append(mSNDCPDataChannel.getTransmitChannel()).append("]").append(" UPLINK ") - .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D)) - .append(" [").append(mSNDCPDataChannel.getReceiveChannel()).append("]\n"); - } - - if(mProtectionParameterBroadcast != null) - { - sb.append(DIVIDER2); - sb.append("ENCRYPTION TYPE:").append(mProtectionParameterBroadcast.getEncryptionType().name()); - sb.append(" ALGORITHM:").append(mProtectionParameterBroadcast.getAlgorithmID()); - sb.append(" KEY:").append(mProtectionParameterBroadcast.getKeyID()); - sb.append(" INBOUND IV:").append(mProtectionParameterBroadcast.getInboundInitializationVector()); - sb.append(" OUTBOUND IV:").append(mProtectionParameterBroadcast.getOutboundInitializationVector()); - sb.append("\n"); - } - - List identifiers = new ArrayList<>(mBands.keySet()); - - Collections.sort(identifiers); - - sb.append(DIVIDER2).append("FREQUENCY BANDS:\n"); - for(Integer id : identifiers) - { - IBandIdentifier band = mBands.get(id); - sb.append(band.toString()).append("\n"); -// sb.append(" ").append(id); -// sb.append(" - BASE: " + mFrequencyFormatter.format( -// (double)band.getBaseFrequency() / 1E6d)); -// sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format( -// (double)band.getChannelSpacing() / 1E6d)); -// sb.append(" UPLINK OFFSET: " + mFrequencyFormatter.format( -// (double)band.getTransmitOffset() / 1E6D)); -// sb.append("\n"); - } - - sb.append(DIVIDER2).append("NEIGHBOR SITES: "); - - if(mNeighborMap.isEmpty()) - { - sb.append("NONE\n"); - } - else - { - for(IAdjacentSite neighbor : mNeighborMap.values()) - { - sb.append("\n"); - sb.append("NAC:").append(((P25Message)neighbor).getNAC()); - sb.append("h SYSTEM:" + neighbor.getSystemID()); - sb.append("h LRA:" + neighbor.getLRA()); - - String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); - sb.append("h RFSS-SITE:" + neighborID); - - sb.append("h "); - - if(hasAliasList()) - { - Alias siteAlias = getAliasList().getSiteID(neighborID); - - if(siteAlias != null) - { - sb.append(siteAlias.getName()); - } - } - - sb.append("\n PCCH: DOWNLINK ") - .append(mFrequencyFormatter.format((double)neighbor.getDownlinkFrequency() / 1E6d)) - .append(" [").append(neighbor.getDownlinkChannel()).append("] UPLINK:") - .append(mFrequencyFormatter.format((double)neighbor.getUplinkFrequency() / 1E6d)) - .append(" [").append(neighbor.getDownlinkChannel()).append("] SERVICES: ") - .append(neighbor.getSystemServiceClass()); - } - } - - return sb.toString(); - } - - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mCurrentChannelFrequency = event.getFrequency(); - break; - case TRAFFIC_CHANNEL_ALLOCATION: - if(event.getSource() != P25DecoderState.this) - { - if(event instanceof TrafficChannelAllocationEvent) - { - TrafficChannelAllocationEvent allocationEvent = (TrafficChannelAllocationEvent)event; - - mCurrentCallEvent = (P25CallEvent)allocationEvent.getCallEvent(); - - mCurrentChannel = allocationEvent.getCallEvent().getChannel(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, mCurrentChannel)); - - mCurrentChannelFrequency = allocationEvent.getCallEvent().getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, mCurrentChannelFrequency)); - - mFromTalkgroupMonitor.reset(); - mFromTalkgroupMonitor.process(allocationEvent.getCallEvent().getFromID()); - - mToTalkgroupMonitor.reset(); - mToTalkgroupMonitor.process(allocationEvent.getCallEvent().getToID()); - } - } - break; - default: - break; - } - } - - @Override - public void start() - { - //Change the default (45-second) traffic channel timeout to 1 second - if(mChannelType == ChannelType.TRAFFIC) - { - broadcast(new ChangeChannelTimeoutEvent(this, ChannelType.TRAFFIC, 1000)); - } - } -} diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java deleted file mode 100644 index 3dc771fc..00000000 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ /dev/null @@ -1,225 +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 module.decode.p25; - -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.DibitTap; -import instrument.tap.stream.FloatTap; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import alias.AliasList; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; -import source.SourceEvent; - -public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener -{ - private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); - - /* Instrumentation Taps */ - private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; - private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; - - private List mAvailableTaps; - private C4FMSymbolFilter mSymbolFilter; - private C4FMSlicer mC4FMSlicer; - private P25MessageFramer mMessageFramer; - - /** - * P25 Phase 1 C4FM Decoder processes real buffers of un-filtered, - * demodulated audio and produces decoded P25 Phase 1 Messages. - * - * Provides Frequency Control to steer external tuner source and incorporates - * internal gain control. - * - * Note: use the P25AudioModule to convert the decoded messages into audio. - * - * @param aliasList - optional (can be null) list of alias values for network - * infrastructure and subscriber identities that will be included in each - * decoded message - */ - public P25_C4FMDecoder( AliasList aliasList, int frequencyCorrectionMaximum ) - { - super( aliasList ); - - /* Shape gain and frequency offsets to optimize sample stream */ - mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum ); - - /* Convert samples to symbols */ - mC4FMSlicer = new C4FMSlicer(); - mSymbolFilter.setListener( mC4FMSlicer ); - - /* Sync pattern detection and message construction */ - mMessageFramer = new P25MessageFramer( aliasList ); - mC4FMSlicer.addListener( mMessageFramer ); - - /* Process and broadcast messages */ - mMessageFramer.setListener( getMessageProcessor() ); - } - - @Override - public void dispose() - { - super.dispose(); - - mSymbolFilter.dispose(); - mSymbolFilter = null; - - mMessageFramer.dispose(); - mMessageFramer = null; - - mC4FMSlicer.dispose(); - mC4FMSlicer = null; - } - - public Modulation getModulation() - { - return Modulation.C4FM; - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mSymbolFilter; - } - - /** - * Instrumentation taps for monitoring internal processing - */ - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList<>(); - - TapGroup group = new TapGroup( "P25 C4FM Decoder" ); - - group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); - group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); - - mAvailableTaps.add( group ); - - if( mSymbolFilter != null ) - { - mAvailableTaps.addAll( mSymbolFilter.getTapGroups() ); - } - } - - return mAvailableTaps; - } - - /** - * Adds an instrumentation tap to monitor internal processing. - */ - @Override - public void registerTap( Tap tap ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.registerTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: - FloatTap symbolTap = (FloatTap)tap; - mSymbolFilter.setListener( symbolTap ); - symbolTap.setListener( mC4FMSlicer ); - break; - case INSTRUMENT_C4FM_SLICER_OUTPUT: - DibitTap slicerTap = (DibitTap)tap; - - if( mC4FMSlicer != null ) - { - mC4FMSlicer.addListener( slicerTap ); - } - break; - default: - break; - } - } - - /** - * Removes the instrumentation tap. - */ - @Override - public void unregisterTap( Tap tap ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.unregisterTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: - mSymbolFilter.setListener( mC4FMSlicer ); - break; - case INSTRUMENT_C4FM_SLICER_OUTPUT: - DibitTap slicerTap = (DibitTap)tap; - - if( mC4FMSlicer != null ) - { - mC4FMSlicer.removeListener( slicerTap ); - } - break; - default: - throw new IllegalArgumentException( "Unrecognized tap: " + - tap.getName() ); - } - } - - @Override - public void setSourceEventListener(Listener listener ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.setFrequencyChangeListener( listener ); - } - } - - @Override - public void removeSourceEventListener() - { - if( mSymbolFilter != null ) - { - mSymbolFilter.setFrequencyChangeListener( null ); - } - } - - @Override - public Listener getSourceEventListener() - { - if( mSymbolFilter != null ) - { - return mSymbolFilter.getSourceEventListener(); - } - - return null; - } -} diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java deleted file mode 100644 index d461cfd0..00000000 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ /dev/null @@ -1,240 +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 module.decode.p25; - -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.ComplexSampleTap; -import instrument.tap.stream.ComplexTap; -import instrument.tap.stream.DibitTap; -import instrument.tap.stream.QPSKTap; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import alias.AliasList; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.ComplexBufferToStreamConverter; -import sample.complex.IComplexBufferListener; -import dsp.filter.FilterFactory; -import dsp.filter.Window.WindowType; -import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; -import dsp.gain.ComplexFeedForwardGainControl; -import dsp.psk.LSMDemodulator; -import dsp.psk.QPSKPolarSlicer; -import source.SourceEvent; - -public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener -{ - private final static Logger mLog = LoggerFactory.getLogger( P25_LSMDecoder.class ); - - /* Instrumentation Taps */ - private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; - private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; - private static final String INSTRUMENT_LSM_DEMODULATOR_OUTPUT = "Tap Point: LSM Demodulator Output"; - private static final String INSTRUMENT_QPSK_SLICER_OUTPUT = "Tap Point: QPSK Slicer Output"; - - private List mAvailableTaps; - - private ComplexFIRFilter_CB_CB mBasebandFilter; - private ComplexBufferToStreamConverter mStreamConverter = new ComplexBufferToStreamConverter(); - private ComplexFeedForwardGainControl mAGC = - new ComplexFeedForwardGainControl( 32 ); - private LSMDemodulator mLSMDemodulator = new LSMDemodulator(); - private QPSKPolarSlicer mQPSKSlicer = new QPSKPolarSlicer(); - private P25MessageFramer mMessageFramer; - - public P25_LSMDecoder( AliasList aliasList ) - { - super( aliasList ); - - mBasebandFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( - 48000, 7250, 8000, 60, WindowType.HANN, true ), 1.0f ); - - mBasebandFilter.setListener( mStreamConverter ); - - mStreamConverter.setListener( mAGC ); - - mAGC.setListener( mLSMDemodulator ); - - mLSMDemodulator.setSymbolListener( mQPSKSlicer ); - - mMessageFramer = new P25MessageFramer( aliasList, mLSMDemodulator ); - mQPSKSlicer.addListener( mMessageFramer ); - - mMessageFramer.setListener( getMessageProcessor() ); - } - - public void dispose() - { - super.dispose(); - - mBasebandFilter.dispose(); - mBasebandFilter = null; - - mStreamConverter.dispose(); - mStreamConverter = null; - - mAGC.dispose(); - mAGC = null; - - mLSMDemodulator.dispose(); - mLSMDemodulator = null; - - mQPSKSlicer.dispose(); - mQPSKSlicer = null; - - mMessageFramer.dispose(); - mMessageFramer = null; - } - - @Override - public void setSourceEventListener(Listener listener) - { - } - - @Override - public void removeSourceEventListener() - { - } - - @Override - public Listener getSourceEventListener() - { - return new Listener() - { - @Override - public void receive(SourceEvent sourceEvent) - { - //Ignored - } - }; - } - - @Override - public Listener getComplexBufferListener() - { - return mBasebandFilter; - } - - public Modulation getModulation() - { - return Modulation.CQPSK; - } - - /** - * Provides a list of instrumentation taps for monitoring internal processing - */ - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList<>(); - - TapGroup group = new TapGroup( "P25 LSM Decoder" ); - - group.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); - group.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); - group.add( new QPSKTap( INSTRUMENT_LSM_DEMODULATOR_OUTPUT, 0, 1.0f ) ); - group.add( new DibitTap( INSTRUMENT_QPSK_SLICER_OUTPUT, 0, 0.1f ) ); - - mAvailableTaps.add( group ); - - if( mLSMDemodulator != null ) - { - mAvailableTaps.addAll( mLSMDemodulator.getTapGroups() ); - } - } - - return mAvailableTaps; - } - - /** - * Adds the instrumentation tap - */ - @Override - public void registerTap( Tap tap ) - { - if( mLSMDemodulator != null ) - { - mLSMDemodulator.registerTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_BASEBAND_FILTER_OUTPUT: - ComplexSampleTap baseband = (ComplexSampleTap)tap; - mStreamConverter.setListener( baseband ); - baseband.setListener( mAGC ); - break; - case INSTRUMENT_AGC_OUTPUT: - ComplexSampleTap agcSymbol = (ComplexSampleTap)tap; - mAGC.setListener( agcSymbol ); - agcSymbol.setListener( mLSMDemodulator ); - break; - case INSTRUMENT_LSM_DEMODULATOR_OUTPUT: - QPSKTap qpsk = (QPSKTap)tap; - mLSMDemodulator.setSymbolListener( qpsk ); - qpsk.setListener( mQPSKSlicer ); - break; - case INSTRUMENT_QPSK_SLICER_OUTPUT: - mQPSKSlicer.addListener( (DibitTap)tap ); - break; - default: - throw new IllegalArgumentException( "Unrecognized tap: " + - tap.getName() ); - } - } - - /** - * Removes the instrumentation tap - */ - @Override - public void unregisterTap( Tap tap ) - { - if( mLSMDemodulator != null ) - { - mLSMDemodulator.unregisterTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_BASEBAND_FILTER_OUTPUT: - mStreamConverter.setListener( mAGC ); - break; - case INSTRUMENT_AGC_OUTPUT: - mAGC.setListener( mLSMDemodulator ); - break; - case INSTRUMENT_LSM_DEMODULATOR_OUTPUT: - mLSMDemodulator.setSymbolListener( mQPSKSlicer ); - break; - case INSTRUMENT_QPSK_SLICER_OUTPUT: - mQPSKSlicer.removeListener( (DibitTap)tap ); - break; - default: - throw new IllegalArgumentException( "Unrecognized tap: " + - tap.getName() ); - } - } -} diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java deleted file mode 100644 index c49dd2f4..00000000 --- a/src/module/decode/p25/audio/P25AudioModule.java +++ /dev/null @@ -1,269 +0,0 @@ -package module.decode.p25.audio; - -import audio.AudioFormats; -import audio.AudioPacket; -import audio.IAudioPacketProvider; -import audio.squelch.ISquelchStateListener; -import audio.squelch.SquelchState; -import channel.metadata.Metadata; -import dsp.gain.NonClippingGain; -import jmbe.iface.AudioConversionLibrary; -import jmbe.iface.AudioConverter; -import message.IMessageListener; -import message.Message; -import module.Module; -import module.decode.p25.message.hdu.HDUMessage; -import module.decode.p25.message.ldu.LDU1Message; -import module.decode.p25.message.ldu.LDU2Message; -import module.decode.p25.message.ldu.LDUMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; - -import java.util.concurrent.ScheduledExecutorService; - -public class P25AudioModule extends Module implements Listener, IAudioPacketProvider, IMessageListener, - ISquelchStateListener -{ - private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class); - - private static final String IMBE_CODEC = "IMBE"; - private static boolean mLibraryLoadStatusLogged = false; - - private boolean mCanConvertAudio = false; - private boolean mEncryptedCall = false; - private boolean mEncryptedCallStateEstablished = false; - - private AudioConverter mAudioConverter; - private Listener mAudioPacketListener; - private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); - private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f); - private Metadata mMetadata; - private LDU1Message mCachedLDU1Message = null; - - public P25AudioModule(Metadata metadata) - { - mMetadata = metadata; - loadConverter(); - } - - @Override - public Listener getMessageListener() - { - return this; - } - - @Override - public Listener getSquelchStateListener() - { - return mSquelchStateListener; - } - - @Override - public void dispose() - { - mAudioConverter = null; - } - - @Override - public void reset() - { - } - - @Override - public void start() - { - - } - - @Override - public void stop() - { - - } - - /** - * Processes call header (HDU) and voice frame (LDU1/LDU2) messages to decode audio and to determine the - * encrypted audio status of a call event. Only the HDU and LDU2 messages convey encrypted call status. If an - * LDU1 message is received without a preceding HDU message, then the LDU1 message is cached until the first - * LDU2 message is received and the encryption state can be determined. Both the LDU1 and the LDU2 message are - * then processed for audio if the call is unencrypted. - */ - public void receive(Message message) - { - if(mCanConvertAudio && mAudioPacketListener != null) - { - if(mEncryptedCallStateEstablished) - { - if(message instanceof LDUMessage) - { - processAudio((LDUMessage)message); - } - } - else - { - if(message instanceof HDUMessage) - { - mEncryptedCallStateEstablished = true; - mEncryptedCall = ((HDUMessage)message).isEncryptedAudio(); - } - else if(message instanceof LDU1Message) - { - //When we receive an LDU1 message with first receiving the HDU message, cache the LDU1 Message - //until we can determine the encrypted call state from the next LDU2 message - mCachedLDU1Message = (LDU1Message)message; - } - else if(message instanceof LDU2Message) - { - mEncryptedCallStateEstablished = true; - LDU2Message ldu2 = (LDU2Message)message; - mEncryptedCall = ldu2.isEncryptedAudio(); - - if(mCachedLDU1Message != null) - { - processAudio(mCachedLDU1Message); - mCachedLDU1Message = null; - } - - processAudio(ldu2); - } - } - } - } - - /** - * Processes an audio packet by decoding the IMBE audio frames and rebroadcasting them as PCM audio packets. - */ - private void processAudio(LDUMessage ldu) - { - if(!mEncryptedCall) - { - for(byte[] frame : ldu.getIMBEFrames()) - { - float[] audio = mAudioConverter.decode(frame); - - audio = mGain.apply(audio); - - mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf())); - } - } - else - { - //Encrypted audio processing not implemented - } - } - - /** - * Loads audio frame processing chain. Constructs an imbe targetdataline - * to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion - * stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper. - */ - private void loadConverter() - { - AudioConversionLibrary library = null; - - try - { - @SuppressWarnings("rawtypes") - Class temp = Class.forName("jmbe.JMBEAudioLibrary"); - - library = (AudioConversionLibrary)temp.newInstance(); - - if((library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 && - library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1) - { - mAudioConverter = library.getAudioConverter(IMBE_CODEC, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO); - - if(mAudioConverter != null) - { - mCanConvertAudio = true; - - if(!mLibraryLoadStatusLogged) - { - StringBuilder sb = new StringBuilder(); - sb.append("JMBE audio conversion library ["); - sb.append(library.getVersion()); - sb.append("] successfully loaded - P25 audio will be available"); - - mLog.info(sb.toString()); - - mLibraryLoadStatusLogged = true; - } - } - else - { - if(!mLibraryLoadStatusLogged) - { - mLog.info("JMBE audio conversion library NOT FOUND"); - mLibraryLoadStatusLogged = true; - } - } - } - else - { - mLog.warn("JMBE library version 0.3.3 or higher is required - found: " + library.getVersion()); - } - } - catch(ClassNotFoundException e1) - { - if(!mLibraryLoadStatusLogged) - { - mLog.error("Couldn't find/load JMBE audio conversion library"); - mLibraryLoadStatusLogged = true; - } - } - catch(InstantiationException e1) - { - if(!mLibraryLoadStatusLogged) - { - mLog.error("Couldn't instantiate JMBE audio conversion library class"); - mLibraryLoadStatusLogged = true; - } - } - catch(IllegalAccessException e1) - { - if(!mLibraryLoadStatusLogged) - { - mLog.error("Couldn't load JMBE audio conversion library due to " - + "security restrictions"); - mLibraryLoadStatusLogged = true; - } - } - } - - @Override - public void setAudioPacketListener(Listener listener) - { - mAudioPacketListener = listener; - } - - @Override - public void removeAudioPacketListener() - { - mAudioPacketListener = null; - } - - /** - * Wrapper for squelch state to process end of call actions. At call end the encrypted call state established - * flag is reset so that the encrypted audio state for the next call can be properly detected and we send an - * END audio packet so that downstream processors like the audio recorder can properly close out a call sequence. - */ - public class SquelchStateListener implements Listener - { - @Override - public void receive(SquelchState state) - { - if(state == SquelchState.SQUELCH) - { - if(mAudioPacketListener != null) - { - mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); - } - - mEncryptedCallStateEstablished = false; - mEncryptedCall = false; - mCachedLDU1Message = null; - } - } - } -} diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java deleted file mode 100644 index 1c298888..00000000 --- a/src/module/decode/passport/PassportDecoder.java +++ /dev/null @@ -1,112 +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 module.decode.passport; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import module.decode.Decoder; -import module.decode.DecoderType; -import module.decode.config.DecodeConfiguration; -import sample.Listener; -import sample.real.IUnFilteredRealBufferListener; -import sample.real.RealBuffer; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.fsk.LTRFSKDecoder; - -public class PassportDecoder extends Decoder - implements IUnFilteredRealBufferListener, Instrumentable -{ - public static final int PASSPORT_MESSAGE_LENGTH = 68; - public static final int PASSPORT_SYNC_LENGTH = 9; - - private LTRFSKDecoder mPassportFSKDecoder; - private MessageFramer mPassportMessageFramer; - private PassportMessageProcessor mPassportMessageProcessor; - - private List mAvailableTaps; - - /** - * Passport Decoder. Decodes unfiltered (e.g. demodulated but with no DC or - * audio filtering) samples and produces Passport messages. - */ - public PassportDecoder( DecodeConfiguration config, AliasList aliasList ) - { - mPassportFSKDecoder = new LTRFSKDecoder(); - - mPassportMessageFramer = - new MessageFramer( SyncPattern.PASSPORT.getPattern(), - PASSPORT_MESSAGE_LENGTH ); - - mPassportFSKDecoder.addListener( mPassportMessageFramer ); - - mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); - mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); - mPassportMessageProcessor.setMessageListener( this ); - } - - @Override - public Listener getUnFilteredRealBufferListener() - { - return mPassportFSKDecoder; - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.PASSPORT; - } - - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList<>(); - - mAvailableTaps.addAll( mPassportFSKDecoder.getTapGroups() ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - mPassportFSKDecoder.registerTap( tap ); - } - - @Override - public void unregisterTap( Tap tap ) - { - mPassportFSKDecoder.unregisterTap( tap ); - } - - @Override - public void reset() - { - mPassportMessageFramer.reset(); - } -} diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java deleted file mode 100644 index c98ef2c6..00000000 --- a/src/module/decode/passport/PassportDecoderState.java +++ /dev/null @@ -1,574 +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 module.decode.passport; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import module.decode.event.CallEvent; -import module.decode.event.CallEvent.CallEventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -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 -{ - private final static Logger mLog = - LoggerFactory.getLogger(PassportDecoderState.class); - - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mMobileIDs = new TreeSet(); - private HashMap mSiteLCNs = new HashMap(); - private HashMap mNeighborLCNs = new HashMap(); - - private AliasedStringAttributeMonitor mFromMobileIDAttribute; - private AliasedStringAttributeMonitor mToTalkgroupAttribute; - private int mChannelNumber; - private int mSiteNumber; - private PassportBand mSiteBand; - private HashMap mActiveCalls = new HashMap(); - private long mFrequency; - - public PassportDecoderState(AliasList aliasList) - { - super(aliasList); - - mFromMobileIDAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MIN); - mToTalkgroupAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.PASSPORT; - } - - private void logTalkgroup(String talkgroup) - { - if(mTalkgroupsFirstHeard.contains(talkgroup)) - { - mTalkgroups.add(talkgroup); - } - else - { - mTalkgroupsFirstHeard.add(talkgroup); - } - } - - private PassportCallEvent getCurrentCallEvent() - { - return (PassportCallEvent) mCurrentCallEvent; - } - - /** - * Indicates if the talkgroup is different than the talkgroup specified in - * the current call event - */ - private boolean isDifferentTalkgroup(String talkgroup) - { - return talkgroup != null && - mCurrentCallEvent != null && - mCurrentCallEvent.getToID() != null && - !mCurrentCallEvent.getToID().contentEquals(talkgroup); - } - - @Override - public void receive(Message message) - { - if(message instanceof PassportMessage) - { - PassportMessage passport = (PassportMessage) message; - - if(passport.isValid()) - { - switch(passport.getMessageType()) - { - case CA_STRT: - mSiteLCNs.put(passport.getLCN(), - passport.getLCNFrequencyFormatted()); - - String talkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(talkgroup); - - if(mChannelNumber == 0) - { - setChannelNumber(passport.getLCN()); - } - - /* Call on this channel */ - if(passport.getLCN() == mChannelNumber) - { - mToTalkgroupAttribute.process(talkgroup); - - PassportCallEvent current = getCurrentCallEvent(); - - /* If we're already in a call event, add the message - * to the current call event ... if false, then we - * have a different call ... cleanup the old one. */ - if(current != null && isDifferentTalkgroup(talkgroup)) - { - mCurrentCallEvent.end(); - mCurrentCallEvent = null; - } - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new PassportCallEvent - .Builder(CallEventType.CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(passport.getLCNFrequency()) - .to(String.valueOf(passport.getTalkgroupID())) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, - Event.START, State.CALL)); - } - else - { - broadcast(new DecoderStateEvent(this, - Event.CONTINUATION, State.CALL)); - } - } - else - { - //Call Detection - int lcn = passport.getLCN(); - String tg = String.valueOf(passport.getTalkgroupID()); - - if(!mActiveCalls.containsKey(lcn) || - !mActiveCalls.get(lcn).contentEquals(tg)) - { - mActiveCalls.put(lcn, tg); - - broadcast(new PassportCallEvent - .Builder(CallEventType.CALL_DETECT) - .aliasList(getAliasList()) - .channel(String.valueOf(lcn)) - .details("Site: " + passport.getSite()) - .frequency(passport.getLCNFrequency()) - .to(tg) - .build()); - } - } - break; - case CA_ENDD: - String endTalkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(endTalkgroup); - mToTalkgroupAttribute.process(endTalkgroup); - - broadcast(new DecoderStateEvent(this, - Event.END, State.CALL)); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - mCurrentCallEvent = null; - - broadcast(new DecoderStateEvent(this, Event.RESET, State.FADE)); - } - break; - case DA_STRT: - mSiteLCNs.put(passport.getLCN(), - passport.getLCNFrequencyFormatted()); - - String dataTalkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(dataTalkgroup); - - if(mChannelNumber == 0) - { - setChannelNumber(passport.getLCN()); - } - - /* Data call on this channel */ - if(passport.getLCN() == mChannelNumber) - { - mToTalkgroupAttribute.process(dataTalkgroup); - - PassportCallEvent current = getCurrentCallEvent(); - - /* If we're already in a call event, add the message - * to the current call event ... if false, then we - * have a different call ... cleanup the old one. */ - if(current != null && isDifferentTalkgroup(dataTalkgroup)) - { - mCurrentCallEvent.end(); - mCurrentCallEvent = null; - } - - if(mCurrentCallEvent == null) - { - mCurrentCallEvent = new PassportCallEvent - .Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(mChannelNumber)) - .frequency(passport.getLCNFrequency()) - .to(String.valueOf(passport.getTalkgroupID())) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, - Event.START, State.DATA)); - } - else - { - broadcast(new DecoderStateEvent(this, - Event.CONTINUATION, State.DATA)); - } - } - else - { - //Call Detection - int lcn = passport.getLCN(); - String tg = String.valueOf(passport.getTalkgroupID()); - - if(!mActiveCalls.containsKey(lcn) || - !mActiveCalls.get(lcn).contentEquals(tg)) - { - mActiveCalls.put(lcn, tg); - - broadcast(new PassportCallEvent - .Builder(CallEventType.DATA_CALL) - .aliasList(getAliasList()) - .channel(String.valueOf(lcn)) - .details("Site: " + passport.getSite()) - .frequency(passport.getLCNFrequency()) - .to(tg) - .build()); - } - } - break; - case DA_ENDD: - String dataEndTalkgroup = - String.valueOf(passport.getTalkgroupID()); - - logTalkgroup(dataEndTalkgroup); - mToTalkgroupAttribute.process(dataEndTalkgroup); - - broadcast(new DecoderStateEvent(this, - Event.END, State.DATA)); - - if(mCurrentCallEvent != null) - { - mCurrentCallEvent.end(); - broadcast(mCurrentCallEvent); - mCurrentCallEvent = null; - - broadcast(new DecoderStateEvent(this, Event.RESET, State.FADE)); - } - break; - case ID_RDIO: - String min = passport.getMobileID(); - - if(min != null) - { - mMobileIDs.add(min); - } - - mFromMobileIDAttribute.process(min); - - final CallEvent current = getCurrentCallEvent(); - - if(current != null) - { - current.setFromID(min); - broadcast(current); - - } - break; - case RA_REGI: - if(mCurrentCallEvent == null || - mCurrentCallEvent.getCallEventType() != CallEventType.REGISTER) - { - mCurrentCallEvent = new PassportCallEvent - .Builder(CallEventType.REGISTER) - .aliasList(getAliasList()) - .channel(String.valueOf(passport.getLCN())) - .frequency(passport.getLCNFrequency()) - .to(passport.getToID()) - .build(); - - broadcast(mCurrentCallEvent); - - broadcast(new DecoderStateEvent(this, - Event.START, State.DATA)); - } - else - { - broadcast(new DecoderStateEvent(this, - Event.CONTINUATION, State.DATA)); - } - break; - case SY_IDLE: - if(passport.getFree() != 0) - { - mNeighborLCNs.put(passport.getFree(), - passport.getFreeFrequencyFormatted()); - } - setSiteNumber(passport.getSite()); - - PassportBand band = passport.getSiteBand(); - setSiteBand(band); - setChannelNumber(getSiteBand().getChannel(mFrequency)); - break; - default: - break; - } - } - } - } - - public static String formatTalkgroup(String talkgroup) - { - StringBuilder sb = new StringBuilder(); - - if(talkgroup.length() == 6) - { - sb.append(talkgroup.substring(0, 1)); - sb.append("-"); - sb.append(talkgroup.substring(1, 3)); - sb.append("-"); - sb.append(talkgroup.substring(3, 6)); - - return sb.toString(); - } - else - { - return talkgroup; - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("Activity Summary\n"); - sb.append("Decoder:\tPassport\n\n"); - sb.append("Site Channels\n"); - - if(mSiteLCNs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - ArrayList channels = new ArrayList<>(mSiteLCNs.keySet()); - Collections.sort(channels); - - for(Integer channel : channels) - { - sb.append(" " + channel); - sb.append("\t" + mSiteLCNs.get(channel)); - sb.append("\n"); - } - } - - sb.append("\nNeighbor Channels\n"); - - if(mNeighborLCNs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - ArrayList channels = new ArrayList<>(mNeighborLCNs.keySet()); - Collections.sort(channels); - - for(Integer channel : channels) - { - sb.append(" " + channel); - sb.append("\t" + mNeighborLCNs.get(channel)); - sb.append("\n"); - } - } - - sb.append("\nTalkgroups\n"); - - if(mTalkgroups.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while(it.hasNext()) - { - String tgid = it.next(); - - sb.append(" "); - sb.append(tgid); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(tgid); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - sb.append("\nMobile ID Numbers\n"); - - if(mMobileIDs.isEmpty()) - { - sb.append(" None\n"); - } - else - { - Iterator it = mMobileIDs.iterator(); - - while(it.hasNext()) - { - String min = it.next(); - - sb.append(" "); - sb.append(min); - sb.append(" "); - - if(hasAliasList()) - { - Alias alias = getAliasList().getMobileIDNumberAlias(min); - - if(alias != null) - { - sb.append(alias.getName()); - } - } - - sb.append("\n"); - } - } - - return sb.toString(); - } - - public void reset() - { - mTalkgroupsFirstHeard.clear(); - mTalkgroups.clear(); - mMobileIDs.clear(); - mSiteLCNs.clear(); - mNeighborLCNs.clear(); - - resetState(); - } - - private void resetState() - { - mToTalkgroupAttribute.reset(); - mFromMobileIDAttribute.reset(); - } - - public int getChannelNumber() - { - return mChannelNumber; - } - - private void setChannelNumber(int channel) - { - if(mChannelNumber != channel) - { - mChannelNumber = channel; - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY_LABEL, "CHAN:" + mChannelNumber)); - } - } - - public int getSiteNumber() - { - return mSiteNumber; - } - - private void setSiteNumber(int site) - { - if(mSiteNumber != site) - { - mSiteNumber = site; - Alias alias = hasAliasList() ? getAliasList().getSiteID(String.valueOf(getSiteNumber())) : null; - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_1, "SITE:" + mSiteNumber, alias)); - } - } - - private PassportBand getSiteBand() - { - return mSiteBand; - } - - private void setSiteBand(PassportBand band) - { - if(mSiteBand == null) - { - mSiteBand = band; - broadcast(new AttributeChangeRequest(Attribute.NETWORK_ID_2, "BAND:" + band.getDescription())); - } - } - - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - broadcast(new AttributeChangeRequest(Attribute.CHANNEL_FREQUENCY, mFrequency)); - break; - default: - break; - } - } -} diff --git a/src/module/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java deleted file mode 100644 index 917a2192..00000000 --- a/src/module/decode/tait/Tait1200Decoder.java +++ /dev/null @@ -1,193 +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 module.decode.tait; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatBufferTap; -import instrument.tap.stream.FloatTap; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; - -import module.decode.Decoder; -import module.decode.DecoderType; -import sample.Broadcaster; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; -import alias.AliasList; -import bits.MessageFramer; -import bits.SyncPattern; -import dsp.filter.Filters; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.fsk.FSK2Decoder; -import dsp.fsk.FSK2Decoder.Output; - -/** - * TAIT 1200 - 1200 baud 2FSK decoder - */ -public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListener, - Instrumentable -{ - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ - private static final int DECIMATED_SAMPLE_RATE = 24000; - - /* Baud or Symbol Rate */ - private static final int SYMBOL_RATE = 1200; - - /* Message length ... */ - private static final int MESSAGE_LENGTH = 440; - - /* Instrumentation Taps */ - private ArrayList mAvailableTaps; - private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = - "Tap Point: Bandpass Filter > < FSK2 Decoder"; - private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = - "Tap Point: FSK2 Decoder > < Message Framer"; - - private FSK2Decoder mFSKDecoder; - - private HalfBandFilter_RB_RB mDecimationFilter; - private RealFIRFilter_RB_RB mBandPassFilter; - private MessageFramer mMessageFramerGPS; - private MessageFramer mMessageFramerANI; - private Broadcaster mFSKBroadcaster = new Broadcaster(); - private Tait1200GPSMessageProcessor mMessageAProcessor; - private Tait1200ANIMessageProcessor mMessageBProcessor; - - public Tait1200Decoder( AliasList aliasList ) - { - mDecimationFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true ); - - mBandPassFilter = new RealFIRFilter_RB_RB( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); - mDecimationFilter.setListener( mBandPassFilter ); - - mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE, - SYMBOL_RATE, Output.INVERTED ); - mBandPassFilter.setListener( mFSKDecoder ); - - - mFSKDecoder.setListener( mFSKBroadcaster ); - - mMessageFramerGPS = new MessageFramer( - SyncPattern.TAIT_CCDI_GPS_MESSAGE.getPattern(), MESSAGE_LENGTH ); - mMessageFramerANI = new MessageFramer( - SyncPattern.TAIT_SELCAL_MESSAGE.getPattern(), MESSAGE_LENGTH ); - - mFSKBroadcaster.addListener( mMessageFramerGPS ); - mFSKBroadcaster.addListener( mMessageFramerANI ); - - mMessageAProcessor = new Tait1200GPSMessageProcessor( aliasList ); - mMessageBProcessor = new Tait1200ANIMessageProcessor( aliasList ); - - mMessageFramerGPS.addMessageListener( mMessageAProcessor ); - mMessageFramerANI.addMessageListener( mMessageBProcessor ); - - mMessageAProcessor.setMessageListener( this ); - mMessageBProcessor.setMessageListener( this ); - } - - public void dispose() - { - super.dispose(); - - mDecimationFilter.dispose(); - mBandPassFilter.dispose(); - mFSKDecoder.dispose(); - mMessageFramerGPS.dispose(); - mMessageFramerANI.dispose(); - mMessageAProcessor.dispose(); - mMessageBProcessor.dispose(); - } - - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList<>(); - - TapGroup group = new TapGroup( "Tait 1200 Decoder" ); - - group.add( new FloatTap( - INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); - group.add( new BinaryTap( - INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER, 0, 0.025f ) ); - - mAvailableTaps.add( group ); - - mAvailableTaps.addAll( mFSKDecoder.getTapGroups() ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - mFSKDecoder.registerTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatBufferTap bpTap = (FloatBufferTap)tap; - mBandPassFilter.setListener( bpTap ); - bpTap.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER: - BinaryTap decoderTap = (BinaryTap)tap; - mFSKBroadcaster.addListener( decoderTap ); - break; - } - } - - @Override - public void unregisterTap( Tap tap ) - { - mFSKDecoder.unregisterTap( tap ); - - switch( tap.getName() ) - { - case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - mBandPassFilter.setListener( mFSKDecoder ); - break; - case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER: - mFSKBroadcaster.removeListener( (BinaryTap)tap ); - break; - } - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mDecimationFilter; - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.TAIT_1200; - } -} diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java deleted file mode 100644 index d8750b52..00000000 --- a/src/module/decode/tait/Tait1200DecoderState.java +++ /dev/null @@ -1,212 +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 module.decode.tait; - -import alias.Alias; -import alias.AliasList; -import alias.id.AliasIDType; -import channel.metadata.AliasedStringAttributeMonitor; -import channel.metadata.Attribute; -import channel.metadata.AttributeChangeRequest; -import channel.state.DecoderState; -import channel.state.DecoderStateEvent; -import channel.state.DecoderStateEvent.Event; -import channel.state.State; -import message.Message; -import module.decode.DecoderType; -import module.decode.event.CallEvent.CallEventType; -import org.jdesktop.swingx.mapviewer.GeoPosition; -import util.StringUtils; - -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 -{ - private TreeSet mIdents = new TreeSet(); - - private AliasedStringAttributeMonitor mFromAttribute; - private AliasedStringAttributeMonitor mToAttribute; - private String mMessage; - private String mMessageType; - - public Tait1200DecoderState(AliasList aliasList) - { - super(aliasList); - - mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - mToAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, - getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.TAIT_1200; - } - - @Override - public void reset() - { - mIdents.clear(); - - resetState(); - } - - private void resetState() - { - mFromAttribute.reset(); - mToAttribute.reset(); - mMessage = null; - mMessageType = null; - } - - @Override - public void receive(Message message) - { - if(message instanceof Tait1200GPSMessage) - { - Tait1200GPSMessage gps = (Tait1200GPSMessage) message; - - mFromAttribute.process(gps.getFromID()); - mIdents.add(gps.getFromID()); - - mToAttribute.process(gps.getToID()); - mIdents.add(gps.getToID()); - - GeoPosition position = gps.getGPSLocation(); - - if(position != null) - { - setMessage(gps.getGPSLocation().toString().replace("[", "") - .replace("]", "")); - } - - setMessageType("GPS"); - - broadcast(new Tait1200CallEvent(CallEventType.GPS, getAliasList(), - gps.getFromID(), gps.getToID(), gps.getGPSLocation().toString())); - - broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); - } - else if(message instanceof Tait1200ANIMessage) - { - Tait1200ANIMessage ani = (Tait1200ANIMessage) message; - mFromAttribute.process(ani.getFromID()); - mIdents.add(ani.getFromID()); - - mToAttribute.process(ani.getToID()); - mIdents.add(ani.getToID()); - - setMessage(null); - setMessageType("ANI"); - - broadcast(new Tait1200CallEvent(CallEventType.ID_ANI, getAliasList(), - ani.getFromID(), ani.getToID(), "ANI")); - - broadcast(new DecoderStateEvent(this, Event.DECODE, State.CALL)); - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append("=============================\n"); - sb.append("Decoder:\tTait-1200I\n\n"); - - if(!mIdents.isEmpty()) - { - sb.append("Radio Identifiers:\n"); - - List idents = new ArrayList(mIdents); - - Collections.sort(idents); - - for(String ident : idents) - { - sb.append("\t"); - sb.append(ident); - - if(hasAliasList()) - { - Alias alias = getAliasList().getTalkgroupAlias(ident); - - if(alias != null) - { - sb.append("\t"); - sb.append(alias.getName()); - } - } - sb.append("\n"); - } - } - - return sb.toString(); - } - - /** - * Responds to reset events issued by the channel state - */ - @Override - public void receiveDecoderStateEvent(DecoderStateEvent event) - { - switch(event.getEvent()) - { - case RESET: - resetState(); - break; - default: - break; - } - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage(String message) - { - if(!StringUtils.isEqual(mMessage, message)) - { - mMessage = message; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE, getMessage())); - } - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType(String type) - { - if(!StringUtils.isEqual(mMessageType, type)) - { - mMessageType = type; - broadcast(new AttributeChangeRequest(Attribute.MESSAGE_TYPE, getMessageType())); - } - } -} diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java deleted file mode 100644 index 9c9f1ace..00000000 --- a/src/module/demodulate/am/AMDemodulatorModule.java +++ /dev/null @@ -1,110 +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 module.demodulate.am; - -import dsp.am.AMDemodulator_CB; -import dsp.filter.FilterFactory; -import dsp.filter.Window.WindowType; -import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import dsp.gain.AutomaticGainControl_RB; -import module.Module; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferListener; -import sample.real.IFilteredRealBufferProvider; -import sample.real.RealBuffer; - -public class AMDemodulatorModule extends Module - 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 AutomaticGainControl_RB mAGC; - - /** - * AM Demodulator Module - */ - 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); - - mLowPassFilter = new RealFIRFilter_RB_RB( - FilterFactory.getLowPass(48000, 3000, 31, WindowType.COSINE), 1.0f); - mDemodulator.setListener(mLowPassFilter); - - mAGC = new AutomaticGainControl_RB(); - mLowPassFilter.setListener(mAGC); - } - - @Override - public Listener getComplexBufferListener() - { - return mIQFilter; - } - - @Override - public void dispose() - { - mIQFilter.dispose(); - mIQFilter = null; - - mDemodulator.dispose(); - mDemodulator = null; - - mLowPassFilter.dispose(); - mLowPassFilter = null; - - 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/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java deleted file mode 100644 index fa73f27d..00000000 --- a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java +++ /dev/null @@ -1,121 +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 module.demodulate.audio; - -import dsp.filter.FilterFactory; -import dsp.filter.Window.WindowType; -import dsp.filter.dc.DCRemovalFilter_RealBuffer; -import dsp.filter.fir.real.RealFIRFilter_RB_RB; -import module.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.real.IFilteredRealBufferProvider; -import sample.real.IUnFilteredRealBufferListener; -import sample.real.RealBuffer; - -import java.util.concurrent.ScheduledExecutorService; - -public class DemodulatedAudioFilterModule extends Module implements IUnFilteredRealBufferListener, IFilteredRealBufferProvider -{ - private final static Logger mLog = LoggerFactory.getLogger(DemodulatedAudioFilterModule.class); - - private DCRemovalFilter_RealBuffer mDCFilter; - private RealFIRFilter_RB_RB mBandPassFilter; - - /** - * Filters demodulated audio and removes any DC component from the buffer - * - * @param pass - audio pass band pass frequency - * @param stop - audio pass band stop frequency - */ - public DemodulatedAudioFilterModule(int pass, int stop) - { - assert (stop > pass); - - mDCFilter = new DCRemovalFilter_RealBuffer(); - - //TODO: change this to a band pass filter - mBandPassFilter = new RealFIRFilter_RB_RB(FilterFactory.getLowPass(48000, pass, stop, 60, - WindowType.HANN, true), 1.0f); - - mDCFilter.setListener(mBandPassFilter); - } - - /** - * Constructs a demodulated audio filter using the filter taps and gain provided - */ - public DemodulatedAudioFilterModule(float[] taps, float gain) - { - mBandPassFilter = new RealFIRFilter_RB_RB(taps, gain); - } - - @Override - public void dispose() - { - mBandPassFilter.dispose(); - mBandPassFilter = null; - - if(mDCFilter != null) - { - mDCFilter.dispose(); - mDCFilter = null; - } - } - - @Override - public void reset() - { - } - - @Override - public void setFilteredRealBufferListener(Listener listener) - { - mBandPassFilter.setListener(listener); - } - - @Override - public void removeFilteredRealBufferListener() - { - mBandPassFilter.removeListener(); - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } - - @Override - public Listener getUnFilteredRealBufferListener() - { - if(mDCFilter != null) - { - return mDCFilter; - } - else - { - return mBandPassFilter; - } - } -} diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java deleted file mode 100644 index d3278e68..00000000 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 module.demodulate.fm; - -import dsp.filter.FilterFactory; -import dsp.filter.Window.WindowType; -import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; -import dsp.fm.FMDemodulator_CB; -import module.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferListener; -import sample.real.IUnFilteredRealBufferProvider; -import sample.real.RealBuffer; - -import java.util.concurrent.ScheduledExecutorService; - -public class FMDemodulatorModule extends Module implements IComplexBufferListener, IUnFilteredRealBufferProvider -{ - private final static Logger mLog = LoggerFactory.getLogger(FMDemodulatorModule.class); - - private static final int SAMPLE_RATE = 48000; - - private ComplexFIRFilter_CB_CB mIQFilter; - private FMDemodulator_CB mDemodulator; - - /** - * FM Demodulator with I/Q filter. Demodulated output is unfiltered and - * may contain a DC component. - * - * @param pass - pass frequency for IQ filtering prior to demodulation. This - * frequency should be half of the signal bandwidth since the filter will - * be applied against each of the inphase and quadrature signals and the - * combined pass bandwidth will be twice this value. - * @param stop - stop frequency for IQ filtering prior to demodulation. - */ - public FMDemodulatorModule(int pass, int stop) - { - assert (stop > pass); - - mIQFilter = new ComplexFIRFilter_CB_CB(FilterFactory.getLowPass( - SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true), 1.0f); - - mDemodulator = new FMDemodulator_CB(1.0f); - mIQFilter.setListener(mDemodulator); - } - - public FMDemodulatorModule(float[] filter) - { - mIQFilter = new ComplexFIRFilter_CB_CB(filter, 1.0f); - mDemodulator = new FMDemodulator_CB(1.0f); - mIQFilter.setListener(mDemodulator); - } - - @Override - public Listener getComplexBufferListener() - { - return mIQFilter; - } - - @Override - public void dispose() - { - mIQFilter.dispose(); - mIQFilter = null; - - mDemodulator.dispose(); - mDemodulator = null; - } - - @Override - public void reset() - { - mDemodulator.reset(); - } - - @Override - public void setUnFilteredRealBufferListener(Listener listener) - { - mDemodulator.setListener(listener); - } - - @Override - public void removeUnFilteredRealBufferListener() - { - mDemodulator.removeListener(); - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } -} diff --git a/src/module/log/EventLogger.java b/src/module/log/EventLogger.java deleted file mode 100644 index dcd90caa..00000000 --- a/src/module/log/EventLogger.java +++ /dev/null @@ -1,138 +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 module.log; - -import module.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import util.TimeStamp; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -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 -{ - private final static Logger mLog = LoggerFactory.getLogger(EventLogger.class); - - private static final String[] ILLEGAL_FILENAME_CHARACTERS = {"#", "%", "&", "{", "}", "\\", "<", ">", - "*", "?", "/", " ", "$", "!", "'", "\"", ":", "@", "+", "`", "|", "="}; - - private Path mLogDirectory; - private String mFileNameSuffix; - private String mLogFileName; - protected Writer mLogFile; - - public EventLogger(Path logDirectory, String fileNameSuffix) - { - mLogDirectory = logDirectory; - mFileNameSuffix = fileNameSuffix; - } - - public String toString() - { - if(mLogFileName != null) - { - return mLogFileName; - } - else - { - return "Unknown"; - } - } - - public abstract String getHeader(); - - @Override - public void start() - { - if(mLogFile == null) - { - try - { - StringBuilder sb = new StringBuilder(); - sb.append(mLogDirectory); - sb.append(File.separator); - sb.append(TimeStamp.getTimeStamp("_")); - sb.append("_"); - sb.append(replaceIllegalCharacters(mFileNameSuffix)); - - mLogFileName = sb.toString(); - - mLog.info("Creating log file:" + mLogFileName); - - mLogFile = new OutputStreamWriter(new FileOutputStream(mLogFileName)); - - write(getHeader()); - } - catch(FileNotFoundException e) - { - mLog.error("Couldn't create log file in directory:" + mLogDirectory); - } - } - } - - /** - * Replaces any illegal filename characters in the proposed filename - */ - private String replaceIllegalCharacters(String filename) - { - for(String illegalCharacter : ILLEGAL_FILENAME_CHARACTERS) - { - filename = filename.replace(illegalCharacter, "_"); - } - - return filename; - } - - public void stop() - { - if(mLogFile != null) - { - try - { - mLogFile.flush(); - mLogFile.close(); - mLogFile = null; - } - catch(Exception e) - { - mLog.error("Couldn't close log file:" + mFileNameSuffix); - } - } - } - - protected void write(String eventLogEntry) - { - try - { - mLogFile.write(eventLogEntry + "\n"); - mLogFile.flush(); - } - catch(IOException e) - { - mLog.error("Error writing entry to event log file", e); - } - } -} diff --git a/src/record/AudioRecorder.java b/src/record/AudioRecorder.java deleted file mode 100644 index 3c1b7f64..00000000 --- a/src/record/AudioRecorder.java +++ /dev/null @@ -1,336 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 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 record; - -import audio.AudioPacket; -import audio.IAudioPacketListener; -import channel.metadata.Metadata; -import module.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import util.ThreadPool; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -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; - -public abstract class AudioRecorder extends Module implements Listener, IAudioPacketListener -{ - private final static Logger mLog = LoggerFactory.getLogger(AudioRecorder.class); - - private LinkedBlockingQueue mAudioPacketQueue = new LinkedBlockingQueue<>(500); - private List mPacketsToProcess = new ArrayList<>(); - - private FileOutputStream mFileOutputStream; - private AtomicBoolean mRunning = new AtomicBoolean(); - - protected Path mPath; - protected Metadata mMetadata; - protected long mTimeRecordingStart; - protected long mTimeLastPacketReceived; - private BufferProcessor mBufferProcessor; - private ScheduledFuture mProcessorHandle; - private Listener mRecordingClosedListener; - - private long mSampleCount; - - /** - * Abstract audio recorder that implements audio packet queueing and threaded audio conversion/writing to a file - * - * @param path for the output recording - */ - public AudioRecorder(Path path) - { - mPath = path; - } - - /** - * Path for the audio recording file - */ - public Path getPath() - { - return mPath; - } - - /** - * Latest audio metadata received for this recording - */ - public Metadata getMetadata() - { - return mMetadata; - } - - /** - * Timestamp of the last buffer received by this recorder - allows this recorder to be monitored for automatic - * closure after a time period has elapsed. - */ - public long getTimeLastPacketReceived() - { - return mTimeLastPacketReceived; - } - - public long getTimeRecordingStart() - { - return mTimeRecordingStart; - } - - /** - * Recording length in milliseconds - */ - public long getRecordingLength() - { - //Assumes audio sample rate of 8000 samples/second or 8 samples/milli-second - return mSampleCount / 8; - } - - /** - * Implements the IAudioPacketListener interface and simply redirects to the Listener interface. - * This is necessary since you can't have multiple methods with the same erasure (ie Listener) in the - * parent module class. - */ - @Override - public Listener getAudioPacketListener() - { - return this; - } - - /** - * Processes the audio packet and captures the latest Metadata for the recording for easy access. - */ - @Override - public void receive(AudioPacket audioPacket) - { - if(mRunning.get()) - { - mTimeRecordingStart = System.currentTimeMillis(); - mTimeLastPacketReceived = mTimeRecordingStart; - - if(audioPacket.hasMetadata()) - { - mMetadata = audioPacket.getMetadata(); - } - - boolean success = mAudioPacketQueue.offer(audioPacket); - - if(!success) - { - mLog.error("recorder buffer overflow - stopping recorder [" + getPath().toString() + "]"); - stop(); - } - } - } - - /** - * File output stream for the current recording. Intended to allow sub-classes to write binary data to the file. - */ - protected OutputStream getOutputStream() - { - return mFileOutputStream; - } - - /** - * Stops the recorder and flags the recording to be closed. Use this method if you do not need any details about - * the final recording. Otherwise, use the close() method and register a closing listener. - */ - public void stop() - { - close(null); - } - - /** - * Closes the recording file. Upon successful closing of the recording file, the listener is notified that the - * audio recorder is closed. There is potential for the calling thread (here) and the buffer processor thread to - * both inform the recording closed listener that the recording is ended. So, we synchronize on the listener and - * the first thread to get the lock informs the listener and then nullifies the listener pointer so that if the - * second thread attempts a duplicate notification the listener would be null at that point. - */ - public void close(Listener listener) - { - mRecordingClosedListener = listener; - - if(!mRunning.compareAndSet(true, false)) - { - synchronized(mRecordingClosedListener) - { - if(mRecordingClosedListener != null) - { - mRecordingClosedListener.receive(AudioRecorder.this); - mRecordingClosedListener = null; - } - } - } - } - - - /** - * Records the list of audio packets in the sub-class specific audio format. - */ - protected abstract void record(List audioPackets) throws IOException; - - /** - * Starts this recorder as a scheduled thread running under the executor argument - * - * @param executor to use in scheduling audio conversion and file writes. - */ - public void start() - { - if(mRunning.compareAndSet(false, true)) - { - mTimeLastPacketReceived = System.currentTimeMillis(); - - if(mBufferProcessor == null) - { - mBufferProcessor = new BufferProcessor(); - } - - try - { - mFileOutputStream = new FileOutputStream(mPath.toFile()); - - /* Schedule the handler to run every half second */ - mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS); - } - catch(IOException io) - { - mLog.error("Error starting audio recorder [" + getPath().toString() + "]", io); - - mRunning.set(false); - } - } - } - - /** - * Processes the audio packet queue. - */ - private void processAudioPacketQueue() - { - mAudioPacketQueue.drainTo(mPacketsToProcess); - - if(!mPacketsToProcess.isEmpty()) - { - try - { - record(mPacketsToProcess); - - for(AudioPacket packet : mPacketsToProcess) - { - if(packet.getType() == AudioPacket.Type.AUDIO) - { - mSampleCount += packet.getAudioBuffer().getSamples().length; - } - } - } - catch(IOException ioe) - { - mLog.debug("Error while recording audio to [" + getPath().toString() + "] - stopping recorder"); - stop(); - } - - mPacketsToProcess.clear(); - } - } - - /** - * Disposes this audio recorder and prepares it for reclamation - */ - @Override - public void dispose() - { - stop(); - } - - /** - * Not implemented. Recorder modules are not appropriate for reset and reuse. - */ - @Override - public void reset() - { - } - - /** - * Flushes any remaining audio to the output file. This method should be implemented by subclasses where an audio - * converter may contain residual frame data that should be flushed to disk before closing the audio file. - */ - protected void flush() - { - } - - /** - * Drains the audio packet queue and records the audio packets to file - */ - public class BufferProcessor implements Runnable - { - private AtomicBoolean mProcessing = new AtomicBoolean(); - - public void run() - { - if(mProcessing.compareAndSet(false, true)) - { - processAudioPacketQueue(); - - //If we've been stopped or closed, finish the queue, close the recording, notify the listener, and - // cancel the future - if(!mRunning.get()) - { - //Allow sub-classes to flush remaining audio frame data to disk. - flush(); - - if(mFileOutputStream != null) - { - try - { - mFileOutputStream.flush(); - mFileOutputStream.close(); - } - catch(IOException e) - { - mLog.error("Error closing output stream", e); - } - } - - synchronized(mRecordingClosedListener) - { - if(mRecordingClosedListener != null) - { - mRecordingClosedListener.receive(AudioRecorder.this); - mRecordingClosedListener = null; - } - } - - if(mProcessorHandle != null) - { - mProcessorHandle.cancel(false); - mProcessorHandle = null; - } - } - - mProcessing.set(false); - } - } - } -} diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java deleted file mode 100644 index 4e054b40..00000000 --- a/src/record/RecorderManager.java +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************************* - * 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 record; - -import audio.AudioPacket; -import channel.metadata.Metadata; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import properties.SystemProperties; -import record.wave.ComplexBufferWaveRecorder; -import record.wave.RealBufferWaveRecorder; -import sample.Listener; -import sample.OverflowableTransferQueue; -import sample.real.IOverflowListener; -import util.ThreadPool; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -public class RecorderManager implements Listener -{ - private static final Logger mLog = LoggerFactory.getLogger(RecorderManager.class); - - public static final int AUDIO_SAMPLE_RATE = 8000; - public static final long IDLE_RECORDER_REMOVAL_THRESHOLD = 6000; //6 seconds - - private Map mRecorders = new HashMap<>(); - private OverflowableTransferQueue mAudioPacketQueue = new OverflowableTransferQueue<>(1000, 100); - private ScheduledFuture mBufferProcessorFuture; - - private boolean mCanStartNewRecorders = true; - - /** - * Audio recording manager. Monitors stream of audio packets produced by decoding channels and automatically starts - * audio recorders when the channel's metadata designates a call as recordable. Routes call audio to each recorder - * based on audio packet metadata. Recorders are shutdown when the channel sends an end-call audio packet - * indicating that the call is complete. A separate recording monitor periodically checks for idled recorders to - * be stopped for cases when the channel fails to send an end-call audio packet. - */ - public RecorderManager() - { - mAudioPacketQueue.setOverflowListener(new IOverflowListener() - { - @Override - public void sourceOverflow(boolean overflow) - { - if(overflow) - { - mLog.warn("overflow - audio packets will be dropped until recording catches up"); - } - else - { - mLog.info("audio recorder packet processing has returned to normal"); - } - } - }); - - mBufferProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new BufferProcessor(), 0, - 1, TimeUnit.SECONDS); - } - - /** - * Prepares this class for shutdown - */ - public void dispose() - { - if(mBufferProcessorFuture != null) - { - mBufferProcessorFuture.cancel(true); - } - } - - /** - * Primary ingest point for audio packets from all decoding channels - * @param audioPacket to process - */ - @Override - public void receive(AudioPacket audioPacket) - { - if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable()) - { - mAudioPacketQueue.offer(audioPacket); - } - } - - /** - * Process any queued audio buffers and dispatch them to the audio recorders - */ - private void processBuffers() - { - List audioPackets = new ArrayList<>(); - - mAudioPacketQueue.drainTo(audioPackets, 50); - - while(!audioPackets.isEmpty()) - { - for(AudioPacket audioPacket: audioPackets) - { - String identifier = audioPacket.getMetadata().getUniqueIdentifier(); - - if(mRecorders.containsKey(identifier)) - { - RealBufferWaveRecorder recorder = mRecorders.get(identifier); - - if(audioPacket.getType() == AudioPacket.Type.AUDIO) - { - recorder.receive(audioPacket.getAudioBuffer()); - } - else if(audioPacket.getType() == AudioPacket.Type.END) - { - RealBufferWaveRecorder finished = mRecorders.remove(identifier); - finished.stop(); - } - } - else if(audioPacket.getType() == AudioPacket.Type.AUDIO) - { - if(mCanStartNewRecorders) - { - String filePrefix = getFilePrefix(audioPacket); - - RealBufferWaveRecorder recorder = null; - - try - { - recorder = new RealBufferWaveRecorder(AUDIO_SAMPLE_RATE, filePrefix); - - recorder.start(); - - recorder.receive(audioPacket.getAudioBuffer()); - mRecorders.put(identifier, recorder); - } - catch(Exception ioe) - { - mCanStartNewRecorders = false; - - mLog.error("Error attempting to start new audio wave recorder. All (future) audio recording " + - "is disabled", ioe); - - if(recorder != null) - { - recorder.stop(); - } - } - } - } - } - - audioPackets.clear(); - mAudioPacketQueue.drainTo(audioPackets, 50); - } - } - - /** - * Removes recorders that have not received any new audio buffers in the last 6 seconds. - */ - private void removeIdleRecorders() - { - Iterator> it = mRecorders.entrySet().iterator(); - - while(it.hasNext()) - { - Map.Entry entry = it.next(); - - if(entry.getValue().getLastBufferReceived() + IDLE_RECORDER_REMOVAL_THRESHOLD < System.currentTimeMillis()) - { - mLog.info("Removing idle recorder [" + entry.getKey() + "]"); - it.remove(); - entry.getValue().stop(); - } - } - } - - /** - * Constructs a file name and path for an audio recording - */ - private String getFilePrefix(AudioPacket packet) - { - StringBuilder sb = new StringBuilder(); - - sb.append(SystemProperties.getInstance().getApplicationFolder("recordings")); - - sb.append(File.separator); - - Metadata metadata = packet.getMetadata(); - - sb.append(metadata.hasChannelConfigurationSystem() ? metadata.getChannelConfigurationSystem() + "_" : ""); - sb.append(metadata.hasChannelConfigurationSite() ? metadata.getChannelConfigurationSite() + "_" : ""); - sb.append(metadata.hasChannelConfigurationName() ? metadata.getChannelConfigurationName() + "_" : ""); - - if(metadata.getPrimaryAddressTo().hasIdentifier()) - { - sb.append("_TO_").append(metadata.getPrimaryAddressTo().getIdentifier()); - - if(metadata.getPrimaryAddressFrom().hasIdentifier()) - { - sb.append("_FROM_").append(metadata.getPrimaryAddressFrom().getIdentifier()); - } - } - - return sb.toString(); - } - - /** - * Constructs a baseband recorder for use in a processing chain. - */ - public ComplexBufferWaveRecorder getBasebandRecorder(String channelName) - { - StringBuilder sb = new StringBuilder(); - sb.append(SystemProperties.getInstance().getApplicationFolder("recordings")); - sb.append(File.separator).append(channelName).append("_baseband"); - - return new ComplexBufferWaveRecorder(AUDIO_SAMPLE_RATE, sb.toString()); - } - - /** - * Processes queued audio packets and distributes to each of the audio recorders. Removes any idle recorders - * that have not been updated according to an idle threshold period - */ - public class BufferProcessor implements Runnable - { - @Override - public void run() - { - processBuffers(); - removeIdleRecorders(); - } - } -} diff --git a/src/record/wave/ComplexBufferWaveRecorder.java b/src/record/wave/ComplexBufferWaveRecorder.java deleted file mode 100644 index ea369c65..00000000 --- a/src/record/wave/ComplexBufferWaveRecorder.java +++ /dev/null @@ -1,218 +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 record.wave; - -import module.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Buffer; -import sample.ConversionUtils; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferListener; -import util.ThreadPool; -import util.TimeStamp; - -import javax.sound.sampled.AudioFormat; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * WAVE audio recorder module for recording complex (I&Q) samples to a wave file - */ -public class ComplexBufferWaveRecorder extends Module - implements IComplexBufferListener, Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger(ComplexBufferWaveRecorder.class); - - private WaveWriter mWriter; - private String mFilePrefix; - private Path mFile; - private AudioFormat mAudioFormat; - - private BufferProcessor mBufferProcessor; - private ScheduledFuture mProcessorHandle; - private LinkedBlockingQueue mBuffers = new LinkedBlockingQueue<>(500); - - private AtomicBoolean mRunning = new AtomicBoolean(); - - public ComplexBufferWaveRecorder(int sampleRate, String filePrefix) - { - mAudioFormat = new AudioFormat(sampleRate, //SampleRate - 16, //Sample Size - 2, //Channels - true, //Signed - false); //Little Endian - - mFilePrefix = filePrefix; - } - - public Path getFile() - { - return mFile; - } - - public void start() - { - if(mRunning.compareAndSet(false, true)) - { - if(mBufferProcessor == null) - { - mBufferProcessor = new BufferProcessor(); - } - - try - { - StringBuilder sb = new StringBuilder(); - sb.append(mFilePrefix); - sb.append("_"); - sb.append(TimeStamp.getTimeStamp("_")); - sb.append(".wav"); - - mFile = Paths.get(sb.toString()); - - mWriter = new WaveWriter(mAudioFormat, mFile); - - /* Schedule the processor to run every 500 milliseconds */ - mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate( - mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS); - } - catch(IOException io) - { - mLog.error("Error starting complex baseband recorder", io); - } - } - } - - public void stop() - { - if(mRunning.compareAndSet(true, false)) - { - receive(new PoisonPill()); - } - } - - @Override - public void receive(ComplexBuffer buffer) - { - if(mRunning.get()) - { - boolean success = mBuffers.offer(buffer); - - if(!success) - { - mLog.error("recorder buffer overflow - purging [" + - mFile.toFile().getAbsolutePath() + "]"); - - mBuffers.clear(); - } - } - } - - @Override - public Listener getComplexBufferListener() - { - return this; - } - - @Override - public void dispose() - { - stop(); - } - - @Override - public void reset() - { - } - - public class BufferProcessor implements Runnable - { - public void run() - { - try - { - Buffer buffer = mBuffers.poll(); - - while(buffer != null) - { - if(buffer instanceof PoisonPill) - { - buffer = null; - - mBuffers.clear(); - - if(mWriter != null) - { - try - { - mWriter.close(); - mWriter = null; - } - catch(IOException io) - { - mLog.error("Error stopping complex wave recorder [" + - getFile() + "]", io); - } - } - - mFile = null; - - if(mProcessorHandle != null) - { - mProcessorHandle.cancel(true); - } - - mProcessorHandle = null; - } - else - { - mWriter.write(ConversionUtils.convertToSigned16BitSamples(buffer)); - buffer = mBuffers.poll(); - } - } - } - catch(IOException ioe) - { - /* Stop this module if/when we get an IO exception */ - mBuffers.clear(); - stop(); - - mLog.error("IOException while trying to write to the wave " - + "writer", ioe); - } - } - } - - /** - * This is used as a sentinel value to signal the buffer processor to end - */ - public class PoisonPill extends ComplexBuffer - { - public PoisonPill() - { - super(new float[1]); - } - } -} diff --git a/src/record/wave/RealBufferWaveRecorder.java b/src/record/wave/RealBufferWaveRecorder.java deleted file mode 100644 index d006fe4a..00000000 --- a/src/record/wave/RealBufferWaveRecorder.java +++ /dev/null @@ -1,212 +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 record.wave; - -import module.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.ConversionUtils; -import sample.Listener; -import sample.real.IFilteredRealBufferListener; -import sample.real.RealBuffer; -import util.ThreadPool; -import util.TimeStamp; - -import javax.sound.sampled.AudioFormat; -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; - -/** - * WAVE audio recorder module for recording real sample buffers to a wave file - */ -public class RealBufferWaveRecorder extends Module - implements IFilteredRealBufferListener, Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(RealBufferWaveRecorder.class); - - private WaveWriter mWriter; - private String mFilePrefix; - private Path mFile; - private AudioFormat mAudioFormat; - - private BufferProcessor mBufferProcessor; - private ScheduledFuture mProcessorHandle; - private LinkedBlockingQueue mBuffers = new LinkedBlockingQueue<>(500); - private long mLastBufferReceived; - - private AtomicBoolean mRunning = new AtomicBoolean(); - - public RealBufferWaveRecorder(int sampleRate, String filePrefix) - { - mAudioFormat = new AudioFormat(sampleRate, //SampleRate - 16, //Sample Size - 1, //Channels - true, //Signed - false); //Little Endian - - mFilePrefix = filePrefix; - } - - /** - * Indicates if the recorder is currently running. - */ - public boolean isRunning() - { - return mRunning.get(); - } - - /** - * Timestamp of when the latest buffer was received by this recorder - */ - public long getLastBufferReceived() - { - return mLastBufferReceived; - } - - public Path getFile() - { - return mFile; - } - - public void start() - { - if(mRunning.compareAndSet(false, true)) - { - if(mBufferProcessor == null) - { - mBufferProcessor = new BufferProcessor(); - } - - try - { - StringBuilder sb = new StringBuilder(); - sb.append(mFilePrefix); - sb.append("_"); - sb.append(TimeStamp.getLongTimeStamp("_")); - sb.append(".wav"); - - mFile = Paths.get(sb.toString()); - - mWriter = new WaveWriter(mAudioFormat, mFile); - - /* Schedule the processor to run every 500 milliseconds */ - mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS); - } - catch(IOException io) - { - mLog.error("Error starting real buffer recorder", io); - } - } - } - - public void stop() - { - if(mRunning.compareAndSet(true, false)) - { - try - { - write(); - - if(mWriter != null) - { - mWriter.close(); - mWriter = null; - } - } - catch(IOException ioe) - { - mLog.error("Error writing final audio buffers to recording during shutdown", ioe); - } - } - } - - @Override - public void receive(RealBuffer buffer) - { - if(mRunning.get()) - { - boolean success = mBuffers.offer(buffer); - - if(!success) - { - mLog.error("recorder buffer overflow - purging [" + mFile.toFile().getAbsolutePath() + "]"); - mBuffers.clear(); - } - - mLastBufferReceived = System.currentTimeMillis(); - } - } - - @Override - public Listener getFilteredRealBufferListener() - { - return this; - } - - @Override - public void dispose() - { - stop(); - } - - @Override - public void reset() - { - } - - /** - * Writes all audio currently in the queue to the file - * @throws IOException if there are any errors writing the audio - */ - private void write() throws IOException - { - RealBuffer buffer = mBuffers.poll(); - - while(buffer != null) - { - mWriter.write(ConversionUtils.convertToSigned16BitSamples(buffer)); - buffer = mBuffers.poll(); - } - } - - public class BufferProcessor implements Runnable - { - public void run() - { - try - { - write(); - } - catch(IOException ioe) - { - /* Stop this module if/when we get an IO exception */ - mBuffers.clear(); - stop(); - - mLog.error("IO Exception while trying to write to the wave writer", ioe); - } - } - } -} diff --git a/src/sample/Broadcaster.java b/src/sample/Broadcaster.java deleted file mode 100644 index b3bf951a..00000000 --- a/src/sample/Broadcaster.java +++ /dev/null @@ -1,124 +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 sample; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Broadcasts an item to multiple listeners - */ -public class Broadcaster implements Listener -{ - private List> mListeners = new CopyOnWriteArrayList<>(); - - /** - * 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); - } - - /** - * Clear listeners to prepare for garbage collection - */ - public void dispose() - { - 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/sample/OverflowableTransferQueue.java b/src/sample/OverflowableTransferQueue.java deleted file mode 100644 index e532bc61..00000000 --- a/src/sample/OverflowableTransferQueue.java +++ /dev/null @@ -1,151 +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 sample; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.real.IOverflowListener; -import source.Source; - -import java.util.Collection; -import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -public class OverflowableTransferQueue -{ - private final static Logger mLog = LoggerFactory.getLogger(OverflowableTransferQueue.class); - - public enum State {NORMAL, OVERFLOW}; - private IOverflowListener mOverflowListener; - private Source mSourceOverflowListener; - - private LinkedTransferQueue mQueue = new LinkedTransferQueue(); - private AtomicInteger mCounter = new AtomicInteger(); - private AtomicBoolean mOverflow = new AtomicBoolean(); - private int mMaximumSize; - private int mResetThreshold; - - /** - * Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for - * monitoring queue size. When the queue size exceeds maximum size (overflow), all inbound elements are ignored - * until the queue size is reduced to or below the reset threshold. - * - * @param maximumSize of the queue. Overflow state will occur once queue size exceeds this value. - * @param resetThreshold for resetting overflow state to normal, once queue size is at or below this value. - */ - public OverflowableTransferQueue(int maximumSize, int resetThreshold) - { - mMaximumSize = maximumSize; - mResetThreshold = resetThreshold; - } - - /** - * Adds the element to the queue if able to do so without exceeding maximum queue size. Otherwise, ignores - * the element. - */ - public void offer(E e) - { - if(!mOverflow.get()) - { - mQueue.offer(e); - - int size = mCounter.incrementAndGet(); - - if(size > mMaximumSize) - { - setOverflow(true); - } - } - } - - /** - * 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 - */ - public int drainTo(Collection collection, int maxElements) - { - int drainCount = mQueue.drainTo(collection, maxElements); - - int size = mCounter.addAndGet(-drainCount); - - if(mOverflow.get() && size <= mResetThreshold) - { - setOverflow(false); - } - - return drainCount; - } - - /** - * 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 - */ - private void setOverflow(boolean overflow) - { - if(mOverflow.compareAndSet(!overflow, overflow)) - { - if(mOverflowListener != null) - { - mOverflowListener.sourceOverflow(overflow); - } - - if(mSourceOverflowListener != null) - { - mSourceOverflowListener.broadcastOverflowState(overflow); - } - } - } - - /** - * Clears all elements from the queue and resets the internal counter to 0 - */ - public void clear() - { - synchronized(mQueue) - { - mQueue.clear(); - mCounter.set(0); - mOverflow.set(false); - } - } -} diff --git a/src/sample/SampleType.java b/src/sample/SampleType.java deleted file mode 100644 index 8aedd8e2..00000000 --- a/src/sample/SampleType.java +++ /dev/null @@ -1,7 +0,0 @@ -package sample; - -public enum SampleType -{ - COMPLEX, // I/Q - REAL; // Real -} diff --git a/src/source/ComplexSource.java b/src/source/ComplexSource.java deleted file mode 100644 index b864aae8..00000000 --- a/src/source/ComplexSource.java +++ /dev/null @@ -1,32 +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 source; - -import sample.Provider; -import sample.SampleType; -import sample.complex.ComplexBuffer; - -public abstract class ComplexSource extends Source implements Provider -{ - @Override - public SampleType getSampleType() - { - return SampleType.COMPLEX; - } -} diff --git a/src/source/InvalidFrequencyException.java b/src/source/InvalidFrequencyException.java deleted file mode 100644 index e60a8843..00000000 --- a/src/source/InvalidFrequencyException.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2016 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 source; - -public class InvalidFrequencyException extends SourceException -{ - private long mInvalidFrequency; - private long mValidFrequency; - - /** - * Thrown when the user or the application requests to tune to an invalid frequency. Invalid means that the - * frequency is outside of the tunable range of the current tuner device. This class provides both the requested - * invalid frequency and the suggested valid frequency to use in lieu of the requested invalid frequency. - * @param message optional text - * @param invalidFrequency that was requested - * @param validFrequency that should be used instead - */ - public InvalidFrequencyException( String message, long invalidFrequency, long validFrequency ) - { - super( message ); - - mInvalidFrequency = invalidFrequency; - mValidFrequency = validFrequency; - } - - /** - * The invalid requested frequency - */ - public long getInvalidFrequency() - { - return mInvalidFrequency; - } - - /** - * The valid frequency that should be used - */ - public long getValidFrequency() - { - return mValidFrequency; - } - - @Override - public String getMessage() - { - return super.getMessage() + " Invalid Frequency: " + getInvalidFrequency() + - " - current valid frequency:" + getValidFrequency(); - } -} diff --git a/src/source/RealSource.java b/src/source/RealSource.java deleted file mode 100644 index 8b08ebca..00000000 --- a/src/source/RealSource.java +++ /dev/null @@ -1,32 +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 source; - -import sample.Provider; -import sample.SampleType; -import sample.real.RealBuffer; - -public abstract class RealSource extends Source implements Provider -{ - @Override - public SampleType getSampleType() - { - return SampleType.REAL; - } -} diff --git a/src/source/Source.java b/src/source/Source.java deleted file mode 100644 index 16a8caeb..00000000 --- a/src/source/Source.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 source; - -import source.heartbeat.HeartbeatManager; -import source.heartbeat.IHeartbeatProvider; -import module.Module; -import sample.SampleType; -import sample.real.IOverflowListener; - -/** - * Abstract class to define the minimum functionality of a sample data provider. - */ -public abstract class Source extends Module implements ISourceEventListener, ISourceEventProvider, IHeartbeatProvider -{ - private HeartbeatManager mHeartbeatManager = new HeartbeatManager(); - - /** - * Heartbeat manager for this source - */ - public HeartbeatManager getHeartbeatManager() - { - return mHeartbeatManager; - } - - - protected IOverflowListener mOverflowListener; - - /** - * 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(); - - /** - * Registers the listener to receive overflow state changes. Use null argument to clear the listener - */ - public void setOverflowListener(IOverflowListener listener) - { - mOverflowListener = listener; - } - - /** - * Broadcasts an overflow state - * - * @param overflow true if overflow, false if normal - */ - public void broadcastOverflowState(boolean overflow) - { - if(mOverflowListener != null) - { - mOverflowListener.sourceOverflow(overflow); - } - } -} diff --git a/src/source/config/SourceConfigRecording.java b/src/source/config/SourceConfigRecording.java deleted file mode 100644 index d92b4fe5..00000000 --- a/src/source/config/SourceConfigRecording.java +++ /dev/null @@ -1,70 +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 source.config; - -import source.SourceType; -import source.tuner.channel.TunerChannel; - -import javax.xml.bind.annotation.XmlAttribute; - -public class SourceConfigRecording extends SourceConfiguration -{ - private String mRecordingAlias; - private long mFrequency; - - public SourceConfigRecording() - { - super(SourceType.RECORDING); - } - - @XmlAttribute(name = "recording_alias") - public String getRecordingAlias() - { - return mRecordingAlias; - } - - public void setRecordingAlias(String alias) - { - mRecordingAlias = alias; - } - - @XmlAttribute(name = "frequency") - public long getFrequency() - { - return mFrequency; - } - - public void setFrequency(long frequency) - { - mFrequency = frequency; - } - - @Override - public String getDescription() - { - return getRecordingAlias(); - } - - public TunerChannel getTunerChannel() - { - /* Create a tuner channel with a default bandwidth of 12.5 kHz */ - return new TunerChannel(mFrequency, 12500); - } - -} diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java deleted file mode 100644 index 8fe54732..00000000 --- a/src/source/config/SourceConfigTuner.java +++ /dev/null @@ -1,68 +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 source.config; - -import source.SourceType; -import source.tuner.channel.TunerChannel; - -import javax.xml.bind.annotation.XmlAttribute; -import java.text.DecimalFormat; - -public class SourceConfigTuner extends SourceConfiguration -{ - private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000"); - - private long mFrequency = 0; - private int mBandwidth = 12500; - - public SourceConfigTuner() - { - super(SourceType.TUNER); - } - - public SourceConfigTuner(TunerChannel tunerChannel) - { - this(); - - mFrequency = tunerChannel.getFrequency(); - mBandwidth = tunerChannel.getBandwidth(); - } - - @XmlAttribute(name = "frequency") - public long getFrequency() - { - return mFrequency; - } - - public void setFrequency(long frequency) - { - mFrequency = frequency; - } - - @Override - public String getDescription() - { - return FREQUENCY_FORMAT.format((double)mFrequency / 1000000.0d) + " MHz"; - } - - public TunerChannel getTunerChannel() - { - return new TunerChannel(mFrequency, mBandwidth); - } -} diff --git a/src/source/heartbeat/IHeartbeatListener.java b/src/source/heartbeat/IHeartbeatListener.java deleted file mode 100644 index 296860a7..00000000 --- a/src/source/heartbeat/IHeartbeatListener.java +++ /dev/null @@ -1,26 +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 source.heartbeat; - -import sample.Listener; - -public interface IHeartbeatListener -{ - public Listener getHeartbeatListener(); -} diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java deleted file mode 100644 index 83d72d3d..00000000 --- a/src/source/mixer/RealMixerSource.java +++ /dev/null @@ -1,306 +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 source.mixer; - -import source.heartbeat.Heartbeat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.adapter.ISampleAdapter; -import sample.real.RealBuffer; -import source.RealSource; -import source.SourceException; -import source.SourceEvent; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -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 -{ - private final static Logger mLog = LoggerFactory.getLogger(RealMixerSource.class); - - private long mFrequency = 0; - private int mBufferSize = 16384; - - private BufferReader mBufferReader = new BufferReader(); - private TargetDataLine mTargetDataLine; - private AudioFormat mAudioFormat; - private int mBytesPerFrame = 0; - private ISampleAdapter mSampleAdapter; - private Listener mHeartbeatListener; - private static final Heartbeat HEARTBEAT = new Heartbeat(); - - private CopyOnWriteArrayList> mSampleListeners = new CopyOnWriteArrayList>(); - - /** - * Real Mixer Source - constructs a reader on the mixer/sound card target - * data line using the specified audio format (sample size, sample rate ) - * and broadcasts real buffers (float arrays) to all registered listeners. - * Reads buffers sized to 10% of the sample rate specified in audio format. - * - * @param targetDataLine - mixer or sound card to be used - * @param format - audio format - * @param sampleAdapter - adapter to convert byte array data read from the - * mixer into float array data. Can optionally invert the channel data if - * the left/right stereo channels are inverted. - */ - public RealMixerSource(TargetDataLine targetDataLine, - AudioFormat format, - ISampleAdapter sampleAdapter) - { - mTargetDataLine = targetDataLine; - mAudioFormat = format; - mSampleAdapter = sampleAdapter; - } - - @Override - public void setSourceEventListener(Listener listener) - { - //Not implemented - } - - @Override - public void removeSourceEventListener() - { - //Not implemented - } - - @Override - public Listener getSourceEventListener() - { - //Not implemented - return null; - } - - @Override - public void reset() - { - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } - - public void setListener(Listener listener) - { - mSampleListeners.add(listener); - - /* If this is the first listener, start the reader thread */ - if(!mBufferReader.isRunning()) - { - Thread thread = new Thread(mBufferReader); - thread.setDaemon(true); - thread.setName("Sample Reader"); - thread.start(); - } - } - - public void removeListener(Listener listener) - { - mSampleListeners.remove(listener); - - /* If this is the laster listener, stop the reader thread */ - if(mSampleListeners.isEmpty()) - { - mBufferReader.stop(); - } - } - - public void broadcast(RealBuffer samples) - { - Iterator> it = mSampleListeners.iterator(); - - while(it.hasNext()) - { - Listener next = it.next(); - - /* if this is the last (or only) listener, send him the original - * buffer, otherwise send him a copy of the buffer */ - if(it.hasNext()) - { - next.receive(samples.copyOf()); - } - else - { - next.receive(samples); - } - } - } - - public double getSampleRate() - { - if(mTargetDataLine != null) - { - return mTargetDataLine.getFormat().getSampleRate(); - } - else - { - return 0; - } - } - - /** - * Returns the frequency of this source. Default is 0. - */ - public long getFrequency() - { - return mFrequency; - } - - /** - * Specify the frequency that will be returned from this source. This may - * be useful if you are streaming an external audio source in through the - * sound card and you want to specify a frequency for that source - */ - public void setFrequency(long frequency) - { - mFrequency = frequency; - } - - /** - * Reader thread. Performs blocking read against the mixer target data - * line, converts the samples to an array of floats using the specified - * adapter. Dispatches float arrays to all registered listeners. - */ - public class BufferReader implements Runnable - { - private AtomicBoolean mRunning = new AtomicBoolean(); - - @Override - public void run() - { - if(mRunning.compareAndSet(false, true)) - { - //Send a heartbeat every time we run - if(mHeartbeatListener != null) - { - mHeartbeatListener.receive(HEARTBEAT); - } - - if(mTargetDataLine == null) - { - mRunning.set(false); - - mLog.error("ComplexMixerSource - mixer target data line" - + " is null"); - } - else - { - mBytesPerFrame = mAudioFormat.getSampleSizeInBits() / 8; - - if(mBytesPerFrame == AudioSystem.NOT_SPECIFIED) - { - mBytesPerFrame = 2; - } - - /* Set buffer size to 1/10 second of samples */ - mBufferSize = (int)(mAudioFormat.getSampleRate() - * 0.05) * mBytesPerFrame; - - /* We'll reuse the same buffer for each read */ - byte[] buffer = new byte[mBufferSize]; - - try - { - mTargetDataLine.open(mAudioFormat); - - mTargetDataLine.start(); - } - catch(LineUnavailableException e) - { - mLog.error("ComplexMixerSource - mixer target data line" - + "not available to read data from", e); - - mRunning.set(false); - } - - while(mRunning.get()) - { - try - { - /* Blocking read - waits until the buffer fills */ - mTargetDataLine.read(buffer, 0, buffer.length); - - /* Convert samples to float array */ - float[] samples = - mSampleAdapter.convert(buffer); - - /* Dispatch samples to registered listeners */ - broadcast(new RealBuffer(samples)); - } - catch(Exception e) - { - mLog.error("ComplexMixerSource - error while reading" - + "from the mixer target data line", e); - - mRunning.set(false); - } - } - } - - /* Close the data line if it is still open */ - if(mTargetDataLine != null && mTargetDataLine.isOpen()) - { - mTargetDataLine.close(); - } - } - } - - /** - * Stops the reader thread - */ - public void stop() - { - if(mRunning.compareAndSet(true, false)) - { - mTargetDataLine.stop(); - mTargetDataLine.close(); - } - } - - /** - * Indicates if the reader thread is running - */ - public boolean isRunning() - { - return mRunning.get(); - } - } - - @Override - public void dispose() - { - if(mBufferReader != null) - { - mBufferReader.stop(); - mSampleListeners.clear(); - } - } -} diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java deleted file mode 100644 index 938dc6a6..00000000 --- a/src/source/recording/Recording.java +++ /dev/null @@ -1,117 +0,0 @@ -package source.recording; - -import java.io.File; -import java.util.ArrayList; -import java.util.concurrent.RejectedExecutionException; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.ITunerChannelProvider; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; -import source.SourceEvent; -import source.SourceEvent.Event; -import source.ISourceEventProcessor; - -public class Recording implements Comparable, - ISourceEventProcessor, - ITunerChannelProvider -{ - private RecordingConfiguration mConfiguration; - - private ArrayList mTunerChannels = - new ArrayList(); - - @SuppressWarnings( "unused" ) - private long mCenterFrequency; - - @SuppressWarnings( "unused" ) - private SettingsManager mSettingsManager; - - public Recording( SettingsManager settingsManager, - RecordingConfiguration configuration ) - { - mSettingsManager = settingsManager; - mConfiguration = configuration; - mCenterFrequency = mConfiguration.getCenterFrequency(); - } - - public void setAlias( String alias ) - { - mConfiguration.setAlias( alias ); -// mSettingsManager.save(); - } - - public void setRecordingFile( File file ) throws SourceException - { - if( hasChannels() ) - { - throw new SourceException( "Recording source - can't change " - + "recording file while channels are enabled against the " - + "current recording." ); - } - - mConfiguration.setFilePath( file.getAbsolutePath() ); -// mSettingsManager.save(); - } - - /** - * Indicates if this recording is currently playing/providing samples to - * tuner channel sources - */ - public boolean hasChannels() - { - return mTunerChannels.size() > 0; - } - - @Override - public TunerChannelSource getChannel( TunerChannel channel ) - throws RejectedExecutionException, SourceException - { - // TODO Auto-generated method stub - return null; - } - - @Override - public void releaseChannel( TunerChannelSource source ) - { - // TODO Auto-generated method stub - } - - public RecordingConfiguration getRecordingConfiguration() - { - return mConfiguration; - } - - public String toString() - { - return mConfiguration.getAlias(); - } - - @Override - public void process(SourceEvent event ) throws SourceException - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - long frequency = event.getValue().longValue(); - - mConfiguration.setCenterFrequency( frequency ); - -// mSettingsManager.save(); - - mCenterFrequency = frequency; - - for( TunerChannelSource channel: mTunerChannels ) - { - channel.process( event ); - } - } - } - - @Override - public int compareTo( Recording other ) - { - return getRecordingConfiguration().getAlias() - .compareTo( other.getRecordingConfiguration().getAlias() ); - } -} diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java deleted file mode 100644 index d5fb4a69..00000000 --- a/src/source/recording/RecordingSourceManager.java +++ /dev/null @@ -1,146 +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 source.recording; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.Source; -import source.SourceException; -import source.config.SourceConfigRecording; -import source.config.SourceConfiguration; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; - -public class RecordingSourceManager -{ - private final static Logger mLog = - LoggerFactory.getLogger( RecordingSourceManager.class ); - - private ArrayList mRecordings = new ArrayList(); - - private SettingsManager mSettingsManager; - - public RecordingSourceManager( SettingsManager settingsManager ) - { - mSettingsManager = settingsManager; - loadRecordings(); - } - - /** - * 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 ) - throws SourceException - { - TunerChannelSource retVal = null; - - if( config instanceof SourceConfigRecording ) - { - SourceConfigRecording configRecording = (SourceConfigRecording)config; - - TunerChannel tunerChannel = configRecording.getTunerChannel(); - - tunerChannel.setBandwidth( bandwidth ); - - Recording recording = getRecordingFromAlias( - configRecording.getRecordingAlias() ); - - if( recording != null ) - { - retVal = recording.getChannel( tunerChannel ); - } - else - { - throw new SourceException( "Recording with alias name [" + - configRecording.getRecordingAlias() + "] is not currently available" ); - } - } - - return retVal; - } - - public Recording getRecordingFromAlias( String alias ) - { - for( Recording recording: mRecordings ) - { - if( recording.getRecordingConfiguration() - .getAlias().contentEquals( alias ) ) - { - return recording; - } - } - - return null; - } - - /** - * Get list of loaded IQ files - */ - public List getRecordings() - { - Collections.sort( mRecordings ); - - return mRecordings; - } - - public void addRecording( Recording recording ) - { - mRecordings.add( recording ); - } - - public Recording addRecording( RecordingConfiguration config ) - { - Recording recording = new Recording( mSettingsManager, config ); - - mRecordings.add( recording ); - - return recording; - } - - public void removeRecording( Recording recording ) - { - /* Remove the config from the settings manager */ - mSettingsManager.removeRecordingConfiguration( - recording.getRecordingConfiguration() ); - - mRecordings.remove( recording ); - } - - private void loadRecordings() - { - List recordingConfigurations = - mSettingsManager.getRecordingConfigurations(); - - mLog.info( "RecordingSourceManager - discovered [" + - recordingConfigurations.size() + "] recording configurations" ); - - for( RecordingConfiguration config: recordingConfigurations ) - { - Recording recording = new Recording( mSettingsManager, config ); - - mRecordings.add( recording ); - } - } -} diff --git a/src/source/tuner/ITunerChannelProvider.java b/src/source/tuner/ITunerChannelProvider.java deleted file mode 100644 index 17558dbc..00000000 --- a/src/source/tuner/ITunerChannelProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner; - -import java.util.concurrent.RejectedExecutionException; - -import source.SourceException; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; - -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 ) - throws RejectedExecutionException, SourceException; - - /** - * Releases the tuned channel resources - * - * @param channel - previously obtained tuner channel - */ - public abstract void releaseChannel( TunerChannelSource source ); - - -} diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java deleted file mode 100644 index ee475f4e..00000000 --- a/src/source/tuner/Tuner.java +++ /dev/null @@ -1,148 +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 source.tuner; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Broadcaster; -import sample.Listener; -import source.ISourceEventProcessor; -import source.SourceEvent; - -/** - * Tuner - provides tuner channel sources, representing a channel frequency - */ -public abstract class Tuner -{ - private final static Logger mLog = LoggerFactory.getLogger(Tuner.class); - - protected Broadcaster mTunerEventBroadcaster = new Broadcaster<>(); - private TunerController mTunerController; - private String mName; - - /** - * Abstract tuner class. - * @param name of the tuner - * @param tunerController for the tuner - */ - public Tuner(String name, TunerController tunerController) - { - mName = name; - mTunerController = tunerController; - - //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, source.tuner.TunerEvent.Event.FREQUENCY)); - break; - case NOTIFICATION_SAMPLE_RATE_CHANGE: - broadcast(new TunerEvent(Tuner.this, source.tuner.TunerEvent.Event.SAMPLE_RATE)); - break; - default: - break; - } - } - }); - } - - public TunerController getTunerController() - { - return mTunerController; - } - - public String toString() - { - 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 tunerEvent) - { - mTunerEventBroadcaster.broadcast(tunerEvent); - } -} diff --git a/src/source/tuner/TunerClass.java b/src/source/tuner/TunerClass.java deleted file mode 100644 index 9d898cda..00000000 --- a/src/source/tuner/TunerClass.java +++ /dev/null @@ -1,278 +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 source.tuner; - -import javax.usb.UsbDeviceDescriptor; - -public enum TunerClass -{ - AIRSPY( TunerType.AIRSPY_R820T, "1D50", "60A1", "Airspy", "Airspy" ), - GENERIC_2832( TunerType.RTL2832_VARIOUS, "0BDA", "2832", "RTL2832", "SDR" ), - GENERIC_2838( TunerType.RTL2832_VARIOUS, "0BDA", "2838", "RTL2832", "SDR" ), - COMPRO_VIDEOMATE_U620F( TunerType.ELONICS_E4000, "185B", "0620", "Compro", "Videomate U620F" ), - COMPRO_VIDEOMATE_U650F( TunerType.ELONICS_E4000, "185B", "0650", "Compro", "Videomate U620F" ), - COMPRO_VIDEOMATE_U680F( TunerType.ELONICS_E4000, "185B", "0680", "Compro", "Videomate U620F" ), - DEXATEK_LOGILINK_VG002A( TunerType.FCI_FC2580, "1D19", "1101", "Dexatek", "Logilink VG0002A" ), - DEXATEK_DIGIVOX_MINI_II_REV3( TunerType.FCI_FC2580, "1D19", "1102", "Dexatek", "MSI Digivox Mini II v3.0" ), - DEXATEK_5217_DVBT( TunerType.FCI_FC2580, "1D19", "1103", "Dexatek", "5217 DVB-T" ), - ETTUS_USRP_B100( TunerType.ETTUS_VARIOUS, "2500", "0002", "Ettus Research", "USRP B100" ), - FUNCUBE_DONGLE_PRO( TunerType.FUNCUBE_DONGLE_PRO, "04D8", "FB56", "Hamlincrest", "Funcube Dongle Pro" ), - FUNCUBE_DONGLE_PRO_PLUS( TunerType.FUNCUBE_DONGLE_PRO_PLUS, "04D8", "FB31", "Hamlincrest", "Funcube Dongle Pro Plus" ), - GIGABYTE_GTU7300( TunerType.FITIPOWER_FC0012, "1B80", "D393", "Gigabyte", "GT-U7300" ), - GTEK_T803( TunerType.FITIPOWER_FC0012, "1F4D", "B803", "GTek", "T803" ), - HACKRF_ONE( TunerType.HACKRF, "1D50", "6089", "Great Scott Gadgets", "HackRF One" ), - RAD1O( TunerType.HACKRF, "1D50", "CC15", "Munich hackerspace", "Rad1o" ), - LIFEVIEW_LV5T_DELUXE( TunerType.FITIPOWER_FC0012, "1F4D", "C803", "Liveview", "LV5T Deluxe" ), - MYGICA_TD312( TunerType.FITIPOWER_FC0012, "1F4D", "D286", "MyGica", "TD312" ), - PEAK_102569AGPK( TunerType.FITIPOWER_FC0012, "1B80", "D395", "Peak", "102569AGPK" ), - PROLECTRIX_DV107669( TunerType.FITIPOWER_FC0012, "1F4D", "D803", "Prolectrix", "DV107669" ), - SVEON_STV20( TunerType.FITIPOWER_FC0012, "1B80", "D39D", "Sveon", "STV20 DVB-T USB & FM" ), - TERRATEC_CINERGY_T_REV1( TunerType.FITIPOWER_FC0012, "0CCD", "00A9", "Terratec", "Cinergy T R1" ), - TERRATEC_CINERGY_T_REV3( TunerType.ELONICS_E4000, "0CCD", "00D3", "Terratec", "Cinergy T R3" ), - TERRATEC_NOXON_REV1_B3( TunerType.FITIPOWER_FC0013, "0CCD", "00B3", "Terratec", "NOXON R1 (B3)" ), - TERRATEC_NOXON_REV1_B4( TunerType.FITIPOWER_FC0013, "0CCD", "00B4", "Terratec", "NOXON R1 (B4)" ), - TERRATEC_NOXON_REV1_B7( TunerType.FITIPOWER_FC0013, "0CCD", "00B7", "Terratec", "NOXON R1 (B7)" ), - TERRATEC_NOXON_REV1_C6( TunerType.FITIPOWER_FC0013, "0CCD", "00C6", "Terratec", "NOXON R1 (C6)" ), - TERRATEC_NOXON_REV2( TunerType.ELONICS_E4000, "0CCD", "00E0", "Terratec", "NOXON R2" ), - TERRATEC_T_STICK_PLUS( TunerType.ELONICS_E4000, "0CCD", "00D7", "Terratec", "T Stick Plus" ), - TWINTECH_UT40( TunerType.FITIPOWER_FC0013, "1B80", "D3A4", "Twintech", "UT-40" ), - ZAAPA_ZTMINDVBZP( TunerType.FITIPOWER_FC0012, "1B80", "D398", "Zaapa", "ZT-MINDVBZP" ), - TEST_TUNER(TunerType.TEST, "0", "0", "ABC Tuners Inc.", "Model XYZ"), - UNKNOWN( TunerType.UNKNOWN, "0", "0", "Unknown Manufacturer", "Unknown Device" ); - - private TunerType mTunerType; - private String mVendorID; - private String mDeviceID; - private String mVendorDescription; - private String mDeviceDescription; - - private TunerClass( TunerType tunerType, - String vendorID, - String deviceID, - String vendorDescription, - String deviceDescription ) - { - mTunerType = tunerType; - mVendorID = vendorID; - mDeviceID = deviceID; - mVendorDescription = vendorDescription; - mDeviceDescription = deviceDescription; - } - - public String toString() - { - return "USB" + - " Tuner:" + mTunerType.toString() + - " Vendor:" + mVendorDescription + - " Device:" + mDeviceDescription + - " Address:" + mVendorID + ":" + mDeviceID; - } - - public String getVendorDeviceLabel() - { - return mVendorDescription + " " + mDeviceDescription; - } - - public TunerType getTunerType() - { - return mTunerType; - } - - public static TunerClass valueOf( UsbDeviceDescriptor descriptor ) - { - return valueOf( descriptor.idVendor(), descriptor.idProduct() ); - } - - public static TunerClass valueOf( short vendor, short product ) - { - TunerClass retVal = TunerClass.UNKNOWN; - - //Cast the short to integer so that we can switch on unsigned numbers - int vendorID = vendor & 0xFFFF; - int productID = product & 0xFFFF; - - switch( vendorID ) - { - case 1240: //04DA - if( productID == 64305 ) //FB31 - { - retVal = FUNCUBE_DONGLE_PRO_PLUS; - } - else if( productID == 64342 ) //FB56 - { - retVal = FUNCUBE_DONGLE_PRO; - } - break; - case 3034: //0BDA - if( productID == 10290 ) //2832 - { - retVal = GENERIC_2832; - } - else if( productID == 10296 ) //2838 - { - retVal = GENERIC_2838; - } - break; - case 6235: //185B - if( productID == 1568 ) //0620 - { - retVal = COMPRO_VIDEOMATE_U620F; - } - else if( productID == 1616 ) //0650 - { - retVal = COMPRO_VIDEOMATE_U650F; - } - else if( productID == 1664 ) //0680 - { - retVal = COMPRO_VIDEOMATE_U680F; - } - break; - case 7040: //1B80 - if( productID == 54163 ) //D393 - { - retVal = GIGABYTE_GTU7300; - } - else if( productID == 54165 ) //D395 - { - retVal = PEAK_102569AGPK; - } - else if( productID == 54168 ) //D398 - { - retVal = ZAAPA_ZTMINDVBZP; - } - else if( productID == 54173 ) //D39D - { - retVal = SVEON_STV20; - } - else if( productID == 54180 ) //D3A4 - { - retVal = TWINTECH_UT40; - } - break; - case 7449: //1D19 - if( productID == 4353 ) //1101 - { - retVal = DEXATEK_LOGILINK_VG002A; - } - else if( productID == 4354 ) //1102 - { - retVal = DEXATEK_DIGIVOX_MINI_II_REV3; - } - else if( productID == 4355 ) //1103 - { - retVal = DEXATEK_5217_DVBT; - } - break; - case 7504: //1D50 - if( productID == 24713 ) //6089 - { - retVal = HACKRF_ONE; - } - else if( productID == 24737 ) //60A1 - { - retVal = AIRSPY; - } - else if( productID == 52245 ) //CC15 - { - retVal = HACKRF_ONE; - } - break; - case 8013: //1F4D - if( productID == 47107 ) //B803 - { - retVal = GTEK_T803; - } - else if( productID == 51203 ) //C803 - { - retVal = LIFEVIEW_LV5T_DELUXE; - } - else if( productID == 53894 ) //D286 - { - retVal = MYGICA_TD312; - } - else if( productID == 55299 ) //D803 - { - retVal = PROLECTRIX_DV107669; - } - break; - case 3277: //0CCD - if( productID == 169 ) //00A9 - { - retVal = TERRATEC_CINERGY_T_REV1; - } - else if( productID == 179 ) //00B3 - { - retVal = TERRATEC_NOXON_REV1_B3; - } - else if( productID == 180 ) //00B4 - { - retVal = TERRATEC_NOXON_REV1_B4; - } - else if( productID == 181 ) //00B5 - { - retVal = TERRATEC_NOXON_REV1_B7; - } - else if( productID == 198 ) //00C6 - { - retVal = TERRATEC_NOXON_REV1_C6; - } - else if( productID == 211 ) //00D3 - { - retVal = TERRATEC_CINERGY_T_REV3; - } - else if( productID == 215 ) //00D7 - { - retVal = TERRATEC_T_STICK_PLUS; - } - else if( productID == 224) //00E0 - { - retVal = TERRATEC_NOXON_REV2; - } - break; - case 9472: //2500 - if( productID == 2 ) //0002 - { - retVal = ETTUS_USRP_B100; - } - break; - default: - } - - return retVal; - } - - public String getVendorID() - { - return mVendorID; - } - - public String getDeviceID() - { - return mDeviceID; - } - - public String getVendorDescription() - { - return mVendorDescription; - } - - public String getDeviceDescription() - { - return mDeviceDescription; - } -} diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java deleted file mode 100644 index 24043844..00000000 --- a/src/source/tuner/TunerController.java +++ /dev/null @@ -1,202 +0,0 @@ -/******************************************************************************* - * 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 source.tuner; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Broadcaster; -import sample.Listener; -import sample.complex.ComplexBuffer; -import sample.complex.IComplexBufferProvider; -import source.ISourceEventListener; -import source.ISourceEventProcessor; -import source.SourceEvent; -import source.SourceEventListenerToProcessorAdapter; -import source.SourceException; -import source.tuner.channel.ChannelManager; -import source.tuner.channel.cic.CICTunerChannelSource; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.polyphase.PolyphaseChannelManager; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.channel.FrequencyController; -import source.tuner.channel.FrequencyController.Tunable; - -import java.util.SortedSet; -import java.util.concurrent.ConcurrentSkipListSet; - -public abstract class TunerController implements Tunable, ISourceEventProcessor, ISourceEventListener, - IComplexBufferProvider, Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(TunerController.class); - protected Broadcaster mSampleBroadcaster = new Broadcaster<>(); - - /* List of currently tuned channels being served to demod channels */ - private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); - private FrequencyController mFrequencyController; - private ChannelManager mChannelManager; - private Listener mSourceEventListener; - - /** - * Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels - * that are being fed samples from the tuner. - * - * @param minimumFrequency minimum uncorrected tunable frequency - * @param maximumFrequency maximum uncorrected tunable frequency - * @param middleUnusable is the +/- value center DC spike to avoid for channels - * @param usableBandwidth percentage of usable bandwidth relative to space at the extreme ends of the spectrum - * @throws SourceException - for any issues related to constructing the - * class, tuning a frequency, or setting the bandwidth - */ - public TunerController(long minimumFrequency, long maximumFrequency, long middleUnusable, double usableBandwidth) - { - mFrequencyController = new FrequencyController(this, minimumFrequency, maximumFrequency, - 0.0d, middleUnusable, usableBandwidth); - - mChannelManager = new PolyphaseChannelManager(mFrequencyController, this); - - mSourceEventListener = new SourceEventListenerToProcessorAdapter(this); - } - - /** - * Frequency controller for managing frequency and sample rate. - */ - public FrequencyController getFrequencyController() - { - return mFrequencyController; - } - - /** - * Channel manager for managing Digital Drop Chanel (DDC) tuner channel sources - */ - public ChannelManager getChannelManager() - { - return mChannelManager; - } - - /** - * Implements the ISourceEventListener interface to receive requests from sample consumers - */ - @Override - public Listener getSourceEventListener() - { - return mSourceEventListener; - } - - /** - * Applies the settings in the tuner configuration - */ - public abstract void apply(TunerConfiguration config) throws SourceException; - - /** - * Responds to requests to set the frequency - */ - @Override - public void process(SourceEvent sourceEvent ) throws SourceException - { - switch(sourceEvent.getEvent()) - { - case REQUEST_FREQUENCY_CHANGE: - getFrequencyController().setFrequency( sourceEvent.getValue().longValue() ); - break; - case REQUEST_START_SAMPLE_STREAM: - if(sourceEvent.getSource() instanceof CICTunerChannelSource) - { - addComplexBufferListener((CICTunerChannelSource)sourceEvent.getSource()); - } - break; - case REQUEST_STOP_SAMPLE_STREAM: - if(sourceEvent.getSource() instanceof CICTunerChannelSource) - { - removeComplexBufferListener((CICTunerChannelSource)sourceEvent.getSource()); - } - break; - default: - mLog.error("Ignoring unrecognized source event: " + sourceEvent.getEvent().name() + " from [" + - sourceEvent.getSource().getClass() + "]" ); - } - } - @Override - public boolean canTune(long frequency) - { - return getFrequencyController().canTune(frequency); - } - - /** - * Sets the listener to be notified any time that the tuner changes frequency - * or bandwidth/sample rate. - * - * Note: this is normally used by the Tuner. Any additional listeners can - * be registered on the tuner. - */ - public void addListener( ISourceEventProcessor processor ) - { - mFrequencyController.addListener(processor); - } - - /** - * Removes the frequency change listener - */ - public void removeListener( ISourceEventProcessor processor ) - { - mFrequencyController.removeFrequencyChangeProcessor(processor); - } - - /** - * Adds the listener to receive complex buffer samples - */ - @Override - public void addComplexBufferListener(Listener listener) - { - mSampleBroadcaster.addListener(listener); - } - - /** - * Removes the listener from receiving complex buffer samples - */ - @Override - public void removeComplexBufferListener(Listener listener) - { - mSampleBroadcaster.removeListener(listener); - } - - /** - * Indicates if there are any complex buffer listeners registered on this controller - */ - @Override - public boolean hasComplexBufferListeners() - { - return mSampleBroadcaster.hasListeners(); - } - - /** - * Broadcasts the buffer to any registered listeners - */ - protected void broadcast(ComplexBuffer complexBuffer) - { - mSampleBroadcaster.broadcast(complexBuffer); - } - - /** - * Implements the Listener interface to receive and distribute complex buffers from subclass implementations - */ - @Override - public void receive(ComplexBuffer complexBuffer) - { - broadcast(complexBuffer); - } -} diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java deleted file mode 100644 index f1ffa2d9..00000000 --- a/src/source/tuner/TunerEditor.java +++ /dev/null @@ -1,356 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner; - -import gui.control.JFrequencyControl; -import gui.editor.Editor; -import gui.editor.EmptyEditor; - -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.RowFilter; -import javax.swing.ScrollPaneConstants; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.TableRowSorter; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationFactory; -import source.tuner.configuration.TunerConfigurationModel; - -import com.jidesoft.swing.JideSplitPane; - -public class TunerEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( TunerEditor.class ); - - private TunerConfigurationModel mTunerConfigurationModel; - private JTable mTunerConfigurationTable; - private TableRowSorter mRowSorter; - private JFrequencyControl mFrequencyControl = new JFrequencyControl(); - private JLabel mSelectedTunerType = new JLabel( "No Tuner Selected" ); - private JButton mNewConfigurationButton = new JButton( "New" ); - private JButton mDeleteConfigurationButton = new JButton( "Delete" ); - private JButton mAssignConfigurationButton = new JButton( "Assign" ); - private JScrollPane mEditorScroller; - private Editor mEditor = new EmptyEditor<>( "a tuner" ); - private JideSplitPane mEditorSplitPane = new JideSplitPane(); - - public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) - { - mTunerConfigurationModel = tunerConfigurationModel; - init(); - } - - public void init() - { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]" ) ); - - JPanel listPanel = new JPanel(); - listPanel.setLayout( new MigLayout( "fill,wrap 3", "[grow,fill]", "[][][][grow,fill][]" ) ); - - listPanel.add( mSelectedTunerType, "span" ); - - mFrequencyControl.setEnabled( false ); - listPanel.add( mFrequencyControl, "span" ); - - mRowSorter = new TableRowSorter<>( mTunerConfigurationModel ); - mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); - mTunerConfigurationTable.setRowSorter( mRowSorter ); - mTunerConfigurationTable.setEnabled( false ); - mTunerConfigurationTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() - { - @Override - public void valueChanged( ListSelectionEvent event ) - { - if( !event.getValueIsAdjusting() ) - { - int row = mTunerConfigurationTable.getSelectedRow(); - - if( row >= 0 ) - { - int modelRow = mTunerConfigurationTable.convertRowIndexToModel( row ); - - if( modelRow >= 0 ) - { - setTunerConfiguration( mTunerConfigurationModel.getTunerConfiguration( modelRow ) ); - } - } - } - } - } ); - - JScrollPane tableScroller = new JScrollPane( mTunerConfigurationTable ); - tableScroller.setHorizontalScrollBarPolicy( - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); - listPanel.add( new JLabel( "Tuner Configurations" ), "span" ); - listPanel.add( tableScroller, "span" ); - - mNewConfigurationButton.setEnabled( false ); - mNewConfigurationButton.setToolTipText( "Create a new configuration for the currently selected tuner" ); - mNewConfigurationButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Tuner tuner = getItem(); - - String name = mTunerConfigurationModel - .getDistinctName( tuner.getTunerType(), tuner.getUniqueID() ); - - TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration( tuner.getTunerType(), - tuner.getUniqueID(), name ); - - mTunerConfigurationModel.addTunerConfiguration( config ); - } - } ); - listPanel.add( mNewConfigurationButton ); - - mDeleteConfigurationButton.setEnabled( false ); - mDeleteConfigurationButton.setToolTipText( "Deletes the currently selected tuner" ); - mDeleteConfigurationButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int choice = JOptionPane.showConfirmDialog( TunerEditor.this, - "Do you want to delete this tuner configuration?", - "Delete Tuner Configuration?", JOptionPane.YES_NO_OPTION ); - - if( choice == JOptionPane.YES_OPTION ) - { - TunerConfiguration selected = getSelectedTunerConfiguration(); - - mTunerConfigurationModel.removeTunerConfiguration( selected ); - mTunerConfigurationTable.setRowSelectionInterval( 0, 0 ); - } - } - } ); - listPanel.add( mDeleteConfigurationButton ); - - mAssignConfigurationButton.setEnabled( false ); - mAssignConfigurationButton.setToolTipText( "Assigns to the currently selected tuner configuration to the selected tuner" ); - mAssignConfigurationButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - TunerConfiguration selected = getSelectedTunerConfiguration(); - - mTunerConfigurationModel.assignTunerConfiguration( selected ); - setTunerConfiguration( getSelectedTunerConfiguration() ); - } - } ); - listPanel.add( mAssignConfigurationButton ); - - //Set preferred size on both scrollers to same width, so they split the space - listPanel.setPreferredSize( new Dimension( 100, 80 ) ); - JScrollPane listScroller = new JScrollPane( listPanel ); - listScroller.setPreferredSize( new Dimension( 100, 80 ) ); - - mEditorScroller = new JScrollPane( mEditor ); - mEditorScroller.setPreferredSize( new Dimension( 100, 80 ) ); - - mEditorSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); - mEditorSplitPane.add( listScroller ); - mEditorSplitPane.add( mEditorScroller ); - - add( mEditorSplitPane ); - } - - private TunerConfiguration getSelectedTunerConfiguration() - { - int tableRow = mTunerConfigurationTable.getSelectedRow(); - - if( tableRow >= 0 ) - { - int modelRow = mTunerConfigurationTable.convertRowIndexToModel( tableRow ); - - if( modelRow >= 0 ) - { - return mTunerConfigurationModel.getTunerConfiguration( modelRow ); - } - } - - return null; - } - - @Override - public void save() - { - //Unused - } - - @Override - public void setItem( Tuner tuner ) - { - //Cleanup previously selected tuner - if( hasItem() ) - { - Tuner previous = getItem(); - previous.getTunerController().removeListener( mFrequencyControl ); - mFrequencyControl.removeListener( previous.getTunerController() ); - } - - super.setItem( tuner ); - - if( hasItem() ) - { - mSelectedTunerType.setText( tuner.getName() ); - mFrequencyControl.setEnabled( true ); - mTunerConfigurationTable.setEnabled( true ); - mNewConfigurationButton.setEnabled( true ); - - //Change to an editor for this config's tuner type - mEditor = TunerConfigurationFactory.getEditor( getItem(), mTunerConfigurationModel ); - - mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType(), - tuner.getUniqueID() ) ); - - TunerConfiguration assigned = mTunerConfigurationModel - .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - - int modelIndex = mTunerConfigurationModel.getRowIndex( assigned ); - - if( modelIndex >= 0 ) - { - int viewIndex = mTunerConfigurationTable - .convertRowIndexToView( modelIndex ); - - if( viewIndex >= 0 ) - { - mTunerConfigurationTable - .setRowSelectionInterval( viewIndex, viewIndex ); - } - } - - //Link frequency control to the tuner - mFrequencyControl.addListener( tuner.getTunerController() ); - - //Link tuner to frequency control - tuner.getTunerController().addListener( mFrequencyControl ); - - //Set the displayed frequency without adjusting the tuner's frequency - mFrequencyControl.setFrequency( - tuner.getTunerController().getFrequencyController().getFrequency(), false ); - } - else - { - mSelectedTunerType.setText( "No Tuner Selected" ); - mFrequencyControl.setEnabled( false ); - mTunerConfigurationTable.setEnabled( false ); - mNewConfigurationButton.setEnabled( false ); - mRowSorter.setRowFilter( null ); - mEditor = new EmptyEditor(); - } - - //Swap out the editor - int split = mEditorSplitPane.getDividerLocation( 0 ); - mEditorSplitPane.remove( mEditorScroller ); - mEditorScroller = new JScrollPane( mEditor ); - mEditorSplitPane.add( mEditorScroller ); - mEditorSplitPane.setDividerLocation( 0, split ); - } - - private void setTunerConfiguration( TunerConfiguration config ) - { - if( config != null ) - { - if( !config.isAssigned() ) - { - if( !mAssignConfigurationButton.isEnabled() ) - { - mAssignConfigurationButton.setEnabled( true ); - } - if( !mDeleteConfigurationButton.isEnabled() ) - { - mDeleteConfigurationButton.setEnabled( true ); - } - } - else - { - if( mAssignConfigurationButton.isEnabled() ) - { - mAssignConfigurationButton.setEnabled( false ); - } - - if( mDeleteConfigurationButton.isEnabled() ) - { - mDeleteConfigurationButton.setEnabled( false ); - } - } - } - else - { - if( mDeleteConfigurationButton.isEnabled() ) - { - mDeleteConfigurationButton.setEnabled( false ); - } - - if( mAssignConfigurationButton.isEnabled() ) - { - mAssignConfigurationButton.setEnabled( false ); - } - } - - mEditor.setItem( config ); - } - - public class ConfigurationRowFilter extends RowFilter - { - private TunerType mTunerType; - private String mUniqueID; - - public ConfigurationRowFilter( TunerType type, String uniqueID ) - { - mTunerType = type; - mUniqueID = uniqueID; - } - - @Override - public boolean include( javax.swing.RowFilter.Entry entry ) - { - TunerConfiguration config = entry.getModel() - .getTunerConfiguration( entry.getIdentifier() ); - - return config != null && - config.getTunerType() == mTunerType && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( mUniqueID ); - } - } - -} diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java deleted file mode 100644 index 12d228aa..00000000 --- a/src/source/tuner/TunerManager.java +++ /dev/null @@ -1,463 +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 source.tuner; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceList; -import org.usb4java.LibUsb; -import source.SourceException; -import source.mixer.MixerManager; -import source.tuner.airspy.AirspyTuner; -import source.tuner.airspy.AirspyTunerController; -import source.tuner.fcd.FCDTuner; -import source.tuner.fcd.proV1.FCD1TunerController; -import source.tuner.fcd.proplusV2.FCD2TunerController; -import source.tuner.hackrf.HackRFTuner; -import source.tuner.hackrf.HackRFTunerController; -import source.tuner.rtl.RTL2832Tuner; -import source.tuner.rtl.RTL2832TunerController; -import source.tuner.rtl.e4k.E4KTunerController; -import source.tuner.rtl.r820t.R820TTunerController; - -import java.util.Collection; - -public class TunerManager -{ - private final static Logger mLog = LoggerFactory.getLogger(TunerManager.class); - - private MixerManager mMixerManager; - private TunerModel mTunerModel; - - - /** - * Application-wide LibUSB timeout processor for transfer buffers. All classes that need to use USB transfer - * buffers can register with this processor and the processor will auto-start and auto-stop while USB transfer - * processors are registered. - */ - public static final LibUSBTransferProcessor LIBUSB_TRANSFER_PROCESSOR; - - static - { - LIBUSB_TRANSFER_PROCESSOR = new LibUSBTransferProcessor(); - } - - public TunerManager(MixerManager mixerManager, TunerModel tunerModel) - { - mMixerManager = mixerManager; - mTunerModel = tunerModel; - - initTuners(); - } - - /** - * Performs cleanup of USB related issues - */ - public void dispose() - { - LibUsb.exit(null); - } - - /** - * Loads all USB tuners and USB/Mixer tuner devices - */ - private void initTuners() - { - DeviceList deviceList = new DeviceList(); - - int result = LibUsb.init(null); - - if(result != LibUsb.SUCCESS) - { - mLog.error("unable to initialize libusb [" + - LibUsb.errorName(result) + "]"); - } - else - { - mLog.info("LibUSB API Version: " + LibUsb.getApiVersion()); - mLog.info("LibUSB Version: " + LibUsb.getVersion()); - - result = LibUsb.getDeviceList(null, deviceList); - - if(result < 0) - { - mLog.error("unable to get device list from libusb [" + result + " / " + - LibUsb.errorName(result) + "]"); - } - else - { - mLog.info("discovered [" + result + "] attached USB devices"); - } - } - - for(Device device : deviceList) - { - DeviceDescriptor descriptor = new DeviceDescriptor(); - - result = LibUsb.getDeviceDescriptor(device, descriptor); - - if(result != LibUsb.SUCCESS) - { - mLog.error("unable to read device descriptor [" + - LibUsb.errorName(result) + "]"); - } - else - { - TunerInitStatus status = initTuner(device, descriptor); - - StringBuilder sb = new StringBuilder(); - - sb.append("usb device ["); - sb.append(String.format("%04X", descriptor.idVendor())); - sb.append(":"); - sb.append(String.format("%04X", descriptor.idProduct())); - - if(status.isLoaded()) - { - Tuner tuner = status.getTuner(); - - try - { - mTunerModel.addTuner(tuner); - sb.append("] LOADED: "); - sb.append(tuner.toString()); - } - catch(Exception e) - { - sb.append("] NOT LOADED: "); - sb.append(status.getInfo()); - sb.append(" Error:" + e.getMessage()); - } - } - else - { - sb.append("] NOT LOADED: "); - sb.append(status.getInfo()); - } - - mLog.info(sb.toString()); - } - } - - LibUsb.freeDeviceList(deviceList, true); - } - - private TunerInitStatus initTuner(Device device, - DeviceDescriptor descriptor) - { - if(device != null && descriptor != null) - { - TunerClass tunerClass = TunerClass.valueOf(descriptor.idVendor(), - descriptor.idProduct()); - - switch(tunerClass) - { - case AIRSPY: - return initAirspyTuner(device, descriptor); - case ETTUS_USRP_B100: - return initEttusB100Tuner(device, descriptor); - case FUNCUBE_DONGLE_PRO: - return initFuncubeProTuner(device, descriptor); - case FUNCUBE_DONGLE_PRO_PLUS: - return initFuncubeProPlusTuner(device, descriptor); - case HACKRF_ONE: - case RAD1O: - return initHackRFTuner(device, descriptor); - case COMPRO_VIDEOMATE_U620F: - case COMPRO_VIDEOMATE_U650F: - case COMPRO_VIDEOMATE_U680F: - case GENERIC_2832: - case GENERIC_2838: - case DEXATEK_5217_DVBT: - case DEXATEK_DIGIVOX_MINI_II_REV3: - case DEXATEK_LOGILINK_VG002A: - case GIGABYTE_GTU7300: - case GTEK_T803: - case LIFEVIEW_LV5T_DELUXE: - case MYGICA_TD312: - case PEAK_102569AGPK: - case PROLECTRIX_DV107669: - case SVEON_STV20: - case TERRATEC_CINERGY_T_REV1: - case TERRATEC_CINERGY_T_REV3: - case TERRATEC_NOXON_REV1_B3: - case TERRATEC_NOXON_REV1_B4: - case TERRATEC_NOXON_REV1_B7: - case TERRATEC_NOXON_REV1_C6: - case TERRATEC_NOXON_REV2: - case TERRATEC_T_STICK_PLUS: - case TWINTECH_UT40: - case ZAAPA_ZTMINDVBZP: - return initRTL2832Tuner(tunerClass, device, descriptor); - case UNKNOWN: - default: - break; - } - } - - return new TunerInitStatus(null, "Unknown Device"); - } - - private TunerInitStatus initAirspyTuner(Device device, - DeviceDescriptor descriptor) - { - try - { - AirspyTunerController airspyController = new AirspyTunerController(device); - - airspyController.init(); - - AirspyTuner tuner = new AirspyTuner(airspyController); - - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException se) - { - mLog.error("couldn't construct Airspy controller/tuner", se); - - return new TunerInitStatus(null, - "error constructing Airspy tuner controller"); - } - } - - - private TunerInitStatus initEttusB100Tuner(Device device, - DeviceDescriptor descriptor) - { - return new TunerInitStatus(null, "Ettus B100 tuner not currently " - + "supported"); - } - - private TunerInitStatus initFuncubeProTuner(Device device, DeviceDescriptor descriptor) - { - String reason = "NOT LOADED"; - - MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO.getTunerType()); - - if(dataline != null) - { - FCD1TunerController controller = new FCD1TunerController(dataline, device, descriptor); - - try - { - controller.init(); - - FCDTuner tuner = new FCDTuner(controller); - - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException e) - { - mLog.error("couldn't load funcube dongle pro tuner", e); - - reason = "error during initialization - " + e.getLocalizedMessage(); - } - } - else - { - reason = "couldn't find matching mixer dataline"; - } - - return new TunerInitStatus(null, "Funcube Dongle Pro tuner not " - + "loaded - " + reason); - } - - private TunerInitStatus initFuncubeProPlusTuner(Device device, DeviceDescriptor descriptor) - { - String reason = "NOT LOADED"; - - MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType()); - - if(dataline != null) - { - FCD2TunerController controller = new FCD2TunerController(dataline, device, descriptor); - - try - { - controller.init(); - - FCDTuner tuner = new FCDTuner(controller); - - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException e) - { - mLog.error("couldn't load funcube dongle pro plus tuner", e); - - reason = "error during initialization - " + - e.getLocalizedMessage(); - } - } - else - { - reason = "couldn't find matching mixer dataline"; - } - - return new TunerInitStatus(null, "Funcube Dongle Pro tuner not " - + "loaded - " + reason); - } - - private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor) - { - try - { - HackRFTunerController hackRFController = new HackRFTunerController(device, descriptor); - - hackRFController.init(); - - HackRFTuner tuner = new HackRFTuner(hackRFController); - - return new TunerInitStatus(tuner, "LOADED"); - } - catch(SourceException se) - { - mLog.error("couldn't construct HackRF controller/tuner", se); - - return new TunerInitStatus(null, - "error constructing HackRF tuner controller"); - } - } - - private TunerInitStatus initRTL2832Tuner(TunerClass tunerClass, - Device device, - DeviceDescriptor deviceDescriptor) - { - String reason = "NOT LOADED"; - - TunerType tunerType = tunerClass.getTunerType(); - - if(tunerType == TunerType.RTL2832_VARIOUS) - { - try - { - tunerType = RTL2832TunerController.identifyTunerType(device); - } - catch(SourceException e) - { - mLog.error("couldn't determine RTL2832 tuner type", e); - tunerType = TunerType.UNKNOWN; - } - } - - switch(tunerType) - { - case ELONICS_E4000: - try - { - E4KTunerController controller = new E4KTunerController(device, deviceDescriptor); - - controller.init(); - - RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller); - - return new TunerInitStatus(rtlTuner, "LOADED"); - } - catch(SourceException se) - { - return new TunerInitStatus(null, "Error constructing E4K tuner controller - " + - se.getLocalizedMessage()); - } - case RAFAELMICRO_R820T: - try - { - R820TTunerController controller = new R820TTunerController(device, deviceDescriptor); - - controller.init(); - - RTL2832Tuner rtlTuner = new RTL2832Tuner(tunerClass, controller); - - return new TunerInitStatus(rtlTuner, "LOADED"); - } - catch(SourceException se) - { - mLog.error("error constructing tuner", se); - - return new TunerInitStatus(null, "Error constructing R820T " - + "tuner controller - " + se.getLocalizedMessage()); - } - case FITIPOWER_FC0012: - case FITIPOWER_FC0013: - case RAFAELMICRO_R828D: - case UNKNOWN: - default: - reason = "SDRTRunk doesn't currently support RTL2832 " - + "Dongle with [" + tunerType.toString() + - "] tuner for tuner class[" + tunerClass.toString() + "]"; - break; - } - - return new TunerInitStatus(null, reason); - } - - /** - * Gets the first tuner mixer dataline that corresponds to the tuner class. - * - * Note: this method is not currently able to align multiple tuner mixer - * data lines of the same tuner type. If you have multiple Funcube Dongle - * tuners of the same TYPE, there is no guarantee that you will get the - * correct mixer. - * - * @param tunerClass - * @return - */ - private MixerTunerDataLine getMixerTunerDataLine(TunerType tunerClass) - { - Collection datalines = - mMixerManager.getMixerTunerDataLines(); - - for(MixerTunerDataLine mixerTDL : datalines) - { - if(mixerTDL.getMixerTunerType().getTunerClass() == tunerClass) - { - return mixerTDL; - } - } - - return null; - } - - public class TunerInitStatus - { - private Tuner mTuner; - private String mInfo; - - public TunerInitStatus(Tuner tuner, String info) - { - mTuner = tuner; - mInfo = info; - } - - public Tuner getTuner() - { - return mTuner; - } - - public String getInfo() - { - return mInfo; - } - - public boolean isLoaded() - { - return mTuner != null; - } - } -} diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java deleted file mode 100644 index 426ad719..00000000 --- a/src/source/tuner/TunerModel.java +++ /dev/null @@ -1,292 +0,0 @@ -package source.tuner; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import source.Source; -import source.SourceException; -import source.config.SourceConfigTuner; -import source.tuner.TunerEvent.Event; -import source.tuner.channel.TunerChannel; -import source.tuner.channel.TunerChannelSource; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationModel; - -import javax.swing.table.AbstractTableModel; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class TunerModel extends AbstractTableModel implements Listener -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger(TunerModel.class); - - public static final int TUNER_TYPE = 0; - public static final int TUNER_ID = 1; - public static final int SAMPLE_RATE = 2; - public static final int FREQUENCY = 3; - public static final int CHANNEL_COUNT = 4; - public static final int SPECTRAL_DISPLAY_MAIN = 5; - public static final int SPECTRAL_DISPLAY_NEW = 6; - - private static final String MHZ = " MHz"; - private static final String[] COLUMNS = - {"Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Spectral", "Display"}; - - private List mTuners = new ArrayList<>(); - private List> mTunerEventListeners = new ArrayList<>(); - private DecimalFormat mFrequencyFormat = new DecimalFormat("0.00000"); - private DecimalFormat mSampleRateFormat = new DecimalFormat("0.000"); - - private TunerConfigurationModel mTunerConfigurationModel; - - public TunerModel(TunerConfigurationModel tunerConfigurationModel) - { - mTunerConfigurationModel = tunerConfigurationModel; - } - - public TunerConfigurationModel getTunerConfigurationModel() - { - return mTunerConfigurationModel; - } - - /** - * List of Tuners currently in the model - */ - public List getTuners() - { - return mTuners; - } - - public Tuner getTuner(int index) - { - if(index < mTuners.size()) - { - return mTuners.get(index); - } - - return null; - } - - public void addTuners(List Tuners) - { - for(Tuner tuner : Tuners) - { - addTuner(tuner); - } - } - - /** - * Adds the Tuner to this model - */ - public void addTuner(Tuner tuner) - { - if(!mTuners.contains(tuner)) - { - //Get the tuner configuration and apply it to the tuner - this - //call should always produce a tuner configuration - TunerConfiguration config = mTunerConfigurationModel - .getTunerConfiguration(tuner.getTunerType(), tuner.getUniqueID()); - - try - { - tuner.getTunerController().apply(config); - - mTuners.add(tuner); - - int index = mTuners.indexOf(tuner); - - fireTableRowsInserted(index, index); - - tuner.addTunerChangeListener(this); - } - catch(SourceException se) - { - mLog.error("Couldn't apply tuner configuration to tuner - [" - + tuner.getTunerType().name() + "] with id [" - + tuner.getUniqueID() + "] - tuner will not be included"); - } - - } - } - - /** - * Removes the Tuner from this model - */ - public void removeTuner(Tuner tuner) - { - if(mTuners.contains(tuner)) - { - tuner.removeTunerChangeListener(this); - - int index = mTuners.indexOf(tuner); - - mTuners.remove(tuner); - - fireTableRowsDeleted(index, index); - } - } - - public void addListener(Listener listener) - { - mTunerEventListeners.add(listener); - } - - public void removeListener(Listener listener) - { - mTunerEventListeners.remove(listener); - } - - public void broadcast(TunerEvent event) - { - for(Listener listener : mTunerEventListeners) - { - listener.receive(event); - } - } - - /** - * Requests to display the first tuner in this model. Invoke this method - * after all listeners have registered and tuners have been added to this - * model, in order to inform the primary display to use the first tuner. - */ - public void requestFirstTunerDisplay() - { - if(mTuners.size() > 0) - { - broadcast(new TunerEvent(mTuners.get(0), - Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); - } - } - - @Override - public void receive(TunerEvent event) - { - if(event.getTuner() != null) - { - int index = mTuners.indexOf(event.getTuner()); - - if(index >= 0) - { - switch(event.getEvent()) - { - case CHANNEL_COUNT: - fireTableCellUpdated(index, CHANNEL_COUNT); - break; - case FREQUENCY: - fireTableCellUpdated(index, FREQUENCY); - mTunerConfigurationModel.tunerFrequencyChanged(event.getTuner()); - break; - case SAMPLE_RATE: - fireTableCellUpdated(index, SAMPLE_RATE); - break; - case REQUEST_MAIN_SPECTRAL_DISPLAY: - fireTableCellUpdated(index, SPECTRAL_DISPLAY_MAIN); - default: - break; - } - } - } - - broadcast(event); - } - - @Override - public int getRowCount() - { - return mTuners.size(); - } - - @Override - public int getColumnCount() - { - return COLUMNS.length; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - if(rowIndex < mTuners.size()) - { - Tuner tuner = mTuners.get(rowIndex); - - switch(columnIndex) - { - case TUNER_TYPE: - return tuner.getTunerType().getLabel(); - case TUNER_ID: - return tuner.getUniqueID(); - case SAMPLE_RATE: - double sampleRate = tuner.getTunerController().getFrequencyController().getSampleRate(); - - return mSampleRateFormat.format(sampleRate / 1E6D) + MHZ; - case FREQUENCY: - try - { - long frequency = tuner.getTunerController().getFrequencyController().getFrequency(); - - return mFrequencyFormat.format(frequency / 1E6D) + MHZ; - } - catch(Exception e) - { - return 0; - } - case CHANNEL_COUNT: - return tuner.getTunerController().getChannelCount(); - case SPECTRAL_DISPLAY_MAIN: - return "Main"; - case SPECTRAL_DISPLAY_NEW: - return "New"; - default: - break; - } - } - - return null; - } - - @Override - public String getColumnName(int columnIndex) - { - return COLUMNS[columnIndex]; - } - - /** - * Iterates current tuners to get a tuner channel source for the frequency - * specified in the channel config's source config object. - * - * Returns null if no tuner can source the channel - */ - public Source getSource(SourceConfigTuner config, int bandwidth) - { - TunerChannelSource retVal = null; - - TunerChannel tunerChannel = config.getTunerChannel(); - - tunerChannel.setBandwidth(bandwidth); - - Iterator it = mTuners.iterator(); - - Tuner tuner; - - while(it.hasNext() && retVal == null) - { - tuner = it.next(); - - try - { - retVal = tuner.getSourceManager().getSource(tunerChannel); - } - catch(Exception e) - { - mLog.error("Error obtaining channel source from tuner [" + tuner.getName() + "]", e); - } - } - - return retVal; - } - -} diff --git a/src/source/tuner/TunerSourceEditor.java b/src/source/tuner/TunerSourceEditor.java deleted file mode 100644 index 8d92360f..00000000 --- a/src/source/tuner/TunerSourceEditor.java +++ /dev/null @@ -1,95 +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 source.tuner; - -import gui.control.JFrequencyControl; -import gui.editor.Editor; -import net.miginfocom.swing.MigLayout; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; -import source.SourceEvent; -import source.ISourceEventProcessor; -import controller.channel.Channel; - -public class TunerSourceEditor extends Editor -{ - private static final long serialVersionUID = 1L; - private JFrequencyControl mFrequencyControl; - - public TunerSourceEditor() - { - init(); - } - - private void init() - { - setLayout( new MigLayout( "insets 0 0 0 0", "[left]", "" ) ); - mFrequencyControl = new JFrequencyControl(); - mFrequencyControl.setEnabled( false ); - mFrequencyControl.addListener( new ISourceEventProcessor() - { - @Override - public void process(SourceEvent event ) - { - setModified( true ); - } - } ); - add( mFrequencyControl ); - } - - public void save() - { - if( hasItem() && isModified() ) - { - SourceConfigTuner config = new SourceConfigTuner(); - config.setFrequency( mFrequencyControl.getFrequency() ); - getItem().setSourceConfiguration( config ); - } - - setModified( false ); - } - - @Override - public void setItem( Channel item ) - { - super.setItem( item ); - - if( hasItem() ) - { - SourceConfiguration config = getItem().getSourceConfiguration(); - - mFrequencyControl.setEnabled( true ); - - if( config instanceof SourceConfigTuner ) - { - mFrequencyControl.setFrequency( ((SourceConfigTuner)config).getFrequency(), false ); - setModified( false ); - } - else - { - mFrequencyControl.setFrequency( 101000000, false ); - setModified( true ); - } - } - else - { - mFrequencyControl.setEnabled( false ); - setModified( false ); - } - } -} diff --git a/src/source/tuner/TunerType.java b/src/source/tuner/TunerType.java deleted file mode 100644 index 156c6dd6..00000000 --- a/src/source/tuner/TunerType.java +++ /dev/null @@ -1,49 +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 source.tuner; - -public enum TunerType -{ - AIRSPY_R820T( "Airspy R820T" ), - ELONICS_E4000( "E4000" ), - ETTUS_WBX( "WBX" ), - ETTUS_VARIOUS( "Ettus Tuner" ), - FCI_FC2580( "FC2580" ), - FITIPOWER_FC0012( "FC0012" ), - FITIPOWER_FC0013( "FC0013" ), - FUNCUBE_DONGLE_PRO( "Funcube Dongle Pro" ), - FUNCUBE_DONGLE_PRO_PLUS( "Funcube Dongle Pro Plus" ), - HACKRF( "HackRF" ), - RAFAELMICRO_R820T( "R820T" ), - RAFAELMICRO_R828D( "R828D" ), - RTL2832_VARIOUS( "Generic" ), - TEST("Test"), - UNKNOWN( "Unknown" ); - - private String mLabel; - - private TunerType( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } -} diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java deleted file mode 100644 index 649a4eae..00000000 --- a/src/source/tuner/airspy/AirspyTuner.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package source.tuner.airspy; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.tuner.Tuner; -import source.tuner.TunerClass; -import source.tuner.TunerType; -import source.tuner.airspy.AirspyTunerController.BoardID; - -public class AirspyTuner extends Tuner -{ - private final static Logger mLog = LoggerFactory.getLogger( AirspyTuner.class ); - - public AirspyTuner( AirspyTunerController controller ) - { - super( "Airspy " + controller.getDeviceInfo().getSerialNumber(), controller ); - } - - public AirspyTunerController getController() - { - return (AirspyTunerController)getTunerController(); - } - - @Override - public String getUniqueID() - { - try - { - return getController().getDeviceInfo().getSerialNumber(); - } - catch( Exception e ) - { - mLog.error( "error getting serial number", e ); - } - - return BoardID.AIRSPY.getLabel(); - } - - @Override - public TunerClass getTunerClass() - { - return TunerClass.AIRSPY; - } - - @Override - public TunerType getTunerType() - { - return TunerType.AIRSPY_R820T; - } - - @Override - public double getSampleSize() - { - return 13.0; - } -} diff --git a/src/source/tuner/airspy/AirspyTunerController.java b/src/source/tuner/airspy/AirspyTunerController.java deleted file mode 100644 index 126321e7..00000000 --- a/src/source/tuner/airspy/AirspyTunerController.java +++ /dev/null @@ -1,1191 +0,0 @@ -package source.tuner.airspy; - -import org.apache.commons.io.EndianUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import source.SourceException; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.usb.USBTransferProcessor; -import source.tuner.usb.USBTunerController; - -import javax.usb.UsbException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -/** - * SDR Trunk - * Copyright (C) 2015-2017 Dennis Sheirer - * - * Ported from libairspy at: - * https://github.com/airspy/host/tree/master/libairspy - * ----------------------------------------------------------------------------- - * Copyright (c) 2013, Michael Ossmann - * Copyright (c) 2012, Jared Boone - * Copyright (c) 2014, Youssef Touil - * Copyright (c) 2014, Benjamin Vernoux - * Copyright (c) 2015, Ian Gilmour - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 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. - * Neither the name of AirSpy nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. - */ - -public class AirspyTunerController extends USBTunerController -{ - public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14; - public static final Gain SENSITIVITY_GAIN_DEFAULT = Gain.SENSITIVITY_10; - public static final int GAIN_MIN = 1; - public static final int GAIN_MAX = 22; - public static final int LNA_GAIN_MIN = 0; - public static final int LNA_GAIN_MAX = 14; - public static final int LNA_GAIN_DEFAULT = 7; - public static final int MIXER_GAIN_MIN = 0; - public static final int MIXER_GAIN_MAX = 15; - public static final int MIXER_GAIN_DEFAULT = 9; - public static final int IF_GAIN_MIN = 0; - public static final int IF_GAIN_MAX = 15; - public static final int IF_GAIN_DEFAULT = 9; - public static final long FREQUENCY_MIN = 24000000l; - public static final long FREQUENCY_MAX = 1800000000l; - public static final long FREQUENCY_DEFAULT = 101100000; - public static final double USABLE_BANDWIDTH_PERCENT = 0.90; - public static final AirspySampleRate DEFAULT_SAMPLE_RATE = - new AirspySampleRate(0, 10000000, "10.00 MHz"); - - private static final long USB_TIMEOUT_MS = 2000l; //milliseconds - private static final byte USB_INTERFACE = (byte)0x0; - private static final int USB_TRANSFER_BUFFER_SIZE = 262144; - private static final byte USB_REQUEST_IN = (byte)(LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); - private static final byte USB_REQUEST_OUT = (byte)(LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); - public static final DecimalFormat MHZ_FORMATTER = new DecimalFormat("#.00 MHz"); - - private final static Logger mLog = LoggerFactory.getLogger(AirspyTunerController.class); - - private Device mDevice; - private DeviceHandle mDeviceHandle; - - private AirspySampleAdapter mSampleAdapter = new AirspySampleAdapter(); - private AirspyDeviceInformation mDeviceInfo; - private List mSampleRates = new ArrayList<>(); - private int mSampleRate = 0; - private USBTransferProcessor mUSBTransferProcessor; - - public AirspyTunerController(Device device) throws SourceException - { - super(FREQUENCY_MIN, FREQUENCY_MAX, 0, USABLE_BANDWIDTH_PERCENT); - - mDevice = device; - } - - public void init() throws SourceException - { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) - { - if(result == LibUsb.ERROR_ACCESS) - { - mLog.error("Unable to access Airspy - insufficient permissions." - + " If you are running a Linux OS, have you installed the " - + "airspy rules file in \\etc\\udev\\rules.d ??"); - } - - throw new SourceException("Couldn't open airspy device - " + - LibUsb.strError(result)); - } - - try - { - claimInterface(); - } - catch(Exception e) - { - throw new SourceException("Airspy Tuner Controller - error while " - + "setting USB configuration, claiming USB interface or " - + "reset the kernel mode driver", e); - } - - try - { - setSamplePacking(false); - } - catch(LibUsbException | UsbException | UnsupportedOperationException e) - { - mLog.info("Sample packing is not supported by airspy firmware"); - } - - try - { - setReceiverMode(true); - } - catch(Exception e) - { - mLog.error("Couldn't enable airspy receiver mode", e); - } - - getFrequencyController().setFrequency(FREQUENCY_DEFAULT); - - try - { - determineAvailableSampleRates(); - } - catch(LibUsbException | UsbException e) - { - mLog.error("Error identifying available samples rates", e); - } - - try - { - setSampleRate(DEFAULT_SAMPLE_RATE); - } - catch(IllegalArgumentException | LibUsbException | UsbException e) - { - mLog.error("Setting sample rate is not supported by firmware", e); - } - - String deviceName = "Airspy " + getDeviceInfo().getSerialNumber(); - - mUSBTransferProcessor = new USBTransferProcessor(deviceName, mDeviceHandle, mSampleAdapter, - USB_TRANSFER_BUFFER_SIZE); - } - - - @Override - protected USBTransferProcessor getUSBTransferProcessor() - { - return mUSBTransferProcessor; - } - - - /** - * Claims the USB interface. If another application currently has - * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting - * will dictate if the interface is forcibly claimed from the other - * application - */ - private void claimInterface() throws SourceException - { - if(mDeviceHandle != null) - { - int result = LibUsb.kernelDriverActive(mDeviceHandle, USB_INTERFACE); - - if(result == 1) - { - - result = LibUsb.detachKernelDriver(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + - LibUsb.errorName(result) + "]"); - - throw new SourceException("couldn't detach kernel driver " - + "from device"); - } - } - - result = LibUsb.setConfiguration(mDeviceHandle, 1); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't set USB configuration 1 [" + - LibUsb.errorName(result) + "]"); - } - - result = LibUsb.claimInterface(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + - LibUsb.errorName(result) + "]"); - } - } - else - { - throw new SourceException("couldn't claim usb interface - no " - + "device handle"); - } - } - - public static String getTransferStatus(int status) - { - switch(status) - { - case 0: - return "TRANSFER COMPLETED (0)"; - case 1: - return "TRANSFER ERROR (1)"; - case 2: - return "TRANSFER TIMED OUT (2)"; - case 3: - return "TRANSFER CANCELLED (3)"; - case 4: - return "TRANSFER STALL (4)"; - case 5: - return "TRANSFER NO DEVICE (5)"; - case 6: - return "TRANSFER OVERFLOW (6)"; - default: - return "UNKNOWN TRANSFER STATUS (" + status + ")"; - } - } - - - @Override - public void apply(TunerConfiguration config) throws SourceException - { - if(config instanceof AirspyTunerConfiguration) - { - AirspyTunerConfiguration airspy = (AirspyTunerConfiguration)config; - - int sampleRate = airspy.getSampleRate(); - - AirspySampleRate rate = getSampleRate(sampleRate); - - if(rate == null) - { - if(!mSampleRates.isEmpty()) - { - rate = mSampleRates.get(0); - } - else - { - rate = DEFAULT_SAMPLE_RATE; - } - } - - try - { - setSampleRate(rate); - } - catch(UsbException e) - { - throw new SourceException("Couldn't set sample rate [" + - rate.toString() + "]", e); - } - - try - { - setIFGain(airspy.getIFGain()); - setMixerGain(airspy.getMixerGain()); - setLNAGain(airspy.getLNAGain()); - - setMixerAGC(airspy.isMixerAGC()); - setLNAAGC(airspy.isLNAAGC()); - - //Set the gain mode last, so custom values are already set, and - //linearity and sensitivity modes will automatically override - //the custom values. - setGain(airspy.getGain()); - - getFrequencyController().setFrequencyCorrection(airspy.getFrequencyCorrection()); - } - catch(Exception e) - { - throw new SourceException("Couldn't apply gain settings from " - + "airspy config", e); - } - - try - { - getFrequencyController().setFrequency(airspy.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - - } - else - { - throw new IllegalArgumentException("Invalid tuner config:" + - config.getClass()); - } - } - - @Override - public long getTunedFrequency() throws SourceException - { - return getFrequencyController().getTunedFrequency(); - } - - @Override - public void setTunedFrequency(long frequency) throws SourceException - { - if(FREQUENCY_MIN <= frequency && frequency <= FREQUENCY_MAX) - { - ByteBuffer buffer = ByteBuffer.allocateDirect(4); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - buffer.putInt((int)frequency); - - buffer.rewind(); - - try - { - write(Command.SET_FREQUENCY, 0, 0, buffer); - } - catch(UsbException e) - { - mLog.error("error setting frequency [" + frequency + "]", e); - - throw new SourceException("error setting frequency [" + - frequency + "]", e); - } - } - else - { - throw new SourceException("Frequency [" + frequency + "] outside " - + "of tunable range " + FREQUENCY_MIN + "-" + FREQUENCY_MAX); - } - } - - @Override - public double getCurrentSampleRate() - { - return mSampleRate; - } - - /** - * Sets the sample rate to the rate specified by the index value in the - * available sample rates map - * - * @param rate to a sample rate in the available samples rates map. - * @throws IllegalArgumentException if index is not a valid rate index - * @throws LibUsbException if there was a read error or if this operation - * is not supported by the current firmware - * @throws UsbException if there was a USB error - */ - public void setSampleRate(AirspySampleRate rate) throws LibUsbException, UsbException, SourceException - { - if(rate.getRate() != mSampleRate) - { - int result = readByte(Command.SET_SAMPLE_RATE, 0, rate.getIndex(), true); - - if(result != 1) - { - throw new UsbException("Error setting sample rate [" + - rate + "] rate - return value [" + result + "]"); - } - else - { - mSampleRate = rate.getRate(); - getFrequencyController().setSampleRate(mSampleRate); - } - } - } - - /** - * Returns a list of sample rates supported by the firmware version - */ - public List getSampleRates() - { - return mSampleRates; - } - - /** - * Airspy sample rate object that matches the current sample rate setting. - */ - public AirspySampleRate getAirspySampleRate() - { - return getSampleRate(mSampleRate); - } - - /** - * Airspy sample rate object that matches the specified rate in hertz, or - * null if there are no available sample rates for the tuner that match the - * argument value. - */ - public AirspySampleRate getSampleRate(int rate) - { - for(AirspySampleRate sampleRate : mSampleRates) - { - if(sampleRate.getRate() == rate) - { - return sampleRate; - } - } - - //We should never get to here ... - return null; - } - - /** - * Enables/Disables sample packing to allow two 12-bit samples to be packed - * into 3 bytes (enabled) or 4 bytes (disabled). - * - * @param enabled - * @throws UsbException if sample packing is not supported by the current - * device firmware or if there were usb communication issues - */ - public void setSamplePacking(boolean enabled) - throws LibUsbException, UsbException - { - int result = readByte(Command.SET_PACKING, 0, (enabled ? 1 : 0), true); - - if(result != 1) - { - throw new UsbException("Couldnt set sample packing enabled: " + enabled); - } - - /* If we didn't throw an exception above, then update the sample adapter - * to process samples accordingly */ - mSampleAdapter.setSamplePacking(enabled); - } - - /** - * Enables/disables the mixer automatic gain setting - * - * @param enabled - * @throws LibUsbException on unsuccessful read operation - * @throws UsbException on USB error - */ - public void setMixerAGC(boolean enabled) - throws LibUsbException, UsbException - { - int result = readByte(Command.SET_MIXER_AGC, 0, (enabled ? 1 : 0), true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set mixer AGC enabled: " + enabled); - } - } - - /** - * Enables/disables the low noise amplifier automatic gain setting - * - * @param enabled - * @throws LibUsbException on unsuccessful read operation - * @throws UsbException on USB error - */ - public void setLNAAGC(boolean enabled) throws LibUsbException, UsbException - { - int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set LNA AGC enabled: " + enabled); - } - } - - public void setGain(Gain gain) throws UsbException - { - if(gain != Gain.CUSTOM) - { - setMixerAGC(false); - setLNAAGC(false); - setLNAGain(gain.getLNA()); - setMixerGain(gain.getMixer()); - setIFGain(gain.getIF()); - } - } - - /** - * Sets LNA gain - * - * @param gain - value within range of LNA_GAIN_MIN to LNA_GAIN_MAX - * @throws LibUsbException on error in java USB wrapper - * @throws UsbException on error in USB transfer - * @throws IllegalArgumentException if gain value is invalid - */ - public void setLNAGain(int gain) - throws LibUsbException, UsbException, IllegalArgumentException - { - if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX) - { - int result = readByte(Command.SET_LNA_GAIN, 0, gain, true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set LNA gain to: " + gain); - } - } - else - { - throw new IllegalArgumentException("LNA gain value [" + gain + - "] is outside value range: " + LNA_GAIN_MIN + "-" + LNA_GAIN_MAX); - } - } - - /** - * Sets Mixer gain - * - * @param gain - value within range of MIXER_GAIN_MIN to MIXER_GAIN_MAX - * @throws LibUsbException on error in java USB wrapper - * @throws UsbException on error in USB transfer - * @throws IllegalArgumentException if gain value is invalid - */ - public void setMixerGain(int gain) - throws LibUsbException, UsbException, IllegalArgumentException - { - if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX) - { - int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set mixer gain to: " + gain); - } - } - else - { - throw new IllegalArgumentException("Mixer gain value [" + gain + - "] is outside value range: " + MIXER_GAIN_MIN + "-" + MIXER_GAIN_MAX); - } - } - - /** - * Sets IF (VGA) gain - * - * @param gain - value within range of VGA_GAIN_MIN to VGA_GAIN_MAX - * @throws LibUsbException on error in java USB wrapper - * @throws UsbException on error in USB transfer - * @throws IllegalArgumentException if gain value is invalid - */ - public void setIFGain(int gain) - throws LibUsbException, UsbException, IllegalArgumentException - { - if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX) - { - int result = readByte(Command.SET_VGA_GAIN, 0, gain, true); - - if(result != LibUsb.SUCCESS) - { - throw new UsbException("Couldnt set VGA gain to: " + gain); - } - } - else - { - throw new IllegalArgumentException("VGA gain value [" + gain + - "] is outside value range: " + IF_GAIN_MIN + "-" + IF_GAIN_MAX); - } - } - - public void setReceiverMode(boolean enabled) throws LibUsbException, UsbException - { - //Empty buffer to throw away - ByteBuffer buffer = ByteBuffer.allocateDirect(0); - - write(Command.RECEIVER_MODE, (enabled ? 1 : 0), 0, buffer); - } - - /** - * Queries the device for available sample rates. Will always provide at - * least the default 10 MHz sample rate. - */ - private void determineAvailableSampleRates() - throws LibUsbException, UsbException - { - mSampleRates.clear(); - - //Get a count of available sample rates. If we get an exception, then - //we're using an older firmware revision and only the default 10 MHz - //rate is supported - try - { - byte[] rawCount = readArray(Command.GET_SAMPLE_RATES, 0, 0, 4); - - if(rawCount != null) - { - int count = EndianUtils.readSwappedInteger(rawCount, 0); - - byte[] rawRates = readArray(Command.GET_SAMPLE_RATES, 0, - count, (count * 4)); - - for(int x = 0; x < count; x++) - { - int rate = EndianUtils.readSwappedInteger(rawRates, (x * 4)); - - mSampleRates.add(new AirspySampleRate(x, rate, formatSampleRate(rate))); - } - } - } - catch(LibUsbException e) - { - //Press on, nothing else to do here .. - } - - if(mSampleRates.isEmpty()) - { - mSampleRates.add(DEFAULT_SAMPLE_RATE); - } - } - - /** - * Formats the rate in hertz for display as megahertz - */ - private static String formatSampleRate(int rate) - { - return MHZ_FORMATTER.format((double)rate / 1E6d); - } - - /** - * Device information - */ - public AirspyDeviceInformation getDeviceInfo() - { - //Lazy initialization - if(mDeviceInfo == null) - { - readDeviceInfo(); - } - - return mDeviceInfo; - } - - /** - * Reads version information from the device and populates the info object - */ - private void readDeviceInfo() - { - if(mDeviceInfo == null) - { - mDeviceInfo = new AirspyDeviceInformation(); - } - - /* Board ID */ - try - { - int boardID = readByte(Command.BOARD_ID_READ, 0, 0, true); - - mDeviceInfo.setBoardID(boardID); - } - catch(LibUsbException | UsbException e) - { - mLog.error("Error reading airspy board ID", e); - } - - /* Version String */ - try - { - //NOTE: libairspy is internally reading 127 bytes, however airspy_info - //script is telling it to read 255 bytes ... things that make you go hmmmm - byte[] version = readArray(Command.VERSION_STRING_READ, 0, 0, 127); - - mDeviceInfo.setVersion(version); - } - catch(LibUsbException | UsbException e) - { - mLog.error("Error reading airspy version string", e); - } - - /* Part ID and Serial Number */ - try - { - //Read 6 x 32-bit integers = 24 bytes - byte[] serial = readArray( - Command.BOARD_PART_ID_SERIAL_NUMBER_READ, 0, 0, 24); - - mDeviceInfo.setPartAndSerialNumber(serial); - } - catch(LibUsbException | UsbException e) - { - mLog.error("Error reading airspy version string", e); - } - } - - /** - * Reads a single byte value from the device. - * - * @param command - airspy command - * @param value - value field for usb setup packet - * @param index - index field for usb setup packet - * @return - byte value as an integer - * @throws LibUsbException if the operation is unsuccesful - * @throws UsbException on any usb errors - */ - private int readByte(Command command, int value, int index, boolean signed) - throws LibUsbException, UsbException - { - if(mDeviceHandle != null) - { - ByteBuffer buffer = ByteBuffer.allocateDirect(1); - - int transferred = LibUsb.controlTransfer(mDeviceHandle, - USB_REQUEST_IN, - command.getValue(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_MS); - - if(transferred < 0) - { - throw new LibUsbException("read error", transferred); - } - - byte result = buffer.get(0); - - if(signed) - { - return (result & 0xFF); - } - else - { - return result; - } - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - /** - * Reads a multi-byte value from the device - * - * @param command - airspy command - * @param value - usb packet value - * @param index - usb packet index - * @param length - number of bytes to read - * @return - bytes read from the device - * @throws LibUsbException if quantity of bytes read doesn't equal the - * requested number of bytes - * @throws UsbException on error communicating with the device - */ - private byte[] readArray(Command command, int value, int index, int length) - throws LibUsbException, UsbException - { - if(mDeviceHandle != null) - { - ByteBuffer buffer = ByteBuffer.allocateDirect(length); - - int transferred = LibUsb.controlTransfer(mDeviceHandle, - USB_REQUEST_IN, - command.getValue(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_MS); - - if(transferred < 0) - { - throw new LibUsbException("read error", transferred); - } - - byte[] results = new byte[transferred]; - - buffer.get(results); - - return results; - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - /** - * Writes the buffer contents to the device - * - * @param command - airspy command - * @param value - usb packet value - * @param index - usb packet index - * @param buffer - data to write to the device - * @throws UsbException on error - */ - public void write(Command command, int value, int index, ByteBuffer buffer) - throws UsbException - { - if(mDeviceHandle != null) - { - int transferred = LibUsb.controlTransfer(mDeviceHandle, - USB_REQUEST_OUT, - command.getValue(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_MS); - - if(transferred < 0) - { - throw new LibUsbException("error writing byte buffer", - transferred); - } - else if(transferred != buffer.capacity()) - { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); - } - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - public enum GainMode - { - LINEARITY, - SENSITIVITY, - CUSTOM; - } - - public enum Gain - { - LINEARITY_1(1, 4, 0, 0), - LINEARITY_2(2, 5, 0, 0), - LINEARITY_3(3, 6, 1, 0), - LINEARITY_4(4, 7, 1, 0), - LINEARITY_5(5, 8, 1, 0), - LINEARITY_6(6, 9, 1, 0), - LINEARITY_7(7, 10, 2, 0), - LINEARITY_8(8, 10, 2, 1), - LINEARITY_9(9, 10, 0, 3), - LINEARITY_10(10, 10, 0, 5), - LINEARITY_11(11, 10, 1, 6), - LINEARITY_12(12, 10, 0, 8), - LINEARITY_13(13, 10, 0, 9), - LINEARITY_14(14, 10, 5, 8), - LINEARITY_15(15, 10, 6, 9), - LINEARITY_16(16, 11, 6, 9), - LINEARITY_17(17, 11, 7, 10), - LINEARITY_18(18, 11, 8, 12), - LINEARITY_19(19, 11, 9, 13), - LINEARITY_20(20, 11, 11, 14), - LINEARITY_21(21, 12, 12, 14), - LINEARITY_22(22, 13, 12, 14), - SENSITIVITY_1(1, 4, 0, 0), - SENSITIVITY_2(2, 4, 0, 1), - SENSITIVITY_3(3, 4, 0, 2), - SENSITIVITY_4(4, 4, 0, 3), - SENSITIVITY_5(5, 4, 1, 5), - SENSITIVITY_6(6, 4, 2, 6), - SENSITIVITY_7(7, 4, 2, 7), - SENSITIVITY_8(8, 4, 3, 8), - SENSITIVITY_9(9, 4, 4, 9), - SENSITIVITY_10(10, 5, 4, 9), - SENSITIVITY_11(11, 5, 4, 12), - SENSITIVITY_12(12, 5, 7, 12), - SENSITIVITY_13(13, 5, 8, 13), - SENSITIVITY_14(14, 5, 9, 14), - SENSITIVITY_15(15, 6, 9, 14), - SENSITIVITY_16(16, 7, 10, 14), - SENSITIVITY_17(17, 8, 10, 14), - SENSITIVITY_18(18, 9, 11, 14), - SENSITIVITY_19(19, 10, 12, 14), - SENSITIVITY_20(20, 11, 12, 14), - SENSITIVITY_21(21, 12, 12, 14), - SENSITIVITY_22(22, 13, 12, 14), - CUSTOM(1, 0, 0, 0); - - private int mValue; - private int mIF; - private int mMixer; - private int mLNA; - - private Gain(int value, int ifGain, int mixer, int lna) - { - mValue = value; - mIF = ifGain; - mMixer = mixer; - mLNA = lna; - } - - public int getValue() - { - return mValue; - } - - public int getIF() - { - return mIF; - } - - public int getMixer() - { - return mMixer; - } - - public int getLNA() - { - return mLNA; - } - - public static Gain getGain(GainMode mode, int value) - { - assert (GAIN_MIN <= value && value <= GAIN_MAX); - - switch(mode) - { - case LINEARITY: - for(Gain gain : getLinearityGains()) - { - if(gain.getValue() == value) - { - return gain; - } - } - return LINEARITY_GAIN_DEFAULT; - case SENSITIVITY: - for(Gain gain : getSensitivityGains()) - { - if(gain.getValue() == value) - { - return gain; - } - } - return SENSITIVITY_GAIN_DEFAULT; - case CUSTOM: - default: - return Gain.CUSTOM; - } - } - - public static GainMode getGainMode(Gain gain) - { - if(gain == CUSTOM) - { - return GainMode.CUSTOM; - } - else if(getLinearityGains().contains(gain)) - { - return GainMode.LINEARITY; - } - else if(getSensitivityGains().contains(gain)) - { - return GainMode.SENSITIVITY; - } - - return GainMode.CUSTOM; - } - - public static EnumSet getLinearityGains() - { - return EnumSet.range(LINEARITY_1, LINEARITY_22); - } - - public static EnumSet getSensitivityGains() - { - return EnumSet.range(SENSITIVITY_1, SENSITIVITY_22); - } - } - - - /** - * Airspy Board Identifier - */ - public enum BoardID - { - AIRSPY(0, "Airspy"), - UNKNOWN(-1, "Unknown"); - - private int mValue; - private String mLabel; - - private BoardID(int value, String label) - { - mValue = value; - mLabel = label; - } - - public int getValue() - { - return mValue; - } - - public String getLabel() - { - return mLabel; - } - - public static BoardID fromValue(int value) - { - if(value == 0) - { - return AIRSPY; - } - - return UNKNOWN; - } - } - - /** - * Airspy Commands - */ - public enum Command - { - INVALID(0), - RECEIVER_MODE(1), - SI5351C_WRITE(2), - SI5351C_READ(3), - R820T_WRITE(4), - R820T_READ(5), - SPIFLASH_ERASE(6), - SPIFLASH_WRITE(7), - SPIFLASH_READ(8), - BOARD_ID_READ(9), - VERSION_STRING_READ(10), - BOARD_PART_ID_SERIAL_NUMBER_READ(11), - SET_SAMPLE_RATE(12), - SET_FREQUENCY(13), - SET_LNA_GAIN(14), - SET_MIXER_GAIN(15), - SET_VGA_GAIN(16), - SET_LNA_AGC(17), - SET_MIXER_AGC(18), - MS_VENDOR_COMMAND(19), - SET_RF_BIAS_COMMAND(20), - GPIO_WRITE(21), - GPIO_READ(22), - GPIO_DIR__WRITE(23), - GPIO_DIR_READ(24), - GET_SAMPLE_RATES(25), - SET_PACKING(26); - - private int mValue; - - private Command(int value) - { - mValue = value; - } - - public byte getValue() - { - return (byte)mValue; - } - - public static Command fromValue(int value) - { - if(0 <= value && value <= 25) - { - return Command.values()[value]; - } - - return INVALID; - } - } - - public enum ReceiverMode - { - OFF(0), - ON(1); - - private int mValue; - - private ReceiverMode(int value) - { - mValue = value; - } - - public int getValue() - { - return mValue; - } - } - - /** - * General Purpose Input/Output Ports (accessible on the airspy board) - */ - public enum GPIOPort - { - PORT_0(0), - PORT_1(1), - PORT_2(2), - PORT_3(3), - PORT_4(4), - PORT_5(5), - PORT_6(6), - PORT_7(7); - - private int mValue; - - private GPIOPort(int value) - { - mValue = value; - } - - public int getValue() - { - return mValue; - } - } - - /** - * General Purpose Input/Output Pins (accessible on the airspy board) - */ - public enum GPIOPin - { - PIN_0(0), - PIN_1(1), - PIN_2(2), - PIN_3(3), - PIN_4(4), - PIN_5(5), - PIN_6(6), - PIN_7(7), - PIN_8(8), - PIN_9(9), - PIN_10(10), - PIN_11(11), - PIN_12(12), - PIN_13(13), - PIN_14(14), - PIN_15(15), - PIN_16(16), - PIN_17(17), - PIN_18(18), - PIN_19(19), - PIN_20(20), - PIN_21(21), - PIN_22(22), - PIN_23(23), - PIN_24(24), - PIN_25(25), - PIN_26(26), - PIN_27(27), - PIN_28(28), - PIN_29(29), - PIN_30(30), - PIN_31(31); - - private int mValue; - - private GPIOPin(int value) - { - mValue = value; - } - - public int getValue() - { - return mValue; - } - } -} diff --git a/src/source/tuner/airspy/AirspyTunerEditor.java b/src/source/tuner/airspy/AirspyTunerEditor.java deleted file mode 100644 index d5dce404..00000000 --- a/src/source/tuner/airspy/AirspyTunerEditor.java +++ /dev/null @@ -1,770 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015-2016 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 source.tuner.airspy; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; -import java.util.List; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JSeparator; -import javax.swing.JSlider; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import source.SourceException; -import source.tuner.airspy.AirspyTunerController.Gain; -import source.tuner.airspy.AirspyTunerController.GainMode; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; -import source.tuner.configuration.TunerConfigurationEvent; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.configuration.TunerConfigurationEvent.Event; - -public class AirspyTunerEditor extends TunerConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = - LoggerFactory.getLogger( AirspyTunerEditor.class ); - - private JTextField mConfigurationName; - private JButton mTunerInfo; - - private JComboBox mSampleRateCombo; - private JSpinner mFrequencyCorrection; - - private JComboBox mGainModeCombo; - - private JSlider mMasterGain; - private JLabel mMasterGainValueLabel; - - private JSlider mIFGain; - private JLabel mIFGainValueLabel; - - private JSlider mLNAGain; - private JLabel mLNAGainValueLabel; - - private JSlider mMixerGain; - private JLabel mMixerGainValueLabel; - - private JCheckBox mLNAAGC; - private JCheckBox mMixerAGC; - - private AirspyTunerController mController; - private boolean mLoading; - - public AirspyTunerEditor( TunerConfigurationModel tunerConfigurationModel, - AirspyTuner tuner ) - { - super( tunerConfigurationModel ); - mController = tuner.getController(); - - init(); - } - - private AirspyTunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (AirspyTunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][grow]" ) ); - - add( new JLabel( "Airspy Tuner Configuration" ), "span,align center" ); - - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( AirspyTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - /** - * Sample Rate - */ - add( new JLabel( "Sample Rate:" ) ); - - List rates = mController.getSampleRates(); - - mSampleRateCombo = new JComboBox( - new DefaultComboBoxModel( rates.toArray( - new AirspySampleRate[ rates.size() ] ) ) ); - mSampleRateCombo.setEnabled( false ); - mSampleRateCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem(); - - try - { - mController.setSampleRate( rate ); - save(); - } - catch ( LibUsbException | UsbException | SourceException e1 ) - { - JOptionPane.showMessageDialog( AirspyTunerEditor.this, - "Couldn't set sample rate to " + rate.getLabel() ); - - mLog.error( "Error setting airspy sample rate", e1 ); - } - } - } ); - - add( mSampleRateCombo ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.getFrequencyController().setFrequencyCorrection( value ); - } - catch ( SourceException e1 ) - { - mLog.error( "Error setting frequency correction value", e1 ); - } - - save(); - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); - - add( new JSeparator(), "span,growx,push" ); - - /** - * Gain Mode - */ - add( new JLabel( "Gain Mode:" ) ); - mGainModeCombo = new JComboBox( GainMode.values() ); - mGainModeCombo.setEnabled( false ); - mGainModeCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); - - updateGainComponents( mode ); - - if( hasItem() ) - { - AirspyTunerConfiguration airspy = getConfiguration(); - - Gain gain = airspy.getGain(); - - if( mode == GainMode.CUSTOM ) - { - mIFGain.setValue( airspy.getIFGain() ); - mMixerGain.setValue( airspy.getMixerGain() ); - mMixerAGC.setSelected( airspy.isMixerAGC() ); - mLNAGain.setValue( airspy.getLNAGain() ); - mLNAAGC.setSelected( airspy.isLNAAGC() ); - } - else - { - mMixerAGC.setSelected( false ); - mLNAAGC.setSelected( false ); - mMasterGain.setValue( gain.getValue() ); - mIFGain.setValue( gain.getIF() ); - mMixerGain.setValue( gain.getMixer() ); - mLNAGain.setValue( gain.getLNA() ); - } - } - - save(); - } - } ); - - add( mGainModeCombo, "wrap" ); - - /** - * Gain - */ - add( new JLabel( "Master:" ) ); - - mMasterGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.GAIN_MIN, - AirspyTunerController.GAIN_MAX, - AirspyTunerController.GAIN_MIN ); - mMasterGain.setMajorTickSpacing( 1 ); - mMasterGain.setPaintTicks( true ); - - mMasterGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); - int value = mMasterGain.getValue(); - Gain gain = Gain.getGain( mode, value ); - - try - { - mController.setGain( gain ); - save(); - mMasterGainValueLabel.setText( String.valueOf( gain.getValue() ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy gain to:" + gain.name(), e ); - JOptionPane.showMessageDialog( mMasterGain, "Couldn't set gain value to " + - gain.getValue() ); - } - } - } ); - - add( mMasterGain, "span 2" ); - - mMasterGainValueLabel = new JLabel( "0" ); - add( mMasterGainValueLabel ); - - add( new JLabel( "IF:" ) ); - - mIFGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.IF_GAIN_MIN, - AirspyTunerController.IF_GAIN_MAX, - AirspyTunerController.IF_GAIN_MIN ); - - mIFGain.setMajorTickSpacing( 1 ); - mIFGain.setPaintTicks( true ); - - mIFGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - int gain = mIFGain.getValue(); - - try - { - mController.setIFGain( gain ); - save(); - mIFGainValueLabel.setText( String.valueOf( gain ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy IF gain to:" + gain, e ); - - JOptionPane.showMessageDialog( mIFGain, "Couldn't set IF gain value to " + gain ); - } - } - } ); - - add( mIFGain, "span 2" ); - - mIFGainValueLabel = new JLabel( "0" ); - add( mIFGainValueLabel ); - - /** - * Mixer Gain - */ - add( new JLabel( "Mixer:" ) ); - - mMixerGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.MIXER_GAIN_MIN, - AirspyTunerController.MIXER_GAIN_MAX, - AirspyTunerController.MIXER_GAIN_MIN ); - - mMixerGain.setMajorTickSpacing( 1 ); - mMixerGain.setPaintTicks( true ); - - mMixerGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - int gain = mMixerGain.getValue(); - - try - { - mController.setMixerGain( gain ); - save(); - mMixerGainValueLabel.setText( String.valueOf( gain ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy Mixer gain to:" + gain, e ); - JOptionPane.showMessageDialog( mIFGain, "Couldn't set Mixer gain value to " + gain ); - } - } - } ); - - add( mMixerGain, "span 2" ); - - mMixerGainValueLabel = new JLabel( "0" ); - add( mMixerGainValueLabel ); - - add( new JLabel( "LNA:" ) ); - - mLNAGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.LNA_GAIN_MIN, - AirspyTunerController.LNA_GAIN_MAX, - AirspyTunerController.LNA_GAIN_MIN ); - - mLNAGain.setMajorTickSpacing( 1 ); - mLNAGain.setPaintTicks( true ); - - mLNAGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - int gain = mLNAGain.getValue(); - - try - { - mController.setLNAGain( gain ); - save(); - mLNAGainValueLabel.setText( String.valueOf( gain ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy LNA gain to:" + gain, e ); - JOptionPane.showMessageDialog( mIFGain, "Couldn't set LNA gain value to " + gain ); - } - } - } ); - - add( mLNAGain, "span 2" ); - - mLNAGainValueLabel = new JLabel( "0" ); - add( mLNAGainValueLabel ); - - mMixerAGC = new JCheckBox( "Mixer AGC" ); - mMixerAGC.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - try - { - mController.setMixerAGC( mMixerAGC.isSelected() ); - mMixerGain.setEnabled( !mMixerAGC.isSelected() ); - - save(); - } - catch ( Exception e1 ) - { - mLog.error( "Error setting Mixer AGC Enabled" ); - } - } - } ); - - add( mMixerAGC, "span 2,center" ); - - mLNAAGC = new JCheckBox( "LNA AGC" ); - mLNAAGC.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - try - { - mController.setLNAAGC( mLNAAGC.isSelected() ); - mLNAGain.setEnabled( !mLNAAGC.isSelected() ); - - save(); - } - catch ( Exception e1 ) - { - mLog.error( "Error setting LNA AGC Enabled" ); - } - - } - } ); - - add( mLNAAGC, "span 2,center" ); - } - - /** - * Updates the enabled state of each of the gain controls according to the - * specified gain mode. The master gain control is enabled for linearity - * and sensitivity and the individual gain controls are disabled, and - * vice-versa for custom mode. - */ - private void updateGainComponents( GainMode mode ) - { - switch( mode ) - { - case LINEARITY: - if( !mMasterGain.isEnabled() ) - { - mMasterGain.setEnabled( true ); - mMasterGainValueLabel.setEnabled( true ); - } - if( mIFGain.isEnabled() ) - { - mIFGain.setEnabled( false ); - mIFGainValueLabel.setEnabled( false ); - } - if( mLNAGain.isEnabled() ) - { - mLNAAGC.setEnabled( false ); - mLNAGainValueLabel.setEnabled( false ); - mLNAGain.setEnabled( false ); - } - if( mMixerGain.isEnabled() ) - { - mMixerGainValueLabel.setEnabled( false ); - mMixerAGC.setEnabled( false ); - mMixerGain.setEnabled( false ); - } - break; - case SENSITIVITY: - if( !mMasterGain.isEnabled() ) - { - mMasterGain.setEnabled( true ); - mMasterGainValueLabel.setEnabled( true ); - } - if( mIFGain.isEnabled() ) - { - mIFGain.setEnabled( false ); - mIFGainValueLabel.setEnabled( false ); - } - if( mLNAGain.isEnabled() ) - { - mLNAAGC.setEnabled( false ); - mLNAGainValueLabel.setEnabled( false ); - mLNAGain.setEnabled( false ); - } - if( mMixerGain.isEnabled() ) - { - mMixerGainValueLabel.setEnabled( false ); - mMixerAGC.setEnabled( false ); - mMixerGain.setEnabled( false ); - } - break; - case CUSTOM: - if( mMasterGain.isEnabled() ) - { - mMasterGain.setEnabled( false ); - mMasterGainValueLabel.setEnabled( false ); - } - if( !mIFGain.isEnabled() ) - { - mIFGain.setEnabled( true ); - mIFGainValueLabel.setEnabled( true ); - } - if( !mLNAGain.isEnabled() ) - { - mLNAAGC.setEnabled( true ); - mLNAGainValueLabel.setEnabled( true ); - mLNAGain.setEnabled( true ); - } - if( !mMixerGain.isEnabled() ) - { - mMixerGainValueLabel.setEnabled( true ); - mMixerAGC.setEnabled( true ); - mMixerGain.setEnabled( true ); - } - default: - break; - } - } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - AirspyTunerConfiguration config = getConfiguration(); - config.setName( mConfigurationName.getText() ); - config.setSampleRate( ((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - Gain gain = Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ); - - config.setGain( gain ); - config.setIFGain( mIFGain.getValue() ); - config.setMixerGain( mMixerGain.getValue() ); - config.setLNAGain( mLNAGain.getValue() ); - config.setMixerAGC( mMixerAGC.isSelected() ); - config.setLNAAGC( mLNAAGC.isSelected() ); - - getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); - } - } - - private AirspySampleRate fromValue( int value ) - { - List rates = mController.getSampleRates(); - - for( AirspySampleRate rate: rates ) - { - if( rate.getRate() == value ) - { - return rate; - } - } - - if( rates.size() > 0 ) - { - return rates.get( 0 ); - } - - return null; - } - - /** - * Sets all controls to the argument enabled state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mSampleRateCombo.isEnabled() != enabled ) - { - mSampleRateCombo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mGainModeCombo.isEnabled() != enabled ) - { - mGainModeCombo.setEnabled( enabled ); - } - - if( mMasterGain.isEnabled() != enabled ) - { - mMasterGain.setEnabled( enabled ); - } - - if( mIFGain.isEnabled() != enabled ) - { - mIFGain.setEnabled( enabled ); - } - - if( mMixerGain.isEnabled() != enabled ) - { - mMixerGain.setEnabled( enabled ); - } - - if( mMixerAGC.isEnabled() != enabled ) - { - mMixerAGC.setEnabled( enabled ); - } - - if( mLNAGain.isEnabled() != enabled ) - { - mLNAGain.setEnabled( enabled ); - } - - if( mLNAAGC.isEnabled() != enabled ) - { - mLNAAGC.setEnabled( enabled ); - } - } - - /** - * Applies the tuner configuration to the editor. If the configuration is assigned to the - * currently selected tuner, then the settings are applied. - */ - @Override - public void setItem( TunerConfiguration config ) - { - super.setItem( config ); - - mLoading = true; - - if( hasItem() && getItem().isAssigned() ) - { - AirspyTunerConfiguration airspy = getConfiguration(); - - setControlsEnabled( true ); - - mConfigurationName.setText( airspy.getName() ); - - AirspySampleRate rate = fromValue( airspy.getSampleRate() ); - - if( rate != null ) - { - mSampleRateCombo.setSelectedItem( rate ); - } - - mFrequencyCorrection.setValue( airspy.getFrequencyCorrection() ); - - GainMode mode = Gain.getGainMode( airspy.getGain() ); - - //This will set the master, IF, Mixer, LNA and AGC controls - mGainModeCombo.setSelectedItem( mode ); - - if( mode == GainMode.CUSTOM ) - { - mMasterGain.setValue( airspy.getGain().getValue() ); - mIFGain.setValue( airspy.getIFGain() ); - - if( airspy.isMixerAGC() ) - { - mMixerAGC.setSelected( airspy.isMixerAGC() ); - mMixerGain.setEnabled( false ); - } - else - { - mMixerGain.setValue( airspy.getMixerGain() ); - } - - if( airspy.isLNAAGC() ) - { - mLNAAGC.setSelected( airspy.isLNAAGC() ); - mLNAGain.setEnabled( false ); - } - else - { - mLNAGain.setValue( airspy.getLNAGain() ); - } - } - else - { - mMixerAGC.setSelected( false ); - mLNAAGC.setSelected( false ); - - Gain gain = airspy.getGain(); - - mMasterGain.setValue( gain.getValue() ); - mIFGain.setValue( gain.getIF() ); - mMixerGain.setValue( gain.getMixer() ); - mLNAGain.setValue( gain.getLNA() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( hasItem() ? getItem().getName() : "" ); - } - - mLoading = false; - } - - private String getTunerInfo() - { - StringBuilder sb = new StringBuilder(); - - AirspyDeviceInformation info = mController.getDeviceInfo(); - - sb.append( "

Airspy Tuner

" ); - sb.append( "Serial: " ); - sb.append( info.getSerialNumber() ); - sb.append( "
" ); - - sb.append( "Firmware: " ); - String[] firmware = info.getVersion().split( " " ); - sb.append( firmware.length > 1 ? firmware[ 0 ] : info.getVersion() ); - sb.append( "
" ); - - sb.append( "Part: " ); - sb.append( info.getPartNumber() ); - sb.append( "
" ); - - sb.append( "Board ID: " ); - sb.append( info.getBoardID().getLabel() ); - sb.append( "
" ); - - return sb.toString(); - } -} diff --git a/src/source/tuner/channel/ChannelManager.java b/src/source/tuner/channel/ChannelManager.java deleted file mode 100644 index 264b9258..00000000 --- a/src/source/tuner/channel/ChannelManager.java +++ /dev/null @@ -1,337 +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 source.tuner.channel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.Listener; -import source.Source; -import source.SourceEvent; -import source.SourceException; -import source.tuner.Tuner; -import source.tuner.channel.cic.CICTunerChannelSource; - -import java.util.SortedSet; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.RejectedExecutionException; - -public abstract class ChannelManager implements Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(ChannelManager.class); - - private FrequencyController mFrequencyController; - private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); - - /** - * Channel manager provides access to Digital Drop Channel (DDC) tuner channel sources and automatic updating of - * a frequency controller to ensure that the center tuned frequency is correct for the provided DDC channels. - * - * @param frequencyController with a center tuned frequency that can be adjusted - */ - public ChannelManager(FrequencyController frequencyController) - { - mFrequencyController = frequencyController; - } - - protected FrequencyController getFrequencyController() - { - return mFrequencyController; - } - - /** - * Creates a tuner channel source for the tuner channel if possible. - * - * @param tunerChannel to source - * @return tuner channel source or null if the tuner channel cannot be sourced by this channel manager - */ - public TunerChannelSource getSource(TunerChannel tunerChannel) - { - TunerChannelSource tunerChannelSource = null; - - if(canTune(tunerChannel)) - { - //Add the channel so that we can calculate the new center frequency - mTunedChannels.add(tunerChannel); - - long centerFrequency = calculateCenterFrequency(); - - if(centerFrequency != getFrequencyController().getFrequency()) - { - try - { - getFrequencyController().setLockedFrequency(centerFrequency); - tunerChannelSource = getTunerChannelSource(tunerChannel); - } - catch(SourceException se) - { - mLog.error("Error setting tuner frequency while creating new tuner channel source for channel: " + - tunerChannel, se); - } - } - } - - //If we're not successful, remove the tuner channel from the sorted set - if(tunerChannelSource == null && mTunedChannels.contains(tunerChannel)) - { - mTunedChannels.remove(tunerChannel); - } - - return tunerChannelSource; - } - - /** - * Releases the source and performs any cleanup. - * @param source to release that was previously provided by this channel manager - */ - public void releaseSource(TunerChannelSource source) - { - mTunedChannels.remove(source.getTunerChannel()); - } - - /** - * Determine the center frequency to use for the current set of tuner channels. - */ - protected abstract long calculateCenterFrequency(); - - /** - * Constructs the actual tuner channel source for the tuner channel. This method assumes that the tuner channel - * can be tuned and that the tuner is correctly center tuned to source the channel. - * @param tunerChannel to source - * @return tuner channel source or null if there are any errors in creating the tuner channel source - */ - protected abstract TunerChannelSource getTunerChannelSource(TunerChannel tunerChannel); - - /** - * Indicates if channel along with all of the other currently sourced channels can fit within the tunable bandwidth. - */ - private boolean canTune(TunerChannel channel) - { - //Make sure we're within the tunable frequency range of this tuner - if(getFrequencyController().getMinimumFrequency() < channel.getMinFrequency() && - getFrequencyController().getMaximumFrequency() > channel.getMaxFrequency()) - { - //If this is the first lock, then we're good - if(mTunedChannels.isEmpty()) - { - return true; - } - else - { - int usableBandwidth = getFrequencyController().getUsableBandwidth(); - long minLockedFrequency = mTunedChannels.first().getMinFrequency(); - long maxLockedFrequency = mTunedChannels.last().getMaxFrequency(); - - //Requested channel is within current locked channel frequency range - if(minLockedFrequency <= channel.getMinFrequency() && channel.getMaxFrequency() <= maxLockedFrequency) - { - return true; - } - - //Requested channel is higher than min locked frequency - if(channel.getMaxFrequency() > minLockedFrequency && - channel.getMaxFrequency() - minLockedFrequency <= usableBandwidth) - { - return true; - } - - //Requested channel is lower than the max locked frequency - if(channel.getMinFrequency() <= maxLockedFrequency && - maxLockedFrequency - channel.getMinFrequency() <= usableBandwidth) - { - return true; - } - } - } - - return false; - } - - /** - * Constructs a digital drop channel (DDC) as a tuner channel from from the tuner, or returns null if the channel - * cannot be sourced from the tuner. - * - * This controller can't provide a channel if the channel, along with the current set of sourced channels, can't be - * accomodated within the current tuner bandwidth, or if a center frequency cannot be calculated that will ensure - * all of the channels can be accomodated and any defined central DC spike avoided. - * - * @param tuner to source the channel from - * @param channel with defined center frequency and bandwidth - * @return fully constructed tuner channel or null - * @throws RejectedExecutionException if the decimation processor has an error - */ - public TunerChannelSource getChannel(Tuner tuner, TunerChannel channel) throws RejectedExecutionException - { - TunerChannelSource source = null; - - if(canTune(channel)) - { - try - { - mTunedChannels.add(channel); - - if(requiresLOUpdate(channel)) - { - updateLOFrequency(); - } - - source = new CICTunerChannelSource(tuner, channel); - } - catch(SourceException se) - { - mTunedChannels.remove(channel); - source = null; - } - } - - return source; - } - - public int getChannelCount() - { - return mTunedChannels.size(); - } - - /** - * Indicates if the tuner's LO frequency must be updated in order to accommodate the tuner channel - */ - private boolean requiresLOUpdate(TunerChannel channel) throws SourceException - { - int usableBandwidth = getFrequencyController().getUsableBandwidth(); - - long minimumTunedFrequency = getFrequencyController().getFrequency() - (usableBandwidth / 2); - boolean isBelow = channel.getMinFrequency() < minimumTunedFrequency; - - long maximumTunedFrequency = getFrequencyController().getFrequency() + (usableBandwidth / 2); - boolean isAbove = channel.getMaxFrequency() > maximumTunedFrequency; - - long centerBlackoutBandwidth = getFrequencyController().getCenterBlackoutBandwidth(); - boolean overlapsCenter = centerBlackoutBandwidth > 0 && - channel.overlaps(getFrequencyController().getFrequency() - centerBlackoutBandwidth, - getFrequencyController().getFrequency() + centerBlackoutBandwidth); - - return isAbove || isBelow || overlapsCenter; - } - - /** - * Releases the currently sourced tuner channel from this tuner and shuts down the tuner if no other sources exist. - */ - public void releaseChannel(TunerChannelSource tunerChannelSource) - { - if(tunerChannelSource != null) - { - mTunedChannels.remove(tunerChannelSource.getTunerChannel()); - } - } - - /** - * Sets the Local Oscillator frequency to accomodate the current set of tuned channels. - * - * If there is only a single tuned channel, it is placed immediately to the right of the usable bandwidth right - * of the central DC spike. - * - * Otherwise, it places the highest channel frequency at the upper end of the tuner bandwidth, and then iteratively - * moves the center frequency higher until all channels fit within the bandwidth and none of the channels overlap - * any defined central DC spike unusable region. If a center tune frequency cannot be calculated, throw an - * exception so that the most recently added channel can be removed. - * - * Note: the tuned frequency is not changed until a legitimate new frequency can be calculated. If an exception - * is thrown, the current frequency is retained, so that the recently added channel can be removed and all other - * channels can continue as previously arranged. - * - * @throws SourceException if the set of tuner channels, including a recently added channel, cannot be tuned - * within the current tuner bandwidth and any central DC spike unusable region. - */ - private void updateLOFrequency() throws SourceException - { - long candidateFrequency; - - boolean isValidCandidateFrequency = true; - - //If there is only 1 channel set the center frequency so that the channel is positioned to the right of center - if(mTunedChannels.size() == 1) - { - candidateFrequency = mTunedChannels.first().getMinFrequency() - - getFrequencyController().getCenterBlackoutBandwidth() + 1; - } - else - { - long minLockedFrequency = mTunedChannels.first().getMinFrequency(); - long maxLockedFrequency = mTunedChannels.last().getMaxFrequency(); - - //Start by placing the highest frequency channel at the high end of the spectrum - candidateFrequency = maxLockedFrequency - (getFrequencyController().getUsableBandwidth() / 2); - - //Iterate the channels and make sure that none of them overlap the center DC spike buffer, if one exists - if(getFrequencyController().getCenterBlackoutBandwidth() > 0) - { - boolean processingRequired = true; - - while(isValidCandidateFrequency && processingRequired) - { - processingRequired = false; - - long centerBlackout = getFrequencyController().getCenterBlackoutBandwidth(); - - long minAvoid = candidateFrequency - centerBlackout; - long maxAvoid = candidateFrequency + centerBlackout; - - //If any of the center channel(s) overlap the central DC spike avoid area, we'll iteratively - //increase the tuned frequency causing the set of channels to move left in the tuned bandwidth until - //we either find a good center tune frequency, or we walk the lowest frequency channel out of the - //minimum tuned range, in which case we'll throw an exception indicating we don't have a solution. - for(TunerChannel channel : mTunedChannels) - { - if(channel.overlaps(minAvoid, maxAvoid)) - { - //Calculate a tuned frequency adjustment that places this overlapping channel just to the - //left of the central DC spike avoid zone - long adjustment = channel.getMaxFrequency() - minAvoid + 1; - - //If the candidate frequency doesn't push the lowest channel out of bounds, make adjustment - if(candidateFrequency + adjustment - - (getFrequencyController().getUsableBandwidth() / 2) <= minLockedFrequency) - { - candidateFrequency += adjustment; - processingRequired = true; - } - //Otherwise, punt and indicate that we can't find a center frequency or add the channel - else - { - isValidCandidateFrequency = false; - } - - //break out of the for/each loop, so that we can start over again with all of the channels - break; - } - } - } - } - } - - if(isValidCandidateFrequency) - { - mFrequencyController.setLockedFrequency(candidateFrequency); - } - else - { - throw new SourceException("Couldn't calculate viable center frequency from set of tuner channels"); - } - } - -} diff --git a/src/source/tuner/channel/FrequencyController.java b/src/source/tuner/channel/FrequencyController.java deleted file mode 100644 index 8ec85198..00000000 --- a/src/source/tuner/channel/FrequencyController.java +++ /dev/null @@ -1,344 +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 source.tuner.channel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import source.ISourceEventProcessor; -import source.InvalidFrequencyException; -import source.SourceEvent; -import source.SourceException; - -import java.util.ArrayList; -import java.util.List; - -public class FrequencyController -{ - private final static Logger mLog = LoggerFactory.getLogger(FrequencyController.class); - - private Tunable mTunable; - private long mFrequency = 101100000; - private long mTunedFrequency = 101100000; - private long mMinimumFrequency; - private long mMaximumFrequency; - private double mFrequencyCorrection = 0.0d; - private double mSampleRate = 0.0d; - private long mCenterBlackoutBandwidth; - private double mUsableBandwidthPercentage; - private boolean mLocked; - - private List mProcessors = new ArrayList<>(); - - public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection, - long centerBlackoutBandwidth, double usableBandwidthPercentage) - { - mTunable = tunable; - mMinimumFrequency = minFrequency; - mMaximumFrequency = maxFrequency; - mFrequencyCorrection = frequencyCorrection; - mCenterBlackoutBandwidth = centerBlackoutBandwidth; - mUsableBandwidthPercentage = usableBandwidthPercentage; - } - - public boolean isLocked() - { - return mLocked; - } - - /** - * Sets the locked status. When this control is locked, the sample rate cannot be changed and the center - * tuned frequency can only be changed via the setLockedFrequency() method. - * - * Note: this method is package-private and is intended to be used (only) by the ChannelManager. - * - * @param locked - */ - void setLocked(boolean locked) - { - mLocked = locked; - } - - - /** - * Set sample rate/bandwidth in hertz - */ - public void setSampleRate(int sampleRate) throws SourceException - { - if(isLocked()) - { - throw new FrequencyControllerLockedException(); - } - - mSampleRate = sampleRate; - - broadcastSampleRateChange(); - } - - /** - * Get sample rate/bandwidth in hertz - */ - public double getSampleRate() - { - return mSampleRate; - } - - /** - * Get frequency in hertz - */ - public long getFrequency() - { - return mFrequency; - } - - /** - * Set frequency - * - * @param frequency in hertz - * @throws SourceException if tunable doesn't support tuning a corrected - * version of the requested frequency - */ - public void setFrequency(long frequency) throws SourceException - { - if(isLocked()) - { - throw new FrequencyControllerLockedException(); - } - - setFrequency(frequency, true); - } - - /** - * Sets the frequency when the frequency controller is locked. Note: this method is package-private and is - * only intended to be used by the ChannelManager. - * - * @param frequency to set as the center tuned frequency - * @throws SourceException if there is an error while setting the frequency - */ - void setLockedFrequency(long frequency) throws SourceException - { - setFrequency(frequency, true); - } - - /** - * Indicates if the specified frequency can be tuned (ie is within min/max frequency) by this controller. - * @param frequency to evaluate - * @return true if the frequency falls within the tuning range of this controller - */ - public boolean canTune(long frequency) - { - return getMinimumFrequency() <= frequency && frequency <= getMaximumFrequency(); - } - - /** - * Set frequency with optional broadcast of frequency change event. This - * method supports changing the frequency correction value without - * broadcasting a frequency change event. - */ - private void setFrequency(long frequency, boolean broadcastChange) throws SourceException - { - long tunedFrequency = getTunedFrequency(frequency); - - if(tunedFrequency < mMinimumFrequency) - { - throw new InvalidFrequencyException("Requested frequency not valid", frequency, mMinimumFrequency); - } - - if(tunedFrequency > mMaximumFrequency) - { - throw new InvalidFrequencyException("Requested frequency not valid", frequency, mMaximumFrequency); - } - - mFrequency = frequency; - - mTunedFrequency = tunedFrequency; - - if(mTunable != null) - { - mTunable.setTunedFrequency(mTunedFrequency); - } - - /* Broadcast to all listeners that the frequency has changed */ - if(broadcastChange) - { - broadcastFrequencyChange(); - } - } - - public long getTunedFrequency() - { - return mTunedFrequency; - } - - public long getMinimumFrequency() - { - return mMinimumFrequency; - } - - public long getMaximumFrequency() - { - return mMaximumFrequency; - } - - /** - * Calculate the tuned frequency by adding frequency correction to the - * corrected frequency. - * - * @param correctedFrequency - */ - private long getTunedFrequency(long correctedFrequency) - { - return (long)((double)correctedFrequency / - (1.0 + (mFrequencyCorrection / 1000000.0))); - } - - /** - * Calculate the corrected frequency by subtracting frequency correction - * from the tuned frequency. - * - * @param tunedFrequency - */ - @SuppressWarnings("unused") - private long getCorrectedFrequency(long tunedFrequency) - { - return (long)((double)tunedFrequency / - (1.0 - (mFrequencyCorrection / 1000000.0))); - } - - public double getFrequencyCorrection() - { - return mFrequencyCorrection; - } - - public void setFrequencyCorrection(double correction) throws SourceException - { - mFrequencyCorrection = correction; - - if(mFrequency > 0) - { - setFrequency(mFrequency, true); - } - - broadcastFrequencyCorrectionChange(); - } - - /** - * Adds listener to receive frequency change events - */ - public void addListener(ISourceEventProcessor processor) - { - if(!mProcessors.contains(processor)) - { - mProcessors.add(processor); - } - } - - /** - * Removes listener from receiving frequency change events - */ - public void removeFrequencyChangeProcessor(ISourceEventProcessor processor) - { - mProcessors.remove(processor); - } - - - /** - * Broadcasts a change/update to the current (uncorrected) frequency or the - * bandwidth/sample rate value. - */ - protected void broadcastFrequencyChange() throws SourceException - { - broadcast(SourceEvent.frequencyChange(mFrequency)); - } - - /** - * Broadcast a frequency error/correction value change - */ - protected void broadcastFrequencyCorrectionChange() throws SourceException - { - broadcast(SourceEvent.frequencyCorrectionChange((int)mFrequencyCorrection)); - } - - /** - * Broadcasts a sample rate change - */ - protected void broadcastSampleRateChange() throws SourceException - { - broadcast(SourceEvent.sampleRateChange(mSampleRate)); - } - - public void broadcast(SourceEvent event) throws SourceException - { - for(ISourceEventProcessor processor : mProcessors) - { - processor.process(event); - } - } - - /** - * Identifies the bandwidth of a blackout frequency range centered about the center frequency. This is - * used to block the frequency range where a DC spike may exist. - * @return center blackout bandwidth in hertz - */ - public long getCenterBlackoutBandwidth() - { - return mCenterBlackoutBandwidth; - } - - /** - * Percentage of the available bandwidth that is usable. Sources with filter roll-off at the ends of the - * spectral range can limit the usable bandwidth to avoid providing samples at these spectral ends. - * - * @return bandwidth percentage (0.0 <> 1.0) - */ - public double getUsableBandwidthPercentage() - { - return mUsableBandwidthPercentage; - } - - /** - * Usable bandwidth in hertz calculated as the sample rate constrained by the usable bandwidth percentage. - */ - public int getUsableBandwidth() - { - return (int)(getSampleRate() * getUsableBandwidthPercentage()); - } - - public interface Tunable - { - /** - * Gets the tuned frequency of the device - */ - public long getTunedFrequency() throws SourceException; - - /** - * Sets the tuned frequency of the device - */ - public void setTunedFrequency(long frequency) throws SourceException; - - /** - * Gets the current bandwidth setting of the device - */ - public double getCurrentSampleRate() throws SourceException; - - /** - * Indicates if this tunable can tune the frequency - */ - public boolean canTune(long frequency); - } -} diff --git a/src/source/tuner/channel/FrequencyControllerLockedException.java b/src/source/tuner/channel/FrequencyControllerLockedException.java deleted file mode 100644 index 9ad9f09a..00000000 --- a/src/source/tuner/channel/FrequencyControllerLockedException.java +++ /dev/null @@ -1,35 +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 source.tuner.channel; - -import source.SourceException; - -public class FrequencyControllerLockedException extends SourceException -{ - private static final long serialVersionUID = 1L; - - /** - * Indicates an attempt to adjust frequency or sample rate on a locked frequency controller. - */ - public FrequencyControllerLockedException() - { - super( "Frequency controller is locked -- frequency and sample rate cannot be changed until the controller" + - " is unlocked by the channel manager" ); - } -} diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java deleted file mode 100644 index 67fbaf04..00000000 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ /dev/null @@ -1,384 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.configuration; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.table.AbstractTableModel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import source.tuner.Tuner; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfigurationEvent.Event; - -public class TunerConfigurationModel extends AbstractTableModel -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( TunerConfigurationModel.class ); - - public static final int TUNER_TYPE = 0; - public static final int CONFIGURATION_NAME = 1; - public static final int ASSIGNED = 2; - private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name", "Assigned" }; - - private List> mConfigurationChangeListeners = - new ArrayList<>(); - private List mTunerConfigurations = new ArrayList<>(); - - /** - * Table model for managing tuner configurations. Provides tuner configuration events to - * registered listeners - */ - public TunerConfigurationModel() - { - } - - /** - * Adds the list of tuner configurations to the model - */ - public void addTunerConfigurations( List configs ) - { - for( TunerConfiguration config: configs ) - { - addTunerConfiguration( config ); - } - } - - /** - * Returns of list of tuner configurations from the model - */ - public List getTunerConfigurations() - { - return mTunerConfigurations; - } - - /** - * Adds the tuner configuration to the model firing a table model row add - * event and broadcasting a tuner configuration event. - */ - public void addTunerConfiguration( TunerConfiguration config ) - { - if( !mTunerConfigurations.contains( config ) && - config.getUniqueID() != null && - config.getName() != null ) - { - mTunerConfigurations.add( config ); - - int index = mTunerConfigurations.indexOf( config ); - - fireTableRowsInserted( index, index ); - - broadcast( new TunerConfigurationEvent( config, Event.ADD ) ); - } - } - - /** - * Removes the tuner configuration from the model firing a table model row - * delete event and broadcasting a tuner configuration event. - */ - public void removeTunerConfiguration( TunerConfiguration config ) - { - if( mTunerConfigurations.contains( config ) ) - { - int index = mTunerConfigurations.indexOf( config ); - - mTunerConfigurations.remove( config ); - - fireTableRowsDeleted( index, index ); - - broadcast( new TunerConfigurationEvent( config, Event.REMOVE ) ); - } - } - - /** - * Removes all tuner configurations and assignments from the model and - * fires a table clear event and broadcasts a tuner configuration event for - * each row removed. - */ - public void clear() - { - for( TunerConfiguration config: mTunerConfigurations ) - { - broadcast( new TunerConfigurationEvent( config, Event.REMOVE ) ); - } - - int size = mTunerConfigurations.size(); - - mTunerConfigurations.clear(); - - fireTableRowsDeleted( 0, size - 1 ); - } - - /** - * Registers a tuner configuration event listener - */ - public void addListener( Listener listener ) - { - mConfigurationChangeListeners.add( listener ); - } - - /** - * Removes the tuner configuration event listener - */ - public void removeListener( Listener listener ) - { - mConfigurationChangeListeners.remove( listener ); - } - - /** - * Broadcasts a tuner configuration event - */ - public void broadcast( TunerConfigurationEvent event ) - { - for( Listener listener: mConfigurationChangeListeners ) - { - listener.receive( event ); - } - } - - /** - * Designates the configuration argument as the currently assigned configuration and unassigns - * any other configurations with the same tuner type and unique ID. - */ - public void assignTunerConfiguration( TunerConfiguration configToAssign ) - { - if( configToAssign != null ) - { - List configs = - getTunerConfigurations( configToAssign.getTunerType(), configToAssign.getUniqueID() ); - - for( TunerConfiguration config: configs ) - { - if( config.isAssigned() ) - { - config.setAssigned( false ); - - int index = mTunerConfigurations.indexOf( config ); - fireTableCellUpdated( index, ASSIGNED ); - broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); - } - } - - configToAssign.setAssigned( true ); - int index = mTunerConfigurations.indexOf( configToAssign ); - fireTableCellUpdated( index, ASSIGNED ); - broadcast( new TunerConfigurationEvent( configToAssign, Event.CHANGE ) ); - } - } - - /** - * Returns the tuner configuration that is assigned to the tuner with the - * tuner type and uniqueID, or selects the first existing configuration that - * matches the tuner type and unique ID and assigns it, or creates a new - * tuner configuration and assigns it to the tuner. - * - * This method will always return a tuner configuration that is assigned to - * the tuner type and unique ID. - */ - public TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID ) - { - List configs = getTunerConfigurations( type, uniqueID ); - - for( TunerConfiguration config: configs ) - { - if( config.isAssigned() && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( uniqueID ) ) - { - return config; - } - } - - //Reuse an existing configuration to assign - if( !configs.isEmpty() ) - { - configs.get( 0 ).setAssigned( true ); - return configs.get( 0 ); - } - - String name = getDistinctName( type, uniqueID ); - - //We didn't find the config so create a new one and assign it - TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration( type, uniqueID, name ); - - config.setAssigned( true ); - - addTunerConfiguration( config ); - - return config; - } - - /** - * Indicates if a configuration exists in this model matching the tuner type - * and unique ID and name. - */ - public boolean hasTunerConfiguration( TunerType type, String uniqueID, String name ) - { - for( TunerConfiguration config: mTunerConfigurations ) - { - if( config.getTunerType() == type && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( uniqueID ) && - config.getName() != null && - config.getName().contentEquals( name ) ) - { - return true; - } - } - - return false; - } - - /** - * Creates a configuration name for the tuner configuration that is distinct - * among the existing tuner configurations of the same type and uniqueID - */ - public String getDistinctName( TunerType type, String uniqueID ) - { - int counter = 0; - - String name = "New"; - - while( hasTunerConfiguration( type, uniqueID, name ) ) - { - counter++; - - name = "New (" + counter + ")"; - } - - return name; - } - - /** - * Returns all tuner configurations for the specified tuner type - */ - public List getTunerConfigurations( TunerType type, String uniqueID ) - { - List configs = new ArrayList<>(); - - for( TunerConfiguration config: mTunerConfigurations ) - { - if( config.getTunerType() == type && - config.getUniqueID() != null && - config.getUniqueID().contentEquals( uniqueID ) ) - { - configs.add( config ); - } - } - - return configs; - } - - /** - * Column names for the table model - */ - @Override - public String getColumnName( int column ) - { - return COLUMN_HEADERS[ column ]; - } - - /** - * Tuner configuration model row count - */ - @Override - public int getRowCount() - { - return mTunerConfigurations.size(); - } - - /** - * Tuner configuration model column count - */ - @Override - public int getColumnCount() - { - return COLUMN_HEADERS.length; - } - - /** - * Returns the row index for the specified configuration or -1 - */ - public int getRowIndex( TunerConfiguration configuration ) - { - if( mTunerConfigurations.contains( configuration ) ) - { - return mTunerConfigurations.indexOf( configuration ); - } - - return -1; - } - - /** - * Returns the configuration at the specified index or null - */ - public TunerConfiguration getTunerConfiguration( int index ) - { - if( index >= 0 && index < mTunerConfigurations.size() ) - { - return mTunerConfigurations.get( index ); - } - - return null; - } - - /** - * Returns the value at the indicated table cell - */ - @Override - public Object getValueAt( int row, int column ) - { - if( row >= 0 && row < mTunerConfigurations.size() ) - { - TunerConfiguration config = mTunerConfigurations.get( row ); - - switch( column ) - { - case TUNER_TYPE: - return config.getTunerType().getLabel(); - case CONFIGURATION_NAME: - return config.getName(); - case ASSIGNED: - return config.isAssigned() ? "*" : null; - default: - break; - } - } - - return null; - } - - /** - * Stores the latest tuned frequency for the tuner in the currently assigned configuration - */ - public void tunerFrequencyChanged( Tuner tuner ) - { - TunerConfiguration assigned = getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - - assigned.setFrequency( tuner.getTunerController().getFrequencyController().getFrequency() ); - - broadcast( new TunerConfigurationEvent( assigned, Event.CHANGE ) ); - } -} diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java deleted file mode 100644 index 18c89fc6..00000000 --- a/src/source/tuner/fcd/FCDTuner.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.fcd; - -import source.tuner.Tuner; -import source.tuner.TunerClass; -import source.tuner.TunerType; - -public class FCDTuner extends Tuner -{ - public FCDTuner(FCDTunerController controller) - { - super( controller.getConfiguration().toString(), controller); - } - - public void dispose() - { - getController().dispose(); - } - - public FCDTunerController getController() - { - return (FCDTunerController)getTunerController(); - } - - @Override - public TunerClass getTunerClass() - { - return getController().getTunerClass(); - } - - @Override - public TunerType getTunerType() - { - return getController().getTunerType(); - } - - @Override - public String getUniqueID() - { - return getController().getUSBAddress(); - } - - @Override - public double getSampleSize() - { - return 16.0; - } -} diff --git a/src/source/tuner/fcd/FCDTunerController.java b/src/source/tuner/fcd/FCDTunerController.java deleted file mode 100644 index b85721d0..00000000 --- a/src/source/tuner/fcd/FCDTunerController.java +++ /dev/null @@ -1,657 +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 source.tuner.fcd; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import sample.Listener; -import sample.adapter.ShortAdapter; -import sample.complex.ComplexBuffer; -import source.SourceException; -import source.mixer.ComplexMixer; -import source.tuner.MixerTunerDataLine; -import source.tuner.TunerClass; -import source.tuner.TunerController; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.fcd.proV1.FCD1TunerController.Block; - -import javax.sound.sampled.AudioFormat; -import javax.usb.UsbClaimException; -import javax.usb.UsbException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.IntBuffer; - -public abstract class FCDTunerController extends TunerController -{ - private final static Logger mLog = LoggerFactory.getLogger(FCDTunerController.class); - - public static final double USABLE_BANDWIDTH_PERCENT = 1.0; - public static final int DC_SPIKE_AVOID_BUFFER = 5000; - - public final static byte FCD_INTERFACE = (byte)0x2; - public final static byte FCD_ENDPOINT_IN = (byte)0x82; - public final static byte FCD_ENDPOINT_OUT = (byte)0x2; - - private Device mDevice; - private DeviceDescriptor mDeviceDescriptor; - private DeviceHandle mDeviceHandle; - - private FCDConfiguration mConfiguration = new FCDConfiguration(); - protected ComplexMixer mComplexMixer; - - - /** - * Generic FCD tuner controller - contains functionality common across both - * funcube dongle tuners - * - * @param device - * @param descriptor - * @param minTunableFrequency - * @param maxTunableFrequency - */ - public FCDTunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor, String tunerName, - int minTunableFrequency, int maxTunableFrequency, AudioFormat audioFormat) - { - super(minTunableFrequency, maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); - mDevice = device; - mDeviceDescriptor = descriptor; - - try - { - mFrequencyController.setSampleRate((int)audioFormat.getSampleRate()); - } - catch(SourceException se) - { - mLog.error("Error setting sample rate to [" + audioFormat.getSampleRate() + "]", se); - } - - mComplexMixer = new ComplexMixer( mixerTDL.getTargetDataLine(), audioFormat, tunerName, new ShortAdapter(), - mSampleBroadcaster); - } - - /** - * Overrides the super class functionality to auto-start the complex mixer and provide samples to listeners - */ - @Override - public void addComplexBufferListener(Listener listener) - { - boolean hasExistingListeners = hasComplexBufferListeners(); - - super.addComplexBufferListener(listener); - - if(!hasExistingListeners) - { - mComplexMixer.start(); - } - } - - /** - * Overrides the super class functionality to auto-stop the complex mixer and stop sample stream when there are no - * more registered listeners - */ - @Override - public void removeComplexBufferListener(Listener listener) - { - super.removeComplexBufferListener(listener); - - if(!hasComplexBufferListeners()) - { - mComplexMixer.stop(); - } - } - - /** - * Initializes the controller by opening the USB device and claiming the - * HID interface. - * - * Invoke this method after constructing this class to setup the - * controller. - * - * @throws SourceException if cannot open and claim the USB device - */ - public void init() throws SourceException - { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) - { - mDeviceHandle = null; - - throw new SourceException("libusb couldn't open funcube usb device [" + LibUsb.errorName(result) + "]"); - } - - claimInterface(); - } - - /** - * Disposes of resources. Closes the USB device and interface. - */ - public void dispose() - { - if(mDeviceHandle != null) - { - try - { - LibUsb.close(mDeviceHandle); - } - catch(Exception e) - { - mLog.error("error while closing device handle", e); - } - - mDeviceHandle = null; - } - - mDeviceDescriptor = null; - mDevice = null; - } - - /** - * Sample rate of the tuner - */ - public abstract double getCurrentSampleRate(); - - /** - * Tuner class - */ - public abstract TunerClass getTunerClass(); - - /** - * Tuner type - */ - public abstract TunerType getTunerType(); - - /** - * Applies the settings in the tuner configuration - */ - public abstract void apply(TunerConfiguration config) throws SourceException; - - /** - * USB address (bus/port) - */ - public String getUSBAddress() - { - if(mDevice != null) - { - StringBuilder sb = new StringBuilder(); - - sb.append("Bus:"); - int bus = LibUsb.getBusNumber(mDevice); - sb.append(bus); - - sb.append(" Port:"); - int port = LibUsb.getPortNumber(mDevice); - sb.append(port); - - return sb.toString(); - } - - return "UNKNOWN"; - } - - /** - * USB Vendor and Product ID - */ - public String getUSBID() - { - if(mDeviceDescriptor != null) - { - StringBuilder sb = new StringBuilder(); - - sb.append(String.format("%04X", (int)(mDeviceDescriptor.idVendor() & 0xFFFF))); - sb.append(":"); - sb.append(String.format("%04X", (int)(mDeviceDescriptor.idProduct() & 0xFFFF))); - - return sb.toString(); - } - - return "UNKNOWN"; - } - - /** - * USB Port Speed. Should be 2.0 for both types of funcube dongles - */ - public String getUSBSpeed() - { - if(mDevice != null) - { - int speed = LibUsb.getDeviceSpeed(mDevice); - - switch(speed) - { - case 0: - return "1.1 LOW"; - case 1: - return "1.1 FULL"; - case 2: - return "2.0 HIGH"; - case 3: - return "3.0 SUPER"; - default: - } - } - - return "UNKNOWN"; - } - - /** - * Set fcd interface mode - */ - public void setFCDMode(Mode mode) throws UsbException, UsbClaimException - { - ByteBuffer response = null; - - switch(mode) - { - case APPLICATION: - response = send(FCDCommand.BL_QUERY); - break; - case BOOTLOADER: - response = send(FCDCommand.APP_RESET); - break; - default: - break; - } - - if(response != null) - { - mConfiguration.set(response); - } - else - { - mConfiguration.setModeUnknown(); - } - } - - /** - * Sets the actual (uncorrected) device frequency - */ - public void setTunedFrequency(long frequency) throws SourceException - { - try - { - send(FCDCommand.APP_SET_FREQUENCY_HZ, frequency); - } - catch(Exception e) - { - throw new SourceException("Couldn't set FCD Local " + - "Oscillator Frequency [" + frequency + "]", e); - } - } - - /** - * Gets the actual (uncorrected) device frequency - */ - public long getTunedFrequency() throws SourceException - { - try - { - ByteBuffer buffer = send(FCDCommand.APP_GET_FREQUENCY_HZ); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - return (int)(buffer.getInt(2) & 0xFFFFFFFF); - } - catch(Exception e) - { - throw new SourceException("FCDTunerController - " - + "couldn't get LO frequency", e); - } - } - - /** - * Returns the FCD device configuration - */ - public FCDConfiguration getConfiguration() - { - return mConfiguration; - } - - /** - * Claims the USB interface. Attempts to detach the active kernel driver if - * one is currently attached. - */ - private void claimInterface() throws SourceException - { - if(mDeviceHandle != null) - { - int result = LibUsb.kernelDriverActive(mDeviceHandle, FCD_INTERFACE); - - if(result == 1) - { - result = LibUsb.detachKernelDriver(mDeviceHandle, FCD_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + LibUsb.errorName(result) + "]"); - } - } - - result = LibUsb.claimInterface(mDeviceHandle, FCD_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + LibUsb.errorName(result) + "]"); - } - } - else - { - throw new SourceException("couldn't claim usb hid interface - no device handle"); - } - } - - /** - * Performs an interrupt write to the OUT endpoint. - * - * @param buffer - direct allocated buffer. Must be 64 bytes in length. - * @throws LibUsbException on error - */ - private void write(ByteBuffer buffer) throws LibUsbException - { - if(mDeviceHandle != null) - { - IntBuffer transferred = IntBuffer.allocate(1); - - int result = LibUsb.interruptTransfer(mDeviceHandle, FCD_ENDPOINT_OUT, buffer, transferred, 500l); - - if(result != LibUsb.SUCCESS) - { - throw new LibUsbException("error writing byte buffer", result); - } - } - else - { - throw new LibUsbException("device handle is null", LibUsb.ERROR_NO_DEVICE); - } - } - - /** - * Performs an interrupt write to the OUT endpoint for the FCD command. - * - * @param command - no-argument command to write - * @throws LibUsbException - on error - */ - private void write(FCDCommand command) throws LibUsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(64); - - buffer.put(0, command.getCommand()); - buffer.put(1, (byte)0x00); - - write(buffer); - } - - /** - * Convenience logger for debugging read/write operations - */ - @SuppressWarnings("unused") - private void log(String label, ByteBuffer buffer) - { - StringBuilder sb = new StringBuilder(); - - sb.append(label); - - sb.append(" "); - - sb.append(buffer.get(0)); - sb.append(" | "); - - for(int x = 0; x < 64; x++) - { - sb.append(String.format("%02X", (int)(buffer.get(x) & 0xFF))); - sb.append(" "); - } - - mLog.debug(sb.toString()); - } - - /** - * Performs an interrupt write to the OUT endpoint for the FCD command. - * - * @param command - command to write - * @param argument - value to write with the command - * @throws LibUsbException - on error - */ - private void write(FCDCommand command, long argument) throws LibUsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(64); - - /* The FCD expects little-endian formatted values */ - buffer.order(ByteOrder.LITTLE_ENDIAN); - - buffer.put(0, command.getCommand()); - buffer.putLong(1, argument); - - write(buffer); - } - - /** - * Performs an interrupt read against the endpoint - * - * @return buffer read from FCD - * @throws LibUsbException on error - */ - private ByteBuffer read() throws LibUsbException - { - if(mDeviceHandle != null) - { - ByteBuffer buffer = ByteBuffer.allocateDirect(64); - - IntBuffer transferred = IntBuffer.allocate(1); - - - int result = LibUsb.interruptTransfer(mDeviceHandle, FCD_ENDPOINT_IN, buffer, transferred, 500l); - - if(result != LibUsb.SUCCESS) - { - throw new LibUsbException("error reading byte buffer", result); - } - else if(transferred.get(0) != buffer.capacity()) - { - throw new LibUsbException("received bytes [" + transferred.get(0) + - "] didn't match expected length [" + buffer.capacity() + "]", result); - } - - return buffer; - } - - throw new LibUsbException("device handle is null", LibUsb.ERROR_NO_DEVICE); - } - - /** - * Sends the FCD command and argument. Performs a read to complete the - * command. - * - * @param command - command to send - * @param argument - command argument to send - * @throws LibUsbException - on error - */ - protected void send(FCDCommand command, long argument) throws LibUsbException - { - write(command, argument); - read(); - } - - /** - * Sends the no-argument FCD command. Performs a read to complete the - * command. - * - * @param command - command to send - * @throws LibUsbException - on error - */ - protected ByteBuffer send(FCDCommand command) throws LibUsbException - { - write(command); - return read(); - } - - /** - * FCD configuration string parsing class - */ - public class FCDConfiguration - { - private String mConfig; - private Mode mMode; - - public FCDConfiguration() - { - mConfig = null; - mMode = Mode.UNKNOWN; - } - - private void setModeUnknown() - { - mConfig = null; - mMode = Mode.UNKNOWN; - } - - /** - * Extracts the configuration string from the buffer - */ - public void set(ByteBuffer buffer) - { - if(buffer.capacity() == 64) - { - byte[] data = new byte[64]; - - for(int x = 0; x < 64; x++) - { - data[x] = buffer.get(x); - } - - mConfig = new String(data); - mMode = Mode.getMode(mConfig); - } - else - { - mConfig = null; - mMode = Mode.ERROR; - } - } - - public Mode getMode() - { - return mMode; - } - - public FCDModel getModel() - { - FCDModel retVal = FCDModel.FUNCUBE_UNKNOWN; - - switch(mMode) - { - case APPLICATION: - retVal = FCDModel.getFCD(mConfig.substring(15, 22)); - break; - case BOOTLOADER: - case UNKNOWN: - case ERROR: - break; - } - - return retVal; - } - - public Block getBandBlocking() - { - Block retVal = Block.UNKNOWN; - - switch(mMode) - { - case APPLICATION: - retVal = Block.getBlock(mConfig.substring(23, 33).trim()); - break; - case BOOTLOADER: - case UNKNOWN: - case ERROR: - break; - } - - return retVal; - } - - public String getFirmware() - { - String retVal = null; - - switch(mMode) - { - case APPLICATION: - retVal = mConfig.substring(9, 14); - break; - case BOOTLOADER: - case UNKNOWN: - case ERROR: - break; - } - - return retVal; - } - - public String toString() - { - return getModel().getLabel(); - } - } - - public enum Mode - { - APPLICATION, - BOOTLOADER, - ERROR, - UNKNOWN; - - public static Mode getMode(String config) - { - Mode retVal = UNKNOWN; - - if(config == null) - { - retVal = ERROR; - } - else - { - if(config.length() >= 8) - { - String mode = config.substring(2, 8).trim(); - - if(mode.equalsIgnoreCase("FCDAPP")) - { - retVal = APPLICATION; - } - else if(mode.equalsIgnoreCase("FCDBL")) - { - retVal = BOOTLOADER; - } - } - } - - return retVal; - } - } -} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java deleted file mode 100644 index 6b576835..00000000 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ /dev/null @@ -1,717 +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 source.tuner.fcd.proV1; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import source.SourceException; -import source.tuner.MixerTunerDataLine; -import source.tuner.MixerTunerType; -import source.tuner.TunerClass; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.fcd.FCDCommand; -import source.tuner.fcd.FCDTunerController; - -import javax.usb.UsbClaimException; -import javax.usb.UsbException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class FCD1TunerController extends FCDTunerController -{ - private final static Logger mLog = LoggerFactory.getLogger(FCD1TunerController.class); - - public static final int MINIMUM_TUNABLE_FREQUENCY = 64000000; - public static final int MAXIMUM_TUNABLE_FREQUENCY = 1700000000; - public static final int SAMPLE_RATE = 96000; - - private double mDCCorrectionInPhase = 0.0; - private double mDCCorrectionQuadrature = 0.0; - private double mPhaseCorrection = 0.0; - private double mGainCorrection = 0.0; - private LNAGain mLNAGain; - private LNAEnhance mLNAEnhance; - private MixerGain mMixerGain; - private FCD1TunerConfiguration mTunerConfiguration; - private FCD1TunerEditor mEditor; - - public FCD1TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor) - { - super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO.getDisplayString(), MINIMUM_TUNABLE_FREQUENCY, - MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat()); - } - - public void init() throws SourceException - { - super.init(); - - getFrequencyController().setSampleRate(SAMPLE_RATE); - - try - { - setFCDMode(Mode.APPLICATION); - getPhaseAndGainCorrection(); - getDCIQCorrection(); - getLNAGainSetting(); - getLNAEnhanceSetting(); - getMixerGainSetting(); - - send(FCDCommand.APP_SET_MIXER_GAIN, 1l); - } - catch(Exception e) - { - e.printStackTrace(); - - throw new SourceException("FCDTunerController error " + - "during construction", e); - } - } - - public double getCurrentSampleRate() - { - return SAMPLE_RATE; - } - - @Override - public TunerClass getTunerClass() - { - return TunerClass.FUNCUBE_DONGLE_PRO; - } - - @Override - public TunerType getTunerType() - { - return TunerType.FUNCUBE_DONGLE_PRO; - } - - /** - * Get the tuner configuration - * - * @return current config or null if one hasn't been applied yet - */ - public FCD1TunerConfiguration getTunerConfiguration() - { - return mTunerConfiguration; - } - - @Override - public void apply(TunerConfiguration tunerConfig) - throws SourceException - { - FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig; - - try - { - getFrequencyController().setFrequencyCorrection(config.getFrequencyCorrection()); - setLNAGain(config.getLNAGain()); - setLNAEnhance(config.getLNAEnhance()); - setMixerGain(config.getMixerGain()); - setDCCorrectionInPhase(config.getInphaseDCCorrection()); - setDCCorrectionQuadrature(config.getQuadratureDCCorrection()); - setGainCorrection(config.getGainCorrection()); - setPhaseCorrection(config.getPhaseCorrection()); - - //If we get to here without exception, store this config - mTunerConfiguration = config; - } - catch(Exception e) - { - //Re-cast any usb exceptions as source exceptions - throw new SourceException("FCD 1 Controller encountered an " - + "exception while applying tuner config - " + - e.getLocalizedMessage()); - } - - try - { - getFrequencyController().setFrequency(config.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } - - /** - * Gets the current LNA Gain value from the controller and stores it - */ - private void getLNAGainSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_GAIN); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int gain = buffer.getInt(2); - - mLNAGain = LNAGain.valueOf(gain); - } - - /** - * @return - current lna gain setting - */ - public LNAGain getLNAGain() - { - return mLNAGain; - } - - /** - * Sets lna gain for the controller - * - * @param gain - * @throws UsbException - * @throws UsbClaimException - */ - public void setLNAGain(LNAGain gain) - throws UsbException, UsbClaimException - { - send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting()); - } - - /** - * @return - current phase correction setting - */ - public double getPhaseCorrection() - { - return mPhaseCorrection; - } - - /** - * Sets the phase correction - * - * @param value - new phase correction - * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setPhaseCorrection(double value) - throws UsbClaimException, UsbException - { - if(value < -1.0 || value > 1.0) - { - throw new IllegalArgumentException("Phase correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )"); - } - - mPhaseCorrection = value; - setPhaseAndGainCorrection(); - } - - /** - * @return current gain correction setting - */ - public double getGainCorrection() - { - return mGainCorrection; - } - - /** - * @param value - gain correction value - * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setGainCorrection(double value) - throws UsbClaimException, UsbException - { - if(value < -1.0 || value > 1.0) - { - throw new IllegalArgumentException("Gain correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )"); - } - - mGainCorrection = value; - setPhaseAndGainCorrection(); - } - - /** - * Sends the phase and gain correction values to FCD. - * - * The interface expects an argument with the SIGNED phase value in the first - * 16 bits and the UNSIGNED gain value in the second 16 bits, both in - * big-endian format. Since we're sending the argument in java little endian - * format, we place the phase in the high-order bits and the gain in the - * low-order bits, so that the send(command) function will reorder the bytes - * into big endian format, and place the arguments correctly. - * - * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle - * as values 0 to 65534 - * - * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the - * dongle as signed values -32768 to 32767 - */ - private void setPhaseAndGainCorrection() throws UsbClaimException, - UsbException - { - //UNSIGNED short gain value - masked into a long to avoid sign-extension - long gain = (long)((1.0 + mGainCorrection) * Short.MAX_VALUE); - - //Left shift gain to place value in upper 16 bits - long longGain = Long.rotateLeft((0xFFFF & gain), 16); - - //SIGNED short phase value - short phase = (short)(mPhaseCorrection * Short.MAX_VALUE); - - //Merge the results - long correction = longGain | phase; - - send(FCDCommand.APP_SET_IQ_CORRECTION, correction); - } - - /** - * Retrieves the stored phase and gain correction values from the FCD. - * - * Note: testing shows that when the dongle is unplugged, any stored phase - * and gain correction values are reset. Reading the dongle after a fresh - * plugin shows the values reset. - * - * @throws UsbClaimException - * @throws UsbException - */ - private void getPhaseAndGainCorrection() throws UsbClaimException, - UsbException - { - ByteBuffer buffer = send(FCDCommand.APP_GET_IQ_CORRECTION); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int correction = buffer.getInt(2); - - /** - * Gain: mask the upper 16 phase bits and right shift to get the - * unsigned short value and then divide by 32768 to get the double - * value. We place the unsigned short value in an int to avoid sign - * issues. - */ - int gain = (int)(Long.rotateRight(correction & 0xFFFF0000, 16)); - - mGainCorrection = ((double)gain / (double)Short.MAX_VALUE) - 1.0; - - /** - * Phase: mask the lower 16 bits and divide by 32768 to get the double - * value - */ - short phase = (short)(correction & 0x0000FFFF); - - mPhaseCorrection = ((double)phase / (double)Short.MAX_VALUE); - } - - /** - * @return current DC correction for the I (inphase) component - */ - public double getDCCorrectionInPhase() - { - return mDCCorrectionInPhase; - } - - /** - * Sets DC correction for the I (inphase) component and sends the new value - * to the FCD controller - * - * @param value - new DC correction value for the inphase component - * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setDCCorrectionInPhase(double value) - throws UsbClaimException, UsbException - { - if(value < -1.0 || value > 1.0) - { - throw new IllegalArgumentException("DC inphase correction value " - + "[" + value + "] outside limits ( 0.0 <> 1.0 )"); - } - - mDCCorrectionInPhase = value; - setDCIQCorrection(); - } - - /** - * @return current DC correction for the Q (quadrature) component - */ - public double getDCCorrectionQuadrature() - { - return mDCCorrectionQuadrature; - } - - /** - * Sets the DC correction for the Q (quadrature) component and send the - * new value to the FCD controller - * - * @param value - new DC correction value for the quadrature component - * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setDCCorrectionQuadrature(double value) - throws UsbClaimException, UsbException - { - if(value < -1.0 || value > 1.0) - { - throw new IllegalArgumentException("DC quadrature correction " - + "value [" + value + "] outside limits ( 0.0 <> 1.0 )"); - } - - mDCCorrectionQuadrature = value; - setDCIQCorrection(); - } - - /** - * Sends the I/Q DC offset correction values to FCD. - * - * The interface expects an argument with the signed inphase value in the - * first 16 bits and the signed quadrature value in the second 16 bits, - * both in big-endian format. - * - * Since we're sending the argument in java little endian format, we place - * the quadrature in the high-order bits and the inphase in the low-order - * bits, so that the send(command) function will reorder the bytes into big - * endian format, and place the arguments correctly. - * - * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 - * and are applied to the dongle as signed short values -32768 to 0 to 32767 - */ - private void setDCIQCorrection() throws UsbClaimException, UsbException - { - //I & Q DC offset correction values are signed short values - short inphase = (short)(mDCCorrectionInPhase * Short.MAX_VALUE); - short quadrature = (short)(mDCCorrectionQuadrature * Short.MAX_VALUE); - - //Mask the shorts into longs to preserve the sign bit and prepare - //for merging to a single 32-bit value - long maskedInphase = inphase & 0x0000FFFF; - long maskedQuadrature = quadrature & 0x0000FFFF; - - //Left shift quadrature to place value in upper 16 bits - long shiftedQuadrature = Long.rotateLeft(maskedQuadrature, 16); - - //Merge the results - long correction = shiftedQuadrature | maskedInphase; - -// Log.info( "FCD1: setting iq correction," + -// " inphase:" + inphase + -// " masked inphase:" + maskedInphase + -// " quad:" + quadrature + -// " masked quad:" + maskedQuadrature + -// " shifted quadrature:" + shiftedQuadrature + -// " correction:" + correction ); - - send(FCDCommand.APP_SET_DC_CORRECTION, correction); - } - - private void getDCIQCorrection() throws UsbClaimException, - UsbException - { - ByteBuffer buffer = send(FCDCommand.APP_GET_DC_CORRECTION); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int correction = buffer.getInt(2); - - /** - * Quadrature: mask the upper 16 bits and divide by 32768 to get the - * stored quadrature value. Cast the 16-bit value to a short to get - * the signed short value - */ - long shiftedQuadrature = correction & 0xFFFF0000; - long quadrature = Long.rotateRight(shiftedQuadrature, 16); - - mDCCorrectionQuadrature = ((short)quadrature / (double)Short.MAX_VALUE); - - /** - * InPhase: mask the lower 16 bits to get the short - * value and then divide by 32768 to get the stored inphase value. - * Cast the 16-bit value to a short to get the signed short value - */ - long inphase = correction & 0x0000FFFF; - mDCCorrectionInPhase = ((short)inphase / (double)Short.MAX_VALUE); - -// Log.info( "FCD1:" + -// " correction:" + correction + -// " Q:" + quadrature + -// " I:" + inphase + -// " QCorr:" + mDCCorrectionQuadrature + -// " ICorr:" + mDCCorrectionInPhase ); - } - - private void getLNAEnhanceSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_ENHANCE); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int enhance = buffer.getInt(2); - - mLNAEnhance = LNAEnhance.valueOf(enhance); - } - - public LNAEnhance getLNAEnhance() - { - return mLNAEnhance; - } - - public void setLNAEnhance(LNAEnhance enhance) - throws UsbClaimException, UsbException - { - send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting()); - } - - private void getMixerGainSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send(FCDCommand.APP_GET_MIXER_GAIN); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int gain = buffer.getInt(2); - - mMixerGain = MixerGain.valueOf(gain); - } - - public MixerGain getMixerGain() - { - return mMixerGain; - } - - public void setMixerGain(MixerGain gain) throws UsbClaimException, UsbException - { - send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting()); - } - - public enum Block - { - CELLULAR_BAND_BLOCKED("Blocked"), - NO_BAND_BLOCK("Unblocked"), - UNKNOWN("Unknown"); - - private String mLabel; - - private Block(String label) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static Block getBlock(String block) - { - Block retVal = UNKNOWN; - - if(block.equalsIgnoreCase("No blk")) - { - retVal = NO_BAND_BLOCK; - } - else if(block.equalsIgnoreCase("Cell blk")) - { - retVal = CELLULAR_BAND_BLOCKED; - } - - return retVal; - } - } - - /** - * LNA Gain values suppported by the FCD Pro 1.0 - */ - public enum LNAGain - { - LNA_GAIN_MINUS_5_0(0, "-5.0 dB"), - LNA_GAIN_MINUS_2_5(1, "-2.5 dB"), - LNA_GAIN_PLUS_0_0(4, "0.0 dB"), - LNA_GAIN_PLUS_2_5(5, "2.5 dB"), - LNA_GAIN_PLUS_5_0(6, "5.0 dB"), - LNA_GAIN_PLUS_7_5(7, "7.5 dB"), - LNA_GAIN_PLUS_10_0(8, "10.0 dB"), - LNA_GAIN_PLUS_12_5(9, "12.5 dB"), - LNA_GAIN_PLUS_15_0(10, "15.0 dB"), - LNA_GAIN_PLUS_17_5(11, "17.5 dB"), - LNA_GAIN_PLUS_20_0(12, "20.0 dB"), - LNA_GAIN_PLUS_25_0(13, "25.0 dB"), - LNA_GAIN_PLUS_30_0(14, "30.0 dB"); - - private int mSetting; - private String mLabel; - - private LNAGain(int setting, String label) - { - mSetting = setting; - mLabel = label; - } - - public static LNAGain valueOf(int setting) - { - switch(setting) - { - case 0: - return LNA_GAIN_MINUS_5_0; - case 1: - return LNA_GAIN_MINUS_2_5; - case 4: - return LNA_GAIN_PLUS_0_0; - case 5: - return LNA_GAIN_PLUS_2_5; - case 6: - return LNA_GAIN_PLUS_5_0; - case 7: - return LNA_GAIN_PLUS_7_5; - case 8: - return LNA_GAIN_PLUS_10_0; - case 9: - return LNA_GAIN_PLUS_12_5; - case 10: - return LNA_GAIN_PLUS_15_0; - case 11: - return LNA_GAIN_PLUS_17_5; - case 12: - return LNA_GAIN_PLUS_20_0; - case 13: - return LNA_GAIN_PLUS_25_0; - case 14: - return LNA_GAIN_PLUS_30_0; - default: - throw new IllegalArgumentException("FCD 1.0 Tuner " - + "Controller - unrecognized LNA gain setting " - + "value [" + setting + "]"); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } - } - - /** - * LNA Enhance values suppported by the FCD Pro 1.0 - */ - public enum LNAEnhance - { - LNA_ENHANCE_OFF(0, "Off"), - LNA_ENHANCE_0(1, "0"), - LNA_ENHANCE_1(3, "1"), - LNA_ENHANCE_2(5, "2"), - LNA_ENHANCE_3(7, "3"); - - private int mSetting; - private String mLabel; - - private LNAEnhance(int setting, String label) - { - mSetting = setting; - mLabel = label; - } - - public static LNAEnhance valueOf(int setting) - { - switch(setting) - { - case 0: - return LNA_ENHANCE_OFF; - case 1: - return LNA_ENHANCE_0; - case 3: - return LNA_ENHANCE_1; - case 5: - return LNA_ENHANCE_2; - case 7: - return LNA_ENHANCE_3; - default: - throw new IllegalArgumentException("FCD 1.0 Tuner " - + "Controller - unrecognized LNA enhance setting " - + "value [" + setting + "]"); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } - } - - /** - * Mixer Gain values suppported by the FCD Pro 1.0 - */ - public enum MixerGain - { - MIXER_GAIN_PLUS_4_0(0, "4.0 dB"), - MIXER_GAIN_PLUS_12_0(1, "12.0 dB"); - - private int mSetting; - private String mLabel; - - private MixerGain(int setting, String label) - { - mSetting = setting; - mLabel = label; - } - - public static MixerGain valueOf(int setting) - { - switch(setting) - { - case 0: - return MIXER_GAIN_PLUS_4_0; - case 1: - return MIXER_GAIN_PLUS_12_0; - default: - throw new IllegalArgumentException("FCD 1.0 Tuner " - + "Controller - unrecognized mixer gain setting " - + "value [" + setting + "]"); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } - } -} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java deleted file mode 100644 index 40a330b0..00000000 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java +++ /dev/null @@ -1,528 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.fcd.proV1; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; -import source.tuner.configuration.TunerConfigurationEvent; -import source.tuner.configuration.TunerConfigurationEvent.Event; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.fcd.FCDTuner; -import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; -import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; -import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; - -public class FCD1TunerEditor extends TunerConfigurationEditor -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( FCD1TunerEditor.class ); - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JSpinner mFrequencyCorrection; - private JComboBox mComboLNAGain; - private JComboBox mComboLNAEnhance; - private JComboBox mComboMixerGain; - private CorrectionSpinner mCorrectionDCI; - private CorrectionSpinner mCorrectionDCQ; - private CorrectionSpinner mCorrectionGain; - private CorrectionSpinner mCorrectionPhase; - private boolean mLoading; - - private FCD1TunerController mController; - - public FCD1TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) - { - super( tunerConfigurationModel ); - mController = (FCD1TunerController)tuner.getController(); - - init(); - } - - private FCD1TunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (FCD1TunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][][][][][grow]" ) ); - - add( new JLabel( "FCD Pro Tuner Configuration" ), "span,align center" ); - - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.getFrequencyController().setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner " - + "Controller - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDonglePro Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection, "wrap" ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - add( new JLabel( "Gain" ), "span,align center" ); - - mComboLNAGain = new JComboBox( LNAGain.values() ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain setting." ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); - - - try - { - mController.setLNAGain( gain ); - save(); - } - catch ( Exception e ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner Controller" - + " - error setting LNA gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - error setting gain [" + gain.toString() + "]", e ); - } - } - } ); - - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); - - /** - * LNA Enhance - */ - mComboLNAEnhance = new JComboBox( LNAEnhance.values() ); - mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. Default value is OFF" ); - - mComboLNAEnhance.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); - - try - { - mController.setLNAEnhance( enhance ); - save(); - } - catch ( Exception e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner" - + " error setting LNA enhance gain [" + enhance.toString() + "]" ); - - mLog.error( "FCDPro - error setting LNA enhance [" + enhance.toString() + "]", e1 ); - } - } - } ); - - add( new JLabel( "Enhance:" ) ); - add( mComboLNAEnhance ); - - /** - * Mixer Gain - */ - mComboMixerGain = new JComboBox( MixerGain.values() ); - mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); - - - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); - - try - { - mController.setMixerGain( gain ); - save(); - } - catch ( Exception e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error setting" - + " mixer gain [" + gain.toString() + "]" ); - - mLog.error( "FCDPro - error setting mixer gain [" + gain.toString() + "]", e1 ); - } - } - } ); - - add( new JLabel( "Mixer:" ) ); - add( mComboMixerGain, "wrap" ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - add( new JLabel( "Correction" ), "span,align center" ); - - /** - * Inphase DC Correction - */ - mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, 0.0, 0.00001, 5 ); - mCorrectionDCI.setEnabled( false ); - mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "DC Inphase:" ) ); - add( mCorrectionDCI ); - - /** - * Quadrature DC Correction - */ - mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, 0.0, 0.00001, 5 ); - mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "DC Quadrature:" ) ); - add( mCorrectionDCQ ); - - /** - * Gain Correction - */ - mCorrectionGain = new CorrectionSpinner( Correction.GAIN, 0.0, 0.00001, 5 ); - mCorrectionGain.setToolTipText( "Gain Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "Gain:" ) ); - add( mCorrectionGain ); - - /** - * Phase Correction - */ - mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, 0.0, 0.00001, 5 ); - mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - add( new JLabel( "Phase:" ) ); - add( mCorrectionPhase ); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mComboLNAEnhance.isEnabled() != enabled ) - { - mComboLNAEnhance.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboMixerGain.isEnabled() != enabled ) - { - mComboMixerGain.setEnabled( enabled ); - } - - if( mCorrectionDCI.isEnabled() != enabled ) - { - mCorrectionDCI.setEnabled( enabled ); - } - - if( mCorrectionDCQ.isEnabled() != enabled ) - { - mCorrectionDCQ.setEnabled( enabled ); - } - - if( mCorrectionGain.isEnabled() != enabled ) - { - mCorrectionGain.setEnabled( enabled ); - } - - if( mCorrectionPhase.isEnabled() != enabled ) - { - mCorrectionPhase.setEnabled( enabled ); - } - } - - private String getTunerInfo() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "

Funcube Dongle Pro Tuner

" ); - - sb.append( "USB ID: " ); - sb.append( mController.getUSBID() ); - sb.append( "
" ); - - sb.append( "USB Address: " ); - sb.append( mController.getUSBAddress() ); - sb.append( "
" ); - - sb.append( "USB Speed: " ); - sb.append( mController.getUSBSpeed() ); - sb.append( "
" ); - - sb.append( "Cellular Band: " ); - sb.append( mController.getConfiguration().getBandBlocking() ); - sb.append( "
" ); - - sb.append( "Firmware: " ); - sb.append( mController.getConfiguration().getFirmware() ); - sb.append( "
" ); - - return sb.toString(); - } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - FCD1TunerConfiguration config = getConfiguration(); - - config.setName( mConfigurationName.getText() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - config.setLNAEnhance( (LNAEnhance)mComboLNAEnhance.getSelectedItem() ); - config.setLNAGain( (LNAGain)mComboLNAGain.getSelectedItem() ); - config.setMixerGain( (MixerGain)mComboMixerGain.getSelectedItem() ); - - double dci = ((SpinnerNumberModel)mCorrectionDCI.getModel()).getNumber().doubleValue(); - config.setInphaseDCCorrection( dci ); - - double dcq = ((SpinnerNumberModel)mCorrectionDCQ.getModel()).getNumber().doubleValue(); - config.setQuadratureDCCorrection( dcq ); - - double gain = ((SpinnerNumberModel)mCorrectionGain.getModel()).getNumber().doubleValue(); - config.setGainCorrection( gain ); - - double phase = ((SpinnerNumberModel)mCorrectionPhase.getModel()).getNumber().doubleValue(); - config.setPhaseCorrection( phase ); - - getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); - } - } - - @Override - public void setItem( TunerConfiguration tunerConfiguration ) - { - super.setItem( tunerConfiguration ); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if( hasItem() ) - { - FCD1TunerConfiguration config = getConfiguration(); - - if( tunerConfiguration.isAssigned() ) - { - setControlsEnabled( true ); - mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboLNAEnhance.setSelectedItem( config.getLNAEnhance() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); - mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); - mCorrectionGain.setValue( config.getGainCorrection() ); - mCorrectionPhase.setValue( config.getPhaseCorrection() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - - mLoading = false; - } - - public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; - - public class CorrectionSpinner extends JSpinner - { - private static final long serialVersionUID = 1L; - private static final double MIN_VALUE = -1.0d; - private static final double MAX_VALUE = 1.0d; - - private Correction mCorrectionComponent; - - public CorrectionSpinner( Correction component, double initialValue, double step, int decimalPlaces ) - { - mCorrectionComponent = component; - - SpinnerModel model = new SpinnerNumberModel( initialValue, MIN_VALUE, MAX_VALUE, step ); - setModel( model ); - - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( decimalPlaces ); - - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = - ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); - - try - { - switch( mCorrectionComponent ) - { - case DC_INPHASE: - mController.setDCCorrectionInPhase( value ); - break; - case DC_QUADRATURE: - mController.setDCCorrectionQuadrature( value ); - break; - case GAIN: - mController.setGainCorrection( value ); - break; - case PHASE: - mController.setPhaseCorrection( value ); - break; - } - - save(); - } - catch ( Exception e1 ) - { - JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error " - + "applying " + mCorrectionComponent.toString() + " correction value [" - + value + "]" ); - - mLog.error( "FCDPro - error applying "+ mCorrectionComponent.toString() - + " correction value [" + value + "]", e1 ); - } - } - } ); - } - } -} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java deleted file mode 100644 index 5e2412c4..00000000 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ /dev/null @@ -1,238 +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 source.tuner.fcd.proplusV2; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import source.SourceException; -import source.tuner.MixerTunerDataLine; -import source.tuner.MixerTunerType; -import source.tuner.TunerClass; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.fcd.FCDCommand; -import source.tuner.fcd.FCDTunerController; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class FCD2TunerController extends FCDTunerController -{ - private final static Logger mLog = LoggerFactory.getLogger(FCD2TunerController.class); - - public static final int MINIMUM_TUNABLE_FREQUENCY = 150000; - public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000; - public static final int SAMPLE_RATE = 192000; - - public FCD2TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor) - { - super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getDisplayString(), - MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat()); - } - - public void init() throws SourceException - { - super.init(); - - getFrequencyController().setSampleRate(SAMPLE_RATE); - - try - { - setFCDMode(Mode.APPLICATION); - } - catch(Exception e) - { - throw new SourceException("error setting Mode to APPLICATION", e); - } - } - - public double getCurrentSampleRate() - { - return SAMPLE_RATE; - } - - @Override - public TunerClass getTunerClass() - { - return TunerClass.FUNCUBE_DONGLE_PRO_PLUS; - } - - @Override - public TunerType getTunerType() - { - return TunerType.FUNCUBE_DONGLE_PRO_PLUS; - } - - public void setLNAGain(boolean enabled) throws SourceException - { - try - { - send(FCDCommand.APP_SET_LNA_GAIN, enabled ? 1 : 0); - } - catch(Exception e) - { - throw new SourceException("error while setting LNA Gain", e); - } - } - - public void setMixerGain(boolean enabled) throws SourceException - { - try - { - send(FCDCommand.APP_SET_MIXER_GAIN, enabled ? 1 : 0); - } - catch(Exception e) - { - throw new SourceException("error while setting Mixer Gain", e); - } - } - - @Override - public void apply(TunerConfiguration config) throws SourceException - { - if(config instanceof FCD2TunerConfiguration) - { - FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config; - - getFrequencyController().setFrequencyCorrection(fcd2.getFrequencyCorrection()); - - long frequency = MINIMUM_TUNABLE_FREQUENCY; - - if(fcd2.getFrequency() >= MINIMUM_TUNABLE_FREQUENCY && - fcd2.getFrequency() <= MAXIMUM_TUNABLE_FREQUENCY) - { - frequency = fcd2.getFrequency(); - } - - getFrequencyController().setFrequency(frequency); - setLNAGain(fcd2.getGainLNA()); - setMixerGain(fcd2.getGainMixer()); - - try - { - getFrequencyController().setFrequency(fcd2.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } - } - - public int getDCCorrection() - { - int dcCorrection = -999; - - try - { - ByteBuffer buffer = send(FCDCommand.APP_GET_DC_CORRECTION); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - return buffer.getInt(2); - } - catch(Exception e) - { - mLog.error("error getting dc correction value", e); - } - - return dcCorrection; - } - - public void setDCCorrection(int value) - { - try - { - send(FCDCommand.APP_SET_DC_CORRECTION, value); - } - catch(Exception e) - { - mLog.error("error setting dc correction to [" + value + "]", e); - } - } - - public int getIQCorrection() - { - int iqCorrection = -999; - - try - { - ByteBuffer buffer = send(FCDCommand.APP_GET_IQ_CORRECTION); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - return buffer.getInt(2); - } - catch(Exception e) - { - mLog.error("error reading IQ correction value", e); - } - - return iqCorrection; - } - - public void setIQCorrection(int value) - { - try - { - send(FCDCommand.APP_SET_IQ_CORRECTION, value); - } - catch(Exception e) - { - mLog.error("error setting IQ correction to [" + value + "]", e); - } - } - - public enum Block - { - CELLULAR_BAND_BLOCKED("Blocked"), - NO_BAND_BLOCK("Unblocked"), - UNKNOWN("Unknown"); - - private String mLabel; - - private Block(String label) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static Block getBlock(String block) - { - Block retVal = UNKNOWN; - - if(block.equalsIgnoreCase("No blk")) - { - retVal = NO_BAND_BLOCK; - } - else if(block.equalsIgnoreCase("Cell blk")) - { - retVal = CELLULAR_BAND_BLOCKED; - } - - return retVal; - } - } -} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java deleted file mode 100644 index e5c5c8be..00000000 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java +++ /dev/null @@ -1,328 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.fcd.proplusV2; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; -import source.tuner.configuration.TunerConfigurationEvent; -import source.tuner.configuration.TunerConfigurationEvent.Event; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.fcd.FCDTuner; - -public class FCD2TunerEditor extends TunerConfigurationEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerEditor.class ); - private static final long serialVersionUID = 1L; - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JSpinner mFrequencyCorrection; - private JCheckBox mLNAGain; - private JCheckBox mMixerGain; - private boolean mLoading; - - private FCD2TunerController mController; - - public FCD2TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) - { - super( tunerConfigurationModel ); - mController = (FCD2TunerController)tuner.getController(); - - init(); - } - - private FCD2TunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (FCD2TunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][grow]" ) ); - - add( new JLabel( "FCD Pro+ Tuner Configuration" ), "span,align center" ); - - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( FCD2TunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.getFrequencyController().setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( FCD2TunerEditor.this, "FCD Pro Plus Tuner " - + "Controller - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection, "wrap" ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - - mLNAGain = new JCheckBox( "LNA Gain" ); - mLNAGain.setEnabled( false ); - mLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent event ) - { - try - { - mController.setLNAGain( mLNAGain.isSelected() ); - save(); - } - catch( SourceException e ) - { - mLog.error( "Couldn't set LNA gain for FCD2", e ); - } - } - } ); - - add( new JLabel( "" ) ); - add( mLNAGain ); - - /** - * Mixer Gain - */ - mMixerGain = new JCheckBox( "Mixer Gain" ); - - mMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent event ) - { - try - { - mController.setMixerGain( mMixerGain.isSelected() ); - save(); - } - catch( SourceException e ) - { - mLog.error( "Couldn't set mixer gain for FCD2", e ); - } - } - } ); - - add( new JLabel( "" ) ); - add( mMixerGain ); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mLNAGain.isEnabled() != enabled ) - { - mLNAGain.setEnabled( enabled ); - } - - if( mMixerGain.isEnabled() != enabled ) - { - mMixerGain.setEnabled( enabled ); - } - } - - private String getTunerInfo() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "

Funcube Dongle Pro Plus Tuner

" ); - - sb.append( "USB ID: " ); - sb.append( mController.getUSBID() ); - sb.append( "
" ); - - sb.append( "USB Address: " ); - sb.append( mController.getUSBAddress() ); - sb.append( "
" ); - - sb.append( "USB Speed: " ); - sb.append( mController.getUSBSpeed() ); - sb.append( "
" ); - - sb.append( "Cellular Band: " ); - sb.append( mController.getConfiguration().getBandBlocking() ); - sb.append( "
" ); - - sb.append( "Firmware: " ); - sb.append( mController.getConfiguration().getFirmware() ); - sb.append( "
" ); - - return sb.toString(); - } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - FCD2TunerConfiguration config = getConfiguration(); - - config.setName( mConfigurationName.getText() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - config.setGainLNA( mLNAGain.isSelected() ); - config.setGainMixer( mMixerGain.isSelected() ); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); - } - } - - @Override - public void setItem( TunerConfiguration tunerConfiguration ) - { - super.setItem( tunerConfiguration ); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if( hasItem() ) - { - FCD2TunerConfiguration config = getConfiguration(); - - - if( tunerConfiguration.isAssigned() ) - { - setControlsEnabled( true ); - mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mLNAGain.setSelected( config.getGainLNA() ); - mMixerGain.setSelected( config.getGainMixer() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - - mLoading = false; - } -} diff --git a/src/source/tuner/frequency/DCTrackingSourceControl.java b/src/source/tuner/frequency/DCTrackingSourceControl.java deleted file mode 100644 index de042699..00000000 --- a/src/source/tuner/frequency/DCTrackingSourceControl.java +++ /dev/null @@ -1,130 +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 source.tuner.frequency; - -import module.Module; -import sample.Listener; -import sample.real.RealBuffer; -import source.ISourceEventProvider; -import source.SourceEvent; - -import java.util.concurrent.ScheduledExecutorService; - -public class DCTrackingSourceControl extends Module - implements Listener, ISourceEventProvider -{ - private static float COARSE_THRESHOLD = 0.1f; - private static float LOOP_GAIN = 0.1f; - private static float COARSE_GAIN = 1000.0f; - private static float FINE_GAIN = 100.0f; - private int mErrorCorrection = 0; - private int mMaximumFrequencyCorrection = 0; - private Listener mListener; - - /** - * DC offset Automatic Frequency Control. Monitors DC bias present in an FM - * demodulated audio stream and issues automatic frequency corrections to - * maintain the DC bias close to zero. - * - * @param maximum - maximum allowable (+/-) frequency correction value. - */ - public DCTrackingSourceControl(int maximum) - { - mMaximumFrequencyCorrection = maximum; - } - - public void dispose() - { - mListener = null; - } - - @Override - public void receive(RealBuffer buffer) - { - double sum = 0.0d; - - for(float sample : buffer.getSamples()) - { - sum += sample; - } - - float mean = (float)(sum / (double)buffer.getSamples().length); - - float adjustment = 0.0f; - - if(mean > COARSE_THRESHOLD || mean < -COARSE_THRESHOLD) - { - adjustment = mean * COARSE_GAIN; - } - else - { - adjustment = mean * FINE_GAIN; - } - - setErrorCorrection(mErrorCorrection + (int)((float)adjustment * LOOP_GAIN)); - } - - private void setErrorCorrection(int correction) - { - if(mListener != null) - { - if(correction > mMaximumFrequencyCorrection) - { - correction = mMaximumFrequencyCorrection; - } - else if(correction < -mMaximumFrequencyCorrection) - { - correction = -mMaximumFrequencyCorrection; - } - - if(correction != mErrorCorrection) - { - mListener.receive(SourceEvent.channelFrequencyCorrectionRequest(correction)); - } - } - } - - @Override - public void setSourceEventListener(Listener listener) - { - mListener = listener; - } - - @Override - public void removeSourceEventListener() - { - mListener = null; - } - - @Override - public void start() - { - } - - @Override - public void stop() - { - } - - @Override - public void reset() - { - setErrorCorrection(0); - } -} diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java deleted file mode 100644 index fa0ff210..00000000 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.hackrf; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.Tuner; -import source.tuner.TunerClass; -import source.tuner.TunerType; -import source.tuner.hackrf.HackRFTunerController.BoardID; - -public class HackRFTuner extends Tuner -{ - private final static Logger mLog = LoggerFactory.getLogger( HackRFTuner.class ); - - public HackRFTuner( HackRFTunerController controller ) throws SourceException - { - super( "HackRF", controller ); - } - - public HackRFTunerController getController() - { - return (HackRFTunerController)getTunerController(); - } - - public void dispose() - { - } - - @Override - public TunerClass getTunerClass() - { - return TunerClass.HACKRF_ONE; - } - - @Override - public TunerType getTunerType() - { - return TunerType.HACKRF; - } - - @Override - public double getSampleSize() - { - return 11.0; - } - - @Override - public String getUniqueID() - { - try - { - return getController().getSerial().getSerialNumber(); - } - catch( Exception e ) - { - mLog.error( "error gettting serial number", e ); - } - - return BoardID.HACKRF_ONE.getLabel(); - } -} diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java deleted file mode 100644 index 2af8ed00..00000000 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ /dev/null @@ -1,884 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * Ported from libhackrf at: - * https://github.com/mossmann/hackrf/tree/master/host/libhackrf - * - * Copyright (c) 2012, Jared Boone - * Copyright (c) 2013, Benjamin Vernoux - * copyright (c) 2013, Michael Ossmann - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 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. - * - * Neither the name of Great Scott Gadgets nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT - * HOLDER 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. - * - * 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 source.tuner.hackrf; - -import org.apache.commons.io.EndianUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import sample.Broadcaster; -import sample.Listener; -import sample.adapter.ByteSampleAdapter; -import sample.complex.ComplexBuffer; -import source.SourceException; -import source.tuner.TunerController; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.usb.USBTransferProcessor; -import source.tuner.usb.USBTunerController; - -import javax.usb.UsbException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.concurrent.LinkedTransferQueue; - -public class HackRFTunerController extends USBTunerController -{ - private final static Logger mLog = LoggerFactory.getLogger(HackRFTunerController.class); - - public final static long USB_TIMEOUT_US = 1000000l; //uSeconds - public static final byte USB_ENDPOINT = (byte)0x81; - public static final byte USB_INTERFACE = (byte)0x0; - public static final int USB_TRANSFER_BUFFER_SIZE = 262144; - - public static final byte REQUEST_TYPE_IN = (byte)(LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); - public static final byte REQUEST_TYPE_OUT = (byte)(LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE); - - public static final long MIN_FREQUENCY = 10000000l; - public static final long MAX_FREQUENCY = 6000000000l; - public static final long DEFAULT_FREQUENCY = 101100000; - public static final double USABLE_BANDWIDTH_PERCENT = 0.95; - public static final int DC_SPIKE_AVOID_BUFFER = 5000; - - private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); - private USBTransferProcessor mUSBTransferProcessor; - - private HackRFSampleRate mSampleRate = HackRFSampleRate.RATE2_016MHZ; - private boolean mAmplifierEnabled = false; - - private Device mDevice; - private DeviceDescriptor mDeviceDescriptor; - private DeviceHandle mDeviceHandle; - - public HackRFTunerController(Device device, DeviceDescriptor descriptor) throws SourceException - { - super(MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); - - mDevice = device; - mDeviceDescriptor = descriptor; - } - - public void init() throws SourceException - { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != 0) - { - if(result == LibUsb.ERROR_ACCESS) - { - mLog.error("Unable to access HackRF - insufficient permissions. " - + "If you are running a Linux OS, have you installed the " - + "hackRF rules file in \\etc\\udev\\rules.d ??"); - } - - throw new SourceException("Couldn't open hackrf device - " + - LibUsb.strError(result)); - } - - try - { - claimInterface(); - - setMode(Mode.RECEIVE); - - getFrequencyController().setFrequency(DEFAULT_FREQUENCY); - } - catch(Exception e) - { - throw new SourceException("HackRF Tuner Controller - couldn't " - + "claim USB interface or get endpoint or pipe", e); - } - - String name; - - try - { - name = "HackRF " + getSerial().getSerialNumber(); - } - catch(UsbException ue) - { - //Do nothing, we couldn't determine the serial number - name = "HackRF - Unidentified Serial"; - } - - mUSBTransferProcessor = new USBTransferProcessor(name, mDeviceHandle, mSampleAdapter, USB_TRANSFER_BUFFER_SIZE); - } - - @Override - protected USBTransferProcessor getUSBTransferProcessor() - { - return mUSBTransferProcessor; - } - - /** - * Claims the USB interface. If another application currently has - * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting - * will dictate if the interface is forcibly claimed from the other - * application - */ - private void claimInterface() throws SourceException - { - if(mDeviceHandle != null) - { - int result = LibUsb.kernelDriverActive(mDeviceHandle, USB_INTERFACE); - - if(result == 1) - { - result = LibUsb.detachKernelDriver(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + - LibUsb.errorName(result) + "]"); - - throw new SourceException("couldn't detach kernel driver " - + "from device"); - } - } - - result = LibUsb.claimInterface(mDeviceHandle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + - LibUsb.errorName(result) + "]"); - } - } - else - { - throw new SourceException("couldn't claim usb interface - no " - + "device handle"); - } - } - - /** - * HackRF board identifier/type - */ - public BoardID getBoardID() throws UsbException - { - int id = readByte(Request.BOARD_ID_READ, (byte)0, (byte)0, false); - - return BoardID.lookup(id); - } - - /** - * HackRF firmware version string - */ - public String getFirmwareVersion() throws UsbException - { - ByteBuffer buffer = readArray(Request.VERSION_STRING_READ, 0, 0, 255); - - byte[] data = new byte[255]; - - buffer.get(data); - - return new String(data); - } - - /** - * HackRF part id number and serial number - */ - public Serial getSerial() throws UsbException - { - ByteBuffer buffer = readArray(Request.BOARD_PARTID_SERIALNO_READ, 0, 0, 24); - - return new Serial(buffer); - } - - /** - * Sets the HackRF transceiver mode - */ - public void setMode(Mode mode) throws UsbException - { - write(Request.SET_TRANSCEIVER_MODE, mode.getNumber(), 0); - } - - /** - * Sets the HackRF baseband filter - */ - public void setBasebandFilter(BasebandFilter filter) throws UsbException - { - write(Request.BASEBAND_FILTER_BANDWIDTH_SET, - filter.getLowValue(), - filter.getHighValue()); - } - - /** - * Enables (true) or disables (false) the amplifier - */ - public void setAmplifierEnabled(boolean enabled) throws UsbException - { - write(Request.AMP_ENABLE, (enabled ? 1 : 0), 0); - - mAmplifierEnabled = enabled; - } - - public boolean getAmplifier() - { - return mAmplifierEnabled; - } - - /** - * Sets the IF LNA Gain - */ - public void setLNAGain(HackRFLNAGain gain) throws UsbException - { - int result = readByte(Request.SET_LNA_GAIN, 0, gain.getValue(), true); - - if(result != 1) - { - throw new UsbException("couldn't set lna gain to " + gain); - } - } - - /** - * Sets the Baseband VGA Gain - */ - public void setVGAGain(HackRFVGAGain gain) throws UsbException - { - int result = readByte(Request.SET_VGA_GAIN, 0, gain.getValue(), true); - - if(result != 1) - { - throw new UsbException("couldn't set vga gain to " + gain); - } - } - - /** - * Not implemented - */ - public long getTunedFrequency() throws SourceException - { - return getFrequencyController().getTunedFrequency(); - } - - @Override - public void setTunedFrequency(long frequency) throws SourceException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(8); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - int mhz = (int)(frequency / 1E6); - int hz = (int)(frequency - (mhz * 1E6)); - - buffer.putInt(mhz); - buffer.putInt(hz); - - buffer.rewind(); - - try - { - write(Request.SET_FREQUENCY, 0, 0, buffer); - } - catch(UsbException e) - { - mLog.error("error setting frequency [" + frequency + "]", e); - - throw new SourceException("error setting frequency [" + - frequency + "]", e); - } - } - - @Override - public double getCurrentSampleRate() - { - return mSampleRate.getRate(); - } - - @Override - public void apply(TunerConfiguration config) throws SourceException - { - if(config instanceof HackRFTunerConfiguration) - { - HackRFTunerConfiguration hackRFConfig = - (HackRFTunerConfiguration)config; - - try - { - setSampleRate(hackRFConfig.getSampleRate()); - getFrequencyController().setFrequencyCorrection(hackRFConfig.getFrequencyCorrection()); - setAmplifierEnabled(hackRFConfig.getAmplifierEnabled()); - setLNAGain(hackRFConfig.getLNAGain()); - setVGAGain(hackRFConfig.getVGAGain()); - getFrequencyController().setFrequency(getFrequencyController().getFrequency()); - } - catch(UsbException e) - { - throw new SourceException("Error while applying tuner " - + "configuration", e); - } - - try - { - getFrequencyController().setFrequency(hackRFConfig.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } - else - { - throw new IllegalArgumentException("Invalid tuner configuration " - + "type [" + config.getClass() + "]"); - } - } - - public ByteBuffer readArray(Request request, - int value, - int index, - int length) throws UsbException - { - if(mDeviceHandle != null) - { - ByteBuffer buffer = ByteBuffer.allocateDirect(length); - - int transferred = LibUsb.controlTransfer(mDeviceHandle, - REQUEST_TYPE_IN, - request.getRequestNumber(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_US); - - if(transferred < 0) - { - throw new LibUsbException("read error", transferred); - } - - return buffer; - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - public int read(Request request, int value, int index, int length) - throws UsbException - { - if(!(length == 1 || length == 2 || length == 4)) - { - throw new IllegalArgumentException("invalid length [" + length + - "] must be: byte=1, short=2, int=4 to read a primitive"); - } - - ByteBuffer buffer = readArray(request, value, index, length); - - byte[] data = new byte[buffer.capacity()]; - - buffer.get(data); - - switch(data.length) - { - case 1: - return data[0]; - case 2: - return EndianUtils.readSwappedShort(data, 0); - case 4: - return EndianUtils.readSwappedInteger(data, 0); - default: - throw new UsbException("read() primitive returned an " - + "unrecognized byte array " + Arrays.toString(data)); - } - } - - public int readByte(Request request, int value, int index, boolean signed) - throws UsbException - { - ByteBuffer buffer = readArray(request, value, index, 1); - - if(signed) - { - return (int)(buffer.get()); - } - else - { - return (int)(buffer.get() & 0xFF); - } - } - - public void write(Request request, - int value, - int index, - ByteBuffer buffer) throws UsbException - { - if(mDeviceHandle != null) - { - int transferred = LibUsb.controlTransfer(mDeviceHandle, - REQUEST_TYPE_OUT, - request.getRequestNumber(), - (short)value, - (short)index, - buffer, - USB_TIMEOUT_US); - - if(transferred < 0) - { - throw new LibUsbException("error writing byte buffer", - transferred); - } - else if(transferred != buffer.capacity()) - { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); - } - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - /** - * Sends a request that doesn't have a data payload - */ - public void write(Request request, - int value, - int index) throws UsbException - { - write(request, value, index, ByteBuffer.allocateDirect(0)); - } - - /** - * Sample Rate - * - * Note: the libhackrf set sample rate method is designed to allow fractional - * sample rates. However, since we're only using integral sample rates, we - * simply invoke the setSampleRateManual method directly. - */ - public void setSampleRate(HackRFSampleRate rate) throws UsbException, SourceException - { - setSampleRateManual(rate.getRate(), 1); - - getFrequencyController().setSampleRate(rate.getRate()); - - setBasebandFilter(rate.getFilter()); - - mSampleRate = rate; - } - - public void setSampleRateManual(int frequency, int divider) - throws UsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(8); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - buffer.putInt(frequency); - buffer.putInt(divider); - - write(Request.SET_SAMPLE_RATE, 0, 0, buffer); - } - - public double getSampleRate() - { - return mSampleRate.getRate(); - } - - public enum Request - { - SET_TRANSCEIVER_MODE(1), - MAX2837_TRANSCEIVER_WRITE(2), - MAX2837_TRANSCEIVER_READ(3), - SI5351C_CLOCK_GENERATOR_WRITE(4), - SI5351C_CLOCK_GENERATOR_READ(5), - SET_SAMPLE_RATE(6), - BASEBAND_FILTER_BANDWIDTH_SET(7), - RFFC5071_MIXER_WRITE(8), - RFFC5071_MIXER_READ(9), - SPIFLASH_ERASE(10), - SPIFLASH_WRITE(11), - SPIFLASH_READ(12), - BOARD_ID_READ(14), - VERSION_STRING_READ(15), - SET_FREQUENCY(16), - AMP_ENABLE(17), - BOARD_PARTID_SERIALNO_READ(18), - SET_LNA_GAIN(19), - SET_VGA_GAIN(20), - SET_TXVGA_GAIN(21), - ANTENNA_ENABLE(23), - SET_FREQUENCY_EXPLICIT(24); - - private byte mRequestNumber; - - private Request(int number) - { - mRequestNumber = (byte)number; - } - - public byte getRequestNumber() - { - return mRequestNumber; - } - } - - public enum HackRFSampleRate - { - RATE2_016MHZ(2016000, "2.016 MHz", BasebandFilter.F3_50), - RATE3_024MHZ(3024000, "3.024 MHz", BasebandFilter.F5_00), - RATE4_464MHZ(4464000, "4.464 MHz", BasebandFilter.F6_00), - RATE5_376MHZ(5376000, "5.376 MHz", BasebandFilter.F7_00), - RATE7_488MHZ(7488000, "7.488 MHz", BasebandFilter.F9_00), - RATE10_080MHZ(10080000, "10.080 MHz", BasebandFilter.F12_00), - RATE12_000MHZ(12000000, "12.000 MHz", BasebandFilter.F14_00), - RATE13_440MHZ(13440000, "13.440 MHz", BasebandFilter.F15_00), - RATE14_976MHZ(14976000, "14.976 MHz", BasebandFilter.F20_00), - RATE19_968MHZ(19968000, "19.968 MHz", BasebandFilter.F24_00); - - private int mRate; - private String mLabel; - private BasebandFilter mFilter; - - private HackRFSampleRate(int rate, - String label, - BasebandFilter filter) - { - mRate = rate; - mLabel = label; - mFilter = filter; - } - - public int getRate() - { - return mRate; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public BasebandFilter getFilter() - { - return mFilter; - } - } - - public enum BasebandFilter - { - FAUTO(0, "AUTO"), - F1_75(1750000, "1.75 MHz"), - F2_50(2500000, "2.50 MHz"), - F3_50(3500000, "3.50 MHz"), - F5_00(5000000, "5.00 MHz"), - F5_50(5500000, "5.50 MHz"), - F6_00(6000000, "6.00 MHz"), - F7_00(7000000, "7.00 MHz"), - F8_00(8000000, "8.00 MHz"), - F9_00(9000000, "9.00 MHz"), - F10_00(10000000, "10.00 MHz"), - F12_00(12000000, "12.00 MHz"), - F14_00(14000000, "14.00 MHz"), - F15_00(15000000, "15.00 MHz"), - F20_00(20000000, "20.00 MHz"), - F24_00(24000000, "24.00 MHz"), - F28_00(28000000, "28.00 MHz"); - - private int mBandwidth; - private String mLabel; - - private BasebandFilter(int bandwidth, String label) - { - mBandwidth = bandwidth; - mLabel = label; - } - - public int getBandwidth() - { - return mBandwidth; - } - - public int getHighValue() - { - return mBandwidth >> 16; - } - - public int getLowValue() - { - return mBandwidth & 0xFFFF; - } - - public String getLabel() - { - return mLabel; - } - } - - public enum BoardID - { - JELLYBEAN(0x00, "HackRF Jelly Bean"), - JAWBREAKER(0x01, "HackRF Jaw Breaker"), - HACKRF_ONE(0x02, "HackRF One"), - INVALID(0xFF, "HackRF Unknown Board"); - - private byte mIDNumber; - private String mLabel; - - private BoardID(int number, String label) - { - mIDNumber = (byte)number; - mLabel = label; - } - - public String toString() - { - return mLabel; - } - - public String getLabel() - { - return mLabel; - } - - public byte getNumber() - { - return mIDNumber; - } - - public static BoardID lookup(int value) - { - switch(value) - { - case 0: - return JELLYBEAN; - case 1: - return JAWBREAKER; - case 2: - return HACKRF_ONE; - default: - return INVALID; - } - } - } - - public enum Mode - { - OFF(0, "Off"), - RECEIVE(1, "Receive"), - TRANSMIT(2, "Transmit"), - SS(3, "SS"); - - private byte mNumber; - private String mLabel; - - private Mode(int number, String label) - { - mNumber = (byte)number; - mLabel = label; - } - - public byte getNumber() - { - return mNumber; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - } - - public enum HackRFLNAGain - { - GAIN_0(0), - GAIN_8(8), - GAIN_16(16), - GAIN_24(24), - GAIN_32(32), - GAIN_40(40); - - private int mValue; - - private HackRFLNAGain(int value) - { - mValue = value; - } - - public int getValue() - { - return mValue; - } - - public String toString() - { - return String.valueOf(mValue) + " dB"; - } - } - - /** - * Receive (baseband) VGA Gain values - */ - public enum HackRFVGAGain - { - GAIN_0(0), - GAIN_2(2), - GAIN_4(4), - GAIN_6(6), - GAIN_8(8), - GAIN_10(10), - GAIN_12(12), - GAIN_14(14), - GAIN_16(16), - GAIN_18(18), - GAIN_20(20), - GAIN_22(22), - GAIN_23(24), - GAIN_26(26), - GAIN_28(28), - GAIN_30(30), - GAIN_32(32), - GAIN_34(34), - GAIN_36(36), - GAIN_38(38), - GAIN_40(40), - GAIN_42(42), - GAIN_44(44), - GAIN_46(46), - GAIN_48(48), - GAIN_50(50), - GAIN_52(52), - GAIN_54(54), - GAIN_56(56), - GAIN_58(58), - GAIN_60(60), - GAIN_62(62); - - private int mValue; - - private HackRFVGAGain(int value) - { - mValue = value; - } - - public int getValue() - { - return mValue; - } - - public String toString() - { - return String.valueOf(mValue) + " dB"; - } - } - - /** - * HackRF part id and serial number parsing class - */ - public class Serial - { - private byte[] mData; - - public Serial(ByteBuffer buffer) - { - mData = new byte[buffer.capacity()]; - - buffer.get(mData); - } - - public String getPartID() - { - int part0 = EndianUtils.readSwappedInteger(mData, 0); - int part1 = EndianUtils.readSwappedInteger(mData, 4); - - StringBuilder sb = new StringBuilder(); - - sb.append(String.format("%08X", part0)); - sb.append("-"); - sb.append(String.format("%08X", part1)); - - return sb.toString(); - } - - public String getSerialNumber() - { - int serial0 = EndianUtils.readSwappedInteger(mData, 8); - int serial1 = EndianUtils.readSwappedInteger(mData, 12); - int serial2 = EndianUtils.readSwappedInteger(mData, 16); - int serial3 = EndianUtils.readSwappedInteger(mData, 20); - - StringBuilder sb = new StringBuilder(); - - sb.append(String.format("%08X", serial0)); - sb.append("-"); - sb.append(String.format("%08X", serial1)); - sb.append("-"); - sb.append(String.format("%08X", serial2)); - sb.append("-"); - sb.append(String.format("%08X", serial3)); - - return sb.toString(); - } - } -} diff --git a/src/source/tuner/hackrf/HackRFTunerEditor.java b/src/source/tuner/hackrf/HackRFTunerEditor.java deleted file mode 100644 index 2691a345..00000000 --- a/src/source/tuner/hackrf/HackRFTunerEditor.java +++ /dev/null @@ -1,464 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.hackrf; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.JToggleButton; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; -import source.tuner.configuration.TunerConfigurationEvent; -import source.tuner.configuration.TunerConfigurationEvent.Event; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.hackrf.HackRFTunerController.BoardID; -import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; -import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; -import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; -import source.tuner.hackrf.HackRFTunerController.Serial; - -public class HackRFTunerEditor extends TunerConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerEditor.class ); - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JToggleButton mAmplifier; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - private boolean mLoading; - - private HackRFTunerController mController; - - public HackRFTunerEditor( TunerConfigurationModel tunerConfigurationModel, HackRFTuner tuner ) - { - super( tunerConfigurationModel ); - - mController = tuner.getController(); - - init(); - } - - private HackRFTunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (HackRFTunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][grow]" ) ); - - add( new JLabel( "HackRF Tuner Configuration" ), "span,align center" ); - - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - mComboSampleRate = new JComboBox<>( HackRFSampleRate.values() ); - mComboSampleRate.setEnabled( false ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - HackRFSampleRate sampleRate = - (HackRFSampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException|UsbException e2 ) - { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the sample rate setting [" + sampleRate.getLabel() + - "] " + e2.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply sample rate setting [" + - sampleRate.getLabel() + "]", e ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate ); - - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.getFrequencyController().setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply frequency correction " - + "value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - add( new JLabel( "Gain" ) ); - add( new JLabel( "" ), "span 2" ); //filler - - mAmplifier = new JToggleButton( "Amplifier" ); - mAmplifier.setEnabled( false ); - mAmplifier.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - mController.setAmplifierEnabled( mAmplifier.isSelected() ); - save(); - } - catch ( UsbException e ) - { - mLog.error( "couldn't enable/disable amplifier", e ); - - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "Couldn't change amplifier setting", - "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE ); - } - } - } ); - add( mAmplifier ); - - mComboLNAGain = new JComboBox( HackRFLNAGain.values() ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - HackRFLNAGain lnaGain = (HackRFLNAGain)mComboLNAGain.getSelectedItem(); - - if ( lnaGain == null ) - { - lnaGain = HackRFLNAGain.GAIN_0; - } - - mController.setLNAGain( lnaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply LNA gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Adjust to set the IF gain" ); - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); - - mComboVGAGain = new JComboBox( HackRFVGAGain.values() ); - mComboVGAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); - - if( vgaGain == null ) - { - vgaGain = HackRFVGAGain.GAIN_4; - } - - mController.setVGAGain( vgaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" - + " - couldn't apply the VGA gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply VGA gain setting", e ); - } - } - } ); - mComboVGAGain.setToolTipText( "VGA Gain. Adjust to set the baseband gain" ); - add( new JLabel( "VGA:" ) ); - add( mComboVGAGain ); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mComboSampleRate.isEnabled() != enabled ) - { - mComboSampleRate.setEnabled( enabled ); - } - - if( mAmplifier.isEnabled() != enabled ) - { - mAmplifier.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboVGAGain.isEnabled() != enabled ) - { - mComboVGAGain.setEnabled( enabled ); - } - } - - private String getTunerInfo() - { - BoardID board = BoardID.INVALID; - - try - { - board = mController.getBoardID(); - } - catch ( UsbException e ) - { - mLog.error( "couldn't read HackRF board identifier", e ); - } - - StringBuilder sb = new StringBuilder(); - - sb.append( "

HackRF Tuner

" ); - - sb.append( "Board: " ); - sb.append( board.getLabel() ); - sb.append( "
" ); - - Serial serial = null; - - try - { - serial = mController.getSerial(); - } - catch( Exception e ) - { - mLog.error( "couldn't read HackRF serial number", e ); - } - - if( serial != null ) - { - sb.append( "Serial: " ); - sb.append( serial.getSerialNumber() ); - sb.append( "
" ); - - sb.append( "Part: " ); - sb.append( serial.getPartID() ); - sb.append( "
" ); - } - else - { - sb.append( "Serial: Unknown
" ); - sb.append( "Part: Unknown
" ); - } - - String firmware = null; - - try - { - firmware = mController.getFirmwareVersion(); - } - catch( Exception e ) - { - mLog.error( "couldn't read HackRF firmware version", e ); - } - - if( firmware != null ) - { - sb.append( "Firmware: " ); - sb.append( firmware ); - sb.append( "
" ); - } - else - { - sb.append( "Firmware: Unknown
" ); - } - - return sb.toString(); - } - - @Override - public void setItem( TunerConfiguration tunerConfiguration ) - { - super.setItem( tunerConfiguration ); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if( hasItem() ) - { - HackRFTunerConfiguration config = getConfiguration(); - - if( tunerConfiguration.isAssigned() ) - { - setControlsEnabled( true ); - - mConfigurationName.setText( config.getName() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mAmplifier.setSelected( config.getAmplifierEnabled() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboVGAGain.setSelectedItem( config.getVGAGain() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - - mLoading = false; - } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - HackRFTunerConfiguration config = getConfiguration(); - - config.setName( mConfigurationName.getText() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - config.setSampleRate( (HackRFSampleRate)mComboSampleRate.getSelectedItem() ); - config.setAmplifierEnabled( mAmplifier.isSelected() ); - config.setLNAGain( (HackRFLNAGain)mComboLNAGain.getSelectedItem() ); - config.setVGAGain( (HackRFVGAGain)mComboVGAGain.getSelectedItem() ); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); - } - } -} diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java deleted file mode 100644 index dceece1c..00000000 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.rtl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import source.SourceException; -import source.tuner.Tuner; -import source.tuner.TunerClass; -import source.tuner.TunerType; - -public class RTL2832Tuner extends Tuner -{ - private final static Logger mLog = LoggerFactory.getLogger(RTL2832Tuner.class); - - private TunerClass mTunerClass; - - public RTL2832Tuner(TunerClass tunerClass, RTL2832TunerController controller) throws SourceException - { - super(tunerClass.getVendorDeviceLabel() + "/" + controller.getTunerType().getLabel() + " " + - controller.getUniqueID(), controller); - - mTunerClass = tunerClass; - } - - public void dispose() - { - } - - public RTL2832TunerController getController() - { - return (RTL2832TunerController)getTunerController(); - } - - @Override - public String getUniqueID() - { - return getController().getUniqueID(); - } - - @Override - public TunerClass getTunerClass() - { - return mTunerClass; - } - - @Override - public TunerType getTunerType() - { - return getController().getTunerType(); - } - - @Override - public double getSampleSize() - { - //Note: although sample size is 8, we set it to 11 to align with the - //actual noise floor. - return 11.0; - } -} diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java deleted file mode 100644 index 372784e4..00000000 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ /dev/null @@ -1,1555 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2017 Dennis Sheirer - * - * Java version based on librtlsdr - * Copyright (C) 2012-2013 by Steve Markgraf - * Copyright (C) 2012 by Dimitri Stolnikov - * - * 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 source.tuner.rtl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import sample.Broadcaster; -import sample.Listener; -import sample.adapter.ByteSampleAdapter; -import sample.adapter.ISampleAdapter; -import sample.complex.ComplexBuffer; -import source.SourceException; -import source.tuner.TunerController; -import source.tuner.TunerManager; -import source.tuner.TunerType; -import source.tuner.usb.USBTransferProcessor; -import source.tuner.usb.USBTunerController; - -import javax.usb.UsbDisconnectedException; -import javax.usb.UsbException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; - -public abstract class RTL2832TunerController extends USBTunerController -{ - private final static Logger mLog = LoggerFactory.getLogger(RTL2832TunerController.class); - - public final static int TWO_TO_22_POWER = 4194304; - - public final static int USB_TRANSFER_BUFFER_SIZE = 131072; - public final static byte USB_INTERFACE = (byte) 0x0; - public final static byte CONTROL_ENDPOINT_IN = (byte) (LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR); - public final static byte CONTROL_ENDPOINT_OUT = (byte) (LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR); - - public final static long TIMEOUT_US = 1000000l; //uSeconds - public final static byte REQUEST_ZERO = (byte) 0; - public final static byte EEPROM_ADDRESS = (byte) 0xA0; - - public final static byte[] sFIR_COEFFICIENTS = - { - (byte) 0xCA, (byte) 0xDC, (byte) 0xD7, (byte) 0xD8, (byte) 0xE0, - (byte) 0xF2, (byte) 0x0E, (byte) 0x35, (byte) 0x06, (byte) 0x50, - (byte) 0x9C, (byte) 0x0D, (byte) 0x71, (byte) 0x11, (byte) 0x14, - (byte) 0x71, (byte) 0x74, (byte) 0x19, (byte) 0x41, (byte) 0xA5 - }; - - public static final SampleRate DEFAULT_SAMPLE_RATE = SampleRate.RATE_2_400MHZ; - - protected Device mDevice; - protected DeviceDescriptor mDeviceDescriptor; - protected DeviceHandle mDeviceHandle; - - private SampleRate mSampleRate = DEFAULT_SAMPLE_RATE; - - protected ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); - protected int mOscillatorFrequency = 28800000; //28.8 MHz - protected USBTransferProcessor mUSBTransferProcessor; - protected Descriptor mDescriptor; - - /** - * Abstract tuner controller device. Use the static getTunerClass() method - * to determine the tuner type, and construct the corresponding child - * tuner controller class for that tuner type. - */ - public RTL2832TunerController(Device device, DeviceDescriptor deviceDescriptor, long minTunableFrequency, - long maxTunableFrequency, int centerUnusableBandwidth, - double usableBandwidthPercentage) throws SourceException - { - super(minTunableFrequency, - maxTunableFrequency, - centerUnusableBandwidth, - usableBandwidthPercentage); - - mDevice = device; - mDeviceDescriptor = deviceDescriptor; - } - - - public void init() throws SourceException - { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) - { - mDeviceHandle = null; - - throw new SourceException("libusb couldn't open RTL2832 usb " - + "device [" + LibUsb.errorName(result) + "]"); - } - - claimInterface(mDeviceHandle); - - try - { - setSampleRate(DEFAULT_SAMPLE_RATE); - } - catch(Exception e) - { - throw new SourceException("RTL2832 Tuner Controller - couldn't " - + "set default sample rate", e); - } - - byte[] eeprom = null; - - try - { - /* Read the contents of the 256-byte EEPROM */ - eeprom = readEEPROM(mDeviceHandle, (short) 0, 256); - } - catch(Exception e) - { - mLog.error("error while reading the EEPROM device descriptor", e); - } - - try - { - mDescriptor = new Descriptor(eeprom); - - if(eeprom == null) - { - mLog.error("eeprom byte array was null - constructed " - + "empty descriptor object"); - } - } - catch(Exception e) - { - mLog.error("error while constructing device descriptor using " - + "descriptor byte array " + - (eeprom == null ? "[null]" : Arrays.toString(eeprom)), e); - } - - String deviceName = getTunerType().getLabel() + " " + getUniqueID(); - - mUSBTransferProcessor = new RTL2832USBTransferProcessor(deviceName, mDeviceHandle, mSampleAdapter, - USB_TRANSFER_BUFFER_SIZE); - } - - @Override - protected USBTransferProcessor getUSBTransferProcessor() - { - return mUSBTransferProcessor; - } - - /** - * Claims the USB interface. Attempts to detach the active kernel driver - * if one is currently attached. - */ - public static void claimInterface(DeviceHandle handle) throws SourceException - { - if(handle != null) - { - int result = LibUsb.kernelDriverActive(handle, USB_INTERFACE); - - if(result == 1) - { - result = LibUsb.detachKernelDriver(handle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - mLog.error("failed attempt to detach kernel driver [" + - LibUsb.errorName(result) + "]"); - - throw new SourceException("couldn't detach kernel driver " - + "from device"); - } - } - - result = LibUsb.claimInterface(handle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't claim usb interface [" + - LibUsb.errorName(result) + "]"); - } - } - else - { - throw new SourceException("couldn't claim usb interface - no " - + "device handle"); - } - } - - public static void releaseInterface(DeviceHandle handle) - throws SourceException - { - int result = LibUsb.releaseInterface(handle, USB_INTERFACE); - - if(result != LibUsb.SUCCESS) - { - throw new SourceException("couldn't release interface [" + - LibUsb.errorName(result) + "]"); - } - } - - /** - * Descriptor contains all identifiers and labels parsed from the EEPROM. - * - * May return null if unable to get 256 byte eeprom descriptor from tuner - * or if the descriptor doesn't begin with byte values of 0x28 and 0x32 - * meaning it is a valid (and can be parsed) RTL2832 descriptor - */ - public Descriptor getDescriptor() - { - if(mDescriptor != null && mDescriptor.isValid()) - { - return mDescriptor; - } - - return null; - } - - public void setSamplingMode(SampleMode mode) throws LibUsbException - { - switch(mode) - { - case QUADRATURE: - /* Set intermediate frequency to 0 Hz */ - setIFFrequency(0); - - /* Enable I/Q ADC Input */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x08, - (short) 0xCD, - 1); - - /* Enable zero-IF mode */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0xB1, - (short) 0x1B, - 1); - - /* Set default i/q path */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x06, - (short) 0x80, - 1); - break; - case DIRECT: - default: - throw new LibUsbException("QUADRATURE mode is the only mode " - + "currently supported", LibUsb.ERROR_NOT_SUPPORTED); - } - } - - public void setIFFrequency(int frequency) throws LibUsbException - { - long ifFrequency = ((long) TWO_TO_22_POWER * (long) frequency) / - (long) mOscillatorFrequency * -1; - - /* Write byte 2 (high) */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x19, - (short) (Long.rotateRight(ifFrequency, 16) & 0x3F), - 1); - - /* Write byte 1 (middle) */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x1A, - (short) (Long.rotateRight(ifFrequency, 8) & 0xFF), - 1); - - /* Write byte 0 (low) */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x1B, - (short) (ifFrequency & 0xFF), - 1); - } - - public abstract void initTuner(boolean controlI2CRepeater) - throws UsbException; - - /** - * Provides a unique identifier to use in distinctly identifying this - * tuner from among other tuners of the same type, so that we can fetch a - * tuner configuration from the settings manager for this specific tuner. - * - * @return serial number of the device - */ - public String getUniqueID() - { - if(mDescriptor != null && mDescriptor.hasSerial()) - { - return mDescriptor.getSerial(); - } - else - { - int serial = - (0xFF & mDeviceDescriptor.iSerialNumber()); - - return "SER#" + serial; - } - } - - public abstract void setSampleRateFilters(int sampleRate) - throws SourceException; - - public abstract TunerType getTunerType(); - - public static TunerType identifyTunerType(Device device) - throws SourceException - { - DeviceHandle handle = new DeviceHandle(); - - int reason = LibUsb.open(device, handle); - - if(reason != LibUsb.SUCCESS) - { - throw new SourceException("couldn't open device - check permissions" - + " Linux: udev.rule? , or Windows: reinstall Zadig? [" + LibUsb.errorName(reason) + "]"); - } - - TunerType tunerClass = TunerType.UNKNOWN; - - try - { - claimInterface(handle); - - /* Perform a dummy write to see if the device needs reset */ - boolean resetRequired = false; - - try - { - writeRegister(handle, - Block.USB, - Address.USB_SYSCTL.getAddress(), - 0x09, - 1); - } - catch(LibUsbException e) - { - - if(e.getErrorCode() < 0) - { - mLog.error("error performing dummy write - attempting " - + "device reset", e); - - resetRequired = true; - } - else - { - throw new SourceException("error performing dummy write " - + "to device [" + LibUsb.errorName( - e.getErrorCode()) + "]", e); - } - } - - if(resetRequired) - { - reason = LibUsb.resetDevice(handle); - - try - { - writeRegister(handle, - Block.USB, - Address.USB_SYSCTL.getAddress(), - 0x09, - 1); - } - catch(LibUsbException e2) - { - mLog.error("device reset attempted, but lost device handle. " - + "Try restarting the application to use this device"); - - throw new SourceException("couldn't reset device"); - } - } - - /* Initialize the baseband */ - initBaseband(handle); - - enableI2CRepeater(handle, true); - - boolean controlI2CRepeater = false; - - /* Test for each tuner type until we find the correct one */ - if(isTuner(TunerTypeCheck.E4K, handle, controlI2CRepeater)) - { - tunerClass = TunerType.ELONICS_E4000; - } - else if(isTuner(TunerTypeCheck.FC0013, handle, controlI2CRepeater)) - { - tunerClass = TunerType.FITIPOWER_FC0013; - } - else if(isTuner(TunerTypeCheck.R820T, handle, controlI2CRepeater)) - { - tunerClass = TunerType.RAFAELMICRO_R820T; - } - else if(isTuner(TunerTypeCheck.R828D, handle, controlI2CRepeater)) - { - tunerClass = TunerType.RAFAELMICRO_R828D; - } - else if(isTuner(TunerTypeCheck.FC2580, handle, controlI2CRepeater)) - { - tunerClass = TunerType.FCI_FC2580; - } - else if(isTuner(TunerTypeCheck.FC0012, handle, controlI2CRepeater)) - { - tunerClass = TunerType.FITIPOWER_FC0012; - } - - enableI2CRepeater(handle, false); - - releaseInterface(handle); - - LibUsb.close(handle); - } - catch(Exception e) - { - mLog.error("error while determining tuner type", e); - } - - return tunerClass; - } - - /** - * Releases the USB interface - */ - public void release() - { - try - { - if(mUSBTransferProcessor != null) - { - mUSBTransferProcessor.removeListener(); - TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(mUSBTransferProcessor); - } - - LibUsb.releaseInterface(mDeviceHandle, USB_INTERFACE); - } - catch(Exception e) - { - mLog.error("attempt to release USB interface failed", e); - } - } - - public void resetUSBBuffer() throws LibUsbException - { - writeRegister(mDeviceHandle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); - writeRegister(mDeviceHandle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x0000, 2); - } - - public static void initBaseband(DeviceHandle handle) throws LibUsbException - { - /* Initialize USB */ - writeRegister(handle, Block.USB, Address.USB_SYSCTL.getAddress(), 0x09, 1); - writeRegister(handle, Block.USB, Address.USB_EPA_MAXPKT.getAddress(), 0x0002, 2); - writeRegister(handle, Block.USB, Address.USB_EPA_CTL.getAddress(), 0x1002, 2); - - /* Power on demod */ - writeRegister(handle, Block.SYS, Address.DEMOD_CTL_1.getAddress(), 0x22, 1); - writeRegister(handle, Block.SYS, Address.DEMOD_CTL.getAddress(), 0xE8, 1); - - /* Reset demod */ - writeDemodRegister(handle, Page.ONE, (short) 0x01, 0x14, 1); //Bit 3 = soft reset - writeDemodRegister(handle, Page.ONE, (short) 0x01, 0x10, 1); - - /* Disable spectrum inversion and adjacent channel rejection */ - writeDemodRegister(handle, Page.ONE, (short) 0x15, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x16, 0x0000, 2); - - /* Clear DDC shift and IF frequency registers */ - writeDemodRegister(handle, Page.ONE, (short) 0x16, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x17, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x18, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x19, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x1A, 0x00, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x1B, 0x00, 1); - - /* Set FIR coefficients */ - for(int x = 0; x < sFIR_COEFFICIENTS.length; x++) - { - writeDemodRegister(handle, - Page.ONE, - (short) (0x1C + x), - sFIR_COEFFICIENTS[x], - 1); - } - - /* Enable SDR mode, disable DAGC (bit 5) */ - writeDemodRegister(handle, Page.ZERO, (short) 0x19, 0x05, 1); - - /* Init FSM state-holding register */ - writeDemodRegister(handle, Page.ONE, (short) 0x93, 0xF0, 1); - writeDemodRegister(handle, Page.ONE, (short) 0x94, 0x0F, 1); - - /* Disable AGC (en_dagc, bit 0) (seems to have no effect) */ - writeDemodRegister(handle, Page.ONE, (short) 0x11, 0x00, 1); - - /* Disable RF and IF AGC loop */ - writeDemodRegister(handle, Page.ONE, (short) 0x04, 0x00, 1); - - /* Disable PID filter */ - writeDemodRegister(handle, Page.ZERO, (short) 0x61, 0x60, 1); - - /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ - writeDemodRegister(handle, Page.ZERO, (short) 0x06, 0x80, 1); - - /* Enable Zero-if mode (en_bbin bit), - * DC cancellation (en_dc_est), - * IQ estimation/compensation (en_iq_comp, en_iq_est) */ - writeDemodRegister(handle, Page.ONE, (short) 0xB1, 0x1B, 1); - - /* Disable 4.096 MHz clock output on pin TP_CK0 */ - writeDemodRegister(handle, Page.ZERO, (short) 0x0D, 0x83, 1); - } - - protected void deinitBaseband(DeviceHandle handle) - throws IllegalArgumentException, UsbDisconnectedException, UsbException - { - writeRegister(handle, Block.SYS, Address.DEMOD_CTL.getAddress(), 0x20, 1); - } - - /** - * Sets the General Purpose Input/Output (GPIO) register bit - * - * @param handle - USB tuner device - * @param bitMask - bit mask with one for targeted register bits and zero - * for the non-targeted register bits - * @param enabled - true to set the bit and false to clear the bit - * @throws UsbDisconnectedException - if the tuner device is disconnected - * @throws UsbException - if there is a USB error while communicating with - * the device - */ - protected static void setGPIOBit(DeviceHandle handle, - byte bitMask, - boolean enabled) throws LibUsbException - { - //Get current register value - int value = readRegister(handle, Block.SYS, Address.GPO.getAddress(), 1); - - //Update the masked bits - if(enabled) - { - value |= bitMask; - } - else - { - value &= ~bitMask; - } - - //Write the change back to the device - writeRegister(handle, Block.SYS, Address.GPO.getAddress(), value, 1); - } - - /** - * Enables GPIO Output - * - * @param handle - usb tuner device - * @param bitMask - mask containing one bit value in targeted bit field(s) - * @throws UsbDisconnectedException - * @throws UsbException - */ - protected static void setGPIOOutput(DeviceHandle handle, byte bitMask) - throws LibUsbException - { - //Get current register value - int value = readRegister(handle, Block.SYS, Address.GPD.getAddress(), 1); - - //Mask the value and rewrite it - writeRegister(handle, Block.SYS, Address.GPO.getAddress(), - value & ~bitMask, 1); - - //Get current register value - value = readRegister(handle, Block.SYS, Address.GPOE.getAddress(), 1); - - //Mask the value and rewrite it - writeRegister(handle, Block.SYS, Address.GPOE.getAddress(), - value | bitMask, 1); - } - - protected static void enableI2CRepeater(DeviceHandle handle, - boolean enabled) - throws LibUsbException - { - Page page = Page.ONE; - short address = 1; - int value; - - if(enabled) - { - value = 0x18; //ON - } - else - { - value = 0x10; //OFF - } - - writeDemodRegister(handle, page, address, value, 1); - } - - protected boolean isI2CRepeaterEnabled() throws SourceException - { - int register = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x1, 1); - - return register == 0x18; - } - - protected static int readI2CRegister(DeviceHandle handle, - byte i2CAddress, - byte i2CRegister, - boolean controlI2CRepeater) - throws LibUsbException - { - short address = (short) (i2CAddress & 0xFF); - - ByteBuffer buffer = ByteBuffer.allocateDirect(1); - buffer.put(i2CRegister); - buffer.rewind(); - - ByteBuffer data = ByteBuffer.allocateDirect(1); - - if(controlI2CRepeater) - { - enableI2CRepeater(handle, true); - - write(handle, address, Block.I2C, buffer); - read(handle, address, Block.I2C, data); - - enableI2CRepeater(handle, false); - } - else - { - write(handle, address, Block.I2C, buffer); - read(handle, address, Block.I2C, data); - } - - return (int) (data.get() & 0xFF); - } - - protected void writeI2CRegister(DeviceHandle handle, - byte i2CAddress, - byte i2CRegister, - byte value, - boolean controlI2CRepeater) throws LibUsbException - { - - short address = (short) (i2CAddress & 0xFF); - - ByteBuffer buffer = ByteBuffer.allocateDirect(2); - buffer.put(i2CRegister); - buffer.put(value); - - buffer.rewind(); - - if(controlI2CRepeater) - { - enableI2CRepeater(handle, true); - write(handle, address, Block.I2C, buffer); - enableI2CRepeater(handle, false); - } - else - { - write(handle, address, Block.I2C, buffer); - } - } - - protected static void writeDemodRegister(DeviceHandle handle, - Page page, - short address, - int value, - int length) throws LibUsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(length); - buffer.order(ByteOrder.BIG_ENDIAN); - - if(length == 1) - { - buffer.put((byte) (value & 0xFF)); - } - else if(length == 2) - { - buffer.putShort((short) (value & 0xFFFF)); - } - else - { - throw new IllegalArgumentException("Cannot write value greater " - + "than 16 bits to the register - length [" + length + "]"); - } - - short index = (short) (0x10 | page.getPage()); - - short newAddress = (short) (address << 8 | 0x20); - - write(handle, newAddress, index, buffer); - - readDemodRegister(handle, Page.TEN, (short) 1, length); - } - - protected static int readDemodRegister(DeviceHandle handle, - Page page, - short address, - int length) throws LibUsbException - { - short index = page.getPage(); - short newAddress = (short) ((address << 8) | 0x20); - - ByteBuffer buffer = ByteBuffer.allocateDirect(length); - - read(handle, newAddress, index, buffer); - buffer.order(ByteOrder.LITTLE_ENDIAN); - - if(length == 2) - { - return (int) (buffer.getShort() & 0xFFFF); - } - else - { - return (int) (buffer.get() & 0xFF); - } - } - - protected static void writeRegister(DeviceHandle handle, - Block block, - short address, - int value, - int length) throws LibUsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(length); - buffer.order(ByteOrder.BIG_ENDIAN); - - if(length == 1) - { - buffer.put((byte) (value & 0xFF)); - } - else if(length == 2) - { - buffer.putShort((short) value); - } - else - { - throw new IllegalArgumentException("Cannot write value greater " - + "than 16 bits to the register - length [" + length + "]"); - } - - buffer.rewind(); - - write(handle, address, block, buffer); - } - - protected static int readRegister(DeviceHandle handle, - Block block, - short address, - int length) throws LibUsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(2); - - read(handle, address, block, buffer); - - buffer.order(ByteOrder.LITTLE_ENDIAN); - - if(length == 2) - { - return (int) (buffer.getShort() & 0xFFFF); - } - else - { - return (int) (buffer.get() & 0xFF); - } - } - - /** - */ - protected static void write(DeviceHandle handle, - short address, - Block block, - ByteBuffer buffer) throws LibUsbException - { - write(handle, address, block.getWriteIndex(), buffer); - } - - protected static void write(DeviceHandle handle, - short value, - short index, - ByteBuffer buffer) throws LibUsbException - { - if(handle != null) - { - int transferred = LibUsb.controlTransfer(handle, - CONTROL_ENDPOINT_OUT, - REQUEST_ZERO, - value, - index, - buffer, - TIMEOUT_US); - - if(transferred < 0) - { - throw new LibUsbException("error writing byte buffer", - transferred); - } - else if(transferred != buffer.capacity()) - { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); - } - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - /** - * Performs a control type read - */ - protected static void read(DeviceHandle handle, - short address, - short index, - ByteBuffer buffer) throws LibUsbException - { - if(handle != null) - { - int transferred = LibUsb.controlTransfer(handle, - CONTROL_ENDPOINT_IN, - REQUEST_ZERO, - address, - index, - buffer, - TIMEOUT_US); - - if(transferred < 0) - { - throw new LibUsbException("read error", transferred); - } - else if(transferred != buffer.capacity()) - { - throw new LibUsbException("transferred bytes [" + - transferred + "] is not what was expected [" + - buffer.capacity() + "]", transferred); - } - } - else - { - throw new LibUsbException("device handle is null", - LibUsb.ERROR_NO_DEVICE); - } - } - - /** - * Reads byte array from index at the address. - * - * @return big-endian byte array (needs to be swapped to be usable) - */ - protected static void read(DeviceHandle handle, - short address, - Block block, - ByteBuffer buffer) throws LibUsbException - { - read(handle, address, block.getReadIndex(), buffer); - } - - /** - * Tests if the specified tuner type is contained in the usb tuner device. - * - * @param type - tuner type to test for - * @param handle - handle to the usb tuner device - * @param controlI2CRepeater - indicates if the method should control the - * I2C repeater independently - * @return - true if the device is the specified tuner type - */ - protected static boolean isTuner(TunerTypeCheck type, - DeviceHandle handle, - boolean controlI2CRepeater) - { - try - { - if(type == TunerTypeCheck.FC0012 || - type == TunerTypeCheck.FC2580) - { - /* Initialize the GPIOs */ - setGPIOOutput(handle, (byte) 0x20); - - /* Reset tuner before probing */ - setGPIOBit(handle, (byte) 0x20, true); - setGPIOBit(handle, (byte) 0x20, false); - } - - int value = readI2CRegister(handle, type.getI2CAddress(), - type.getCheckAddress(), controlI2CRepeater); - - if(type == TunerTypeCheck.FC2580) - { - return ((value & 0x7F) == type.getCheckValue()); - } - else - { - return (value == type.getCheckValue()); - } - } - catch(LibUsbException e) - { - //Do nothing ... it's not the specified tuner - } - - return false; - } - - public double getCurrentSampleRate() - { - return mSampleRate.getRate(); - } - - public int getSampleRateFromTuner() throws SourceException - { - try - { - int high = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x9F, 2); - int low = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0xA1, 2); - - int ratio = Integer.rotateLeft(high, 16) | low; - - int rate = (int) (mOscillatorFrequency * TWO_TO_22_POWER / ratio); - - SampleRate sampleRate = SampleRate.getClosest(rate); - - /* If we're not currently set to this rate, set it as the current rate */ - if(sampleRate.getRate() != rate) - { - setSampleRate(sampleRate); - - return sampleRate.getRate(); - } - } - catch(Exception e) - { - throw new SourceException("RTL2832 Tuner Controller - cannot get " - + "current sample rate", e); - } - - return DEFAULT_SAMPLE_RATE.getRate(); - } - - public void setSampleRate(SampleRate sampleRate) throws SourceException - { - /* Write high-order 16-bits of sample rate ratio to demod register */ - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x9F, - sampleRate.getRatioHighBits(), 2); - - /* Write low-order 16-bits of sample rate ratio to demod register. - * Note: none of the defined rates have a low order ratio value, so we - * simply write a zero to the register */ - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0xA1, 0, 2); - - /* Set sample rate correction to 0 */ - setSampleRateFrequencyCorrection(0); - - /* Reset the demod for the changes to take effect */ - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x01, 0x14, 1); - writeDemodRegister(mDeviceHandle, Page.ONE, (short) 0x01, 0x10, 1); - - /* Apply any tuner specific sample rate filter settings */ - setSampleRateFilters(sampleRate.getRate()); - - mSampleRate = sampleRate; - - mFrequencyController.setSampleRate(sampleRate.getRate()); - } - - public void setSampleRateFrequencyCorrection(int ppm) throws SourceException - { - int offset = -ppm * TWO_TO_22_POWER / 1000000; - - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x3F, - (offset & 0xFF), - 1); - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x3E, - (Integer.rotateRight(offset, 8) & 0xFF), - 1); - /* Test to retune controller to apply frequency correction */ - try - { - mFrequencyController.setFrequency(mFrequencyController.getFrequency()); - } - catch(Exception e) - { - throw new SourceException("couldn't set sample rate frequency correction", e); - } - } - - public int getSampleRateFrequencyCorrection() throws UsbException - { - int high = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x3E, 1); - int low = readDemodRegister(mDeviceHandle, Page.ONE, (short) 0x3F, 1); - - return (Integer.rotateLeft(high, 8) | low); - } - - /** - * Returns contents of the 256-byte EEPROM. The contents are as follows: - * - * 256-byte EEPROM (in hex): - * 00/01 - 2832 Signature - * 03/02 - 0BDA Vendor ID - * 05/04 - 2832 Product ID - * 06 - A5 (has serial id?) - * 07 - 16 (bit field - bit 0 = remote wakeup, bit 1 = IR enabled - * 08 - 02 or 12 - * 10/09 0310 ETX(0x03) plus label length (includes length and ETX bytes) - * 12/11 First UTF-16 character - * 14/13 Second UTF-16 character ... - * - * Label 1: vendor - * Label 2: product - * Label 3: serial - * Label 4,5 ... (user defined) - */ - public byte[] readEEPROM(DeviceHandle handle, short offset, int length) - throws IllegalArgumentException - { - if(offset + length > 256) - { - throw new IllegalArgumentException("cannot read more than 256 " - + "bytes from EEPROM - requested to read to byte [" + - (offset + length) + "]"); - } - - byte[] data = new byte[length]; - ByteBuffer buffer = ByteBuffer.allocateDirect(1); - - try - { - /* Tell the RTL-2832 to address the EEPROM */ - writeRegister(handle, Block.I2C, EEPROM_ADDRESS, (byte) offset, 1); - } - catch(LibUsbException e) - { - mLog.error("usb error while attempting to set read address to " - + "EEPROM register, prior to reading the EEPROM device " - + "descriptor", e); - } - - for(int x = 0; x < length; x++) - { - try - { - read(handle, EEPROM_ADDRESS, Block.I2C, buffer); - data[x] = buffer.get(); - buffer.rewind(); - } - catch(Exception e) - { - mLog.error("error while reading eeprom byte [" + x + "/" + - length + "] aborting eeprom read and returning partially " - + "filled descriptor byte array", e); - x = length; - } - } - - return data; - } - - /** - * Writes a single byte to the 256-byte EEPROM using the specified offset. - * - * Note: introduce a 5 millisecond delay between each successive write to - * the EEPROM or subsequent writes may fail. - */ - public void writeEEPROMByte(DeviceHandle handle, byte offset, byte value) - throws IllegalArgumentException, UsbDisconnectedException, UsbException - { - if(offset < 0 || offset > 255) - { - throw new IllegalArgumentException("RTL2832 Tuner Controller - " - + "EEPROM offset must be within range of 0 - 255"); - } - - int offsetAndValue = Integer.rotateLeft((0xFF & offset), 8) | - (0xFF & value); - - writeRegister(handle, Block.I2C, EEPROM_ADDRESS, offsetAndValue, 2); - } - - public enum Address - { - USB_SYSCTL(0x2000), - USB_CTRL(0x2010), - USB_STAT(0x2014), - USB_EPA_CFG(0x2144), - USB_EPA_CTL(0x2148), - USB_EPA_MAXPKT(0x2158), - USB_EPA_MAXPKT_2(0x215A), - USB_EPA_FIFO_CFG(0x2160), - DEMOD_CTL(0x3000), - GPO(0x3001), - GPI(0x3002), - GPOE(0x3003), - GPD(0x3004), - SYSINTE(0x3005), - SYSINTS(0x3006), - GP_CFG0(0x3007), - GP_CFG1(0x3008), - SYSINTE_1(0x3009), - SYSINTS_1(0x300A), - DEMOD_CTL_1(0x300B), - IR_SUSPEND(0x300C); - - private int mAddress; - - private Address(int address) - { - mAddress = address; - } - - public short getAddress() - { - return (short) mAddress; - } - } - - public enum Page - { - ZERO(0x0), - ONE(0x1), - TEN(0xA); - - private int mPage; - - private Page(int page) - { - mPage = page; - } - - public byte getPage() - { - return (byte) (mPage & 0xFF); - } - } - - public enum SampleMode - { - QUADRATURE, DIRECT; - } - - public enum Block - { - DEMOD(0), - USB(1), - SYS(2), - TUN(3), - ROM(4), - IR(5), - I2C(6); //I2C controller - - private int mValue; - - private Block(int value) - { - mValue = value; - } - - public int getValue() - { - return mValue; - } - - /** - * Returns the value left shifted 8 bits - */ - public short getReadIndex() - { - return (short) Integer.rotateLeft(mValue, 8); - } - - public short getWriteIndex() - { - return (short) (getReadIndex() | 0x10); - } - } - - /** - * Sample rates supported by the RTL-2832. - * - * Formula to calculate the ratio value: - * - * ratio = ( ( crystal_frequency * 2^22 ) / sample_rate ) & ~3 - * - * Default crystal_frequency is 28,800,000 - * - * This produces a 32-bit value that has to be set in 2 x 16-bit registers. - * Place the high 16-bit value in ratioMSB and the low 16-bit value in - * ratioLSB. Use integer for these values to avoid sign-extension issues. - * - * Mask the value with 0xFFFF when setting the register. - */ - public enum SampleRate - { - /* Note: sample rates below 1.0MHz are subject to aliasing */ - RATE_0_240MHZ(0x1E00, 240000, "0.240 MHz"), - RATE_0_288MHZ(0x1900, 288000, "0.288 MHz"), - RATE_0_960MHZ(0x0780, 960000, "0.960 MHz"), - RATE_1_200MHZ(0x0600, 1200000, "1.200 MHz"), - RATE_1_440MHZ(0x0500, 1440000, "1.440 MHz"), - RATE_1_920MHZ(0x03C0, 2016000, "2.016 MHz"), - RATE_2_304MHZ(0x0320, 2208000, "2.208 MHz"), - RATE_2_400MHZ(0x0300, 2400000, "2.400 MHz"), - RATE_2_880MHZ(0x0280, 2880000, "2.880 MHz"); - - private int mRatioHigh; - private int mRate; - private String mLabel; - - private SampleRate(int ratioHigh, - int rate, - String label) - { - mRatioHigh = ratioHigh; - mRate = rate; - mLabel = label; - } - - public int getRatioHighBits() - { - return mRatioHigh; - } - - public int getRate() - { - return mRate; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - /** - * Returns the sample rate that is equal to the argument or the next - * higher sample rate - * - * @param sampleRate - * @return - */ - public static SampleRate getClosest(int sampleRate) - { - for(SampleRate rate : values()) - { - if(rate.getRate() >= sampleRate) - { - return rate; - } - } - - return DEFAULT_SAMPLE_RATE; - } - } - - public enum TunerTypeCheck - { - E4K(0xC8, 0x02, 0x40), - FC0012(0xC6, 0x00, 0xA1), - FC0013(0xC6, 0x00, 0xA3), - FC2580(0xAC, 0x01, 0x56), - R820T(0x34, 0x00, 0x69), - R828D(0x74, 0x00, 0x69); - - private int mI2CAddress; - private int mCheckAddress; - private int mCheckValue; - - private TunerTypeCheck(int i2c, int address, int value) - { - mI2CAddress = i2c; - mCheckAddress = address; - mCheckValue = value; - } - - public byte getI2CAddress() - { - return (byte) mI2CAddress; - } - - public byte getCheckAddress() - { - return (byte) mCheckAddress; - } - - public byte getCheckValue() - { - return (byte) mCheckValue; - } - } - - /** - * RTL2832 EEPROM byte array descriptor parsing class - */ - public class Descriptor - { - private byte[] mData; - private ArrayList mLabels = new ArrayList(); - - public Descriptor(byte[] data) - { - if(data != null) - { - mData = data; - } - else - { - data = new byte[256]; - } - - getLabels(); - } - - public boolean isValid() - { - return mData[0] == (byte) 0x28 && - mData[1] == (byte) 0x32; - } - - public String getVendorID() - { - int id = Integer.rotateLeft((0xFF & mData[3]), 8) | - (0xFF & mData[2]); - - return String.format("%04X", id); - } - - public String getVendorLabel() - { - return mLabels.get(0); - } - - public String getProductID() - { - int id = Integer.rotateLeft((0xFF & mData[5]), 8) | - (0xFF & mData[4]); - - return String.format("%04X", id); - } - - public String getProductLabel() - { - return mLabels.get(1); - } - - public boolean hasSerial() - { - return mData[6] == (byte) 0xA5; - } - - public String getSerial() - { - return mLabels.get(2); - } - - public boolean remoteWakeupEnabled() - { - byte mask = (byte) 0x01; - - return (mData[7] & mask) == mask; - } - - public boolean irEnabled() - { - byte mask = (byte) 0x02; - - return (mData[7] & mask) == mask; - } - - private void getLabels() - { - mLabels.clear(); - - int start = 0x09; - - while(start < 256) - { - start = getLabel(start); - } - } - - private int getLabel(int start) - { - /* Validate length and check second byte for ETX (0x03) */ - if(start > 254 || mData[start + 1] != (byte) 0x03) - { - return 256; - } - - /* Get label length, including the length and ETX bytes */ - int length = 0xFF & mData[start]; - - if(start + length > 255) - { - return 256; - } - - /* Get the label bytes */ - byte[] data = Arrays.copyOfRange(mData, start + 2, start + length); - - /* Translate the bytes as UTF-16 Little Endian and store the label */ - String label = new String(data, Charset.forName("UTF-16LE")); - - mLabels.add(label); - - return start + length; - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append("RTL-2832 EEPROM Descriptor\n"); - - sb.append("Vendor: "); - sb.append(getVendorID()); - sb.append(" ["); - sb.append(getVendorLabel()); - sb.append("]\n"); - - sb.append("Product: "); - sb.append(getProductID()); - sb.append(" ["); - sb.append(getProductLabel()); - sb.append("]\n"); - - sb.append("Serial: "); - if(hasSerial()) - { - sb.append("yes ["); - sb.append(getSerial()); - sb.append("]\n"); - } - else - { - sb.append("no\n"); - } - - sb.append("Remote Wakeup Enabled: "); - sb.append((remoteWakeupEnabled() ? "yes" : "no")); - sb.append("\n"); - - sb.append("IR Enabled: "); - sb.append((irEnabled() ? "yes" : "no")); - sb.append("\n"); - - if(mLabels.size() > 3) - { - sb.append("Additional Labels: "); - - for(int x = 3; x < mLabels.size(); x++) - { - sb.append(" ["); - sb.append(mLabels.get(x)); - sb.append("\n"); - } - } - - return sb.toString(); - } - } - - /** - * Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started. - */ - @Override - public void addComplexBufferListener(Listener listener) - { - boolean hasExistingListeners = hasComplexBufferListeners(); - - super.addComplexBufferListener(listener); - - if(!hasExistingListeners) - { - mUSBTransferProcessor.setListener(this); - } - } - - /** - * Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners. - */ - @Override - public void removeComplexBufferListener(Listener listener) - { - super.removeComplexBufferListener(listener); - - if(!hasComplexBufferListeners()) - { - mUSBTransferProcessor.removeListener(); - } - } - - /** - * RTL-2832 USB transfer processor. Extends USB transfer processor and allows resetting the device USB - * buffer prior to starting streaming. - */ - public class RTL2832USBTransferProcessor extends USBTransferProcessor - { - /** - * Manages stream of USB transfer buffers and converts buffers to complex buffer samples for distribution to - * any registered listeners. - * - * @param deviceName to use when logging information or errors - * @param deviceHandle to the USB bulk transfer device - * @param sampleAdapter specific to the tuner's byte buffer format for converting to floating point I/Q samples - * @param bufferSize in bytes. Should be a multiple of two: 65536, 131072 or 262144. - */ - public RTL2832USBTransferProcessor(String deviceName, DeviceHandle deviceHandle, ISampleAdapter sampleAdapter, int bufferSize) - { - super(deviceName, deviceHandle, sampleAdapter, bufferSize); - } - - @Override - protected void prepareDeviceStart() - { - resetUSBBuffer(); - } - } -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java deleted file mode 100644 index 437247c8..00000000 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ /dev/null @@ -1,2512 +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 source.tuner.rtl.e4k; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.rtl.RTL2832TunerController; - -import javax.usb.UsbException; -import java.util.Arrays; - -public class E4KTunerController extends RTL2832TunerController -{ - private final static Logger mLog = - LoggerFactory.getLogger(E4KTunerController.class); - - public static final long MIN_FREQUENCY = 52000000; - public static final long MAX_FREQUENCY = 2200000000l; - public static final double USABLE_BANDWIDTH_PERCENT = 0.95; - public static final int DC_SPIKE_AVOID_BUFFER = 15000; - - /* The local oscillator is defined by whole (integer) units of the oscillator - * frequency and fractional units representing 1/65536th of the oscillator - * frequency, meaning we can only tune the local oscillator in units of - * 439.453125 hertz. */ - public static final long E4K_PLL_Y = 65536l; /* 16-bit fractional register */ - public static final byte MASTER1_RESET = (byte) 0x01; - public static final byte MASTER1_NORM_STBY = (byte) 0x02; - public static final byte MASTER1_POR_DET = (byte) 0x04; - - public static final byte SYNTH1_PLL_LOCK = (byte) 0x01; - public static final byte SYNTH1_BAND_SHIF = (byte) 0x01; - - public static final byte SYNTH7_3PHASE_EN = (byte) 0x08; - - public static final byte SYNTH8_VCOCAL_UPD = (byte) 0x04; - - public static final byte FILT3_MASK = (byte) 0x20; - public static final byte FILT3_ENABLE = (byte) 0x00; - public static final byte FILT3_DISABLE = (byte) 0x20; - - public static final byte MIXER_FILTER_MASK = (byte) 0xF0; - public static final byte IF_CHANNEL_FILTER_MASK = (byte) 0x1F; - public static final byte IF_RC_FILTER_MASK = (byte) 0x0F; - - - public static final byte AGC1_LIN_MODE = (byte) 0x10; - public static final byte AGC1_LNA_UPDATE = (byte) 0x20; - public static final byte AGC1_LNA_G_LOW = (byte) 0x40; - public static final byte AGC1_LNA_G_HIGH = (byte) 0x80; - public static final byte AGC1_MOD_MASK = (byte) 0xF; - - public static final byte GAIN1_MOD_MASK = (byte) 0xF; - public static final byte IF_GAIN_MODE_SWITCHING_MASK = (byte) 0x1; - - public static final byte AGC6_LNA_CAL_REQ = (byte) 0x10; - - public static final byte AGC7_MIXER_GAIN_MASK = (byte) 0x01; - public static final byte AGC7_MIX_GAIN_MANUAL = (byte) 0x00; - public static final byte AGC7_MIX_GAIN_AUTO = (byte) 0x01; - public static final byte AGC7_GAIN_STEP_5DB = (byte) 0x20; - - public static final byte AGC8_SENS_LIN_AUTO = (byte) 0x01; - - public static final byte AGC11_LNA_GAIN_ENH = (byte) 0x01; - public static final byte ENH_GAIN_MOD_MASK = (byte) 0x07; - public static final byte MIXER_GAIN_MASK = (byte) 0x01; - - public static final byte DC1_CAL_REQ = (byte) 0x01; - - public static final byte DC5_I_LUT_EN = (byte) 0x01; - public static final byte DC5_Q_LUT_EN = (byte) 0x02; - public static final byte DC5_RANGE_DETECTOR_ENABLED_MASK = (byte) 0x04; //DC Offset Detector Enabled - public static final byte DC5_RANGE_DETECTOR_ENABLED = (byte) 0x04; //DC Offset Detector Enabled - public static final byte DC5_RANGE_EN = (byte) 0x08; - public static final byte DC5_RANGE_DETECTOR_DISABLED_MASK = (byte) 0x03; - public static final byte DC5_RANGE_DETECTOR_DISABLED = (byte) 0x00; - - public static final byte DC5_TIMEVAR_EN = (byte) 0x10; - - public static final byte CLKOUT_DISABLE = (byte) 0x96; - - public static final byte CHFCALIB_CMD = (byte) 0x01; - - public E4KTunerController(Device device, DeviceDescriptor deviceDescriptor) throws SourceException - { - super(device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT); - } - - @Override - public TunerType getTunerType() - { - return TunerType.ELONICS_E4000; - } - - @Override - public void apply(TunerConfiguration config) throws SourceException - { - if(config != null && config instanceof E4KTunerConfiguration) - { - E4KTunerConfiguration e4kConfig = (E4KTunerConfiguration) config; - - try - { - SampleRate sampleRate = e4kConfig.getSampleRate(); - setSampleRate(sampleRate); - - double correction = e4kConfig.getFrequencyCorrection(); - getFrequencyController().setFrequencyCorrection(correction); - - E4KGain masterGain = e4kConfig.getMasterGain(); - setGain(masterGain, true); - - if(masterGain == E4KGain.MANUAL) - { - E4KMixerGain mixerGain = e4kConfig.getMixerGain(); - setMixerGain(mixerGain, true); - - E4KLNAGain lnaGain = e4kConfig.getLNAGain(); - setLNAGain(lnaGain, true); - - E4KEnhanceGain enhanceGain = e4kConfig.getEnhanceGain(); - setEnhanceGain(enhanceGain, true); - } - - try - { - getFrequencyController().setFrequency(e4kConfig.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } - catch(UsbException e) - { - throw new SourceException("E4KTunerController - usb error " - + "while applying tuner config", e); - } - - } - else - { - throw new IllegalArgumentException("E4KTunerController - cannot " - + "apply tuner configuration of type:[" + - config.getClass() + "]"); - } - - } - - public void init() throws SourceException - { - mDeviceHandle = new DeviceHandle(); - - int result = LibUsb.open(mDevice, mDeviceHandle); - - if(result != LibUsb.SUCCESS) - { - mDeviceHandle = null; - - throw new SourceException("libusb couldn't open RTL2832 usb " - + "device [" + LibUsb.errorName(result) + "]"); - } - - claimInterface(mDeviceHandle); - - byte[] eeprom = null; - - try - { - /* Read the contents of the 256-byte EEPROM */ - eeprom = readEEPROM(mDeviceHandle, (short) 0, 256); - } - catch(Exception e) - { - mLog.error("error while reading the EEPROM device descriptor", e); - } - - try - { - mDescriptor = new Descriptor(eeprom); - - if(eeprom == null) - { - mLog.error("eeprom byte array was null - constructed " - + "empty descriptor object"); - } - } - catch(Exception e) - { - mLog.error("error while constructing device descriptor using " - + "descriptor byte array " + - (eeprom == null ? "[null]" : Arrays.toString(eeprom)), e); - } - - try - { - /* Dummy write to test USB interface */ - writeRegister(mDeviceHandle, Block.USB, - Address.USB_SYSCTL.getAddress(), (short) 0x09, 1); - - initBaseband(mDeviceHandle); - - enableI2CRepeater(mDeviceHandle, true); - - boolean i2CRepeaterControl = false; - - initTuner(i2CRepeaterControl); - - enableI2CRepeater(mDeviceHandle, false); - - try - { - setSampleRate(DEFAULT_SAMPLE_RATE); - } - catch(Exception e) - { - throw new SourceException("RTL2832 Tuner Controller - couldn't " - + "set default sample rate", e); - } - - } - catch(UsbException e) - { - e.printStackTrace(); - throw new SourceException("E4K Tuner Controller - error during " - + "init()", e); - } - - String deviceName = getTunerType().getLabel() + " " + getUniqueID(); - - mUSBTransferProcessor = new RTL2832USBTransferProcessor(deviceName, mDeviceHandle, mSampleAdapter, - USB_TRANSFER_BUFFER_SIZE); - } - - /** - * Sets the IF filters (mixer, channel and RC) to the correct filter setting - * for the selected bandwidth/sample rate - */ - @Override - public void setSampleRateFilters(int bandwidth) throws SourceException - { - /* Determine repeater state so we can restore it when done */ - boolean i2CRepeaterEnabled = isI2CRepeaterEnabled(); - - if(!i2CRepeaterEnabled) - { - enableI2CRepeater(mDeviceHandle, true); - } - - boolean controlI2CRepeater = false; - - MixerFilter mixer = MixerFilter.getFilter(bandwidth); - setMixerFilter(mixer, controlI2CRepeater); - - ChannelFilter channel = ChannelFilter.getFilter(bandwidth); - setChannelFilter(channel, controlI2CRepeater); - - RCFilter rc = RCFilter.getFilter(bandwidth); - setRCFilter(rc, controlI2CRepeater); - - if(!i2CRepeaterEnabled) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - @Override - public long getTunedFrequency() throws SourceException - { - try - { - /* Determine repeater state so we can restore it when done */ - boolean i2CRepeaterEnabled = isI2CRepeaterEnabled(); - - if(!i2CRepeaterEnabled) - { - enableI2CRepeater(mDeviceHandle, true); - } - - boolean controlI2CRepeater = false; - - byte z = (byte) readE4KRegister(Register.SYNTH3, controlI2CRepeater); - - int xHigh = readE4KRegister(Register.SYNTH4, controlI2CRepeater); - int xLow = readE4KRegister(Register.SYNTH5, controlI2CRepeater); - - int x = (Integer.rotateLeft(xHigh, 8)) | xLow; - - int pllSetting = readE4KRegister(Register.SYNTH7, controlI2CRepeater); - PLL pll = PLL.fromSetting(pllSetting); - - /* Return the repeater to its previous state */ - if(!i2CRepeaterEnabled) - { - enableI2CRepeater(mDeviceHandle, false); - } - - return calculateActualFrequency(pll, z, x); - } - catch(LibUsbException e) - { - throw new SourceException("E4K tuner controller - couldn't get " - + "tuned frequency", e); - } - } - - @Override - public void setTunedFrequency(long frequency) throws SourceException - { - /* Get the phase locked loop setting */ - PLL pll = PLL.fromFrequency(frequency); - - /* Z is an integer representing the number of scaled oscillator frequency - * increments the multiplied frequency. */ - byte z = (byte) (frequency / pll.getScaledOscillator()); - - /* remainder is just as it describes. It is what is left over after we - * carve out scaled oscillator frequency increments (z) from the desired - * frequency. */ - int remainder = (int) (frequency - ((z & 0xFF) * pll.getScaledOscillator())); - - /* X is a 16-bit representation of the remainder */ - int x = (int) ((double) remainder / (double) pll.getScaledOscillator() * E4K_PLL_Y); - - /* Calculate the exact (tunable) frequency and apply that to the tuner */ - long actualFrequency = calculateActualFrequency(pll, z, x); - - /** - * Hack: if we're trying to set the minimum frequency for the E4K, due - * to rounding errors, 52 mhz becomes 51.999993, so we need to adjust - * x to get 52.000003 mhz ... otherwise the PLL won't lock on the freq - */ - if(actualFrequency < getFrequencyController().getMinimumFrequency()) - { - x++; - actualFrequency = calculateActualFrequency(pll, z, x); - } - - /* Apply the actual frequency */ - try - { - enableI2CRepeater(mDeviceHandle, true); - - boolean controlI2CRepeater = false; - - /* Write the PLL setting */ - writeE4KRegister(Register.SYNTH7, pll.getIndex(), controlI2CRepeater); - - /* Write z (integral) value */ - writeE4KRegister(Register.SYNTH3, z, controlI2CRepeater); - - /* Write the x (fractional) value high-order byte to synth4 register */ - writeE4KRegister(Register.SYNTH4, (byte) (x & 0xFF), controlI2CRepeater); - - /* Write the x (fractional) value low-order byte to synth5 register */ - writeE4KRegister(Register.SYNTH5, - (byte) ((Integer.rotateRight(x, 8)) & 0xFF), - controlI2CRepeater); - - /* Set the band for the new frequency */ - setBand(actualFrequency, controlI2CRepeater); - - /* Set the filter */ - setRFFilter(actualFrequency, controlI2CRepeater); - - /* Check for PLL lock */ - int lock = readE4KRegister(Register.SYNTH1, controlI2CRepeater); - - if(!((lock & 0x1) == 0x1)) - { - throw new SourceException("E4K tuner controller - couldn't " - + "achieve PLL lock for frequency [" + - actualFrequency + "] lock value [" + lock + "]"); - } - - enableI2CRepeater(mDeviceHandle, false); - } - catch(UsbException e) - { - throw new SourceException("E4K tuner controller - error tuning " - + "frequency [" + frequency + "]", e); - } - } - - private long calculateActualFrequency(PLL pll, byte z, int x) - { - long whole = pll.getScaledOscillator() * (z & 0xFF); - - int fractional = (int) (pll.getScaledOscillator() * - ((double) x / (double) E4K_PLL_Y)); - - return whole + fractional; - } - - public void initTuner(boolean controlI2CRepeater) throws UsbException - { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - boolean i2CRepeaterControl = false; - - /* Perform dummy read */ - readE4KRegister(Register.DUMMY, i2CRepeaterControl); - - /* Reset everything and clear POR indicator */ - /* NOTE: register value remains 0010 even after we write 0111 to it */ - writeE4KRegister(Register.MASTER1, (byte) (MASTER1_RESET | - MASTER1_NORM_STBY | - MASTER1_POR_DET), - i2CRepeaterControl); - - /* Configure clock input */ - writeE4KRegister(Register.CLK_INP, (byte) 0x00, i2CRepeaterControl); - - /* Disable clock output */ - writeE4KRegister(Register.REF_CLK, (byte) 0x00, i2CRepeaterControl); - writeE4KRegister(Register.CLKOUT_PWDN, (byte) 0x96, i2CRepeaterControl); - - /* Magic Init */ - magicInit(i2CRepeaterControl); - -// /* Set common mode voltage a bit higher for more margin - 850 mv */ -// writeMaskedE4KRegister( Register.DC7, (byte)0x7, -// (byte)0x4, i2CRepeaterControl ); -// -// /* Initialize DC offset lookup tables */ -// generateDCOffsetTables( i2CRepeaterControl ); -// -// /* Enable time variant DC correction */ -// writeE4KRegister( Register.DCTIME1, (byte)0x01, i2CRepeaterControl ); -// writeE4KRegister( Register.DCTIME2, (byte)0x01, i2CRepeaterControl ); -// - /* Set LNA Mode */ - writeE4KRegister(Register.AGC4, (byte) 0x10, i2CRepeaterControl); //High Threshold - writeE4KRegister(Register.AGC5, (byte) 0x04, i2CRepeaterControl); //Low Threshold - writeE4KRegister(Register.AGC6, (byte) 0x1A, i2CRepeaterControl); //LNA calibrate + loop rate - - //TODO: lna and mixer gain were already set to manual when we generated - //the DC offset tables. Check the LNA gain setting after we set the - //LNA mode above, to see if the lna or mixer gain settings have changed - //and if not, remove the next two steps - -// /* Set LNA gain to manual using current auto LNA gain setting */ -// LNAGain lnaGain = getLNAGain( i2CRepeaterControl ); -// setLNAGain( lnaGain, i2CRepeaterControl ); -// -// /* Set Mixer gain to manual using current auto Mixer gain setting */ -// MixerGain mixerGain = getMixerGain( i2CRepeaterControl ); -// setMixerGain( mixerGain, i2CRepeaterControl ); - - //Temp - manual set lna mode to manual - writeMaskedE4KRegister(Register.AGC1, - AGC1_MOD_MASK, - AGCMode.SERIAL.getValue(), - false); - //Temp - set mixer gain to manual - writeMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - AGC7_MIX_GAIN_MANUAL, - false); - - -// /* Enable enhance gain */ -// setEnhanceGain( EnhanceGain.GAIN_5, i2CRepeaterControl ); -// -// /* Enable automatic IF gain mode switching */ -// writeMaskedE4KRegister( Register.AGC8, -// IF_GAIN_MODE_SWITCHING_MASK, -// AGC8_SENS_LIN_AUTO, -// i2CRepeaterControl ); - - - /* Use automatic gain as a default */ - setLNAGain(E4KLNAGain.AUTOMATIC, i2CRepeaterControl); - setMixerGain(E4KMixerGain.AUTOMATIC, i2CRepeaterControl); - setEnhanceGain(E4KEnhanceGain.AUTOMATIC, i2CRepeaterControl); - - /* Set IF gain stages */ - setIFStage1Gain(IFStage1Gain.GAIN_PLUS6, i2CRepeaterControl); - setIFStage2Gain(IFStage2Gain.GAIN_PLUS0, i2CRepeaterControl); - setIFStage3Gain(IFStage3Gain.GAIN_PLUS0, i2CRepeaterControl); - setIFStage4Gain(IFStage4Gain.GAIN_PLUS0, i2CRepeaterControl); - setIFStage5Gain(IFStage5Gain.GAIN_PLUS9, i2CRepeaterControl); - setIFStage6Gain(IFStage6Gain.GAIN_PLUS9, i2CRepeaterControl); - - /* Set the most narrow filter we can possible use */ - setMixerFilter(MixerFilter.BW_1M9, i2CRepeaterControl); - setRCFilter(RCFilter.BW_1M0, i2CRepeaterControl); - setChannelFilter(ChannelFilter.BW_2M15, i2CRepeaterControl); - - setChannelFilterEnabled(true, i2CRepeaterControl); - -// /* Disable DC detector */ -// setDCRangeDetectorEnabled( false, i2CRepeaterControl ); - - /* Disable time variant DC correction */ - writeMaskedE4KRegister(Register.DC5, (byte) 0x03, (byte) 0, i2CRepeaterControl); - writeMaskedE4KRegister(Register.DCTIME1, (byte) 0x03, (byte) 0, i2CRepeaterControl); - writeMaskedE4KRegister(Register.DCTIME2, (byte) 0x03, (byte) 0, i2CRepeaterControl); - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - @SuppressWarnings("unused") - private void generateDCOffsetTables(boolean controlI2CRepeater) - throws UsbException - { - boolean i2CRepeaterControl = false; - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - /* Capture current gain settings to reapply at the end */ - IFStage1Gain stage1 = getIFStage1Gain(i2CRepeaterControl); - IFStage2Gain stage2 = getIFStage2Gain(i2CRepeaterControl); - IFStage3Gain stage3 = getIFStage3Gain(i2CRepeaterControl); - IFStage4Gain stage4 = getIFStage4Gain(i2CRepeaterControl); - IFStage5Gain stage5 = getIFStage5Gain(i2CRepeaterControl); - IFStage6Gain stage6 = getIFStage6Gain(i2CRepeaterControl); - - /* Set Mixer gain to manual using current auto Mixer gain setting */ - E4KMixerGain mixerGain = getMixerGain(i2CRepeaterControl); - setMixerGain(mixerGain, i2CRepeaterControl); - - /* Set LNA gain to manual using current auto LNA gain setting */ - E4KLNAGain lnaGain = getLNAGain(i2CRepeaterControl); - setLNAGain(lnaGain, i2CRepeaterControl); - - /* Set IF Stage 2 - 6 gains to maximum */ - setIFStage2Gain(IFStage2Gain.GAIN_PLUS9, i2CRepeaterControl); - setIFStage3Gain(IFStage3Gain.GAIN_PLUS9, i2CRepeaterControl); - setIFStage4Gain(IFStage4Gain.GAIN_PLUS2B, i2CRepeaterControl); - setIFStage5Gain(IFStage5Gain.GAIN_PLUS15D, i2CRepeaterControl); - setIFStage6Gain(IFStage6Gain.GAIN_PLUS15D, i2CRepeaterControl); - - /** - * Iterate all DC gain combinations, apply gain settings, read the - * DC offset value and apply it to the lookup table entry - */ - for(DCGainCombination combo : DCGainCombination.values()) - { - /* Set mixer and stage1 gain values */ - setMixerGain(combo.getMixerGain(), i2CRepeaterControl); - setIFStage1Gain(combo.getIFStage1Gain(), i2CRepeaterControl); - - /* Turn on the DC range detector */ - setDCRangeDetectorEnabled(true, i2CRepeaterControl); - - /* Calibrate the DC value */ - writeE4KRegister(Register.DC1, (byte) 0x01, i2CRepeaterControl); - - /* Get the I/Q offset and range values */ - byte offsetI = (byte) (readE4KRegister(Register.DC2, - i2CRepeaterControl) & 0x3F); - byte offsetQ = (byte) (readE4KRegister(Register.DC3, - i2CRepeaterControl) & 0x3F); - byte range = (byte) readE4KRegister(Register.DC4, - i2CRepeaterControl); - byte rangeI = (byte) (range & 0x3); - byte rangeQ = (byte) ((range >> 4) & 0x3); - - /* Write the offset and range values to the lookup table */ - writeE4KRegister(combo.getIRegister(), - (byte) (offsetI | (rangeI << 6)), - i2CRepeaterControl); - - writeE4KRegister(combo.getQRegister(), - (byte) (offsetQ | (rangeQ << 6)), - i2CRepeaterControl); - } - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - public void setDCRangeDetectorEnabled(boolean enabled, - boolean controlI2CRepeater) throws UsbException - { - if(enabled) - { - writeMaskedE4KRegister(Register.DC5, - DC5_RANGE_DETECTOR_ENABLED_MASK, - DC5_RANGE_DETECTOR_ENABLED, - controlI2CRepeater); - } - else - { - writeMaskedE4KRegister(Register.DC5, - DC5_RANGE_DETECTOR_DISABLED_MASK, - DC5_RANGE_DETECTOR_DISABLED, - controlI2CRepeater); - } - } - - /** - * Sets the LNA gain within the E4K Tuner - * - * Note: requires I2C repeater enabled - * - * @param gain - * @throws UsbException - */ - public void setLNAGain(E4KLNAGain gain, boolean controlI2CRepeater) - throws UsbException - { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - if(gain == E4KLNAGain.AUTOMATIC) - { - writeMaskedE4KRegister(Register.AGC1, - AGC1_MOD_MASK, - AGCMode.IF_SERIAL_LNA_AUTON.getValue(), - false); - } - else - { - writeMaskedE4KRegister(Register.AGC1, - AGC1_MOD_MASK, - AGCMode.SERIAL.getValue(), - false); - - - writeMaskedE4KRegister(E4KLNAGain.getRegister(), - E4KLNAGain.getMask(), - gain.getValue(), - false); - } - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - /** - * Reads LNA gain from E4K tuner - * - * Note: requires I2C repeater enabled - */ - public E4KLNAGain getLNAGain(boolean controlI2CRepeater) throws UsbException - { - return E4KLNAGain.fromRegisterValue( - readE4KRegister(E4KLNAGain.getRegister(), controlI2CRepeater)); - } - - /** - * Sets enhanced gain for E4K repeater. - * - * Note: requires I2C repeater enabled - * - * @param gain - * @throws UsbException - */ - public void setEnhanceGain(E4KEnhanceGain gain, boolean controlI2CRepeater) - throws UsbException - { - writeMaskedE4KRegister(E4KEnhanceGain.getRegister(), - E4KEnhanceGain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - /** - * Gets the enhanced gain setting in the E4K tuner. - * - * Note: requires I2C repeater - * - * @return - * @throws UsbException - */ - public E4KEnhanceGain getEnhanceGain(boolean controlI2CRepeater) - throws UsbException - { - return E4KEnhanceGain.fromRegisterValue( - readE4KRegister(E4KEnhanceGain.getRegister(), controlI2CRepeater)); - } - - public void setMixerGain(E4KMixerGain gain, boolean controlI2CRepeater) - throws UsbException - { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - boolean localI2CRepeaterControl = false; - - if(gain == E4KMixerGain.AUTOMATIC) - { - writeMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - AGC7_MIX_GAIN_AUTO, - localI2CRepeaterControl); - } - else - { - writeMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - AGC7_MIX_GAIN_MANUAL, - localI2CRepeaterControl); - - /* Set the desired manual gain setting */ - writeMaskedE4KRegister(E4KMixerGain.getRegister(), - E4KMixerGain.getMask(), - gain.getValue(), - localI2CRepeaterControl); - } - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - public E4KMixerGain getMixerGain(boolean controlI2CRepeater) - throws UsbException - { - byte autoOrManual = readMaskedE4KRegister(Register.AGC7, - AGC7_MIXER_GAIN_MASK, - controlI2CRepeater); - - if(autoOrManual == AGC7_MIX_GAIN_AUTO) - { - return E4KMixerGain.AUTOMATIC; - } - else - { - int register = readE4KRegister(E4KMixerGain.getRegister(), - controlI2CRepeater); - - return E4KMixerGain.fromRegisterValue(register); - } - } - - public void setIFStage1Gain(IFStage1Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage1Gain.getRegister(), - IFStage1Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage1Gain getIFStage1Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage1Gain.fromRegisterValue( - readE4KRegister(IFStage1Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage2Gain(IFStage2Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage2Gain.getRegister(), - IFStage2Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage2Gain getIFStage2Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage2Gain.fromRegisterValue( - readE4KRegister(IFStage2Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage3Gain(IFStage3Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage3Gain.getRegister(), - IFStage3Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage3Gain getIFStage3Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage3Gain.fromRegisterValue( - readE4KRegister(IFStage3Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage4Gain(IFStage4Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage4Gain.getRegister(), - IFStage4Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage4Gain getIFStage4Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage4Gain.fromRegisterValue( - readE4KRegister(IFStage4Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage5Gain(IFStage5Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage5Gain.getRegister(), - IFStage5Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage5Gain getIFStage5Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage5Gain.fromRegisterValue( - readE4KRegister(IFStage5Gain.getRegister(), controlI2CRepeater)); - } - - public void setIFStage6Gain(IFStage6Gain gain, - boolean controlI2CRepeater) throws UsbException - { - writeMaskedE4KRegister(IFStage6Gain.getRegister(), - IFStage6Gain.getMask(), - gain.getValue(), - controlI2CRepeater); - } - - public IFStage6Gain getIFStage6Gain(boolean controlI2CRepeater) - throws UsbException - { - return IFStage6Gain.fromRegisterValue( - readE4KRegister(IFStage6Gain.getRegister(), controlI2CRepeater)); - } - - public void setMixerFilter(MixerFilter filter, - boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(MixerFilter.getRegister(), - MixerFilter.getMask(), - filter.getValue(), - controlI2CRepeater); - } - - public MixerFilter getMixerFilter(boolean controlI2CRepeater) - throws UsbException - { - int value = readE4KRegister(MixerFilter.getRegister(), - controlI2CRepeater); - - return MixerFilter.fromRegisterValue(value); - } - - public void setRCFilter(RCFilter filter, - boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(RCFilter.getRegister(), - RCFilter.getMask(), - filter.getValue(), - controlI2CRepeater); - } - - public RCFilter getRCFilter(boolean controlI2CRepeater) throws UsbException - { - int value = readE4KRegister(RCFilter.getRegister(), controlI2CRepeater); - - return RCFilter.fromRegisterValue(value); - } - - public void setChannelFilter(ChannelFilter filter, - boolean controlI2CRepeater) throws LibUsbException - { - writeMaskedE4KRegister(ChannelFilter.getRegister(), - ChannelFilter.getMask(), - filter.getValue(), - controlI2CRepeater); - } - - public ChannelFilter getChannelFilter(boolean controlI2CRepeater) - throws UsbException - { - int value = readE4KRegister(ChannelFilter.getRegister(), controlI2CRepeater); - - return ChannelFilter.fromRegisterValue(value); - - } - - public void setChannelFilterEnabled(boolean enabled, - boolean controlI2CRepeater) throws UsbException - { - if(enabled) - { - writeMaskedE4KRegister(Register.FILT3, - FILT3_MASK, - FILT3_ENABLE, - controlI2CRepeater); - } - else - { - writeMaskedE4KRegister(Register.FILT3, - FILT3_MASK, - FILT3_DISABLE, - controlI2CRepeater); - } - } - - public void setBand(long frequency, - boolean controlI2CRepeater) throws UsbException - { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - Band band = Band.fromFrequency(frequency); - - /* Set the bias */ - switch(band) - { - case UHF: - case VHF2: - case VHF3: - writeE4KRegister(Register.BIAS, (byte) 0x3, false); - break; - case L: - default: - writeE4KRegister(Register.BIAS, (byte) 0x0, false); - break; - - } - - /** - * Workaround - reset register (set value to 0) before writing to it, to - * avoid a gap around 325-350 MHz - */ - writeMaskedE4KRegister(Register.SYNTH1, Band.getMask(), (byte) 0x0, false); - writeMaskedE4KRegister(Register.SYNTH1, Band.getMask(), band.getValue(), false); - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - private void setRFFilter(long frequency, - boolean controlI2CRepeater) throws UsbException - { - RFFilter filter = RFFilter.fromFrequency(frequency); - - writeMaskedE4KRegister(Register.FILT1, - RFFilter.getMask(), - filter.getValue(), - controlI2CRepeater); - } - - /** - * Sets the gain combination setting. - * - * Note: does not respond to the MANUAL setting. Use the manual setting - * to configure gui components to expose the individual Mixer, LNA and - * Enhance gain settings, to allow the user to change those settings - * individually. - * - * @param gain - requested gain setting - * @throws UsbException if there are any errors - */ - public void setGain(E4KGain gain, boolean controlI2CRepeater) throws UsbException - { - if(gain != E4KGain.MANUAL) - { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - boolean i2CRepeaterControl = false; - - setLNAGain(gain.getLNAGain(), i2CRepeaterControl); - setMixerGain(gain.getMixerGain(), i2CRepeaterControl); - setEnhanceGain(gain.getEnhanceGain(), i2CRepeaterControl); - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - } - - /* - * Performs magic initialization ... and that's all we need to know, I guess - */ - private void magicInit(boolean controlI2CRepeater) throws UsbException - { - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, true); - } - - writeE4KRegister(Register.MAGIC_1, (byte) 0x01, false); - writeE4KRegister(Register.MAGIC_2, (byte) 0xFE, false); - writeE4KRegister(Register.MAGIC_3, (byte) 0x00, false); - writeE4KRegister(Register.MAGIC_4, (byte) 0x50, false); //Polarity A - writeE4KRegister(Register.MAGIC_5, (byte) 0x20, false); - writeE4KRegister(Register.MAGIC_6, (byte) 0x01, false); - writeE4KRegister(Register.MAGIC_7, (byte) 0x7F, false); - writeE4KRegister(Register.MAGIC_8, (byte) 0x07, false); - - if(controlI2CRepeater) - { - enableI2CRepeater(mDeviceHandle, false); - } - } - - private byte readMaskedE4KRegister(Register register, - byte mask, - boolean controlI2CRepeater) - throws UsbException - { - int temp = readE4KRegister(register, controlI2CRepeater); - return (byte) (temp & mask); - } - - private void writeMaskedE4KRegister(Register register, - byte mask, - byte value, - boolean controlI2CRepeater) throws LibUsbException - { - int temp = readE4KRegister(register, controlI2CRepeater); - - /* If the register is not set to the masked value, then change it */ - if((byte) (temp & mask) != value) - { - writeE4KRegister(register, - (byte) ((temp & ~mask) | (value & mask)), - controlI2CRepeater); - - readE4KRegister(register, controlI2CRepeater); - } - } - - private int readE4KRegister(Register register, - boolean controlI2CRepeater) throws LibUsbException - { - return readI2CRegister(mDeviceHandle, Register.I2C_REGISTER.getValue(), - register.getValue(), controlI2CRepeater); - } - - - private void writeE4KRegister(Register register, - byte value, - boolean controlI2CRepeater) throws LibUsbException - { - writeI2CRegister(mDeviceHandle, Register.I2C_REGISTER.getValue(), - register.getValue(), value, controlI2CRepeater); - } - - public enum Band - { - VHF2(0), - VHF3(2), - UHF(4), - L(6); - - private int mValue; - - private Band(int value) - { - mValue = value; - } - - public static byte getMask() - { - return (byte) 0x06; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return toString(); - } - - public static Band fromFrequency(long frequency) - { - if(frequency < 140000000) //140 MHz - { - return VHF2; - } - else if(frequency < 350000000) //350 MHz - { - return VHF3; - } - else if(frequency < 1135000000) //1135 MHz - { - return UHF; - } - else - { - return L; - } - } - } - - public enum PLL - { - PLL_72M4(0x0F, 72400000, 48, 600000, true, "72.4 MHz"), - PLL_81M2(0x0E, 81200000, 40, 720000, true, "81.2 MHz"), - PLL_108M3(0x0D, 108300000, 32, 900000, true, "108.3 MHz"), - PLL_162M5(0x0C, 162500000, 24, 1200000, true, "162.5 MHz"), - PLL_216M6(0x0B, 216600000, 16, 1800000, true, "216.6 MHz"), - PLL_325M0(0x0A, 325000000, 12, 2400000, true, "325.0 MHz"), - PLL_350M0(0x09, 350000000, 8, 3600000, true, "350.0 MHz"), - PLL_432M0(0x03, 432000000, 8, 3600000, false, "432.0 MHz"), - PLL_667M0(0x02, 667000000, 6, 4800000, false, "667.0 MHz"), - PLL_1200M0(0x01, 1200000000, 4, 7200000, false, "1200.0 MHz"); - - private int mValue; - private long mFrequency; - private int mMultiplier; - private int mScaledOscillator; - private boolean mRequires3PhaseMixing; - private String mLabel; - - private PLL(int value, - long frequency, - int multiplier, - int scaledOscillator, - boolean requires3Phase, - String label) - { - mValue = value; - mFrequency = frequency; - mMultiplier = multiplier; - mScaledOscillator = scaledOscillator; - mRequires3PhaseMixing = requires3Phase; - mLabel = label; - - } - - public byte getIndex() - { - return (byte) mValue; - } - - public long getFrequency() - { - return mFrequency; - } - - public byte getMultiplier() - { - return (byte) mMultiplier; - } - - public int getScaledOscillator() - { - return mScaledOscillator; - } - - public boolean requires3PhaseMixing() - { - return mRequires3PhaseMixing; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - /* Returns the PLL setting with the closest frequency that is greater - * than the frequency argument */ - public static PLL fromFrequency(long frequency) - { - for(PLL pll : values()) - { - if(frequency < pll.getFrequency()) - { - return pll; - } - } - - //Default - return PLL.PLL_72M4; - } - - public static PLL fromSetting(int setting) - { - int value = setting & 0xF; - - for(PLL pll : values()) - { - if(value == pll.getIndex()) - { - return pll; - } - } - - //Default - return PLL.PLL_72M4; - } - } - - /** - * RF filters - UHF Band. Each enum entry defines the center frequency for - * the filter and the applicable frequency minimums and maximums for the - * filter. - * - * Note: max frequency overlaps with the next minimum frequency, so valid - * frequencies should be less than the maximum frequency defined for the - * filter. - */ - public enum RFFilter - { - NO_FILTER(0, 0, 0), - - //UHF band Filters - M360(0, 350000000, 370000000), - M380(1, 370000000, 392500000), - M405(2, 392500000, 417500000), - M425(3, 417500000, 437500000), - M450(4, 437500000, 462500000), - M475(5, 462500000, 490000000), - M505(6, 490000000, 522500000), - M540(7, 522500000, 557500000), - M575(8, 557500000, 595000000), - M615(9, 595000000, 642500000), - M670(10, 642500000, 695000000), - M720(11, 695000000, 740000000), - M760(12, 740000000, 800000000), - M840(13, 800000000, 865000000), - M890(14, 865000000, 930000000), - M970(15, 930000000, 1135000000), - - //L band filters - M1300(0, 1135000000, 1310000000), - M1320(1, 1310000000, 1340000000), - M1360(2, 1340000000, 1385000000), - M1410(3, 1385000000, 1427500000), - M1445(4, 1427500000, 1452500000), - M1460(5, 1452500000, 1475000000), - M1490(6, 1475000000, 1510000000), - M1530(7, 1510000000, 1545000000), - M1560(8, 1545000000, 1575000000), - M1590(9, 1575000000, 1615000000), - M1640(10, 1615000000, 1650000000), - M1660(11, 1650000000, 1670000000), - M1680(12, 1670000000, 1690000000), - M1700(13, 1690000000, 1710000000), - M1720(14, 1710000000, 1735000000), - - /* Note: max frequency is currently limited by the max integer value - * and we set the max frequency for this filter to 2147 MHz */ - M1750(15, 1735000000, 2147000000); - - private int mValue; - private long mMinFrequency; - private long mMaxFrequency; - - private RFFilter(int value, long minFrequency, long maxFrequency) - { - mValue = value; - mMinFrequency = minFrequency; - mMaxFrequency = maxFrequency; - } - - public static Register getRegister() - { - return Register.FILT1; - } - - public static byte getMask() - { - return (byte) 0xF; //TODO: make this a constant - } - - public byte getValue() - { - return (byte) mValue; - } - - /** - * Minimum frequency for this filter. - */ - public long getMinimumFrequency() - { - return mMinFrequency; - } - - /** - * Maximum frequency for this filter is less than this value, but does - * not include this value. - */ - public long getMaximumFrequency() - { - return mMaxFrequency; - } - - /** - * Selects the appropriate filter for the frequency - * - * @param frequency - * @return selected filter - * @throws IllegalArgumentException if the frequency is outside the - * max value (2200 MHz) - */ - public static RFFilter fromFrequency(long frequency) - { - if(frequency < 350000000) - { - return NO_FILTER; - } - - for(RFFilter filter : values()) - { - if(filter.getMinimumFrequency() <= frequency && - frequency < filter.getMaximumFrequency()) - { - return filter; - } - } - - throw new IllegalArgumentException("E4KTunerController - " - + "cannot find RF filter for frequency [" + frequency + "]"); - } - } - - public enum MixerFilter - { - BW_27M0(0x00, 27000000, 4800000, 28800000, "27.0 MHz"), - BW_4M6(0x80, 4600000, 4400000, 4800000, "4.6 MHz"), - BW_4M2(0x90, 4200000, 4000000, 4400000, "4.2 MHz"), - BW_3M8(0xA0, 3800000, 3600000, 4000000, "3.8 MHz"), - BW_3M4(0xB0, 3400000, 3200000, 3600000, "3.4 MHz"), - BW_3M0(0xC0, 3000000, 2850000, 3200000, "3.0 MHz"), - BW_2M7(0xD0, 2700000, 2500000, 2850000, "2.7 MHz"), - BW_2M3(0xE0, 2300000, 2100000, 2500000, "2.3 MHz"), - BW_1M9(0xF0, 1900000, 0, 2100000, "1.9 MHz"); - - private int mValue; - private int mBandwidth; - private int mMinBandwidth; - private int mMaxBandwidth; - private String mLabel; - - private MixerFilter(int value, - int bandwidth, - int minimumBandwidth, - int maximumBandwidth, - String label) - { - mValue = value; - mBandwidth = bandwidth; - mMinBandwidth = minimumBandwidth; - mMaxBandwidth = maximumBandwidth; - mLabel = label; - } - - public static Register getRegister() - { - return Register.FILT2; - } - - public static byte getMask() - { - return MIXER_FILTER_MASK; - } - - public byte getValue() - { - return (byte) mValue; - } - - public int getBandwidth() - { - return mBandwidth; - } - - public int getMinimumBandwidth() - { - return mMinBandwidth; - } - - public int getMaximumBandwidth() - { - return mMaxBandwidth; - } - - public String getLabel() - { - return mLabel; - } - - /** - * Returns the correct filter to use for the bandwidth/sample rate - * - * @param bandwidth - current sample rate - * @return - correct filter to use for the bandwidth/sample rate - */ - public static MixerFilter getFilter(int bandwidth) - { - for(MixerFilter filter : values()) - { - if(filter.getMinimumBandwidth() <= bandwidth && - bandwidth < filter.getMaximumBandwidth()) - { - return filter; - } - } - - /* default */ - return MixerFilter.BW_27M0; - } - - public static MixerFilter fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(MixerFilter filter : values()) - { - if(value == filter.getValue()) - { - return filter; - } - } - - throw new IllegalArgumentException("E4KTunerController - " - + "unrecognized mixer filter value [" + value + "]"); - } - } - - public enum RCFilter - { - BW_21M4(0x00, 21400000, 21200000, 28800000, "21.4 MHz"), - BW_21M0(0x01, 21000000, 19300000, 21200000, "21.0 MHz"), - BW_17M6(0x02, 17600000, 16150000, 19300000, "17.6 MHz"), - BW_14M7(0x03, 14700000, 13550000, 16150000, "14.7 MHz"), - BW_12M4(0x04, 12400000, 11500000, 13550000, "12.4 MHz"), - BW_10M6(0x05, 10600000, 9800000, 11500000, "10.6 MHz"), - BW_9M0(0x06, 9000000, 8350000, 9800000, "9.0 MHz"), - BW_7M7(0x07, 7700000, 7050000, 8350000, "7.7 MHz"), - BW_6M4(0x08, 6400000, 5805000, 7050000, "6.4 MHz"), - BW_5M3(0x09, 5300000, 4850000, 5805000, "5.3 MHz"), - BW_4M4(0x0A, 4400000, 3900000, 4850000, "4.4 MHz"), - BW_3M4(0x0B, 3400000, 3000000, 3900000, "3.4 MHz"), - BW_2M6(0x0C, 2600000, 2200000, 3000000, "2.6 MHz"), - BW_1M8(0x0D, 1800000, 1500000, 2200000, "1.8 MHz"), - BW_1M2(0x0E, 1200000, 1100000, 1500000, "1.2 MHz"), - BW_1M0(0x0F, 1000000, 0, 1100000, "1.0 MHz"); - - private int mValue; - private int mBandwidth; - private int mMinBandwidth; - private int mMaxBandwidth; - private String mLabel; - - private RCFilter(int value, - int bandwidth, - int minimumBandwidth, - int maximumBandwidth, - String label) - { - mValue = value; - mBandwidth = bandwidth; - mMinBandwidth = minimumBandwidth; - mMaxBandwidth = maximumBandwidth; - mLabel = label; - } - - public static Register getRegister() - { - return Register.FILT2; - } - - public static byte getMask() - { - return IF_RC_FILTER_MASK; - } - - public byte getValue() - { - return (byte) mValue; - } - - public int getBandwidth() - { - return mBandwidth; - } - - public int getMinimumBandwidth() - { - return mMinBandwidth; - } - - public int getMaximumBandwidth() - { - return mMaxBandwidth; - } - - public String getLabel() - { - return mLabel; - } - - /** - * Returns the correct filter to use for the bandwidth/sample rate - * - * @param bandwidth - current sample rate - * @return - correct filter to use for the bandwidth/sample rate - */ - public static RCFilter getFilter(int bandwidth) - { - for(RCFilter filter : values()) - { - if(filter.getMinimumBandwidth() <= bandwidth && - bandwidth < filter.getMaximumBandwidth()) - { - return filter; - } - } - - /* default */ - return RCFilter.BW_21M4; - } - - public static RCFilter fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(RCFilter filter : values()) - { - if(value == filter.getValue()) - { - return filter; - } - } - - throw new IllegalArgumentException("E4KTunerController - " - + "unrecognized rc filter value [" + value + "]"); - } - } - - public enum ChannelFilter - { - BW_5M50(0x00, 5500000, 5400000, 28800000, "5.50 MHz"), - BW_5M30(0x01, 5300000, 5150000, 5400000, "5.30 MHz"), - BW_5M00(0x02, 5000000, 4900000, 5150000, "5.00 MHz"), - BW_4M80(0x03, 4800000, 4700000, 4900000, "4.80 MHz"), - BW_4M60(0x04, 4600000, 4500000, 4700000, "4.60 MHz"), - BW_4M40(0x05, 4400000, 4350000, 4500000, "4.40 MHz"), - BW_4M30(0x06, 4300000, 4200000, 4350000, "4.30 MHz"), - BW_4M10(0x07, 4100000, 4000000, 4200000, "4.10 MHz"), - BW_3M90(0x08, 3900000, 3850000, 4000000, "3.90 MHz"), - BW_3M80(0x09, 3800000, 3750000, 3850000, "3.80 MHz"), - BW_3M70(0x0A, 3700000, 3650000, 3750000, "3.70 MHz"), - BW_3M60(0x0B, 3600000, 3500000, 3650000, "3.60 MHz"), - BW_3M40(0x0C, 3400000, 3350000, 3500000, "3.40 MHz"), - BW_3M30(0x0D, 3300000, 3250000, 3350000, "3.30 MHz"), - BW_3M20(0x0E, 3200000, 3150000, 3250000, "3.20 MHz"), - BW_3M10(0x0F, 3100000, 3050000, 3150000, "3.10 MHz"), - BW_3M00(0x10, 3000000, 2975000, 3050000, "3.00 MHz"), - BW_2M95(0x11, 2950000, 2925000, 2975000, "2.95 MHz"), - BW_2M90(0x12, 2900000, 2850000, 2925000, "2.90 MHz"), - BW_2M80(0x13, 2800000, 2775000, 2850000, "2.80 MHz"), - BW_2M75(0x14, 2750000, 2750000, 2775000, "2.75 MHz"), - BW_2M70(0x15, 2700000, 2650000, 2750000, "2.70 MHz"), - BW_2M60(0x16, 2600000, 2575000, 2650000, "2.60 MHz"), - BW_2M55(0x17, 2550000, 2525000, 2575000, "2.55 MHz"), - BW_2M50(0x18, 2500000, 2475000, 2525000, "2.50 MHz"), - BW_2M45(0x19, 2450000, 2425000, 2475000, "2.45 MHz"), - BW_2M40(0x1A, 2400000, 2350000, 2425000, "2.40 MHz"), - BW_2M30(0x1B, 2300000, 2290000, 2350000, "2.30 MHz"), - BW_2M28(0x1C, 2280000, 2260000, 2290000, "2.28 MHz"), - BW_2M24(0x1D, 2240000, 2220000, 2260000, "2.24 MHz"), - BW_2M20(0x1E, 2200000, 2175000, 2220000, "2.20 MHz"), - BW_2M15(0x1F, 2150000, 0, 2175000, "2.15 MHz"); - - private int mValue; - private int mBandwidth; - private int mMinBandwidth; - private int mMaxBandwidth; - private String mLabel; - - private ChannelFilter(int value, - int bandwidth, - int minimumBandwidth, - int maximumBandwidth, - String label) - { - mValue = value; - mBandwidth = bandwidth; - mMinBandwidth = minimumBandwidth; - mMaxBandwidth = maximumBandwidth; - mLabel = label; - } - - public static Register getRegister() - { - return Register.FILT3; - } - - public static byte getMask() - { - return IF_CHANNEL_FILTER_MASK; - } - - public byte getValue() - { - return (byte) mValue; - } - - public int getBandwidth() - { - return mBandwidth; - } - - public int getMinimumBandwidth() - { - return mMinBandwidth; - } - - public int getMaximumBandwidth() - { - return mMaxBandwidth; - } - - public String getLabel() - { - return mLabel; - } - - /** - * Returns the correct filter to use for the bandwidth/sample rate - * - * @param bandwidth - current sample rate - * @return - correct filter to use for the bandwidth/sample rate - */ - public static ChannelFilter getFilter(int bandwidth) - { - for(ChannelFilter filter : values()) - { - if(filter.getMinimumBandwidth() <= bandwidth && - bandwidth < filter.getMaximumBandwidth()) - { - return filter; - } - } - - /* default */ - return ChannelFilter.BW_5M50; - } - - public static ChannelFilter fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(ChannelFilter filter : values()) - { - if(value == filter.getValue()) - { - return filter; - } - } - - throw new IllegalArgumentException("E4KTunerController - " - + "unrecognized channel filter value [" + value + "]"); - } - } - - public enum E4KLNAGain - { - AUTOMATIC(-1, "Auto"), - GAIN_MINUS_50(0, "-5.0 db"), - GAIN_MINUS_25(1, "-2.5 db"), - GAIN_PLUS_0(4, "0.0 db"), - GAIN_PLUS_25(5, "2.5 db"), - GAIN_PLUS_50(6, "5.0 db"), - GAIN_PLUS_75(7, "7.5 db"), - GAIN_PLUS_100(8, "10.0 db"), - GAIN_PLUS_125(9, "12.5 db"), - GAIN_PLUS_150(10, "15.0 db"), - GAIN_PLUS_175(11, "17.5 db"), - GAIN_PLUS_200(12, "20.0 db"), - GAIN_PLUS_250(13, "25.0 db"), - GAIN_PLUS_300(14, "30.0 db"); - - private int mValue; - private String mLabel; - - private E4KLNAGain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN1; - } - - public static byte getMask() - { - return GAIN1_MOD_MASK; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static E4KLNAGain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(E4KLNAGain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized LNA Gain value [" + - value + "]"); - } - } - - /** - * Defines the default gain settings that are exposed to the user. - * - * Manual is an override that keeps each of the settings intact and allows - * the user to change each of them individually - */ - public enum E4KGain - { - AUTOMATIC("Auto", E4KMixerGain.AUTOMATIC, E4KLNAGain.AUTOMATIC, E4KEnhanceGain.AUTOMATIC), - MANUAL("Manual", null, null, null), - MINUS_10("-1.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_50, E4KEnhanceGain.AUTOMATIC), - PLUS_15("1.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_MINUS_25, E4KEnhanceGain.AUTOMATIC), - PLUS_40("4.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_0, E4KEnhanceGain.AUTOMATIC), - PLUS_65("6.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_25, E4KEnhanceGain.AUTOMATIC), - PLUS_90("9.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_50, E4KEnhanceGain.AUTOMATIC), - PLUS_115("11.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_75, E4KEnhanceGain.AUTOMATIC), - PLUS_140("14.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_100, E4KEnhanceGain.AUTOMATIC), - PLUS_165("16.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_125, E4KEnhanceGain.AUTOMATIC), - PLUS_190("19.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_150, E4KEnhanceGain.AUTOMATIC), - PLUS_215("21.5 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_175, E4KEnhanceGain.AUTOMATIC), - PLUS_240("24.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_200, E4KEnhanceGain.AUTOMATIC), - PLUS_290("29.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_250, E4KEnhanceGain.AUTOMATIC), - PLUS_340("34.0 db", E4KMixerGain.GAIN_4, E4KLNAGain.GAIN_PLUS_300, E4KEnhanceGain.AUTOMATIC), - PLUS_420("42.0 db", E4KMixerGain.GAIN_12, E4KLNAGain.GAIN_PLUS_300, E4KEnhanceGain.AUTOMATIC); - - private String mLabel; - private E4KMixerGain mMixerGain; - private E4KLNAGain mLNAGain; - private E4KEnhanceGain mEnhanceGain; - - private E4KGain(String label, - E4KMixerGain mixerGain, - E4KLNAGain lnaGain, - E4KEnhanceGain enhanceGain) - { - mLabel = label; - mMixerGain = mixerGain; - mLNAGain = lnaGain; - mEnhanceGain = enhanceGain; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public E4KMixerGain getMixerGain() - { - return mMixerGain; - } - - public E4KLNAGain getLNAGain() - { - return mLNAGain; - } - - public E4KEnhanceGain getEnhanceGain() - { - return mEnhanceGain; - } - } - - public enum E4KEnhanceGain - { - AUTOMATIC(0, "Auto"), - GAIN_1(1, "10 db"), - GAIN_3(3, "30 db"), - GAIN_5(5, "50 db"), - GAIN_7(7, "70 db"); - - private int mValue; - private String mLabel; - - private E4KEnhanceGain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.AGC11; - } - - public static byte getMask() - { - return ENH_GAIN_MOD_MASK; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static E4KEnhanceGain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(E4KEnhanceGain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized Enhance Gain value [" + - value + "]"); - } - } - - public enum DCGainCombination - { - LOOKUP_TABLE_0(Register.QLUT0, - Register.ILUT0, - E4KMixerGain.GAIN_4, - IFStage1Gain.GAIN_MINUS3), - LOOKUP_TABLE_1(Register.QLUT1, - Register.ILUT1, - E4KMixerGain.GAIN_4, - IFStage1Gain.GAIN_PLUS6), - LOOKUP_TABLE_2(Register.QLUT2, - Register.ILUT2, - E4KMixerGain.GAIN_12, - IFStage1Gain.GAIN_MINUS3), - LOOKUP_TABLE_3(Register.QLUT3, - Register.ILUT3, - E4KMixerGain.GAIN_12, - IFStage1Gain.GAIN_PLUS6); - - private Register mQRegister; - private Register mIRegister; - private E4KMixerGain mMixerGain; - private IFStage1Gain mIFStage1Gain; - - private DCGainCombination(Register qRegister, - Register iRegister, - E4KMixerGain mixer, - IFStage1Gain stage1) - { - mQRegister = qRegister; - mIRegister = iRegister; - mMixerGain = mixer; - mIFStage1Gain = stage1; - } - - public Register getQRegister() - { - return mQRegister; - } - - public Register getIRegister() - { - return mIRegister; - } - - public E4KMixerGain getMixerGain() - { - return mMixerGain; - } - - public IFStage1Gain getIFStage1Gain() - { - return mIFStage1Gain; - } - } - - - public enum E4KMixerGain - { - AUTOMATIC(-1, "Auto"), - GAIN_4(0, "4 db"), - GAIN_12(1, "12 db"); - - private int mValue; - private String mLabel; - - private E4KMixerGain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN2; - } - - public static byte getMask() - { - return MIXER_GAIN_MASK; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static E4KMixerGain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(E4KMixerGain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized Mixer Gain value [" + - value + "]"); - } - } - - public enum IFStage1Gain - { - GAIN_MINUS3(0x0, "-3 db"), - GAIN_PLUS6(0x1, "6 db"); - - private int mValue; - private String mLabel; - - private IFStage1Gain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN3; - } - - public static byte getMask() - { - return (byte) 0x1; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static IFStage1Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage1Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 1 value [" + - value + "]"); - } - } - - public enum IFStage2Gain - { - GAIN_PLUS0(0x0, "0 db"), - GAIN_PLUS3(0x2, "3 db"), - GAIN_PLUS6(0x4, "6 db"), - GAIN_PLUS9(0x6, "9 db"); - - private int mValue; - private String mLabel; - - private IFStage2Gain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN3; - } - - public static byte getMask() - { - return (byte) 0x6; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static IFStage2Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage2Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 2 value [" + - value + "]"); - } - } - - public enum IFStage3Gain - { - GAIN_PLUS0(0x00, "0 db"), - GAIN_PLUS3(0x08, "3 db"), - GAIN_PLUS6(0x10, "6 db"), - GAIN_PLUS9(0x30, "9 db"); - - private int mValue; - private String mLabel; - - private IFStage3Gain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN3; - } - - public static byte getMask() - { - return (byte) 0x18; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static IFStage3Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage3Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 3 value [" + - value + "]"); - } - } - - public enum IFStage4Gain - { - GAIN_PLUS0(0x00, "0 db"), - GAIN_PLUS1(0x20, "1 db"), - GAIN_PLUS2A(0x40, "2 db"), - GAIN_PLUS2B(0x60, "2 db"); - - private int mValue; - private String mLabel; - - private IFStage4Gain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN3; - } - - public static byte getMask() - { - return (byte) 0x60; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static IFStage4Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage4Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 4 value [" + - value + "]"); - } - } - - public enum IFStage5Gain - { - GAIN_PLUS3(0x0, "3 db"), - GAIN_PLUS6(0x1, "6 db"), - GAIN_PLUS9(0x2, "9 db"), - GAIN_PLUS12(0x3, "12 db"), - GAIN_PLUS15A(0x4, "15 db"), - GAIN_PLUS15B(0x5, "15 db"), - GAIN_PLUS15C(0x6, "15 db"), - GAIN_PLUS15D(0x7, "15 db"); - - private int mValue; - private String mLabel; - - private IFStage5Gain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN4; - } - - public static byte getMask() - { - return (byte) 0x7; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static IFStage5Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage5Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 5 value [" + - value + "]"); - } - - } - - public enum IFStage6Gain - { - GAIN_PLUS3(0x00, "3 db"), - GAIN_PLUS6(0x08, "6 db"), - GAIN_PLUS9(0x10, "9 db"), - GAIN_PLUS12(0x18, "12 db"), - GAIN_PLUS15A(0x20, "15 db"), - GAIN_PLUS15B(0x28, "15 db"), - GAIN_PLUS15C(0x30, "15 db"), - GAIN_PLUS15D(0x38, "15 db"); - - private int mValue; - private String mLabel; - - private IFStage6Gain(int value, String label) - { - mValue = value; - mLabel = label; - } - - public static Register getRegister() - { - return Register.GAIN4; - } - - public static byte getMask() - { - return (byte) 0x38; - } - - public byte getValue() - { - return (byte) mValue; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return mLabel; - } - - public static IFStage6Gain fromRegisterValue(int registerValue) - { - int value = registerValue & getMask(); - - for(IFStage6Gain setting : values()) - { - if(value == setting.getValue()) - { - return setting; - } - } - - throw new IllegalArgumentException("E4KTunerController" - + " - unrecognized IF Gain Stage 6 value [" + - value + "]"); - } - } - - public enum IFFilter - { - MIX("Mix"), - CHAN("Channel"), - RC("RC"); - - private String mLabel; - - private IFFilter(String label) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - } - - public enum AGCMode - { - SERIAL((byte) 0x0), - IF_PWM_LNA_SERIAL((byte) 0x1), - IF_PWM_LNA_AUTONL((byte) 0x2), - IF_PWM_LNA_SUPERV((byte) 0x3), - IF_SERIAL_LNA_PWM((byte) 0x4), - IF_PWM_LNA_PWM((byte) 0x5), - IF_DIG_LNA_SERIAL((byte) 0x6), - IF_DIG_LNA_AUTON((byte) 0x7), - IF_DIG_LNA_SUPERV((byte) 0x8), - IF_SERIAL_LNA_AUTON((byte) 0x9), - IF_SERIAL_LNA_SUPERV((byte) 0xA); - - private byte mValue; - - private AGCMode(byte value) - { - mValue = value; - } - - public byte getValue() - { - return mValue; - } - } - - public enum Register - { - DUMMY(0x00), - MASTER1(0x00), - MASTER2(0x01), - MASTER3(0x02), - MASTER4(0x03), - MASTER5(0x04), - CLK_INP(0x05), - REF_CLK(0x06), - SYNTH1(0x07), - SYNTH2(0x08), - SYNTH3(0x09), - SYNTH4(0x0A), - SYNTH5(0x0B), - SYNTH6(0x0C), - SYNTH7(0x0D), - SYNTH8(0x0E), - SYNTH9(0x0F), - FILT1(0x10), - FILT2(0x11), - FILT3(0x12), - GAIN1(0x14), - GAIN2(0x15), - GAIN3(0x16), - GAIN4(0x17), - AGC1(0x1A), - AGC2(0x1B), - AGC3(0x1C), - AGC4(0x1D), - AGC5(0x1E), - AGC6(0x1F), - AGC7(0x20), - AGC8(0x21), - AGC11(0x24), - AGC12(0x25), - DC1(0x29), - DC2(0x2A), - DC3(0x2B), - DC4(0x2C), - DC5(0x2D), - DC6(0x2E), - DC7(0x2F), - DC8(0x30), - QLUT0(0x50), - QLUT1(0x51), - QLUT2(0x52), - QLUT3(0x53), - ILUT0(0x60), - ILUT1(0x61), - ILUT2(0x62), - ILUT3(0x63), - DCTIME1(0x70), - DCTIME2(0x71), - DCTIME3(0x72), - DCTIME4(0x73), - PWM1(0x74), - PWM2(0x75), - PWM3(0x76), - PWM4(0x77), - BIAS(0x78), - CLKOUT_PWDN(0x7A), - CHFILT_CALIB(0x7B), - I2C_REG_ADDR(0x7D), - MAGIC_1(0x7E), - MAGIC_2(0x7F), - MAGIC_3(0x82), - MAGIC_4(0x86), - MAGIC_5(0x87), - MAGIC_6(0x88), - MAGIC_7(0x9F), - MAGIC_8(0xA0), - I2C_REGISTER(0xC8); - - private int mValue; - - private Register(int value) - { - mValue = value; - } - - public byte getValue() - { - return (byte) mValue; - } - } -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/source/tuner/rtl/e4k/E4KTunerEditor.java deleted file mode 100644 index 064da0ad..00000000 --- a/src/source/tuner/rtl/e4k/E4KTunerEditor.java +++ /dev/null @@ -1,499 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.rtl.e4k; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import source.SourceException; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; -import source.tuner.configuration.TunerConfigurationEvent; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.rtl.RTL2832Tuner; -import source.tuner.rtl.RTL2832TunerController.Descriptor; -import source.tuner.rtl.RTL2832TunerController.SampleRate; -import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; - -public class E4KTunerEditor extends TunerConfigurationEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( E4KTunerEditor.class ); - - private static final long serialVersionUID = 1L; - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboEnhanceGain; - - private E4KTunerController mController; - private boolean mLoading; - - public E4KTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) - { - super( tunerConfigurationModel ); - mController = (E4KTunerController)tuner.getController(); - - init(); - } - - private E4KTunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (E4KTunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][grow]" ) ); - - add( new JLabel( "E4000 Tuner Configuration" ), "span,align center" ); - - /** - * Tuner Configuration Name - */ - add( new JLabel( "Name:" ) ); - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.setEnabled( false ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = (SampleRate)mComboSampleRate.getSelectedItem(); - - try - { - mController.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, - "E4K Tuner Controller - couldn't apply the sample rate setting [" + - sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply sample rate setting [" + - sampleRate.getLabel() + "]", eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate ); - - SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.getFrequencyController().setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply frequency correction value: " + value + e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply frequency correction value:" - + value, e1 ); - } - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - add( new JLabel( "Gain" ), "wrap" ); - - mComboMasterGain = new JComboBox( E4KGain.values() ); - mComboMasterGain.setEnabled( false ); - mComboMasterGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == E4KGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( mController.getMixerGain( true ) ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( mController.getLNAGain( true ) ); - mComboLNAGain.setEnabled( true ); - - mComboEnhanceGain.setSelectedItem( mController.getEnhanceGain( true ) ); - mComboEnhanceGain.setEnabled( true ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - - mComboEnhanceGain.setEnabled( false ); - mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); - } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply gain setting", e ); - } - } - } ); - - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
independent control of " - + "Mixer, LNA and Enhance gain
" - + "settings, or one of the individual gain settings for
" - + "semi-manual gain control" ); - add( new JLabel( "Master:" ) ); - add( mComboMasterGain ); - - mComboMixerGain = new JComboBox( E4KMixerGain.values() ); - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KMixerGain mixerGain = (E4KMixerGain)mComboMixerGain.getSelectedItem(); - mController.setMixerGain( mixerGain, true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply mixer gain setting", e ); - } - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); - add( new JLabel( "Mixer:" ) ); - add( mComboMixerGain ); - - mComboLNAGain = new JComboBox( E4KLNAGain.values() ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); - mController.setLNAGain( lnaGain, true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply LNA gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); - - mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); - mComboEnhanceGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KEnhanceGain enhance = (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); - mController.setEnhanceGain( enhance, true ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " - + "couldn't apply the enhance gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply enhance gain setting", e ); - } - } - } ); - mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboEnhanceGain.setEnabled( false ); - add( new JLabel( "Enhance:" ) ); - add( mComboEnhanceGain ); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mComboSampleRate.isEnabled() != enabled ) - { - mComboSampleRate.setEnabled( enabled ); - } - - if( mComboMasterGain.isEnabled() != enabled ) - { - mComboMasterGain.setEnabled( enabled ); - } - - if( mComboMixerGain.isEnabled() != enabled ) - { - mComboMixerGain.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboEnhanceGain.isEnabled() != enabled ) - { - mComboEnhanceGain.setEnabled( enabled ); - } - } - - private String getTunerInfo() - { - StringBuilder sb = new StringBuilder(); - - Descriptor descriptor = mController.getDescriptor(); - - sb.append( "

RTL-2832 with E4000 Tuner

" ); - - if( descriptor == null ) - { - sb.append( "No EEPROM Descriptor Available" ); - } - else - { - sb.append( "USB ID: " ); - sb.append( descriptor.getVendorID() ); - sb.append( ":" ); - sb.append( descriptor.getProductID() ); - sb.append( "
" ); - - sb.append( "Vendor: " ); - sb.append( descriptor.getVendorLabel() ); - sb.append( "
" ); - - sb.append( "Product: " ); - sb.append( descriptor.getProductLabel() ); - sb.append( "
" ); - - sb.append( "Serial: " ); - sb.append( descriptor.getSerial() ); - sb.append( "
" ); - - sb.append( "IR Enabled: " ); - sb.append( descriptor.irEnabled() ); - sb.append( "
" ); - - sb.append( "Remote Wake: " ); - sb.append( descriptor.remoteWakeupEnabled() ); - sb.append( "
" ); - } - - return sb.toString(); - } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - E4KTunerConfiguration config = getConfiguration(); - - config.setName( mConfigurationName.getText() ); - - config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - config.setMasterGain( (E4KGain)mComboMasterGain.getSelectedItem() ); - config.setMixerGain( (E4KMixerGain)mComboMixerGain.getSelectedItem() ); - config.setLNAGain( (E4KLNAGain)mComboLNAGain.getSelectedItem() ); - config.setEnhanceGain( (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem() ); - - getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( - getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); - } - - } - - @Override - public void setItem( TunerConfiguration tunerConfiguration ) - { - super.setItem( tunerConfiguration ); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if( hasItem() ) - { - E4KTunerConfiguration config = getConfiguration(); - - if( tunerConfiguration.isAssigned() ) - { - setControlsEnabled( true ); - - mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mComboMasterGain.setSelectedItem( config.getMasterGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboEnhanceGain.setSelectedItem( config.getEnhanceGain() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - - mLoading = false; - } -} diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java deleted file mode 100644 index 4fc9f782..00000000 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ /dev/null @@ -1,1296 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * Java port of librtlsdr - * - * Copyright (C) 2013 Mauro Carvalho Chehab - * Copyright (C) 2013 Steve Markgraf - * - * 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 source.tuner.rtl.r820t; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.Device; -import org.usb4java.DeviceDescriptor; -import org.usb4java.LibUsbException; -import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.rtl.RTL2832TunerController; - -import javax.swing.*; -import javax.usb.UsbException; -import java.nio.ByteBuffer; - -public class R820TTunerController extends RTL2832TunerController -{ - private final static Logger mLog = - LoggerFactory.getLogger(R820TTunerController.class); - - public static final long MIN_FREQUENCY = 3180000; - public static final long MAX_FREQUENCY = 1782030000; - public static final double USABLE_BANDWIDTH_PERCENT = 1.0; - public static final int DC_SPIKE_AVOID_BUFFER = 5000; - - public static final int R820T_IF_FREQUENCY = 3570000; - public static final byte VERSION = (byte) 49; - - public static final byte[] BIT_REV_LOOKUP_TABLE = - {(byte) 0x0, (byte) 0x8, (byte) 0x4, (byte) 0xC, - (byte) 0x2, (byte) 0xA, (byte) 0x6, (byte) 0xE, - (byte) 0x1, (byte) 0x9, (byte) 0x5, (byte) 0xD, - (byte) 0x3, (byte) 0xB, (byte) 0x7, (byte) 0xF}; - - /* R820T I2C address */ - private byte mI2CAddress = (byte) 0x34; - - /* Shadow register is used to keep a cached (in-memory) copy of all - * registers, so that we don't have to read a full byte from a register in - * order to apply a masked value and then re-write the full byte. With the - * shadow register, we can apply the masked value to the cached value, and - * then just write the masked byte, skipping the need to read the byte - * first. */ - private int[] mShadowRegister = - {0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x32, 0x75, - 0xC0, 0x40, 0xD6, 0x6C, 0xF5, 0x63, 0x75, 0x68, - 0x6C, 0x83, 0x80, 0x00, 0x0F, 0x00, 0xC0, 0x30, - 0x48, 0xCC, 0x60, 0x00, 0x54, 0xAE, 0x4A, 0xC0}; - - private JPanel mEditor; - - public R820TTunerController(Device device, DeviceDescriptor deviceDescriptor) throws SourceException - { - super(device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY, DC_SPIKE_AVOID_BUFFER, - USABLE_BANDWIDTH_PERCENT); - } - - @Override - public TunerType getTunerType() - { - return TunerType.RAFAELMICRO_R820T; - } - - @Override - public void setSampleRateFilters(int sampleRate) throws LibUsbException - { - //TODO: why is this being forced as an abstract method on sub classes? - } - - @Override - public void apply(TunerConfiguration tunerConfig) throws SourceException - { - if(tunerConfig != null && - tunerConfig instanceof R820TTunerConfiguration) - { - R820TTunerConfiguration config = (R820TTunerConfiguration) tunerConfig; - - try - { - SampleRate sampleRate = config.getSampleRate(); - setSampleRate(sampleRate); - - double correction = config.getFrequencyCorrection(); - getFrequencyController().setFrequencyCorrection(correction); - - R820TGain masterGain = config.getMasterGain(); - setGain(masterGain, true); - - if(masterGain == R820TGain.MANUAL) - { - R820TMixerGain mixerGain = config.getMixerGain(); - setMixerGain(mixerGain, true); - - R820TLNAGain lnaGain = config.getLNAGain(); - setLNAGain(lnaGain, true); - - R820TVGAGain vgaGain = config.getVGAGain(); - setVGAGain(vgaGain, true); - } - - try - { - getFrequencyController().setFrequency(config.getFrequency()); - } - catch(SourceException se) - { - //Do nothing, we couldn't set the frequency - } - } - catch(UsbException e) - { - throw new SourceException("R820TTunerController - usb error " - + "while applying tuner config", e); - } - } - } - - /** - * Not implemented. - */ - @Override - public long getTunedFrequency() throws SourceException - { - return 0; - } - - /** - * Sets the center frequency. Setting the frequency is a two-part process - * of setting the multiplexer and then setting the Oscillator (PLL). - */ - @Override - public void setTunedFrequency(long frequency) throws SourceException - { - try - { - enableI2CRepeater(mDeviceHandle, true); - - boolean controlI2C = false; - - long offsetFrequency = frequency + R820T_IF_FREQUENCY; - - setMux(offsetFrequency, controlI2C); - - setPLL(offsetFrequency, controlI2C); - - enableI2CRepeater(mDeviceHandle, false); - } - catch(UsbException e) - { - throw new SourceException("R820TTunerController - exception " - + "while setting frequency [" + frequency + "] - " + - e.getLocalizedMessage()); - } - } - - /** - * Overrides the same method from the RTL2832 tuner controller to apply - * settings specific to the R820T tuner. - */ - public void setSamplingMode(SampleMode mode) throws LibUsbException - { - switch(mode) - { - case QUADRATURE: - /* Set intermediate frequency to R820T IF frequency */ - setIFFrequency(R820T_IF_FREQUENCY); - - /* Enable spectrum inversion */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x15, - (short) 0x01, - 1); - - /* Set default i/q path */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x06, - (short) 0x80, - 1); - break; - default: - break; - - } - } - - /** - * Sets the multiplexer for the desired center frequency. - */ - private void setMux(long frequency, boolean controlI2C) throws UsbException - { - FrequencyRange range = FrequencyRange.getRangeForFrequency(frequency); - - /* Set open drain */ - writeR820TRegister(Register.DRAIN, range.getOpenDrain(), controlI2C); - - /* RF_MUX, Polymux */ - writeR820TRegister(Register.RF_POLY_MUX, range.getRFMuxPolyMux(), controlI2C); - - /* TF Band */ - writeR820TRegister(Register.TF_BAND, range.getTFC(), controlI2C); - - /* XTAL CAP & Drive */ - writeR820TRegister(Register.PLL_XTAL_CAPACITOR_AND_DRIVE, - range.getXTALHighCap0P(), controlI2C); - - /* Register 8 - what is it? */ - writeR820TRegister(Register.UNKNOWN_REGISTER_8, (byte) 0x00, controlI2C); - - /* Register 9 - what is it? */ - writeR820TRegister(Register.UNKNOWN_REGISTER_9, (byte) 0x00, controlI2C); - } - - /** - * Initializes the tuner for use. - */ - public void init() throws SourceException - { - /* Initialize the super class to open and claim the usb interface*/ - super.init(); - - try - { - initBaseband(mDeviceHandle); - - enableI2CRepeater(mDeviceHandle, true); - - boolean i2CRepeaterControl = false; - - initTuner(i2CRepeaterControl); - - enableI2CRepeater(mDeviceHandle, false); - } - catch(UsbException e) - { - throw new SourceException("error during init()", e); - } - } - - /** - * Initializes the tuner section. - */ - public void initTuner(boolean controlI2C) throws UsbException - { - /* Disable zero IF mode */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0xB1, - (short) 0x1A, - 1); - - /* Only enable in-phase ADC input */ - writeDemodRegister(mDeviceHandle, - Page.ZERO, - (short) 0x08, - (short) 0x4D, - 1); - - /* Set intermediate frequency to R820T IF frequency (3.57 MHz) */ - setIFFrequency(R820T_IF_FREQUENCY); - - /* Enable spectrum inversion */ - writeDemodRegister(mDeviceHandle, - Page.ONE, - (short) 0x15, - (short) 0x01, - 1); - - initializeRegisters(controlI2C); - - setTVStandard(controlI2C); - - systemFrequencySelect(0, controlI2C); - } - - /** - * Partially implements the r82xx_set_tv_standard() method from librtlsdr. - * Sets standard to digital tv to support sdr operations only. - */ - private void setTVStandard(boolean controlI2C) throws UsbException - { - /* Init Flag & Xtal check Result */ - writeR820TRegister(Register.XTAL_CHECK, (byte) 0x00, controlI2C); - - /* Set version */ - writeR820TRegister(Register.VERSION, VERSION, controlI2C); - - /* LT Gain Test */ - writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C); - - int calibrationCode = 0; - - for(int x = 0; x < 2; x++) - { - /* Set filter cap */ - writeR820TRegister(Register.FILTER_CAPACITOR, (byte) 0x6B, controlI2C); - - /* Set calibration clock on */ - writeR820TRegister(Register.CALIBRATION_CLOCK, (byte) 0x04, controlI2C); - - /* XTAL capacitor 0pF for PLL */ - writeR820TRegister(Register.PLL_XTAL_CAPACITOR, (byte) 0x00, controlI2C); - - setPLL(56000 * 1000, controlI2C); - - /* Start trigger */ - writeR820TRegister(Register.CALIBRATION_TRIGGER, (byte) 0x10, controlI2C); - - /* Stop trigger */ - writeR820TRegister(Register.CALIBRATION_TRIGGER, (byte) 0x00, controlI2C); - - /* Set calibration clock off */ - writeR820TRegister(Register.CALIBRATION_CLOCK, (byte) 0x00, controlI2C); - - calibrationCode = getCalibrationCode(controlI2C); - - if(!calibrationSuccessful(calibrationCode)) - { - mLog.error("Calibration NOT successful - code: " + - calibrationCode); - } - } - - if(calibrationCode == 0x0F) - { - calibrationCode = 0; - } - - /* Write calibration code */ - byte filt_q = 0x10; - - writeR820TRegister(Register.FILTER_CALIBRATION_CODE, - (byte) (calibrationCode | filt_q), controlI2C); - - /* Set BW, Filter gain & HP Corner */ - writeR820TRegister(Register.BANDWIDTH_FILTER_GAIN_HIGHPASS_FILTER_CORNER, - (byte) 0x6B, controlI2C); - - /* Set Image_R */ - writeR820TRegister(Register.IMAGE_REVERSE, (byte) 0x00, controlI2C); - - /* Set filter_3db, V6MHz */ - writeR820TRegister(Register.FILTER_GAIN, (byte) 0x10, controlI2C); - - /* Channel filter extension */ - writeR820TRegister(Register.CHANNEL_FILTER_EXTENSION, (byte) 0x60, controlI2C); - - /* Loop through */ - writeR820TRegister(Register.LOOP_THROUGH, (byte) 0x00, controlI2C); - - /* Loop through attenuation */ - writeR820TRegister(Register.LOOP_THROUGH_ATTENUATION, (byte) 0x00, controlI2C); - - /* Filter extension widest */ - writeR820TRegister(Register.FILTER_EXTENSION_WIDEST, (byte) 0x00, controlI2C); - - /* RF poly filter current */ - writeR820TRegister(Register.RF_POLY_FILTER_CURRENT, (byte) 0x60, controlI2C); - } - - /** - * Indicates if a calibration request was successful. - */ - private boolean calibrationSuccessful(int calibrationCode) - { - return calibrationCode != 0 && calibrationCode != 0x0F; - } - - /** - * Returns the calibration code resulting from a calibration request. - */ - private int getCalibrationCode(boolean controlI2C) throws UsbException - { - return getStatusRegister(4, controlI2C) & 0x0F; - } - - /** - * Sets the system IF frequency - */ - private void systemFrequencySelect(long frequency, boolean controlI2C) - throws UsbException - { - /* LNA top? */ - writeR820TRegister(Register.LNA_TOP2, (byte) 0xE5, controlI2C); - - byte mixer_top; - byte cp_cur; - byte div_buf_cur; - - if(frequency == 506000000 || - frequency == 666000000 || - frequency == 818000000) - { - mixer_top = (byte) 0x14; - cp_cur = (byte) 0x28; - div_buf_cur = (byte) 0x20; - } - else - { - mixer_top = (byte) 0x24; - cp_cur = (byte) 0x38; - div_buf_cur = (byte) 0x30; - } - - writeR820TRegister(Register.MIXER_TOP, mixer_top, controlI2C); - - writeR820TRegister(Register.LNA_VTH_L, (byte) 0x53, controlI2C); - - writeR820TRegister(Register.MIXER_VTH_L, (byte) 0x75, controlI2C); - - /* Air-In only for Astrometa */ - writeR820TRegister(Register.AIR_CABLE1_INPUT_SELECTOR, (byte) 0x00, controlI2C); - - writeR820TRegister(Register.CABLE2_INPUT_SELECTOR, (byte) 0x00, controlI2C); - - writeR820TRegister(Register.CP_CUR, cp_cur, controlI2C); - - writeR820TRegister(Register.DIVIDER_BUFFER_CURRENT, div_buf_cur, controlI2C); - - writeR820TRegister(Register.FILTER_CURRENT, (byte) 0x40, controlI2C); - - /* if( type != TUNER_ANALOG_TV ) ... */ - - writeR820TRegister(Register.LNA_TOP, (byte) 0x00, controlI2C); - - writeR820TRegister(Register.MIXER_TOP2, (byte) 0x00, controlI2C); - - writeR820TRegister(Register.PRE_DETECT, (byte) 0x00, controlI2C); - - writeR820TRegister(Register.AGC_CLOCK, (byte) 0x30, controlI2C); - - writeR820TRegister(Register.LNA_TOP, (byte) 0x18, controlI2C); - - writeR820TRegister(Register.MIXER_TOP2, mixer_top, controlI2C); - - /* LNA discharge current */ - writeR820TRegister(Register.LNA_DISCHARGE_CURRENT, (byte) 0x14, controlI2C); - - /* AGC clock 1 khz, external det1 cap 1u */ - writeR820TRegister(Register.AGC_CLOCK, (byte) 0x20, controlI2C); - } - - /** - * Sets the tuner's Phase-Locked-Loop (PLL) oscillator used for frequency - * (tuning) control - * - * @param frequency - desired center frequency - * @param controlI2C - control the I2C repeater locally - * @throws UsbException - if unable to set any of the R820T registers - */ - private void setPLL(long frequency, boolean controlI2C) throws UsbException - { - /* Set reference divider to 0 */ - writeR820TRegister(Register.REFERENCE_DIVIDER_2, (byte) 0x00, controlI2C); - - /* Set PLL autotune to 128kHz */ - writeR820TRegister(Register.PLL_AUTOTUNE, (byte) 0x00, controlI2C); - - /* Set VCO current to 100 */ - writeR820TRegister(Register.VCO_CURRENT, (byte) 0x80, controlI2C); - - /* Set the frequency divider - adjust for vco_fine_tune status */ - FrequencyDivider divider = FrequencyDivider.fromFrequency(frequency); - - int statusRegister4 = getStatusRegister(4, controlI2C); - - int vco_fine_tune = (statusRegister4 & 0x30) >> 4; - - int div_num = divider.getDividerNumber(vco_fine_tune); - - writeR820TRegister(Register.DIVIDER, (byte) (div_num << 5), controlI2C); - - /* Get the integral number for this divider and frequency */ - Integral integral = divider.getIntegral(frequency); - - writeR820TRegister(Register.PLL, integral.getRegisterValue(), controlI2C); - - /* Calculate the sigma-delta modulator fractional setting. If it's - * non-zero, power up the sdm and apply the fractional setting, - * otherwise turn it off */ - int sdm = divider.getSDM(integral, frequency); - - if(sdm != 0) - { - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER, - (byte) 0x00, controlI2C); - - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_MSB, - (byte) ((sdm >> 8) & 0xFF), controlI2C); - - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_LSB, - (byte) (sdm & 0xFF), controlI2C); - } - else - { - writeR820TRegister(Register.SIGMA_DELTA_MODULATOR_POWER, - (byte) 0x08, controlI2C); - } - - /* Check to see if the PLL locked with these divider, integral and sdm - * settings */ - if(!isPLLLocked(controlI2C)) - { - /* Increase VCO current */ - writeR820TRegister(Register.VCO_CURRENT, (byte) 0x60, controlI2C); - - if(!isPLLLocked(controlI2C)) - { - throw new UsbException("R820T Tuner Controller - couldn't " - + "achieve PLL lock on frequency [" + frequency + "]"); - } - } - - /* set pll autotune to 8kHz */ - writeR820TRegister(Register.PLL_AUTOTUNE_VARIANT, (byte) 0x08, controlI2C); - } - - /** - * Indicates if the Phase Locked Loop (PLL) oscillator is locked following - * a change in the tuned center frequency. Checks status register 2 to see - * if the PLL locked indicator bit is set. - */ - private boolean isPLLLocked(boolean controlI2C) throws UsbException - { - int register = getStatusRegister(2, controlI2C); - - return (register & 0x40) == 0x40; - } - - /** - * Writes initial starting value of registers 0x05 through 0x1F using the - * default value initialized in the shadow register array. This method only - * needs to be called once, upon initialization. - * - * @throws UsbException - */ - private void initializeRegisters(boolean controlI2C) throws UsbException - { - for(int x = 5; x < mShadowRegister.length; x++) - { - writeI2CRegister(mDeviceHandle, - mI2CAddress, - (byte) x, - (byte) mShadowRegister[x], - controlI2C); - } - } - - /** - * Returns the contents of status registers 0 through 4 - */ - private int getStatusRegister(int register, boolean controlI2C) throws UsbException - { - ByteBuffer buffer = ByteBuffer.allocateDirect(5); - - read(mDeviceHandle, mI2CAddress, Block.I2C, buffer); - - return bitReverse(buffer.get(register) & 0xFF); - } - - /** - * Assumes value is a byte value and reverses the bits in the byte. - */ - private static int bitReverse(int value) - { - return BIT_REV_LOOKUP_TABLE[value & 0x0F] << 4 | - BIT_REV_LOOKUP_TABLE[(value & 0xF0) >> 4]; - } - - /** - * Writes the byte value to the specified register, optionally controlling - * the I2C repeater as needed. - */ - public void writeR820TRegister(Register register, - byte value, - boolean controlI2C) throws UsbException - { - if(register.isMasked()) - { - int current = mShadowRegister[register.getRegister()]; - - value = (byte) ((current & ~register.getMask()) | - (value & register.getMask())); - } - - writeI2CRegister(mDeviceHandle, mI2CAddress, - (byte) register.getRegister(), value, controlI2C); - - mShadowRegister[register.getRegister()] = value; - -// Log.info( "R820T writing register " + -// String.format( "%02X", register.getRegister() ) + " value " + -// String.format( "%02X", value ) ); - } - - /** - * Reads the specified register, optionally controlling the I2C repeater - */ - public int readR820TRegister(Register register, boolean controlI2C) - throws UsbException - { - int value = readI2CRegister(mDeviceHandle, - mI2CAddress, - (byte) register.getRegister(), - controlI2C); - - return value; - } - - /** - * Sets master gain by applying gain component values to LNA, Mixer and - * VGA gain registers. - */ - public void setGain(R820TGain gain, boolean controlI2C) throws UsbException - { - setLNAGain(gain.getLNAGain(), controlI2C); - setMixerGain(gain.getMixerGain(), controlI2C); - setVGAGain(gain.getVGAGain(), controlI2C); - } - - /** - * Sets LNA gain - */ - public void setLNAGain(R820TLNAGain gain, boolean controlI2C) throws UsbException - { - writeR820TRegister(Register.LNA_GAIN, gain.getSetting(), controlI2C); - } - - /** - * Sets Mixer gain - */ - public void setMixerGain(R820TMixerGain gain, boolean controlI2C) throws UsbException - { - writeR820TRegister(Register.MIXER_GAIN, gain.getSetting(), controlI2C); - } - - /** - * Sets VGA gain - */ - public void setVGAGain(R820TVGAGain gain, boolean controlI2C) throws UsbException - { - writeR820TRegister(Register.VGA_GAIN, gain.getSetting(), controlI2C); - } - - /** - * R820T VGA gain settings - */ - public enum R820TVGAGain - { - GAIN_0("0", 0x00), - GAIN_26("26", 0x01), - GAIN_52("52", 0x02), - GAIN_82("82", 0x03), - GAIN_124("124", 0x04), - GAIN_159("159", 0x05), - GAIN_183("183", 0x06), - GAIN_196("196", 0x07), - GAIN_210("210", 0x08), - GAIN_242("242", 0x09), - GAIN_278("278", 0x0A), - GAIN_312("312", 0x0B), - GAIN_347("347", 0x0C), - GAIN_384("384", 0x0D), - GAIN_419("419", 0x0E), - GAIN_455("455", 0x0F); - - private String mLabel; - private int mSetting; - - private R820TVGAGain(String label, int setting) - { - mLabel = label; - mSetting = setting; - } - - public String toString() - { - return mLabel; - } - - public byte getSetting() - { - return (byte) mSetting; - } - } - - /** - * R820T Low Noise Amplifier gain settings - */ - public enum R820TLNAGain - { - AUTOMATIC("Automatic", 0x00), - GAIN_0("0", 0x10), - GAIN_9("9", 0x11), - GAIN_21("21", 0x12), - GAIN_61("61", 0x13), - GAIN_99("99", 0x14), - GAIN_112("112", 0x15), - GAIN_143("143", 0x16), - GAIN_165("165", 0x17), - GAIN_191("191", 0x18), - GAIN_222("222", 0x19), - GAIN_248("248", 0x1A), - GAIN_262("262", 0x1B), - GAIN_281("281", 0x1C), - GAIN_286("286", 0x1D), - GAIN_321("321", 0x1E), - GAIN_334("334", 0x1F); - - private String mLabel; - private int mSetting; - - private R820TLNAGain(String label, int setting) - { - mLabel = label; - mSetting = setting; - } - - public String toString() - { - return mLabel; - } - - public byte getSetting() - { - return (byte) mSetting; - } - } - - /** - * R820T mixer gain settings - */ - public enum R820TMixerGain - { - AUTOMATIC("Automatic", 0x10), - GAIN_0("0", 0x00), - GAIN_5("5", 0x01), - GAIN_15("15", 0x02), - GAIN_25("25", 0x03), - GAIN_44("44", 0x04), - GAIN_53("53", 0x05), - GAIN_63("63", 0x06), - GAIN_88("88", 0x07), - GAIN_105("105", 0x08), - GAIN_115("115", 0x09), - GAIN_123("123", 0x0A), - GAIN_139("139", 0x0B), - GAIN_152("152", 0x0C), - GAIN_158("158", 0x0D), - GAIN_161("161", 0x0E), - GAIN_153("153", 0x0F); - - private String mLabel; - private int mSetting; - - private R820TMixerGain(String label, int setting) - { - mLabel = label; - mSetting = setting; - } - - public String toString() - { - return mLabel; - } - - public byte getSetting() - { - return (byte) mSetting; - } - } - - /** - * R820T Master gain settings - */ - public enum R820TGain - { - AUTOMATIC("Automatic", R820TVGAGain.GAIN_312, R820TLNAGain.AUTOMATIC, R820TMixerGain.AUTOMATIC), - MANUAL("Manual", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_248, R820TMixerGain.GAIN_123), - GAIN_0("0", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_0, R820TMixerGain.GAIN_0), - GAIN_9("9", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_9, R820TMixerGain.GAIN_0), - GAIN_14("14", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_9, R820TMixerGain.GAIN_5), - GAIN_26("26", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_21, R820TMixerGain.GAIN_5), - GAIN_36("36", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_21, R820TMixerGain.GAIN_15), - GAIN_76("76", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_61, R820TMixerGain.GAIN_15), - GAIN_86("86", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_61, R820TMixerGain.GAIN_25), - GAIN_124("124", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_99, R820TMixerGain.GAIN_25), - GAIN_143("143", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_99, R820TMixerGain.GAIN_44), - GAIN_156("156", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_112, R820TMixerGain.GAIN_44), - GAIN_165("165", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_112, R820TMixerGain.GAIN_53), - GAIN_196("196", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_143, R820TMixerGain.GAIN_53), - GAIN_208("208", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_143, R820TMixerGain.GAIN_63), - GAIN_228("228", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_165, R820TMixerGain.GAIN_63), - GAIN_253("253", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_165, R820TMixerGain.GAIN_88), - GAIN_279("279", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_191, R820TMixerGain.GAIN_88), - GAIN_296("296", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_191, R820TMixerGain.GAIN_105), - GAIN_327("327", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_222, R820TMixerGain.GAIN_105), - GAIN_337("337", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_222, R820TMixerGain.GAIN_115), - GAIN_363("363", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_248, R820TMixerGain.GAIN_115), - GAIN_371("371", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_248, R820TMixerGain.GAIN_123), - GAIN_385("385", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_262, R820TMixerGain.GAIN_123), - GAIN_401("401", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_262, R820TMixerGain.GAIN_139), - GAIN_420("420", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_281, R820TMixerGain.GAIN_139), - GAIN_433("433", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_281, R820TMixerGain.GAIN_152), - GAIN_438("438", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_286, R820TMixerGain.GAIN_152), - GAIN_444("444", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_286, R820TMixerGain.GAIN_158), - GAIN_479("479", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_321, R820TMixerGain.GAIN_158), - GAIN_482("482", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_321, R820TMixerGain.GAIN_161), - GAIN_495("495", R820TVGAGain.GAIN_210, R820TLNAGain.GAIN_334, R820TMixerGain.GAIN_161); - - private String mLabel; - private R820TVGAGain mVGAGain; - private R820TLNAGain mLNAGain; - private R820TMixerGain mMixerGain; - - private R820TGain(String label, R820TVGAGain vga, R820TLNAGain lna, R820TMixerGain mixer) - { - mLabel = label; - mVGAGain = vga; - mLNAGain = lna; - mMixerGain = mixer; - } - - public String toString() - { - return mLabel; - } - - public R820TVGAGain getVGAGain() - { - return mVGAGain; - } - - public R820TLNAGain getLNAGain() - { - return mLNAGain; - } - - public R820TMixerGain getMixerGain() - { - return mMixerGain; - } - } - - /** - * R820T tuner registers and register mask values - */ - public enum Register - { - LNA_GAIN(0x05, 0x1F), - AIR_CABLE1_INPUT_SELECTOR(0x05, 0x60), - LOOP_THROUGH(0x05, 0x80), - CABLE2_INPUT_SELECTOR(0x06, 0x08), - FILTER_GAIN(0x06, 0x30), - PRE_DETECT(0x06, 0x40), - MIXER_GAIN(0x07, 0x1F), - IMAGE_REVERSE(0x07, 0x80), - UNKNOWN_REGISTER_8(0x08, 0x3F), - UNKNOWN_REGISTER_9(0x09, 0x3F), - FILTER_CALIBRATION_CODE(0x0A, 0x1F), - FILTER_CURRENT(0x0A, 0x60), - CALIBRATION_TRIGGER(0x0B, 0x10), - FILTER_CAPACITOR(0x0B, 0x60), - BANDWIDTH_FILTER_GAIN_HIGHPASS_FILTER_CORNER(0x0B, 0xEF), - XTAL_CHECK(0x0C, 0x0F), - VGA_GAIN(0x0C, 0x9F), - LNA_VTH_L(0x0D, 0x0), - MIXER_VTH_L(0x0E, 0x0), - CALIBRATION_CLOCK(0x0F, 0x04), - FILTER_EXTENSION_WIDEST(0x0F, 0x80), - PLL_XTAL_CAPACITOR(0x10, 0x03), - UNKNOWN_REGISTER_10(0x10, 0x04), - PLL_XTAL_CAPACITOR_AND_DRIVE(0x10, 0x0B), - REFERENCE_DIVIDER_2(0x10, 0x10), - CAPACITOR_SELECTOR(0x10, 0x1B), - DIVIDER(0x10, 0xE0), - CP_CUR(0x11, 0x38), - SIGMA_DELTA_MODULATOR_POWER(0x12, 0x08), - VCO_CURRENT(0x12, 0xE0), - VERSION(0x13, 0x3F), - PLL(0x14, 0x0), - SIGMA_DELTA_MODULATOR_LSB(0x15, 0x0), - SIGMA_DELTA_MODULATOR_MSB(0x16, 0x0), - DRAIN(0x17, 0x08), - DIVIDER_BUFFER_CURRENT(0x17, 0x30), - RF_POLY_FILTER_CURRENT(0x19, 0x60), - PLL_AUTOTUNE(0x1A, 0x0C), - PLL_AUTOTUNE_VARIANT(0x1A, 0x08), - AGC_CLOCK(0x1A, 0x30), - RF_POLY_MUX(0x1A, 0xC3), - TF_BAND(0x1B, 0x0), - MIXER_TOP(0x1C, 0xF8), - MIXER_TOP2(0X1C, 0x04), - LNA_TOP(0x1D, 0x38), - LNA_TOP2(0x1D, 0xC7), - CHANNEL_FILTER_EXTENSION(0x1E, 0x60), - LNA_DISCHARGE_CURRENT(0x1E, 0x1F), - LOOP_THROUGH_ATTENUATION(0x1F, 0x80); - - private int mRegister; - private int mMask; - - private Register(int register, int mask) - { - mRegister = register; - mMask = mask; - } - - public int getRegister() - { - return mRegister; - } - - public byte getMask() - { - return (byte) mMask; - } - - public boolean isMasked() - { - return mMask != 0; - } - } - - public enum FrequencyRange - { - RANGE_024(24000000, 49999999, 0x08, 0x02, 0xDF, 0x02, 0x01), - RANGE_050(50000000, 54999999, 0x08, 0x02, 0xBE, 0x02, 0x01), - RANGE_055(55000000, 59999999, 0x08, 0x02, 0x8B, 0x02, 0x01), - RANGE_060(60000000, 64999999, 0x08, 0x02, 0x7B, 0x02, 0x01), - RANGE_065(65000000, 69999999, 0x08, 0x02, 0x69, 0x02, 0x01), - RANGE_070(70000000, 74999999, 0x08, 0x02, 0x58, 0x02, 0x01), - RANGE_075(75000000, 79999999, 0x00, 0x02, 0x44, 0x02, 0x01), - RANGE_080(80000000, 89999999, 0x00, 0x02, 0x44, 0x02, 0x01), - RANGE_090(90000000, 99999999, 0x00, 0x02, 0x34, 0x01, 0x01), - RANGE_100(100000000, 109999999, 0x00, 0x02, 0x34, 0x01, 0x01), - RANGE_110(110000000, 119999999, 0x00, 0x02, 0x24, 0x01, 0x01), - RANGE_120(120000000, 139999999, 0x00, 0x02, 0x24, 0x01, 0x01), - RANGE_140(140000000, 179999999, 0x00, 0x02, 0x14, 0x01, 0x01), - RANGE_180(180000000, 219999999, 0x00, 0x02, 0x13, 0x00, 0x00), - RANGE_220(220000000, 249999999, 0x00, 0x02, 0x13, 0x00, 0x00), - RANGE_250(250000000, 279999999, 0x00, 0x02, 0x11, 0x00, 0x00), - RANGE_280(280000000, 309999999, 0x00, 0x02, 0x00, 0x00, 0x00), - RANGE_310(310000000, 449999999, 0x00, 0x41, 0x00, 0x00, 0x00), - RANGE_450(450000000, 587999999, 0x00, 0x41, 0x00, 0x00, 0x00), - RANGE_588(588000000, 649999999, 0x00, 0x40, 0x00, 0x00, 0x00), - RANGE_650(650000000, 1766000000, 0x00, 0x40, 0x00, 0x00, 0x00), - RANGE_UNK(0, 0, 0, 0, 0, 0, 0); - - private long mMinFrequency; - private long mMaxFrequency; - private int mOpenDrain; - private int mRFMux_PolyMux; - private int mTF_c; - private int mXtalCap20p; - private int mXtalCap10p; - - private FrequencyRange(long minFrequency, - long maxFrequency, - int openDrain, - int rfMuxPloy, - int tf_c, - int xtalCap20p, - int xtalCap10p) - { - mMinFrequency = minFrequency; - mMaxFrequency = maxFrequency; - mOpenDrain = openDrain; - mRFMux_PolyMux = rfMuxPloy; - mTF_c = tf_c; - mXtalCap20p = xtalCap20p; - mXtalCap10p = xtalCap10p; - } - - public boolean contains(long frequency) - { - return mMinFrequency <= frequency && frequency <= mMaxFrequency; - } - - public static FrequencyRange getRangeForFrequency(long frequency) - { - for(FrequencyRange range : values()) - { - if(range.contains(frequency)) - { - return range; - } - } - - return RANGE_UNK; - } - - public long getMinFrequency() - { - return mMinFrequency; - } - - public long getMaxFrequency() - { - return mMaxFrequency; - } - - public byte getOpenDrain() - { - return (byte) mOpenDrain; - } - - public byte getRFMuxPolyMux() - { - return (byte) mRFMux_PolyMux; - } - - public byte getTFC() - { - return (byte) mTF_c; - } - - public byte getXTALCap20P() - { - return (byte) mXtalCap20p; - } - - public byte getXTALCap10P() - { - return (byte) mXtalCap10p; - } - - public byte getXTALLowCap0P() - { - return (byte) 0x08; - } - - public byte getXTALHighCap0P() - { - return (byte) 0x00; - } - } - - /** - * Frequency Divider Ranges - * - * Actual Tuned Frequency Ranges (after subtracting IF = 3.57 MHz ) - * - * Divider 0: 860.43 to 1782.03 MHz - * Divider 1: 428.43 to 889.23 MHz - * Divider 2: 212.43 to 457.23 MHz - * Divider 3: 104.43 to 219.63 MHz - * Divider 4: 50.43 to 108.03 MHz - * Divider 5: 23.43 to 52.23 MHz - * Divider 6: 9.93 to 24.33 MHz - * Divider 7: 3.18 to 10.38 MHz - */ - public enum FrequencyDivider - { - DIVIDER_0(0, 2, 864000000, 1785600000, 0x00, 28800000), - DIVIDER_1(1, 4, 432000000, 892800000, 0x20, 14400000), - DIVIDER_2(2, 8, 216000000, 460800000, 0x40, 7200000), - DIVIDER_3(3, 16, 108000000, 223200000, 0x60, 3600000), - DIVIDER_4(4, 32, 54000000, 111600000, 0x80, 1800000), - DIVIDER_5(5, 64, 27000000, 55800000, 0xA0, 900000), - DIVIDER_6(6, 128, 13500000, 27900000, 0xC0, 450000), - DIVIDER_7(7, 256, 6750000, 13950000, 0xE0, 225000); - - private int mDividerNumber; - private int mMixerDivider; - private long mMinimumFrequency; - private long mMaximumFrequency; - private int mRegisterSetting; - private int mIntegralValue; - private static final int mVCOPowerReference = 2; - - private FrequencyDivider(int dividerNumber, - int mixerDivider, - long minimumFrequency, - long maximumFrequency, - int registerSetting, - int integralValue) - { - mDividerNumber = dividerNumber; - mMixerDivider = mixerDivider; - mMinimumFrequency = minimumFrequency; - mMaximumFrequency = maximumFrequency; - mRegisterSetting = registerSetting; - mIntegralValue = integralValue; - } - - public int getDividerNumber(int vcoFineTune) - { - if(vcoFineTune == mVCOPowerReference) - { - return mDividerNumber; - } - else if(vcoFineTune < mVCOPowerReference) - { - return mDividerNumber - 1; - } - else if(vcoFineTune > mVCOPowerReference) - { - return mDividerNumber + 1; - } - - return mDividerNumber; - } - - public int getMixerDivider() - { - return mMixerDivider; - } - - public long getMinimumFrequency() - { - return mMinimumFrequency; - } - - public long getMaximumFrequency() - { - return mMaximumFrequency; - } - - public byte getDividerRegisterSetting() - { - return (byte) mRegisterSetting; - } - - public boolean contains(long frequency) - { - return mMinimumFrequency <= frequency && - frequency <= mMaximumFrequency; - } - - /** - * Returns the correct frequency divider for the specified frequency, - * or returns divider #5, if the frequency is outside of the specified - * frequency ranges. - * - * @param frequency - desired frequency - * @return - FrequencyDivider to use for the specified frequency - */ - public static FrequencyDivider fromFrequency(long frequency) - { - for(FrequencyDivider divider : FrequencyDivider.values()) - { - if(divider.contains(frequency)) - { - return divider; - } - } - - return FrequencyDivider.DIVIDER_5; - } - - /** - * Returns the integral to use for this frequency and divider - */ - public Integral getIntegral(long frequency) - { - if(contains(frequency)) - { - int delta = (int) (frequency - mMinimumFrequency); - - int integral = (int) ((double) delta / (double) mIntegralValue); - - return Integral.fromValue(integral); - } - - throw new IllegalArgumentException("PLL frequency [" + frequency + - "] is not valid for this frequency divider " + this.toString()); - } - - /** - * Calculates the 16-bit value of the sigma-delta modulator setting - * which represents the fractional portion of the requested frequency - * that is left over after subtracting the divider minimum frequency - * and the integral frequency units. That residual value is divided - * by the integral unit value to derive a 16-bit fractional value, - * returned as an integer - */ - public int getSDM(Integral integral, long frequency) - { - if(contains(frequency)) - { - int delta = (int) (frequency - mMinimumFrequency - - (integral.getNumber() * mIntegralValue)); - - double fractional = (double) delta / (double) mIntegralValue; - - //Left shift the double value 16 bits and truncate to an integer - return (int) (fractional * 0x10000) & 0xFFFF; - } - - return 0; - } - } - - /** - * PLL Integral values. Each value represents one unit of the divided - * crystal frequency as follows: - * - * Divider Value - * 64 I * 0.9 MHz - * 32 I * 1.8 MHz - * 16 I * 3.6 MHz - * 8 I * 7.2 MHz - * 4 I * 14.4 MHz - * 2 I * 28.8 MHz - */ - public enum Integral - { - I00(0, 0x44), - I01(1, 0x84), - I02(2, 0xC4), - I03(3, 0x05), - I04(4, 0x45), - I05(5, 0x85), - I06(6, 0xC5), - I07(7, 0x06), - I08(8, 0x46), - I09(9, 0x86), - I10(10, 0xC6), - I11(11, 0x07), - I12(12, 0x47), - I13(13, 0x87), - I14(14, 0xC7), - I15(15, 0x08), - I16(16, 0x48), - I17(17, 0x88), - I18(18, 0xC8), - I19(19, 0x09), - I20(20, 0x49), - I21(21, 0x89), - I22(22, 0xC9), - I23(23, 0x0A), - I24(24, 0x4A), - I25(25, 0x8A), - I26(26, 0xCA), - I27(27, 0x0B), - I28(28, 0x4B), - I29(29, 0x8B), - I30(30, 0xCB), - I31(31, 0x0C); - - private int mNumber; - private int mRegister; - - private Integral(int number, int register) - { - mNumber = number; - mRegister = register; - } - - public int getNumber() - { - return mNumber; - } - - public byte getRegisterValue() - { - return (byte) mRegister; - } - - public static Integral fromValue(int value) - { - if(0 <= value && value <= 31) - { - return Integral.values()[value]; - } - - throw new IllegalArgumentException("PLL integral value [" + value + - "] must be in the range 0 - 31"); - } - } -} diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java deleted file mode 100644 index bf3cbf16..00000000 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ /dev/null @@ -1,559 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 source.tuner.rtl.r820t; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JSeparator; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import source.SourceException; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; -import source.tuner.configuration.TunerConfigurationEvent; -import source.tuner.configuration.TunerConfigurationEvent.Event; -import source.tuner.configuration.TunerConfigurationModel; -import source.tuner.rtl.RTL2832Tuner; -import source.tuner.rtl.RTL2832TunerController.Descriptor; -import source.tuner.rtl.RTL2832TunerController.SampleRate; -import source.tuner.rtl.r820t.R820TTunerController.R820TGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; - -public class R820TTunerEditor extends TunerConfigurationEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( R820TTunerEditor.class ); - - private static final long serialVersionUID = 1L; - private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; - - private R820TTunerController mController; - - private JTextField mConfigurationName; - private JButton mTunerInfo; - private JComboBox mComboSampleRate; - private JSpinner mFrequencyCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - private boolean mLoading; - - public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) - { - super( tunerConfigurationModel ); - mController = (R820TTunerController)tuner.getController(); - - init(); - } - - private R820TTunerConfiguration getConfiguration() - { - if( hasItem() ) - { - return (R820TTunerConfiguration)getItem(); - } - - return null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][][grow]" ) ); - - add( new JLabel( "R820T Tuner Configuration" ), "span,align center" ); - - mConfigurationName = new JTextField(); - mConfigurationName.setEnabled( false ); - mConfigurationName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span 2" ); - - mTunerInfo = new JButton( "Tuner Info" ); - mTunerInfo.setEnabled( false ); - mTunerInfo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, getTunerInfo(), - "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); - } - } ); - add( mTunerInfo ); - - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.setEnabled( false ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - mFrequencyCorrection.setEnabled( false ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.getFrequencyController().setFrequencyCorrection( value ); - } - catch ( SourceException e1 ) - { - mLog.error( "Error setting frequency correction value", e1 ); - } - - save(); - } - } ); - - add( new JLabel( "PPM:" ) ); - add( mFrequencyCorrection ); - - add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - - /** - * Gain Controls - */ - add( new JLabel( "Gain" ), "wrap" ); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox( R820TGain.values() ); - mComboMasterGain.setEnabled( false ); - mComboMasterGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (R820TGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == R820TGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - mComboLNAGain.setEnabled( true ); - - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - mComboVGAGain.setEnabled( true ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - - mComboVGAGain.setEnabled( false ); - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "gain setting - ", e ); - } - } - } ); - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
independent control of " - + "Mixer, LNA and Enhance gain
" - + "settings, or one of the individual gain settings for
" - + "semi-manual gain control" ); - add( new JLabel( "Master:" ) ); - add( mComboMasterGain ); - - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox( R820TMixerGain.values() ); - mComboMixerGain.setEnabled( false ); - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TMixerGain mixerGain = - (R820TMixerGain)mComboMixerGain.getSelectedItem(); - - if( mixerGain == null ) - { - mixerGain = DEFAULT_GAIN.getMixerGain(); - } - - if( mComboMixerGain.isEnabled() ) - { - mController.setMixerGain( mixerGain, true ); - } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply mixer " - + "gain setting - ", e ); - } - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - - add( new JLabel( "Mixer:" ) ); - add( mComboMixerGain ); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( R820TLNAGain.values() ); - mComboLNAGain.setEnabled( false ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TLNAGain lnaGain = - (R820TLNAGain)mComboLNAGain.getSelectedItem(); - - if ( lnaGain == null ) - { - lnaGain = DEFAULT_GAIN.getLNAGain(); - } - - if( mComboLNAGain.isEnabled() ) - { - mController.setLNAGain( lnaGain, true ); - } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - - add( new JLabel( "LNA:" ) ); - add( mComboLNAGain ); - - /* VGA Gain Control */ - mComboVGAGain = new JComboBox( R820TVGAGain.values() ); - mComboVGAGain.setEnabled( false ); - mComboVGAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TVGAGain vgaGain = - (R820TVGAGain)mComboVGAGain.getSelectedItem(); - - if( vgaGain == null ) - { - vgaGain = DEFAULT_GAIN.getVGAGain(); - } - - if( mComboVGAGain.isEnabled() ) - { - mController.setVGAGain( vgaGain, true ); - } - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the VGA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply VGA " - + "gain setting", e ); - } - } - } ); - mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - add( new JLabel( "VGA:" ) ); - add( mComboVGAGain ); - } - - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mComboSampleRate.isEnabled() != enabled ) - { - mComboSampleRate.setEnabled( enabled ); - } - - if( mComboMasterGain.isEnabled() != enabled ) - { - mComboMasterGain.setEnabled( enabled ); - } - - if( mComboMixerGain.isEnabled() != enabled ) - { - mComboMixerGain.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboVGAGain.isEnabled() != enabled ) - { - mComboVGAGain.setEnabled( enabled ); - } - } - - private String getTunerInfo() - { - StringBuilder sb = new StringBuilder(); - - Descriptor descriptor = mController.getDescriptor(); - - sb.append( "

RTL-2832 with R820T Tuner

" ); - - if( descriptor == null ) - { - sb.append( "No EEPROM Descriptor Available" ); - } - else - { - sb.append( "USB ID: " ); - sb.append( descriptor.getVendorID() ); - sb.append( ":" ); - sb.append( descriptor.getProductID() ); - sb.append( "
" ); - - sb.append( "Vendor: " ); - sb.append( descriptor.getVendorLabel() ); - sb.append( "
" ); - - sb.append( "Product: " ); - sb.append( descriptor.getProductLabel() ); - sb.append( "
" ); - - sb.append( "Serial: " ); - sb.append( descriptor.getSerial() ); - sb.append( "
" ); - - sb.append( "IR Enabled: " ); - sb.append( descriptor.irEnabled() ); - sb.append( "
" ); - - sb.append( "Remote Wake: " ); - sb.append( descriptor.remoteWakeupEnabled() ); - sb.append( "
" ); - } - - return sb.toString(); - } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - R820TTunerConfiguration config = getConfiguration(); - - config.setName( mConfigurationName.getText() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); - - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - config.setMasterGain( gain ); - - R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); - config.setMixerGain( mixerGain ); - - R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); - config.setLNAGain( lnaGain ); - - R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); - config.setVGAGain( vgaGain ); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); - } - } - - @Override - public void setItem( TunerConfiguration tunerConfiguration ) - { - super.setItem( tunerConfiguration ); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if( hasItem() ) - { - R820TTunerConfiguration config = getConfiguration(); - - if( tunerConfiguration.isAssigned() ) - { - setControlsEnabled( true ); - mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mComboMasterGain.setSelectedItem( config.getMasterGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboVGAGain.setSelectedItem( config.getVGAGain() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - - mLoading = false; - } -} diff --git a/src/source/tuner/usb/USBTransferProcessor.java b/src/source/tuner/usb/USBTransferProcessor.java deleted file mode 100644 index 60d6642a..00000000 --- a/src/source/tuner/usb/USBTransferProcessor.java +++ /dev/null @@ -1,434 +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 source.tuner.usb; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.DeviceHandle; -import org.usb4java.LibUsb; -import org.usb4java.LibUsbException; -import org.usb4java.Transfer; -import org.usb4java.TransferCallback; -import sample.Broadcaster; -import sample.Listener; -import sample.OverflowableTransferQueue; -import sample.adapter.ISampleAdapter; -import sample.complex.ComplexBuffer; -import sample.real.IOverflowListener; -import source.tuner.TunerManager; -import util.ThreadPool; - -import javax.usb.UsbException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -public class USBTransferProcessor implements TransferCallback -{ - private final static Logger mLog = LoggerFactory.getLogger(USBTransferProcessor.class); - - private static final byte USB_BULK_TRANSFER_ENDPOINT = (byte) 0x81; - private static final long USB_TIMEOUT_MS = 2000l; //milliseconds - - //Number of native byte buffers to allocate for transferring data from the USB device - private static final int TRANSFER_BUFFER_POOL_SIZE = 16; - - //Maximum number of filled buffers for the blocking queue - private static final int FILLED_BUFFER_MAX_CAPACITY = 500; - - //Threshold for resetting buffer overflow condition - private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 100; - - private String mDeviceName; - - //Handle to the USB bulk transfer device - private DeviceHandle mDeviceHandle; - - //Tuner format-specific byte to IQ float sample converter - private ISampleAdapter mSampleAdapter; - - //Byte array transfer buffers size in bytes - private int mBufferSize; - - private OverflowableTransferQueue mFilledBuffers; - private LinkedTransferQueue mAvailableTransfers = new LinkedTransferQueue<>(); - private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); - private Listener mComplexBufferListener; - - private AtomicBoolean mRunning = new AtomicBoolean(); - - private ByteBuffer mLibUsbHandlerStatus = ByteBuffer.allocateDirect(4); - - private BufferDispatcher mBufferDispatcher = new BufferDispatcher(); - private ScheduledFuture mBufferDispatcherFuture; - - /** - * Manages stream of USB transfer buffers and converts buffers to complex buffer samples for distribution to - * any registered listeners. - * - * @param deviceName to use when logging information or errors - * @param deviceHandle to the USB bulk transfer device - * @param sampleAdapter specific to the tuner's byte buffer format for converting to floating point I/Q samples - * @param bufferSize in bytes. Should be a multiple of two: 65536, 131072 or 262144. - */ - public USBTransferProcessor(String deviceName, - DeviceHandle deviceHandle, - ISampleAdapter sampleAdapter, - int bufferSize) - { - mDeviceName = deviceName; - mDeviceHandle = deviceHandle; - mSampleAdapter = sampleAdapter; - mBufferSize = bufferSize; - - mFilledBuffers = new OverflowableTransferQueue<>(FILLED_BUFFER_MAX_CAPACITY, FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD); - mFilledBuffers.setOverflowListener(new IOverflowListener() - { - @Override - public void sourceOverflow(boolean overflow) - { - if(overflow) - { - mLog.debug(mDeviceName + " - buffer overflow - temporary pause until processing catches up"); - } - else - { - mLog.debug(mDeviceName + " - buffer overflow cleared - resuming normal processing"); - } - } - }); - } - - /** - * Start USB transfer buffer processing. Subsequent calls to this method after started will be ignored. - */ - private void start() - { - if(mRunning.compareAndSet(false, true)) - { - prepareDeviceStart(); - - prepareTransfers(); - - while(!mAvailableTransfers.isEmpty()) - { - Transfer transfer = mAvailableTransfers.poll(); - - if(transfer != null) - { - int result = LibUsb.submitTransfer(transfer); - - if(result == LibUsb.SUCCESS) - { - mTransfersInProgress.add(transfer); - } - else if(result == LibUsb.ERROR_PIPE) - { - int resetResult = LibUsb.clearHalt(mDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); - - if(resetResult == LibUsb.SUCCESS) - { - int resubmitResult = LibUsb.submitTransfer(transfer); - - if(resubmitResult == LibUsb.SUCCESS) - { - mTransfersInProgress.add(transfer); - } - else - { - mLog.error(mDeviceName + " - error resubmitting transfer after endpoint clear halt"); - } - } - else - { - mLog.error(mDeviceName + " - unable to clear device endpoint halt"); - } - } - else - { - //TODO: broadcast to each listener that this source has an error and is shutting down - mLog.error(mDeviceName + "- error submitting transfer [" + LibUsb.errorName(result) + "]"); - } - } - } - - //Start transferred buffer dispatcher - mBufferDispatcherFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mBufferDispatcher, - 0, 11, TimeUnit.MILLISECONDS); - - //Register with LibUSB processor so that it auto-starts LibUSB processing - TunerManager.LIBUSB_TRANSFER_PROCESSOR.registerTransferProcessor(this); - } - } - - /** - * Stop USB transfer buffer processing. Subsequent calls to this method after stopped will be ignored. - */ - private void stop() - { - if(mRunning.compareAndSet(true, false)) - { - mBufferDispatcherFuture.cancel(true); - - mFilledBuffers.clear(); - - //Cancel the lib usb process timer - for(Transfer transfer : mTransfersInProgress) - { - LibUsb.cancelTransfer(transfer); - } - - - //Unregister from LibUSB processor so that it auto-stops LibUSB processing - TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(this); - - //Directly invoke the timeout handler to ensure that our cancelled transfer buffers are flushed. - int result = LibUsb.handleEventsTimeoutCompleted(null, USB_TIMEOUT_MS, - mLibUsbHandlerStatus.asIntBuffer()); - - if(result != LibUsb.SUCCESS) - { - mLog.error(mDeviceName + " - error while cancelling transfer buffers during shutdown/pause - error code:" + result); - } - - mLibUsbHandlerStatus.rewind(); - - executeDeviceStop(); - } - } - - /** - * Allows sub-class implementations to execute any device-specific operations to prepare for starting USB transfers - */ - protected void prepareDeviceStart() - { - - } - - /** - * Allows sub-class implementations to execute any device-specific operations after stopping USB transfers - */ - protected void executeDeviceStop() - { - - } - - /** - * Sets the listener and auto-starts the buffer processor - */ - public void setListener(Listener listener) - { - mComplexBufferListener = listener; - start(); - } - - /** - * Auto-stops the buffer processor and removes the listener - */ - public void removeListener() - { - if(mComplexBufferListener != null) - { - stop(); - mComplexBufferListener = null; - } - } - - /** - * Prepares (allocates) a set of transfer buffers for use in transferring data from the USB device via the bulk - * interface. Since we're using direct allocation (native), buffers are retained and reused across multiple - * start/stop cycles. - */ - private void prepareTransfers() throws LibUsbException - { - while(mAvailableTransfers.size() < TRANSFER_BUFFER_POOL_SIZE) - { - Transfer transfer = LibUsb.allocTransfer(); - - if(transfer == null) - { - throw new LibUsbException("Couldn't allocate USB transfer buffer", LibUsb.ERROR_NO_MEM); - } - - final ByteBuffer buffer = ByteBuffer.allocateDirect(mBufferSize); - - LibUsb.fillBulkTransfer(transfer, mDeviceHandle, USB_BULK_TRANSFER_ENDPOINT, buffer, this, - "Buffer", USB_TIMEOUT_MS); - - mAvailableTransfers.add(transfer); - } - } - - /** - * Process a filled transfer buffer received back from the USB device - */ - @Override - public void processTransfer(Transfer transfer) - { - mTransfersInProgress.remove(transfer); - - switch(transfer.status()) - { - case LibUsb.TRANSFER_COMPLETED: - case LibUsb.TRANSFER_STALL: - case LibUsb.TRANSFER_TIMED_OUT: - if(transfer.actualLength() > 0) - { - ByteBuffer buffer = transfer.buffer(); - - byte[] data = new byte[transfer.actualLength()]; - buffer.get(data); - buffer.rewind(); - - if(mRunning.get()) - { - mFilledBuffers.offer(data); - } - } - break; - case LibUsb.TRANSFER_CANCELLED: - break; - default: - //Unexpected transfer error - mLog.error(mDeviceName + " - transfer error [" + getTransferStatus(transfer.status()) + - "] transferred actual: " + transfer.actualLength()); - } - - - if(mRunning.get()) - { - int result = LibUsb.submitTransfer(transfer); - - if(result == LibUsb.SUCCESS) - { - mTransfersInProgress.add(transfer); - } - else if(result == LibUsb.ERROR_PIPE) - { - int resetResult = LibUsb.clearHalt(mDeviceHandle, USB_BULK_TRANSFER_ENDPOINT); - - if(resetResult == LibUsb.SUCCESS) - { - int resubmitResult = LibUsb.submitTransfer(transfer); - - if(resubmitResult == LibUsb.SUCCESS) - { - mTransfersInProgress.add(transfer); - } - else - { - mLog.error(mDeviceName + " - error resubmitting transfer after endpoint clear halt"); - } - } - else - { - mLog.error(mDeviceName + " - unable to clear device endpoint halt"); - } - } - else - { - mAvailableTransfers.add(transfer); - mLog.error(mDeviceName + " - error submitting transfer [" + LibUsb.errorName(result) + "]"); - - if(mTransfersInProgress.isEmpty()) - { - mLog.warn(mDeviceName + " - all transfer buffer processing is stopped"); - //TODO: no transfers are in progress ... need to alert the user and the registered complex - //TODO: buffer listeners so that they can respond accordingly - } - } - } - else - { - //We're stopping - park the transfer buffers - if(!mAvailableTransfers.contains(transfer)) - { - mAvailableTransfers.add(transfer); - } - } - } - - /** - * Converts the USB transfer status number into a descriptive label - */ - public static String getTransferStatus(int status) - { - switch(status) - { - case 0: - return "TRANSFER COMPLETED (0)"; - case 1: - return "TRANSFER ERROR (1)"; - case 2: - return "TRANSFER TIMED OUT (2)"; - case 3: - return "TRANSFER CANCELLED (3)"; - case 4: - return "TRANSFER STALL (4)"; - case 5: - return "TRANSFER NO DEVICE (5)"; - case 6: - return "TRANSFER OVERFLOW (6)"; - default: - return "UNKNOWN TRANSFER STATUS (" + status + ")"; - } - } - - /** - * Fetches byte[] chunks from the raw sample buffer. Converts each byte - * array and broadcasts the array to all registered listeners - */ - public class BufferDispatcher implements Runnable - { - private List mBuffersToDispatch = new ArrayList<>(); - - @Override - public void run() - { - mFilledBuffers.drainTo(mBuffersToDispatch, 50); - - while(!mBuffersToDispatch.isEmpty()) - { - try - { - for(byte[] buffer : mBuffersToDispatch) - { - float[] complexSamples = mSampleAdapter.convert(buffer); - - if(mComplexBufferListener != null) - { - mComplexBufferListener.receive(new ComplexBuffer(complexSamples)); - } - } - } - catch(Exception e) - { - mLog.error(mDeviceName + " - error while dispatching complex IQ buffer samples", e); - } - - mBuffersToDispatch.clear(); - mFilledBuffers.drainTo(mBuffersToDispatch, 50); - } - } - } -} diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java deleted file mode 100644 index 5c76c847..00000000 --- a/src/source/wave/ComplexWaveSource.java +++ /dev/null @@ -1,285 +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 source.wave; - -import source.heartbeat.Heartbeat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.ConversionUtils; -import sample.Listener; -import sample.complex.ComplexBuffer; -import source.ComplexSource; -import source.IControllableFileSource; -import source.IFrameLocationListener; -import source.SourceEvent; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.UnsupportedAudioFileException; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.ScheduledExecutorService; - -public class ComplexWaveSource extends ComplexSource implements IControllableFileSource -{ - private final static Logger mLog = - LoggerFactory.getLogger(ComplexWaveSource.class); - - private IFrameLocationListener mFrameLocationListener; - private int mBytesPerFrame; - private int mFrameCounter = 0; - private long mFrequency = 0; - private Listener mListener; - private AudioInputStream mInputStream; - private File mFile; - - public ComplexWaveSource(File file) throws IOException - { - mFile = file; - } - - @Override - public void setSourceEventListener(Listener listener) - { - //Not implemented - } - - @Override - public void removeSourceEventListener() - { - //Not implemented - } - - @Override - public Listener getSourceEventListener() - { - //Not implemented - return null; - } - - @Override - public void reset() - { - stop(); - start(); - } - - @Override - public void start() - { - try - { - open(); - } - catch(IOException | UnsupportedAudioFileException e) - { - mLog.error("Error starting complex wave source"); - } - } - - @Override - public void stop() - { - try - { - close(); - } - catch(IOException e) - { - mLog.error("Error stopping complex wave source"); - } - } - - @Override - public long getFrameCount() throws IOException - { - // TODO Auto-generated method stub - return 0; - } - - @Override - public double getSampleRate() - { - if(mInputStream != null) - { - return mInputStream.getFormat().getSampleRate(); - } - - return 0; - } - - /** - * Returns the frequency set for this file. Normally returns zero, but - * the value can be set with setFrequency() method. - */ - public long getFrequency() - { - return mFrequency; - } - - /** - * Changes the value returned from getFrequency() for this source. - */ - public void setFrequency(long frequency) - { - mFrequency = frequency; - } - - /** - * Closes the source file - */ - public void close() throws IOException - { - if(mInputStream != null) - { - mInputStream.close(); - } - else - { - throw new IOException("Can't close wave source - was not opened"); - } - - mInputStream = null; - } - - /** - * Opens the source file for reading - */ - public void open() throws IOException, UnsupportedAudioFileException - { - if(mInputStream == null) - { - mInputStream = AudioSystem.getAudioInputStream(mFile); - } - else - { - throw new IOException("Can't open wave source - is already opened"); - } - - - AudioFormat format = mInputStream.getFormat(); - - mBytesPerFrame = format.getFrameSize(); - - if(format.getChannels() != 2 || format.getSampleSizeInBits() != 16) - { - throw new IOException("Unsupported Wave Format - EXPECTED: 2 " + - "channels 16-bit samples FOUND: " + - mInputStream.getFormat().getChannels() + " channels " + - mInputStream.getFormat().getSampleSizeInBits() + "-bit samples"); - } - - /* Broadcast that we're at frame location 0 */ - broadcast(0); - } - - /** - * Reads the number of frames and sends a buffer to the listener - */ - @Override - public void next(int frames) throws IOException - { - next(frames, true); - } - - /** - * Reads the number of frames and optionally sends the buffer to the listener - */ - public void next(int frames, boolean broadcast) throws IOException - { - if(mInputStream != null) - { - byte[] buffer = new byte[mBytesPerFrame * frames]; - - /* Fill the buffer with samples from the file */ - int samplesRead = mInputStream.read(buffer); - - mFrameCounter += samplesRead; - - broadcast(mFrameCounter); - - if(broadcast && mListener != null) - { - if(samplesRead < buffer.length) - { - buffer = Arrays.copyOf(buffer, samplesRead); - } - - float[] samples = ConversionUtils - .convertFromSigned16BitSamples(buffer); - - mListener.receive(new ComplexBuffer(samples)); - } - } - } - - /** - * Registers the listener to receive sample buffers as they are read from - * the wave file - */ - @Override - public void setListener(Listener listener) - { - mListener = listener; - } - - /** - * Unregisters the listener from receiving sample buffers - */ - public void removeListener(Listener listener) - { - mListener = null; - } - - @Override - public void dispose() - { - mListener = null; - } - - @Override - public File getFile() - { - return mFile; - } - - private void broadcast(int byteLocation) - { - int frameLocation = (int)(byteLocation / mBytesPerFrame); - - if(mFrameLocationListener != null) - { - mFrameLocationListener.frameLocationUpdated(frameLocation); - } - } - - @Override - public void setListener(IFrameLocationListener listener) - { - mFrameLocationListener = listener; - } - - @Override - public void removeListener(IFrameLocationListener listener) - { - mFrameLocationListener = null; - } -} diff --git a/src/source/wave/RealWaveSource.java b/src/source/wave/RealWaveSource.java deleted file mode 100644 index c0698e6a..00000000 --- a/src/source/wave/RealWaveSource.java +++ /dev/null @@ -1,290 +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 source.wave; - -import source.heartbeat.Heartbeat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sample.ConversionUtils; -import sample.Listener; -import sample.real.RealBuffer; -import source.IControllableFileSource; -import source.IFrameLocationListener; -import source.RealSource; -import source.SourceEvent; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.UnsupportedAudioFileException; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.concurrent.ScheduledExecutorService; - -public class RealWaveSource extends RealSource implements IControllableFileSource, AutoCloseable -{ - private final static Logger mLog = LoggerFactory.getLogger(RealWaveSource.class); - - private IFrameLocationListener mFrameLocationListener; - private int mBytesPerFrame; - private int mFrameCounter = 0; - private long mFrequency = 0; - private Listener mListener; - private AudioInputStream mInputStream; - private File mFile; - - public RealWaveSource(File file) throws IOException - { - mFile = file; - } - - @Override - public void setSourceEventListener(Listener listener) - { - //Not implemented - } - - @Override - public void removeSourceEventListener() - { - //Not implemented - } - - @Override - public Listener getSourceEventListener() - { - //Not implemented - return null; - } - - @Override - public void reset() - { - stop(); - start(); - } - - - @Override - public void start() - { - try - { - open(); - } - catch(IOException | UnsupportedAudioFileException e) - { - mLog.error("Error starting real wave source", e); - } - } - - - @Override - public void stop() - { - try - { - close(); - } - catch(IOException e) - { - mLog.error("Error stopping real wave source", e); - } - } - - - @Override - public long getFrameCount() throws IOException - { - return 0; - } - - @Override - public double getSampleRate() - { - if(mInputStream != null) - { - return mInputStream.getFormat().getSampleRate(); - } - - return 0; - } - - /** - * Returns the frequency set for this file. Normally returns zero, but - * the value can be set with setFrequency() method. - */ - public long getFrequency() - { - return mFrequency; - } - - /** - * Changes the value returned from getFrequency() for this source. - */ - public void setFrequency(long frequency) - { - mFrequency = frequency; - } - - /** - * Closes the source file - */ - public void close() throws IOException - { - if(mInputStream != null) - { - mInputStream.close(); - } - else - { - throw new IOException("Can't close wave source - was not opened"); - } - - mInputStream = null; - } - - /** - * Opens the source file for reading - */ - public void open() throws IOException, UnsupportedAudioFileException - { - if(mInputStream == null) - { - mInputStream = AudioSystem.getAudioInputStream(mFile); - } - else - { - throw new IOException("Can't open wave source - is already opened"); - } - - - AudioFormat format = mInputStream.getFormat(); - - mBytesPerFrame = format.getFrameSize(); - - if(format.getChannels() != 1 || format.getSampleSizeInBits() != 16) - { - throw new IOException("Unsupported Wave Format - EXPECTED: 1 " + - "channel 16-bit samples FOUND: " + - mInputStream.getFormat().getChannels() + " channels " + - mInputStream.getFormat().getSampleSizeInBits() + "-bit samples"); - } - - /* Broadcast that we're at frame location 0 */ - broadcast(0); - } - - /** - * Reads the number of frames and sends a buffer to the listener - */ - @Override - public void next(int frames) throws IOException - { - next(frames, true); - } - - /** - * Reads the number of frames and optionally sends the buffer to the listener - */ - public void next(int frames, boolean broadcast) throws IOException - { - if(mInputStream != null) - { - byte[] buffer = new byte[mBytesPerFrame * frames]; - - /* Fill the buffer with samples from the file */ - int samplesRead = mInputStream.read(buffer); - - mFrameCounter += samplesRead; - - broadcast(mFrameCounter); - - if(broadcast && mListener != null) - { - if(samplesRead < buffer.length) - { - if(samplesRead == -1) - { - throw new IOException("End of recording"); - } - - buffer = Arrays.copyOf(buffer, samplesRead); - } - - float[] samples = ConversionUtils - .convertFromSigned16BitSamples(buffer); - - mListener.receive(new RealBuffer(samples)); - } - } - } - - /** - * Registers the listener to receive sample buffers as they are read from - * the wave file - */ - @Override - public void setListener(Listener listener) - { - mListener = listener; - } - - /** - * Unregisters the listener from receiving sample buffers - */ - public void removeListener(Listener listener) - { - mListener = null; - } - - @Override - public void dispose() - { - mListener = null; - } - - @Override - public File getFile() - { - return mFile; - } - - private void broadcast(int byteLocation) - { - int frameLocation = (int)(byteLocation / mBytesPerFrame); - - if(mFrameLocationListener != null) - { - mFrameLocationListener.frameLocationUpdated(frameLocation); - } - } - - @Override - public void setListener(IFrameLocationListener listener) - { - mFrameLocationListener = listener; - } - - @Override - public void removeListener(IFrameLocationListener listener) - { - mFrameLocationListener = null; - } -} diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java deleted file mode 100644 index 3e280c60..00000000 --- a/src/spectrum/ChannelSpectrumPanel.java +++ /dev/null @@ -1,489 +0,0 @@ -package spectrum; - -import java.awt.Component; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.swing.JLayeredPane; -import javax.swing.JMenu; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.SwingUtilities; - -import module.ProcessingChain; -import net.miginfocom.swing.MigLayout; -import sample.Listener; -import sample.SampleType; -import sample.complex.ComplexBuffer; -import sample.real.RealBuffer; -import settings.ColorSetting.ColorSettingName; -import settings.ColorSettingMenuItem; -import settings.Setting; -import settings.SettingChangeListener; -import settings.SettingsManager; -import source.SourceEvent; -import source.SourceEvent.Event; -import spectrum.converter.DFTResultsConverter; -import spectrum.converter.RealDecibelConverter; -import spectrum.menu.AveragingItem; -import spectrum.menu.DFTSizeItem; -import spectrum.menu.FFTWindowTypeItem; -import spectrum.menu.FrameRateItem; -import spectrum.menu.SmoothingItem; -import spectrum.menu.SmoothingTypeItem; -import controller.channel.Channel; -import controller.channel.ChannelEvent; -import controller.channel.ChannelEventListener; -import controller.channel.ChannelProcessingManager; -import dsp.filter.Filters; -import dsp.filter.Window.WindowType; -import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.filter.smoothing.SmoothingFilter.SmoothingType; - -public class ChannelSpectrumPanel extends JPanel - implements ChannelEventListener, - Listener, - SettingChangeListener, - SpectralDisplayAdjuster -{ - private static final long serialVersionUID = 1L; - - private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.REAL ); - private DFTResultsConverter mDFTConverter = new RealDecibelConverter(); - private JLayeredPane mLayeredPane; - private SpectrumPanel mSpectrumPanel; - private ChannelOverlayPanel mOverlayPanel; - - private Channel mCurrentChannel; - - private int mSampleBufferSize = 2400; - - private HalfBandFilter_RB_RB mDecimatingFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - - private AtomicBoolean mEnabled = new AtomicBoolean(); - - private SettingsManager mSettingsManager; - private ChannelProcessingManager mChannelProcessingManager; - - public ChannelSpectrumPanel( SettingsManager settingsManager, - ChannelProcessingManager channelProcessingManager ) - { - mSettingsManager = settingsManager; - mChannelProcessingManager = channelProcessingManager; - - if( mSettingsManager != null ) - { - mSettingsManager.addListener( this ); - } - - mSpectrumPanel = new SpectrumPanel( mSettingsManager ); - mSpectrumPanel.setAveraging( 1 ); - - mOverlayPanel = new ChannelOverlayPanel( mSettingsManager ); - mDFTProcessor.addConverter( mDFTConverter ); - mDFTConverter.addListener( mSpectrumPanel ); - - /* Set the DFTProcessor to the decimated 24kHz sample rate */ - mDFTProcessor.process(SourceEvent.sampleRateChange(24000.0)); - - initGui(); - } - - public void dispose() - { - setEnabled( false ); - - mDFTProcessor.dispose(); - - if( mSettingsManager != null ) - { - mSettingsManager.removeListener( this ); - } - - mSettingsManager = null; - mCurrentChannel = null; - mDFTProcessor = null; - mSpectrumPanel = null; - } - - public void setFrameRate( int framesPerSecond ) - { - mSampleBufferSize = (int)( 48000 / framesPerSecond ); - - mDFTProcessor.setFrameRate( framesPerSecond ); - } - - private void initGui() - { - setLayout( new MigLayout( "insets 0 0 0 0 ", - "[grow,fill]", - "[grow,fill]") ); - - mLayeredPane = new JLayeredPane(); - mLayeredPane.addComponentListener( new ResizeListener() ); - - MouseEventProcessor mouser = new MouseEventProcessor(); - - mOverlayPanel.addMouseListener( mouser ); - mOverlayPanel.addMouseMotionListener( mouser ); - - mLayeredPane.add( mSpectrumPanel, new Integer( 0 ), 0 ); - mLayeredPane.add( mOverlayPanel, new Integer( 1 ), 0 ); - - add( mLayeredPane ); - } - - public void setEnabled( boolean enabled ) - { - if( enabled && mEnabled.compareAndSet( false, true ) ) - { - start(); - } - else if( !enabled && mEnabled.compareAndSet( true, false ) ) - { - stop(); - } - } - - @Override - @SuppressWarnings( "incomplete-switch" ) - public void channelChanged( ChannelEvent event ) - { - switch( event.getEvent() ) - { - case NOTIFICATION_SELECTION_CHANGE: - //ChannelSelectionManager ensures that only 1 channel can be - //selected and any previously selected channel will be first - //deselected before we get a new selection event - if( event.getChannel().isSelected() ) - { - if( mCurrentChannel != null ) - { - stop(); - mCurrentChannel = null; - } - - mCurrentChannel = event.getChannel(); - - if( mEnabled.get() ) - { - start(); - } - } - else - { - stop(); - mCurrentChannel = null; - } - break; - case NOTIFICATION_PROCESSING_STOP: - if( event.getChannel() == mCurrentChannel ) - { - if( mEnabled.get() ) - { - stop(); - } - - mCurrentChannel = null; - } - break; - } - } - - private void start() - { - if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.getEnabled() ) - { - ProcessingChain processingChain = mChannelProcessingManager - .getProcessingChain( mCurrentChannel ); - - if( processingChain != null ) - { - processingChain.addRealBufferListener( this ); - - mDFTProcessor.start(); - } - } - } - - private void stop() - { - if( mCurrentChannel != null && mCurrentChannel.getEnabled() ) - { - ProcessingChain processingChain = mChannelProcessingManager - .getProcessingChain( mCurrentChannel ); - - if( processingChain != null ) - { - processingChain.removeRealBufferListener( this ); - } - } - - mDFTProcessor.stop(); - - mSpectrumPanel.clearSpectrum(); - } - - @Override - public void settingChanged( Setting setting ) - { - if( mSpectrumPanel != null ) - { - mSpectrumPanel.settingChanged( setting ); - } - if( mOverlayPanel != null ) - { - mOverlayPanel.settingChanged( setting ); - } - } - - @Override - public void settingDeleted( Setting setting ) - { - if( mSpectrumPanel != null ) - { - mSpectrumPanel.settingDeleted( setting ); - } - - if( mOverlayPanel != null ) - { - mOverlayPanel.settingDeleted( setting ); - } - } - - @Override - public void receive( RealBuffer buffer ) - { - RealBuffer decimated = mDecimatingFilter.filter( buffer ); - - //Hack: we're placing real samples in a complex buffer that the DFT - //processor is expecting. - mDFTProcessor.receive( new ComplexBuffer( decimated.getSamples() ) ); - } - - /** - * Monitors the sizing of the layered pane and resizes the spectrum and - * channel panels whenever the layered pane is resized - */ - public class ResizeListener implements ComponentListener - { - @Override - public void componentResized( ComponentEvent e ) - { - Component c = e.getComponent(); - - mSpectrumPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() ); - mOverlayPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() ); - } - - @Override - public void componentHidden( ComponentEvent arg0 ) {} - @Override - public void componentMoved( ComponentEvent arg0 ) {} - @Override - public void componentShown( ComponentEvent arg0 ) {} - } - - /** - * Mouse event handler for the channel panel. - */ - public class MouseEventProcessor implements MouseMotionListener, MouseListener - { - @Override - public void mouseMoved( MouseEvent event ) - { - update( event ); - } - @Override - public void mouseDragged( MouseEvent event ) - { - update( event ); - } - - private void update( MouseEvent event ) - { - if( event.getComponent() == mOverlayPanel ) - { - mOverlayPanel.setCursorLocation( event.getPoint() ); - } - } - - @Override - public void mouseEntered( MouseEvent e ) - { - if( e.getComponent() == mOverlayPanel ) - { - mOverlayPanel.setCursorVisible( true ); - } - } - - @Override - public void mouseExited( MouseEvent e ) - { - mOverlayPanel.setCursorVisible( false ); - } - - /** - * Displays the context menu. - */ - @Override - public void mouseClicked( MouseEvent event ) - { - if( SwingUtilities.isRightMouseButton( event ) ) - { - JPopupMenu contextMenu = new JPopupMenu(); - - /** - * Color Menus - */ - JMenu colorMenu = new JMenu( "Color" ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_CURSOR ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_LINE ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_BACKGROUND ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_GRADIENT_BOTTOM ) ); - - colorMenu.add( new ColorSettingMenuItem( mSettingsManager, - ColorSettingName.SPECTRUM_GRADIENT_TOP ) ); - - contextMenu.add( colorMenu ); - - /** - * Display items: fft and frame rate - */ - JMenu displayMenu = new JMenu( "Display" ); - contextMenu.add( displayMenu ); - - /** - * Averaging menu - */ - JMenu averagingMenu = new JMenu( "Averaging" ); - averagingMenu.add( - new AveragingItem( ChannelSpectrumPanel.this, 2 ) ); - displayMenu.add( averagingMenu ); - - /** - * FFT width - */ - JMenu fftWidthMenu = new JMenu( "FFT Width" ); - displayMenu.add( fftWidthMenu ); - - for( DFTSize width: DFTSize.values() ) - { - fftWidthMenu.add( new DFTSizeItem( mDFTProcessor, width ) ); - } - - /** - * DFT Processor Frame Rate - */ - JMenu frameRateMenu = new JMenu( "Frame Rate" ); - displayMenu.add( frameRateMenu ); - - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 14 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 16 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 18 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 20 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 25 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 30 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 40 ) ); - frameRateMenu.add( new FrameRateItem( mDFTProcessor, 50 ) ); - - /** - * FFT Window Type - */ - JMenu fftWindowType = new JMenu( "Window Type" ); - displayMenu.add( fftWindowType ); - - for( WindowType type: WindowType.values() ) - { - fftWindowType.add( - new FFTWindowTypeItem( mDFTProcessor, type ) ); - } - - /** - * Smoothing menu - */ - JMenu smoothingMenu = new JMenu( "Smoothing" ); - - if( mSpectrumPanel.getSmoothingType() != SmoothingType.NONE ) - { - smoothingMenu.add( new SmoothingItem( ChannelSpectrumPanel.this, 5 ) ); - smoothingMenu.add( new JSeparator() ); - } - - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.GAUSSIAN ) ); - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.TRIANGLE ) ); - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.RECTANGLE ) ); - smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.NONE ) ); - - displayMenu.add( smoothingMenu ); - - if( contextMenu != null ) - { - contextMenu.show( mOverlayPanel, - event.getX(), - event.getY() ); - } - } - } - - @Override - public void mousePressed( MouseEvent e ) {} - @Override - public void mouseReleased( MouseEvent e ) {} - } - - @Override - public int getAveraging() - { - return mSpectrumPanel.getAveraging(); - } - - @Override - public void setAveraging( int averaging ) - { - mSpectrumPanel.setAveraging( averaging ); - } - - public void setSampleSize( double sampleSize ) - { - mSpectrumPanel.setSampleSize( sampleSize ); - } - - @Override - public int getSmoothing() - { - return mSpectrumPanel.getSmoothing(); - } - - @Override - public void setSmoothing( int smoothing ) - { - mSpectrumPanel.setSmoothing( smoothing ); - } - - @Override - public SmoothingType getSmoothingType() - { - return mSpectrumPanel.getSmoothingType(); - } - - @Override - public void setSmoothingType( SmoothingType type ) - { - mSpectrumPanel.setSmoothingType( type ); - } -} diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java deleted file mode 100644 index 73515b87..00000000 --- a/src/spectrum/DFTProcessor.java +++ /dev/null @@ -1,482 +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 spectrum; - -import dsp.filter.Window; -import dsp.filter.Window.WindowType; -import org.jtransforms.fft.FloatFFT_1D; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import properties.SystemProperties; -import sample.Buffer; -import sample.Listener; -import sample.SampleType; -import sample.complex.ComplexBuffer; -import source.SourceEvent; -import source.ISourceEventProcessor; -import spectrum.converter.DFTResultsConverter; -import util.ThreadPool; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Processes both complex samples or float samples and dispatches a float array - * of DFT results, using configurable fft size and output dispatch timelines. - */ -public class DFTProcessor implements Listener, ISourceEventProcessor, IDFTWidthChangeProcessor -{ - private final static Logger mLog = LoggerFactory.getLogger(DFTProcessor.class); - - private CopyOnWriteArrayList mListeners = new CopyOnWriteArrayList(); - - //Fixed size sample queue. We set the size at 12 based on the highest - //sample rate tuner (10 MHz) producing 153 frames per second and the lowest - //DFT processing frame rate of 12 frames per second. This means that the - //maximum size needed is 153 / 12 = 11 frames per DFT processing iteration - //so we set it at 12. - private BlockingQueue mQueue = new ArrayBlockingQueue<>(12); - - private ScheduledFuture mProcessorTaskHandle; - - public static final String FRAME_RATE_PROPERTY = "spectral.display.frame.rate"; - - private DFTSize mDFTSize = DFTSize.FFT04096; - private DFTSize mNewDFTSize = DFTSize.FFT04096; - - private double[] mWindow; - - /* The Cosine and Hanning windows seem to offer the best spectral display - * with minimal bin leakage/smearing */ - private WindowType mWindowType = WindowType.BLACKMAN_HARRIS_7; - - private FloatFFT_1D mFFT = new FloatFFT_1D(mDFTSize.getSize()); - - private int mFrameRate; - private int mSampleRate; - private int mFFTFloatsPerFrame; - private float mNewFloatsPerFrame; - private float mNewFloatResidual; - private float[] mPreviousFrame = new float[8192]; - - private float[] mCurrentBuffer; - private int mCurrentBufferPointer = 0; - - private SampleType mSampleType; - - private AtomicBoolean mRunning = new AtomicBoolean(); - - public DFTProcessor(SampleType sampleType) - { - setSampleType(sampleType); - mFrameRate = SystemProperties.getInstance().get(FRAME_RATE_PROPERTY, 20); - calculateConsumptionRate(); - start(); - } - - public void dispose() - { - stop(); - - mListeners.clear(); - mQueue.clear(); - mWindow = null; - mCurrentBuffer = null; - } - - public WindowType getWindowType() - { - return mWindowType; - } - - public void setWindowType(WindowType windowType) - { - mWindowType = windowType; - - if(mSampleType == SampleType.COMPLEX) - { - mWindow = Window.getWindow(mWindowType, mDFTSize.getSize() * 2); - } - else - { - mWindow = Window.getWindow(mWindowType, mDFTSize.getSize()); - } - } - - /** - * Sets the processor mode to Float or Complex, depending on the sample - * types that will be delivered for processing - */ - public void setSampleType(SampleType type) - { - mSampleType = type; - setWindowType(mWindowType); - } - - public SampleType getSampleType() - { - return mSampleType; - } - - /** - * Queues an FFT size change request. The scheduled executor will apply - * the change when it runs. - */ - public void setDFTSize(DFTSize size) - { - mNewDFTSize = size; - } - - public DFTSize getDFTSize() - { - return mDFTSize; - } - - public int getFrameRate() - { - return mFrameRate; - } - - public void setFrameRate(int framesPerSecond) - { - //TODO: make sure frame rate & sample rate sample requirement doesn't - //expect overlap greater than the previous frame length - - if(framesPerSecond < 1 || framesPerSecond > 1000) - { - throw new IllegalArgumentException("DFTProcessor cannot run " - + "more than 1000 times per second -- requested setting:" - + framesPerSecond); - } - - mFrameRate = framesPerSecond; - - SystemProperties.getInstance().set(FRAME_RATE_PROPERTY, mFrameRate); - - calculateConsumptionRate(); - - restart(); - } - - public void start() - { - //Schedule the DFT to run calculations at a fixed rate - int initialDelay = 0; - int period = (int) (1000 / mFrameRate); - - mProcessorTaskHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(new DFTCalculationTask(), initialDelay, period, - TimeUnit.MILLISECONDS); - } - - public void stop() - { - //Cancel running DFT calculation task - if(mProcessorTaskHandle != null) - { - mProcessorTaskHandle.cancel(true); - } - } - - public void restart() - { - stop(); - start(); - } - - public int getCalculationsPerSecond() - { - return mFrameRate; - } - - /** - * Places the sample into a transfer queue for future processing. - */ - @Override - public void receive(ComplexBuffer sampleBuffer) - { - mQueue.offer(sampleBuffer); - } - - private void getNextBuffer() - { - mCurrentBuffer = null; - - try - { - Buffer buffer = mQueue.take(); - mCurrentBuffer = buffer.getSamples(); - } - catch(InterruptedException e) - { - mCurrentBuffer = null; - } - - mCurrentBufferPointer = 0; - } - - private float[] getSamples() - { - int remaining = (int) mFFTFloatsPerFrame; - - float[] currentFrame = new float[remaining]; - - int currentFramePointer = 0; - - float integralFloatsToConsume = mNewFloatsPerFrame + mNewFloatResidual; - - int newFloatsToConsumeThisFrame = (int) integralFloatsToConsume; - - mNewFloatResidual = integralFloatsToConsume - newFloatsToConsumeThisFrame; - - /* If the number of required floats for the fft is greater than the - * consumption rate per frame, we have to reach into the previous - * frame to makeup the difference. */ - if(newFloatsToConsumeThisFrame < remaining) - { - int previousFloatsRequired = remaining - newFloatsToConsumeThisFrame; - - System.arraycopy(mPreviousFrame, - mPreviousFrame.length - previousFloatsRequired, - currentFrame, - currentFramePointer, - previousFloatsRequired); - - remaining -= previousFloatsRequired; - currentFramePointer += previousFloatsRequired; - } - - /* Fill the rest of the buffer with new samples */ - while(mRunning.get() && remaining > 0) - { - if(mCurrentBuffer == null || - mCurrentBufferPointer >= mCurrentBuffer.length) - { - getNextBuffer(); - } - - /* If we don't have new samples to use, send the current frame with - * the remaining values as zero */ - if(mCurrentBuffer == null) - { - remaining = 0; - } - else - { - int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; - - while(remaining > 0 && samplesAvailable > 0) - { - currentFrame[currentFramePointer++] = - (float) mCurrentBuffer[mCurrentBufferPointer++]; - - samplesAvailable--; - remaining--; - newFloatsToConsumeThisFrame--; - } - } - } - - /* If the incoming float rate is greater than the fft consumption rate, - * then we have to purge some floats, otherwise, store the previous - * frame, because we have overlapping frames */ - if(newFloatsToConsumeThisFrame > 0) - { - purge(newFloatsToConsumeThisFrame); - } - else - { - mPreviousFrame = Arrays.copyOf(currentFrame, currentFrame.length); - } - - return currentFrame; - } - - private void calculate() - { - float[] samples = getSamples(); - - Window.apply(mWindow, samples); - - if(mSampleType == SampleType.REAL) - { - mFFT.realForward(samples); - } - else - { - mFFT.complexForward(samples); - } - - dispatch(samples); - } - - - private void purge(int samplesToPurge) - { - if(samplesToPurge <= 0) - { - throw new IllegalArgumentException("DFTProcessor - cannot purge " - + "negative sample amount"); - } - - while(mRunning.get() && samplesToPurge > 0) - { - if(mCurrentBuffer == null || - mCurrentBufferPointer >= mCurrentBuffer.length) - { - getNextBuffer(); - } - - if(mCurrentBuffer != null) - { - int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; - - if(samplesAvailable >= samplesToPurge) - { - mCurrentBufferPointer += samplesToPurge; - samplesToPurge = 0; - } - else - { - samplesToPurge -= samplesAvailable; - mCurrentBufferPointer = mCurrentBuffer.length; - } - } - else - { - samplesToPurge = 0; - } - } - } - - /** - * Takes a calculated DFT results set, reformats the data, and sends it - * out to all registered listeners. - */ - private void dispatch(float[] results) - { - Iterator it = mListeners.iterator(); - - while(it.hasNext()) - { - it.next().receive(results); - } - } - - public void addConverter(DFTResultsConverter listener) - { - mListeners.add(listener); - } - - public void removeConverter(DFTResultsConverter listener) - { - mListeners.remove(listener); - } - - private class DFTCalculationTask implements Runnable - { - @Override - public void run() - { - try - { - /* Only run if we're not currently running */ - if(mRunning.compareAndSet(false, true)) - { - checkFFTSize(); - - calculate(); - - mRunning.set(false); - } - } - catch(Exception e) - { - mLog.error("error during dft processor calculation task", e); - } - } - } - - /** - * Checks for a queued FFT width change request and applies it. This - * method will only be accessed by the scheduled executor that gains - * access to run a calculate method, thus providing thread safety. - */ - private void checkFFTSize() - { - if(mNewDFTSize.getSize() != mDFTSize.getSize()) - { - mDFTSize = mNewDFTSize; - - calculateConsumptionRate(); - - setWindowType(mWindowType); - - if(mSampleType == SampleType.COMPLEX) - { - mPreviousFrame = new float[mDFTSize.getSize() * 2]; - } - else - { - mPreviousFrame = new float[mDFTSize.getSize()]; - } - - mFFT = new FloatFFT_1D(mDFTSize.getSize()); - } - } - - public void clearBuffer() - { - mQueue.clear(); - } - - @Override - public void process(SourceEvent event) - { - switch(event.getEvent()) - { - case NOTIFICATION_SAMPLE_RATE_CHANGE: - mSampleRate = event.getValue().intValue(); - calculateConsumptionRate(); - break; - default: - break; - } - } - - /** - * - */ - private void calculateConsumptionRate() - { - mNewFloatResidual = 0.0f; - - mNewFloatsPerFrame = ((float) mSampleRate / (float) mFrameRate) * - (mSampleType == SampleType.COMPLEX ? 2.0f : 1.0f); - - mFFTFloatsPerFrame = (mSampleType == SampleType.COMPLEX ? - mDFTSize.getSize() * 2 : - mDFTSize.getSize()); - } -} diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java deleted file mode 100644 index dc211bae..00000000 --- a/src/spectrum/OverlayPanel.java +++ /dev/null @@ -1,913 +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 spectrum; - -import controller.channel.Channel; -import controller.channel.Channel.ChannelType; -import controller.channel.ChannelEvent; -import controller.channel.ChannelEventListener; -import controller.channel.ChannelModel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; -import settings.Setting; -import settings.SettingChangeListener; -import settings.SettingsManager; -import source.tuner.channel.TunerChannel; -import source.SourceEvent; -import source.ISourceEventProcessor; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.geom.Line2D; -import java.awt.geom.Rectangle2D; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.concurrent.CopyOnWriteArrayList; - -public class OverlayPanel extends JPanel implements ChannelEventListener, ISourceEventProcessor, - SettingChangeListener -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger(OverlayPanel.class); - private final DecimalFormat PPM_FORMATTER = new DecimalFormat( "#.0" ); - - private final static RenderingHints RENDERING_HINTS = new RenderingHints(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - - static - { - RENDERING_HINTS.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - } - - private final static BasicStroke DASHED_STROKE = new BasicStroke(0.8f, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, 5.0f, new float[]{2.0f, 4.0f}, 0.0f); - - private static DecimalFormat CURSOR_FORMAT = new DecimalFormat("000.00000"); - private long mFrequency = 0; - private int mBandwidth = 0; - private Point mCursorLocation = new Point(0, 0); - private boolean mCursorVisible = false; - - private DFTSize mDFTSize = DFTSize.FFT04096; - private int mZoom = 0; - private int mDFTZoomWindowOffset = 0; - - /** - * Colors used by this component - */ - private Color mColorChannelConfig; - private Color mColorChannelConfigProcessing; - private Color mColorChannelConfigSelected; - private Color mColorSpectrumBackground; - private Color mColorSpectrumCursor; - private Color mColorSpectrumLine; - - //Currently visible/displayable channels - private CopyOnWriteArrayList mVisibleChannels = new CopyOnWriteArrayList(); - private ChannelDisplay mChannelDisplay = ChannelDisplay.ALL; - - //Defines the offset at the bottom of the spectral display to account for - //the frequency labels - private double mSpectrumInset = 20.0d; - private LabelSizeManager mLabelSizeMonitor = new LabelSizeManager(); - - private SettingsManager mSettingsManager; - private ChannelModel mChannelModel; - - /** - * Translucent overlay panel for displaying channel configurations, - * processing channels, selected channels, frequency labels and lines, and - * a cursor with a frequency readout. - */ - public OverlayPanel(SettingsManager settingsManager, ChannelModel channelModel) - { - mSettingsManager = settingsManager; - - if(mSettingsManager != null) - { - mSettingsManager.addListener(this); - } - - mChannelModel = channelModel; - - if(mChannelModel != null) - { - mChannelModel.addListener(this); - } - - addComponentListener(mLabelSizeMonitor); - - //Set the background transparent, so the spectrum display can be seen - setOpaque(false); - - //Fetch color settings from settings manager - setColors(); - } - - public void dispose() - { - if(mChannelModel != null) - { - mChannelModel.removeListener(this); - } - - mChannelModel = null; - - mVisibleChannels.clear(); - - if(mSettingsManager != null) - { - mSettingsManager.removeListener(this); - } - - mSettingsManager = null; - } - - /** - * Sets/changes the DFT bin size - */ - public void setDFTSize(DFTSize size) - { - mDFTSize = size; - } - - public ChannelDisplay getChannelDisplay() - { - return mChannelDisplay; - } - - public void setChannelDisplay(ChannelDisplay display) - { - mChannelDisplay = display; - } - - public void setCursorLocation(Point point) - { - mCursorLocation = point; - - repaint(); - } - - public void setCursorVisible(boolean visible) - { - mCursorVisible = visible; - - repaint(); - } - - /** - * Sets the current zoom level (2^zoom) - * - * 0 No Zoom - * 1 2x Zoom - * 2 4x Zoom - * 3 8x Zoom - * 4 16x Zoom - * 5 32x Zoom - * 6 64x Zoom - * - * @param zoom level, 0 - 6. - */ - public void setZoom(int zoom) - { - mZoom = zoom; - - mLabelSizeMonitor.update(); - } - - public void setZoomWindowOffset(int offset) - { - mDFTZoomWindowOffset = offset; - } - - /** - * Fetches the color settings from the settings manager - */ - private void setColors() - { - mColorChannelConfig = getColor(ColorSettingName.CHANNEL_CONFIG); - - mColorChannelConfigProcessing = getColor(ColorSettingName.CHANNEL_CONFIG_PROCESSING); - - mColorChannelConfigSelected = getColor(ColorSettingName.CHANNEL_CONFIG_SELECTED); - - mColorSpectrumCursor = getColor(ColorSettingName.SPECTRUM_CURSOR); - - mColorSpectrumLine = getColor(ColorSettingName.SPECTRUM_LINE); - - mColorSpectrumBackground = getColor(ColorSettingName.SPECTRUM_BACKGROUND); - } - - /** - * Fetches a named color setting from the settings manager. If the setting - * doesn't exist, creates the setting using the defaultColor - */ - private Color getColor(ColorSettingName name) - { - ColorSetting setting = mSettingsManager.getColorSetting(name); - - return setting.getColor(); - } - - /** - * Monitors for setting changes. Colors can be changed by external actions - * and will automatically update in this class - */ - @Override - public void settingChanged(Setting setting) - { - if(setting instanceof ColorSetting) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch(colorSetting.getColorSettingName()) - { - case CHANNEL_CONFIG: - mColorChannelConfig = colorSetting.getColor(); - break; - case CHANNEL_CONFIG_PROCESSING: - mColorChannelConfigProcessing = colorSetting.getColor(); - break; - case CHANNEL_CONFIG_SELECTED: - mColorChannelConfigSelected = colorSetting.getColor(); - break; - case SPECTRUM_BACKGROUND: - mColorSpectrumBackground = colorSetting.getColor(); - break; - case SPECTRUM_CURSOR: - mColorSpectrumCursor = colorSetting.getColor(); - break; - case SPECTRUM_LINE: - mColorSpectrumLine = colorSetting.getColor(); - break; - default: - break; - } - } - } - - /** - * Renders the channel configs, lines, labels, and cursor - */ - @Override - public void paintComponent(Graphics g) - { - super.paintComponent(g); - - Graphics2D graphics = (Graphics2D)g; - graphics.setBackground(mColorSpectrumBackground); - - graphics.setRenderingHints(RENDERING_HINTS); - - drawFrequencies(graphics); - drawChannels(graphics); - drawCursor(graphics); - } - - /** - * Draws a cursor on the panel, whenever the mouse is hovering over the - * panel - */ - private void drawCursor(Graphics2D graphics) - { - if(mCursorVisible) - { - drawFrequencyLine(graphics, mCursorLocation.x, mColorSpectrumCursor); - - String frequency = CURSOR_FORMAT.format(getFrequencyFromAxis(mCursorLocation.getX()) / 1E6D); - - FontMetrics fontMetrics = graphics.getFontMetrics(this.getFont()); - - Rectangle2D rect = fontMetrics.getStringBounds(frequency, graphics); - - if(mCursorLocation.y > rect.getHeight()) - { - graphics.drawString(frequency, mCursorLocation.x + 5, mCursorLocation.y); - } - - if(mZoom != 0) - { - graphics.drawString("Zoom: " + (int)Math.pow(2.0, mZoom) + "x", mCursorLocation.x + 17, - mCursorLocation.y + 11); - } - } - } - - /** - * Draws the frequency lines and labels every 10kHz - */ - private void drawFrequencies(Graphics2D graphics) - { - Stroke currentStroke = graphics.getStroke(); - - long minFrequency = getMinDisplayFrequency(); - long maxFrequency = getMaxDisplayFrequency(); - - //Frequency increments for label and tick spacing - int label = mLabelSizeMonitor.getLabelIncrement(graphics); - int major = mLabelSizeMonitor.getMajorTickIncrement(graphics); - int minor = mLabelSizeMonitor.getMinorTickIncrement(graphics); - - //Avoid divide by zero error - if(minor == 0) - { - minor = 1; - } - - //Adjust the start frequency to a multiple of the minor tick spacing - long frequency = minFrequency - (minFrequency % minor); - - while(frequency < maxFrequency) - { - if(frequency % label == 0) - { - drawFrequencyLineAndLabel(graphics, frequency); - } - else if(frequency % major == 0) - { - drawTickLine(graphics, frequency, true); - } - else - { - drawTickLine(graphics, frequency, false); - } - - frequency += minor; - } - } - - /** - * Draws a vertical line and a corresponding frequency label at the bottom - */ - private void drawFrequencyLineAndLabel(Graphics2D graphics, long frequency) - { - double xAxis = getAxisFromFrequency(frequency); - - drawFrequencyLine(graphics, xAxis, mColorSpectrumLine); - - drawTickLine(graphics, frequency, false); - - graphics.setColor(mColorSpectrumLine); - - drawFrequencyLabel(graphics, xAxis, frequency); - } - - /** - * Draws a vertical line at the xaxis - */ - private void drawTickLine(Graphics2D graphics, long frequency, boolean major) - { - graphics.setColor(mColorSpectrumLine); - - double xAxis = getAxisFromFrequency(frequency); - - double start = getSize().getHeight() - mSpectrumInset; - double end = start + (major ? 9.0d : 3.0d); - - graphics.draw(new Line2D.Double(xAxis, start, xAxis, end)); - } - - - /** - * Draws a vertical line at the xaxis - */ - private void drawFrequencyLine(Graphics2D graphics, double xaxis, Color color) - { - graphics.setColor(color); - - graphics.draw(new Line2D.Double(xaxis, 0.0d, xaxis, getSize().getHeight() - mSpectrumInset)); - } - - /** - * Draws a vertical line at the xaxis - */ - private void drawChannelCenterLine(Graphics2D graphics, double xaxis) - { - double height = getSize().getHeight() - mSpectrumInset; - - graphics.setColor(Color.LIGHT_GRAY); - - graphics.draw(new Line2D.Double(xaxis, height * 0.65d, xaxis, height - 1.0d)); - } - - /** - * Draws the Automatic Frequency Control (AFC) channel center offset - */ - private void drawAFC(Graphics2D graphics, double frequencyAxis, double errorAxis, double bandwidth, - int correction, long frequency) - { - double height = getSize().getHeight() - mSpectrumInset; - double verticalAxisTop = height * 0.88d; - double verticalAxisBottom = height * 0.98d; - - double halfBandwidth = bandwidth / 2.0; - double errorEdgeStart = errorAxis - halfBandwidth; - double errorEdgeStop = errorAxis + halfBandwidth; - - graphics.setColor(Color.YELLOW); - - //Horizontal line connecting frequency and error line - graphics.draw(new Line2D.Double(errorEdgeStart, verticalAxisBottom, errorEdgeStop, verticalAxisBottom)); - - //Vertical band edge lines - graphics.draw(new Line2D.Double(errorEdgeStart, verticalAxisTop, errorEdgeStart, verticalAxisBottom)); - graphics.draw(new Line2D.Double(errorEdgeStop, verticalAxisTop, errorEdgeStop, verticalAxisBottom)); - - double ppm = (double)correction / ((double)frequency / 1E6d); - - String label = "PPM " + PPM_FORMATTER.format(ppm) ; - - FontMetrics fontMetrics = graphics.getFontMetrics(this.getFont()); - - Rectangle2D rect = fontMetrics.getStringBounds(label, graphics); - - //Only render the correction value label if the spacing is large enough - if(rect.getWidth() <= bandwidth && rect.getHeight() * 5 <= height) - { - graphics.drawString(label, (float)(errorEdgeStart + 1.0), (float)(verticalAxisBottom - 2.0)); - } - } - - /** - * Returns the x-axis value corresponding to the frequency - */ - private double getAxisFromFrequency(long frequency) - { - double screenWidth = (double)getSize().getWidth(); - - double pixelsPerBin = screenWidth / (double)mDFTSize.getSize(); - - double pixelOffsetToMinDisplayFrequency = pixelsPerBin * 2.0d; - - //Calculate frequency offset from the min frequency - double frequencyOffset = (double)(frequency - getMinDisplayFrequency()); - - //Determine ratio of frequency offset to overall bandwidth - double ratio = frequencyOffset / (double)getDisplayBandwidth(); - - //Apply the ratio to the screen width minus 1 bin width - double screenOffset = screenWidth * ratio; - - return pixelOffsetToMinDisplayFrequency + screenOffset; - } - - /** - * Returns the frequency corresponding to the x-axis value using the current - * zoom level. - */ - public long getFrequencyFromAxis(double xAxis) - { - double width = getSize().getWidth(); - - double offset = xAxis / width; - - long frequency = getMinDisplayFrequency() + Math.round((double)getDisplayBandwidth() * offset); - - if(frequency > (getMaxFrequency())) - { - frequency = getMaxFrequency(); - } - - return frequency; - } - - /** - * Draws a frequency label at the x-axis position, at the bottom of the panel - */ - private void drawFrequencyLabel(Graphics2D graphics, double xaxis, long frequency) - { - String label = mLabelSizeMonitor.format(frequency); - - FontMetrics fontMetrics = graphics.getFontMetrics(this.getFont()); - - Rectangle2D rect = fontMetrics.getStringBounds(label, graphics); - - float xOffset = (float)rect.getWidth() / 2; - - graphics.drawString(label, (float)(xaxis - xOffset), (float)(getSize().getHeight() - 2.0f)); - } - - - /** - * Draws visible channel configs as translucent shaded frequency regions - */ - private void drawChannels(Graphics2D graphics) - { - for(Channel channel : mVisibleChannels) - { - if(mChannelDisplay == ChannelDisplay.ALL || - (mChannelDisplay == ChannelDisplay.ENABLED && channel.getEnabled())) - { - //Choose the correct background color to use - if(channel.isSelected()) - { - graphics.setColor(mColorChannelConfigSelected); - } - else if(channel.getEnabled()) - { - graphics.setColor(mColorChannelConfigProcessing); - } - else - { - graphics.setColor(mColorChannelConfig); - } - - TunerChannel tunerChannel = channel.getTunerChannel(); - - if(tunerChannel != null) - { - double xAxis = getAxisFromFrequency(tunerChannel.getFrequency()); - - double width = (double)(tunerChannel.getBandwidth()) / (double)getDisplayBandwidth() * getSize().getWidth(); - - Rectangle2D.Double box = - new Rectangle2D.Double(xAxis - (width / 2.0d), 0.0d, width, getSize().getHeight() - mSpectrumInset); - - //Fill the box with the correct color - graphics.fill(box); - - graphics.draw(box); - - //Change to the line color to render the channel name, etc. - graphics.setColor(mColorSpectrumLine); - - //Draw the labels starting at yAxis position 0 - double yAxis = 0; - - //Draw the system label and adjust the y-axis position - String system = channel.hasSystem() ? channel.getSystem() : " "; - - yAxis += drawLabel(graphics, system, this.getFont(), xAxis, yAxis, width); - - //Draw the site label and adjust the y-axis position - String site = channel.hasSite() ? channel.getSite() : " "; - - yAxis += drawLabel(graphics, site, this.getFont(), xAxis, yAxis, width); - - //Draw the channel label and adjust the y-axis position - yAxis += drawLabel(graphics, channel.getName(), this.getFont(), xAxis, yAxis, width); - - //Draw the decoder label - drawLabel(graphics, channel.getDecodeConfiguration().getDecoderType().getShortDisplayString(), - this.getFont(), xAxis, yAxis, width); - - long frequency = tunerChannel.getFrequency(); - - double frequencyAxis = getAxisFromFrequency(frequency); - - drawChannelCenterLine(graphics, frequencyAxis); - - /* Draw Automatic Frequency Control line */ - int correction = channel.getChannelFrequencyCorrection(); - - if(correction != 0) - { - long error = frequency + correction; - - drawAFC(graphics, frequencyAxis, getAxisFromFrequency(error), width, correction, - tunerChannel.getFrequency()); - } - } - } - } - } - - - /** - * Draws a textual label at the x/y position, clipping the end of the text - * to fit within the maxwidth value. - * - * @return height of the drawn label - */ - private double drawLabel(Graphics2D graphics, String text, Font font, double x, double baseY, double maxWidth) - { - FontMetrics fontMetrics = graphics.getFontMetrics(font); - - Rectangle2D label = fontMetrics.getStringBounds(text, graphics); - - double offset = label.getWidth() / 2.0d; - double y = baseY + label.getHeight(); - - /** - * If the label is wider than the max width, left justify the text and - * clip the end of it - */ - if(offset > (maxWidth / 2.0d)) - { - label.setRect(x - (maxWidth / 2.0d), y - label.getHeight(), maxWidth, label.getHeight()); - - graphics.setClip(label); - - graphics.drawString(text, (float)(x - (maxWidth / 2.0d)), (float)y); - - graphics.setClip(null); - } - else - { - graphics.drawString(text, (float)(x - offset), (float)y); - } - - return label.getHeight(); - } - - /** - * Frequency change event handler - */ - @Override - public void process(SourceEvent event) - { - switch(event.getEvent()) - { - case NOTIFICATION_SAMPLE_RATE_CHANGE: - mBandwidth = event.getValue().intValue(); - mLabelSizeMonitor.update(); - break; - case NOTIFICATION_FREQUENCY_CHANGE: - mFrequency = event.getValue().longValue(); - mLabelSizeMonitor.update(); - break; - default: - break; - } - - /** - * Reset the visible channels list - */ - mVisibleChannels.clear(); - mVisibleChannels.addAll(mChannelModel.getChannelsInFrequencyRange(getMinFrequency(), getMaxFrequency())); - } - - /** - * Channel change event handler - */ - @Override - @SuppressWarnings("incomplete-switch") - public void channelChanged(ChannelEvent event) - { - Channel channel = event.getChannel(); - - switch(event.getEvent()) - { - case NOTIFICATION_ADD: - case NOTIFICATION_PROCESSING_START: - if(!mVisibleChannels.contains(channel) && channel.isWithin(getMinFrequency(), getMaxFrequency())) - { - mVisibleChannels.add(channel); - } - break; - case NOTIFICATION_DELETE: - mVisibleChannels.remove(channel); - break; - case NOTIFICATION_PROCESSING_STOP: - if(channel.getChannelType() == ChannelType.TRAFFIC) - { - mVisibleChannels.remove(channel); - } - break; - case NOTIFICATION_CONFIGURATION_CHANGE: - if(mVisibleChannels.contains(channel) && !channel.isWithin(getMinFrequency(), getMaxFrequency())) - { - mVisibleChannels.remove(channel); - } - - if(!mVisibleChannels.contains(channel) && channel.isWithin(getMinFrequency(), getMaxFrequency())) - { - mVisibleChannels.add(channel); - } - break; - default: - break; - } - - repaint(); - } - - public int getBandwidth() - { - return mBandwidth; - } - - /** - * Currently displayed minimum frequency - */ - public long getMinFrequency() - { - return mFrequency - (mBandwidth / 2); - } - - /** - * Currently displayed maximum frequency - */ - public long getMaxFrequency() - { - return mFrequency + (mBandwidth / 2); - } - - public boolean containsFrequency(long frequency) - { - return Math.abs(mFrequency - frequency) <= (mBandwidth / 2); - } - - private long getMinDisplayFrequency() - { - double bandwidthPerBin = (double)mBandwidth / (double)(mDFTSize.getSize()); - - return getMinFrequency() + (int)((mDFTZoomWindowOffset) * bandwidthPerBin); - } - - private long getMaxDisplayFrequency() - { - return getMinDisplayFrequency() + getDisplayBandwidth(); - } - - private int getDisplayBandwidth() - { - if(mZoom != 0) - { - return mBandwidth / (int)Math.pow(2.0, mZoom); - } - - return mBandwidth; - } - - /** - * Returns a list of channel configs that contain the frequency within their - * min/max frequency settings. - */ - public ArrayList getChannelsAtFrequency(long frequency) - { - ArrayList configs = new ArrayList(); - - for(Channel config : mVisibleChannels) - { - TunerChannel channel = config.getTunerChannel(); - - if(channel != null && channel.getMinFrequency() <= frequency && channel.getMaxFrequency() >= frequency) - { - configs.add(config); - } - } - - return configs; - } - - @Override - public void settingDeleted(Setting setting) - { /* not implemented */ } - - /** - * Calculates correct spacing and format for frequency labels and major/minor - * tick lines based on current frequency, bandwidth, zoom and screen size. - */ - public class LabelSizeManager implements ComponentListener - { - private static final double LABEL_FILL_THRESHOLD = 0.5d; - - private DecimalFormat mFrequencyFormat = new DecimalFormat("0.0"); - - private boolean mUpdateRequired = true; - private int mLabelIncrement = 1; - private int mMajorTickIncrement = 1; - private int mMinorTickIncrement = 1; - - public String format(long frequency) - { - return mFrequencyFormat.format((double)frequency / 1E6D); - } - - private void setPrecision(int precision) - { - if(precision < 1) - { - precision = 1; - } - - if(precision > 5) - { - precision = 5; - } - - mFrequencyFormat.setMinimumFractionDigits(precision); - mFrequencyFormat.setMaximumFractionDigits(precision); - } - - private void update(Graphics2D graphics) - { - if(mUpdateRequired) - { - //Set maximum precision as a starting point - setPrecision(5); - - FontMetrics fontMetrics = - graphics.getFontMetrics(OverlayPanel.this.getFont()); - - int maxLabelWidth = fontMetrics.stringWidth(format(getMaxDisplayFrequency())); - - double maxLabels = ((double)OverlayPanel.this.getWidth() * LABEL_FILL_THRESHOLD) / (double)maxLabelWidth; - - //Calculate the next smallest base 10 value for the major increment - int power = (int)Math.log10((double)getDisplayBandwidth() / maxLabels); - - //Set the number of decimal places to display in frequency labels - int precision = 5 - power; - - int start = (int)Math.pow(10.0, power + 1); - - int minimum = (int)Math.pow(10.0, power); - - int labelIncrement = start; - - while(((double)getDisplayBandwidth() / (double)labelIncrement) < maxLabels && labelIncrement >= minimum) - { - labelIncrement /= 2; - precision++; - } - - if(labelIncrement == minimum) - { - precision = 5 - power; - } - - setPrecision(precision); - - mLabelIncrement = labelIncrement; - mMajorTickIncrement = labelIncrement / 2; - mMinorTickIncrement = labelIncrement / 10; - - mUpdateRequired = false; - } - } - - /** - * Forces the display to update the label and frequency display - * calculations - */ - public void update() - { - mUpdateRequired = true; - } - - public int getMajorTickIncrement(Graphics2D graphics) - { - //Check to see if a calculation update is scheduled - update(graphics); - - return mMajorTickIncrement; - } - - public int getMinorTickIncrement(Graphics2D graphics) - { - return mMinorTickIncrement; - } - - public int getLabelIncrement(Graphics2D graphics) - { - return mLabelIncrement; - } - - @Override - public void componentResized(ComponentEvent arg0) - { - update(); - } - - public void componentHidden(ComponentEvent arg0) - { - } - - public void componentMoved(ComponentEvent arg0) - { - } - - public void componentShown(ComponentEvent arg0) - { - } - - } - - public enum ChannelDisplay - { - ALL, ENABLED, NONE; - } -} diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java deleted file mode 100644 index 46d79731..00000000 --- a/src/spectrum/SpectralDisplayPanel.java +++ /dev/null @@ -1,941 +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 spectrum; - -import com.jidesoft.swing.JideSplitPane; -import controller.channel.Channel; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; -import controller.channel.ChannelUtils; -import dsp.filter.Window.WindowType; -import dsp.filter.smoothing.SmoothingFilter.SmoothingType; -import module.decode.DecoderType; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import properties.SystemProperties; -import sample.Listener; -import sample.SampleType; -import sample.complex.ComplexBuffer; -import settings.ColorSetting.ColorSettingName; -import settings.ColorSettingMenuItem; -import settings.SettingsManager; -import source.ISourceEventProcessor; -import source.SourceEvent; -import source.tuner.Tuner; -import spectrum.OverlayPanel.ChannelDisplay; -import spectrum.converter.ComplexDecibelConverter; -import spectrum.converter.DFTResultsConverter; -import spectrum.menu.AveragingItem; -import spectrum.menu.DFTSizeItem; -import spectrum.menu.FFTWindowTypeItem; -import spectrum.menu.FrameRateItem; -import spectrum.menu.SmoothingItem; -import spectrum.menu.SmoothingTypeItem; - -import javax.swing.*; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.MouseInputAdapter; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Hashtable; - -public class SpectralDisplayPanel extends JPanel implements Listener, ISourceEventProcessor, IDFTWidthChangeProcessor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger(SpectralDisplayPanel.class); - - private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat("000.00000"); - - public static final String FFT_SIZE_PROPERTY = "spectral.display.dft.size"; - public static final int NO_ZOOM = 0; - public static final int MAX_ZOOM = 6; - - private DFTSize mDFTSize = DFTSize.FFT04096; - private int mZoom = 0; - private int mDFTZoomWindowOffset = 0; - - private JScrollPane mScrollPane; - private JLayeredPane mLayeredPanel; - private SpectrumPanel mSpectrumPanel; - private WaterfallPanel mWaterfallPanel; - private OverlayPanel mOverlayPanel; - private DFTProcessor mDFTProcessor; - private DFTResultsConverter mDFTConverter; - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SettingsManager mSettingsManager; - private Tuner mTuner; - - /** - * Spectral Display Panel provides a frequency component display with a - * historical waterfall display and a transparent overlay to show frequency, - * cursor and channel information. - * - * Mouse scrolling and zooming are supported and the waterfall display can - * be paused. - * - * Complex sample buffers are processed by a DFTProcessor and the output of - * the DFT is translated to decibels for display in the spectrum and - * waterfall components. - */ - public SpectralDisplayPanel(ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - SettingsManager settingsManager) - { - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSettingsManager = settingsManager; - - mSpectrumPanel = new SpectrumPanel(mSettingsManager); - mOverlayPanel = new OverlayPanel(mSettingsManager, mChannelModel); - mWaterfallPanel = new WaterfallPanel(mSettingsManager); - - init(); - - loadSettings(); - } - - private void loadSettings() - { - SystemProperties properties = SystemProperties.getInstance(); - - String rawSize = properties.get(FFT_SIZE_PROPERTY, DFTSize.FFT04096.name()); - - DFTSize size = null; - - if(rawSize != null) - { - try - { - size = DFTSize.valueOf(rawSize); - } - catch(Exception e) - { - //Do nothing - } - } - - if(size == null) - { - size = DFTSize.FFT04096; - } - - setDFTSize(size, false); - } - - public void dispose() - { - /* De-register from receiving samples when the window closes */ - clearTuner(); - - mSettingsManager = null; - - mDFTProcessor.dispose(); - mDFTProcessor = null; - - mDFTConverter.dispose(); - mDFTConverter = null; - - mSpectrumPanel.dispose(); - mSpectrumPanel = null; - - mWaterfallPanel.dispose(); - mWaterfallPanel = null; - - mOverlayPanel.dispose(); - mOverlayPanel = null; - - - mTuner = null; - } - - /** - * Queues an FFT size change request. The scheduled executor will apply - * the change when it runs. - */ - public void setDFTSize(DFTSize size, boolean save) - { - mDFTProcessor.setDFTSize(size); - mOverlayPanel.setDFTSize(size); - mDFTSize = size; - - if(save) - { - SystemProperties.getInstance().set(FFT_SIZE_PROPERTY, size.name()); - } - - setZoom(0, 0, 0); - } - - public void setDFTSize(DFTSize size) - { - setDFTSize(size, true); - } - - @Override - public DFTSize getDFTSize() - { - return mDFTSize; - } - - public int getZoom() - { - return mZoom; - } - - /** - * Sets the current zoom level which will be 2 to the power of zoom (2^zoom) - * - * 0 No Zoom - * 1 2x Zoom - * 2 4x Zoom - * 3 8x Zoom - * 4 16x Zoom - * 5 32x Zoom - * 6 64x Zoom - * - * @param zoom level, 0 - 6. - * @param frequency under the mouse to maintain while zooming - * @param windowOffset where to maintain the frequency under the mouse - */ - public void setZoom(int zoom, long frequency, double windowOffset) - { - if(zoom < NO_ZOOM) - { - zoom = NO_ZOOM; - } - else if(zoom > MAX_ZOOM) - { - zoom = MAX_ZOOM; - } - - if(zoom != mZoom) - { - mZoom = zoom; - - //Calculate the bin offset that would place the reference frequency - //at the left edge of the zoom window. - double binOffsetToFrequency = getBinOffset(frequency); - - //Calculate the bin offset into the newly sized zoom window that - //would place the frequency in the same proportional window location - //that it was in the previous zoom size - double windowBinOffset = (double)getZoomWindowSizeInBins() * windowOffset; - - //Set the overall offset to place the reference frequency in the - //same location in the newly zoomed window - double offset = binOffsetToFrequency - windowBinOffset; - - mSpectrumPanel.setZoom(mZoom); - mOverlayPanel.setZoom(mZoom); - mWaterfallPanel.setZoom(mZoom); - - setZoomWindowOffset(offset); - } - } - - /** - * Sets the offset (in DFT bins) to the first bin that the zoom window displays - */ - public void setZoomWindowOffset(double offset) - { - if(offset < 0) - { - offset = 0; - } - - if(offset > (mDFTSize.getSize() - getZoomWindowSizeInBins())) - { - offset = mDFTSize.getSize() - getZoomWindowSizeInBins(); - } - - mDFTZoomWindowOffset = (int)offset; - - mSpectrumPanel.setZoomWindowOffset(mDFTZoomWindowOffset); - mOverlayPanel.setZoomWindowOffset(mDFTZoomWindowOffset); - mWaterfallPanel.setZoomWindowOffset(mDFTZoomWindowOffset); - } - - /** - * Calculates the size of the current zoom window in DFT bins - */ - private int getZoomWindowSizeInBins() - { - return mDFTSize.getSize() / getZoomMultiplier(); - } - - - public int getZoomMultiplier() - { - return (int)Math.pow(2.0, mZoom); - } - - /** - * Calculates the overall offset of the frequency from the current minimum - * frequency in terms of total FFT width - * - * @param frequency - * @return - */ - private double getBinOffset(long frequency) - { - double offset = 0.0; - - if(mOverlayPanel.containsFrequency(frequency)) - { - offset = (double)mDFTSize.getSize() * - ((double)(frequency - mOverlayPanel.getMinFrequency()) / - (double)mOverlayPanel.getBandwidth()); - } - - return offset; - } - - /** - * Overrides JComponent method to return false, since we have overlapping - * panels with the spectrum and channel panels - */ - public boolean isOptimizedDrawingEnabled() - { - return false; - } - - private void init() - { - setLayout(new MigLayout("insets 0 0 0 0", "[grow]", "[grow]")); - - /** - * The layered pane holds the overlapping spectrum and channel panels - * and manages the sizing of each panel with the resize listener - */ - mLayeredPanel = new JLayeredPane(); - mLayeredPanel.addComponentListener(new ResizeListener()); - - /** - * Create a mouse adapter to handle mouse events over the spectrum - * and waterfall panels - */ - MouseEventProcessor mouser = new MouseEventProcessor(); - - mOverlayPanel.addMouseListener(mouser); - mOverlayPanel.addMouseMotionListener(mouser); - mOverlayPanel.addMouseWheelListener(mouser); - - //Add the spectrum and channel panels to the layered panel - mLayeredPanel.add(mSpectrumPanel, new Integer(0), 0); - mLayeredPanel.add(mOverlayPanel, new Integer(1), 0); - - //Create the waterfall - mWaterfallPanel.addMouseListener(mouser); - mWaterfallPanel.addMouseMotionListener(mouser); - mWaterfallPanel.addMouseWheelListener(mouser); - - /* Attempt to set a 50/50 split preferred size for the split pane */ - double totalHeight = mLayeredPanel.getPreferredSize().getHeight() + - mWaterfallPanel.getPreferredSize().getHeight(); - - mLayeredPanel.setPreferredSize(new Dimension((int)mLayeredPanel - .getPreferredSize().getWidth(), (int)(totalHeight / 2.0d))); - - mWaterfallPanel.setPreferredSize(new Dimension((int)mWaterfallPanel - .getPreferredSize().getWidth(), (int)(totalHeight / 2.0d))); - - //Create the split pane to hold the layered pane and the waterfall - JideSplitPane splitPane = new JideSplitPane(JSplitPane.VERTICAL_SPLIT); - splitPane.setDividerSize(5); - splitPane.add(mLayeredPanel); - splitPane.add(mWaterfallPanel); - - mScrollPane = new JScrollPane(splitPane); - - add(mScrollPane, "grow"); - - /** - * Setup DFTProcessor to process samples and register the waterfall and - * spectrum panel to receive the processed dft results - */ - mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); - mDFTConverter = new ComplexDecibelConverter(); - mDFTProcessor.addConverter(mDFTConverter); - - mDFTConverter.addListener((DFTResultsListener)mSpectrumPanel); - mDFTConverter.addListener((DFTResultsListener)mWaterfallPanel); - } - - /** - * Receives frequency change events -- primarily from tuner components. - */ - public void process(SourceEvent event) - { - mOverlayPanel.process(event); - mDFTProcessor.process(event); - } - - /** - * Complex sample buffer receive method - */ - @Override - public void receive(ComplexBuffer sampleBuffer) - { - mDFTProcessor.receive(sampleBuffer); - } - - /** - * Responds to tuner event by deregistering from the current - * complex sample buffer source and registering with the tuner argument. - */ - public void showTuner(Tuner tuner) - { - clearTuner(); - - mDFTProcessor.clearBuffer(); - - mTuner = tuner; - - if(mTuner != null) - { - //Register to receive frequency change events - mTuner.getTunerController().addListener(this); - - //Register the dft processor to receive samples from the tuner - mTuner.getSourceManager().addComplexBufferListener(mDFTProcessor); - - mSpectrumPanel.setSampleSize(mTuner.getSampleSize()); - - //Fire frequency and sample rate change events so that the spectrum - //and overlay panels can synchronize - process(SourceEvent.frequencyChange(mTuner.getTunerController().getFrequencyController().getFrequency())); - - process(SourceEvent.sampleRateChange(mTuner.getTunerController().getFrequencyController().getSampleRate())); - } - } - - /** - * Tuner de-selection cleanup method - */ - public void clearTuner() - { - if(mTuner != null) - { - //Deregister for frequency change events from the tuner - mTuner.getTunerController().removeListener(this); - - //Deregister the dft processor from receiving samples - mTuner.getTunerController().removeComplexBufferListener(mDFTProcessor); - mTuner = null; - } - } - - /** - * Monitors the sizing of the layered pane and resizes the spectrum and - * channel panels whenever the layered pane is resized - */ - public class ResizeListener implements ComponentListener - { - @Override - public void componentResized(ComponentEvent e) - { - Component c = e.getComponent(); - - mSpectrumPanel.setBounds(0, 0, c.getWidth(), c.getHeight()); - mOverlayPanel.setBounds(0, 0, c.getWidth(), c.getHeight()); - } - - @Override - public void componentHidden(ComponentEvent arg0) - { - } - - @Override - public void componentMoved(ComponentEvent arg0) - { - } - - @Override - public void componentShown(ComponentEvent arg0) - { - } - } - - /** - * Mouse event handler for the spectral display panel. - */ - public class MouseEventProcessor extends MouseInputAdapter - { - private int mDFTZoomWindowOffsetAtDragStart = 0; - private int mDragStartX = 0; - private double mPixelsPerBin; - - public MouseEventProcessor() - { - } - - @Override - public void mouseWheelMoved(MouseWheelEvent e) - { - int zoom = mZoom - e.getWheelRotation(); - - long frequency = mOverlayPanel.getFrequencyFromAxis(e.getX()); - - double windowOffset = (double)e.getX() / (double)getWidth(); - - setZoom(zoom, frequency, windowOffset); - } - - @Override - public void mouseMoved(MouseEvent event) - { - update(event); - } - - @Override - public void mouseDragged(MouseEvent event) - { - update(event); - - int dragDistance = mDragStartX - event.getX(); - - double binDistance = (double)dragDistance / mPixelsPerBin; - - int offset = (int)(mDFTZoomWindowOffsetAtDragStart + binDistance); - - if(offset < 0) - { - offset = 0; - } - - int maxOffset = mDFTSize.getSize() - (mDFTSize.getSize() / getZoomMultiplier()); - - if(offset > maxOffset) - { - offset = maxOffset; - } - - setZoomWindowOffset(offset); - } - - @Override - public void mousePressed(MouseEvent e) - { - mDragStartX = e.getX(); - - mDFTZoomWindowOffsetAtDragStart = mDFTZoomWindowOffset; - - mPixelsPerBin = (double)getWidth() / - ((double)(mDFTSize.getSize()) / - (double)getZoomMultiplier()); - } - - /** - * Updates the cursor display while the mouse is performing actions - */ - private void update(MouseEvent event) - { - if(event.getComponent() == mOverlayPanel) - { - mOverlayPanel.setCursorLocation(event.getPoint()); - } - else - { - mWaterfallPanel.setCursorLocation(event.getPoint()); - mWaterfallPanel.setCursorFrequency( - mOverlayPanel.getFrequencyFromAxis(event.getPoint().x)); - } - } - - @Override - public void mouseEntered(MouseEvent e) - { - if(e.getComponent() == mOverlayPanel) - { - mOverlayPanel.setCursorVisible(true); - } - else - { - mWaterfallPanel.setCursorVisible(true); - } - } - - @Override - public void mouseExited(MouseEvent e) - { - mOverlayPanel.setCursorVisible(false); - mWaterfallPanel.setCursorVisible(false); - } - - /** - * Displays the context menu. - */ - @Override - public void mouseClicked(MouseEvent event) - { - if(SwingUtilities.isRightMouseButton(event)) - { - JPopupMenu contextMenu = new JPopupMenu(); - - if(event.getComponent() == mWaterfallPanel) - { - contextMenu.add(new PauseItem(mWaterfallPanel, "Pause")); - } - - long frequency = - mOverlayPanel.getFrequencyFromAxis(event.getX()); - - if(event.getComponent() == mOverlayPanel) - { - ArrayList channels = - mOverlayPanel.getChannelsAtFrequency(frequency); - - for(Channel channel : channels) - { - JMenu channelMenu = ChannelUtils.getContextMenu(mChannelModel, - mChannelProcessingManager, channel, - SpectralDisplayPanel.this); - - if(channelMenu != null) - { - contextMenu.add(channelMenu); - } - } - - if(!channels.isEmpty()) - { - contextMenu.add(new JSeparator()); - } - - } - -// JMenu frequencyMenu = new JMenu( -// sCURSOR_FORMAT.format((float) frequency / 1000000.0f)); -// -// JMenu decoderMenu = new JMenu("Add Decoder"); -// -// for(DecoderType type : DecoderType.getPrimaryDecoders()) -// { -// decoderMenu.add(new DecoderItem(type, frequency)); -// } -// -// frequencyMenu.add(decoderMenu); -// -// contextMenu.add(frequencyMenu); -// -// contextMenu.add(new JSeparator()); - - /** - * Color Menus - */ - JMenu colorMenu = new JMenu("Color"); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.CHANNEL_CONFIG)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.CHANNEL_CONFIG_PROCESSING)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.CHANNEL_CONFIG_SELECTED)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.SPECTRUM_CURSOR)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.SPECTRUM_LINE)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.SPECTRUM_BACKGROUND)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.SPECTRUM_GRADIENT_BOTTOM)); - - colorMenu.add(new ColorSettingMenuItem(mSettingsManager, - ColorSettingName.SPECTRUM_GRADIENT_TOP)); - - contextMenu.add(colorMenu); - - /** - * Display items: fft and frame rate - */ - JMenu displayMenu = new JMenu("Display"); - contextMenu.add(displayMenu); - - if(event.getComponent() != mWaterfallPanel) - { - /** - * Averaging menu - */ - JMenu averagingMenu = new JMenu("Averaging"); - averagingMenu.add( - new AveragingItem(mSpectrumPanel, 4)); - displayMenu.add(averagingMenu); - - /** - * Channel Display setting menu - */ - JMenu channelDisplayMenu = new JMenu("Channel"); - - channelDisplayMenu.add(new ChannelDisplayItem( - mOverlayPanel, ChannelDisplay.ALL)); - channelDisplayMenu.add(new ChannelDisplayItem( - mOverlayPanel, ChannelDisplay.ENABLED)); - channelDisplayMenu.add(new ChannelDisplayItem( - mOverlayPanel, ChannelDisplay.NONE)); - - displayMenu.add(channelDisplayMenu); - } - - - /** - * FFT width - */ - JMenu fftWidthMenu = new JMenu("FFT Width"); - displayMenu.add(fftWidthMenu); - - for(DFTSize width : DFTSize.values()) - { - fftWidthMenu.add(new DFTSizeItem(SpectralDisplayPanel.this, width)); - } - - /** - * DFT Processor Frame Rate - */ - JMenu frameRateMenu = new JMenu("Frame Rate"); - displayMenu.add(frameRateMenu); - - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 14)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 16)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 18)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 20)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 25)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 30)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 40)); - frameRateMenu.add(new FrameRateItem(mDFTProcessor, 50)); - - /** - * FFT Window Type - */ - JMenu fftWindowType = new JMenu("Window Type"); - displayMenu.add(fftWindowType); - - for(WindowType type : WindowType.values()) - { - fftWindowType.add( - new FFTWindowTypeItem(mDFTProcessor, type)); - } - - if(event.getComponent() != mWaterfallPanel) - { - /** - * Smoothing menu - */ - JMenu smoothingMenu = new JMenu("Smoothing"); - - if(mSpectrumPanel.getSmoothingType() != SmoothingType.NONE) - { - smoothingMenu.add(new SmoothingItem(mSpectrumPanel, 5)); - smoothingMenu.add(new JSeparator()); - } - smoothingMenu.add(new SmoothingTypeItem(mSpectrumPanel, SmoothingType.GAUSSIAN)); - smoothingMenu.add(new SmoothingTypeItem(mSpectrumPanel, SmoothingType.TRIANGLE)); - smoothingMenu.add(new SmoothingTypeItem(mSpectrumPanel, SmoothingType.RECTANGLE)); - smoothingMenu.add(new SmoothingTypeItem(mSpectrumPanel, SmoothingType.NONE)); - - displayMenu.add(smoothingMenu); - } - - /* - * Zoom menu - */ - JMenuItem zoomMenu = new JMenu("Zoom"); - - double windowOffset = (double)event.getX() / (double)getWidth(); - - zoomMenu.add(new ZoomItem(frequency, windowOffset)); - - contextMenu.add(zoomMenu); - - - if(contextMenu != null) - { - if(event.getComponent() == mOverlayPanel) - { - contextMenu.show(mOverlayPanel, - event.getX(), - event.getY()); - } - else - { - contextMenu.show(mWaterfallPanel, - event.getX(), - event.getY()); - } - } - } - } - } - - public class PauseItem extends JCheckBoxMenuItem - { - private static final long serialVersionUID = 1L; - - private Pausable mPausable; - - public PauseItem(Pausable pausable, String label) - { - super(label); - - final boolean paused = pausable.isPaused(); - - setSelected(paused); - - mPausable = pausable; - - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mPausable.setPaused(!paused); - } - }); - } - }); - } - } - - public class ZoomItem extends JSlider - { - private static final long serialVersionUID = 1L; - - private long mFrequency; - private double mWindowOffset; - - public ZoomItem(long frequency, double windowOffset) - { - super(NO_ZOOM, MAX_ZOOM, mZoom); - - mFrequency = frequency; - mWindowOffset = windowOffset; - - Hashtable labels = new Hashtable<>(); - labels.put(new Integer(0), new JLabel("1x")); - labels.put(new Integer(1), new JLabel("2x")); - labels.put(new Integer(2), new JLabel("4x")); - labels.put(new Integer(3), new JLabel("8x")); - labels.put(new Integer(4), new JLabel("16x")); - labels.put(new Integer(5), new JLabel("32x")); - labels.put(new Integer(6), new JLabel("64x")); - - setLabelTable(labels); - - setMajorTickSpacing(1); - setMinorTickSpacing(1); - setPaintTicks(true); - setPaintLabels(true); - - this.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent e) - { - setZoom(getValue(), mFrequency, mWindowOffset); - } - }); - } - } - - public class ChannelDisplayItem extends JCheckBoxMenuItem - { - private static final long serialVersionUID = 1L; - - private OverlayPanel mOverlayPanel; - private ChannelDisplay mChannelDisplay; - - public ChannelDisplayItem(OverlayPanel panel, ChannelDisplay display) - { - super(display.name()); - - mOverlayPanel = panel; - - mChannelDisplay = display; - - setSelected(mOverlayPanel.getChannelDisplay() == mChannelDisplay); - - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mOverlayPanel.setChannelDisplay(mChannelDisplay); - } - }); - } - }); - } - } - - /** - * Context menu item to provide one-click access to starting a channel - * processing with the selected decoder - */ - public class DecoderItem extends JMenuItem - { - private static final long serialVersionUID = 1L; - - private ChannelModel mChannelModel; - private long mFrequency; - private DecoderType mDecoder; - - public DecoderItem(DecoderType type, long frequency) - { - super(type.getDisplayString()); - - mFrequency = frequency; - mDecoder = type; - - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - mChannelModel.createChannel(mDecoder, mFrequency); - } - }); - } - } -}