diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java
index 8b5b0189..8636ec67 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelCalculator.java
@@ -1,31 +1,34 @@
-/*******************************************************************************
- * *********************************************************************************************************************
- * sdr-trunk
- * Copyright (C) 2014-2017 Dennis Sheirer
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2023 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.dsp.filter.channelizer;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
-
public class ChannelCalculator
{
+ private static final DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
private final static Logger mLog = LoggerFactory.getLogger(ChannelCalculator.class);
/**
@@ -546,13 +549,13 @@ public class ChannelCalculator
public String toString()
{
StringBuilder sb = new StringBuilder();
- sb.append("Channel Calculator Settings");
- sb.append(" Channels:").append(getChannelCount());
- sb.append(" Sample Rate:").append(getSampleRate());
- sb.append(" Channel Bandwidth:").append(getChannelBandwidth());
- sb.append(" Channel Rate:").append(getChannelSampleRate());
- sb.append(" Min:").append(getMinimumFrequency());
- sb.append(" Max:").append(getMaximumFrequency());
+ sb.append("Channel Calculator | Tuner SR:").append(FREQUENCY_FORMAT.format(getSampleRate() / 1E6D));
+ sb.append(" CF:").append(FREQUENCY_FORMAT.format(getCenterFrequency() / 1E6D));
+ sb.append(" MIN:").append(FREQUENCY_FORMAT.format(getMinimumFrequency() / 1E6D));
+ sb.append(" MAX:").append(FREQUENCY_FORMAT.format(getMaximumFrequency() / 1E6D));
+ sb.append(" | Channel COUNT:").append(getChannelCount());
+ sb.append(" BW:").append(FREQUENCY_FORMAT.format(getChannelBandwidth() / 1E6D));
+ sb.append(" SR:").append(FREQUENCY_FORMAT.format(getChannelSampleRate() / 1E6D));
return sb.toString();
}
}
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java
index 382c936d..e8a8c1d0 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java
@@ -35,6 +35,7 @@ import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.channel.TunerChannel;
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
import io.github.dsheirer.util.Dispatcher;
+import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -43,6 +44,7 @@ import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,6 +67,7 @@ import org.slf4j.LoggerFactory;
*/
public class PolyphaseChannelManager implements ISourceEventProcessor
{
+ private static final DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelManager.class);
private static final double MINIMUM_CHANNEL_BANDWIDTH = 25000.0;
private static final double CHANNEL_OVERSAMPLING = 2.0;
@@ -123,6 +126,34 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
this(tunerController, tunerController.getFrequency(), tunerController.getSampleRate());
}
+ /**
+ * Provides a description of the state of this manager.
+ */
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Polyphase Channel Manager Providing [").append(mChannelSources.size()).append("] Channels");
+ sb.append("\n\t").append(mChannelCalculator);
+ for(PolyphaseChannelSource pcs: mChannelSources)
+ {
+ List indexes = pcs.getOutputProcessorIndexes();
+ double sampleRate = pcs.getSampleRate();
+ long indexCenterFrequency = pcs.getIndexCenterFrequency();
+ long appliedFrequencyOffset = pcs.getFrequencyOffset();
+ long requestedCenterFrequency = pcs.getFrequency();
+
+ sb.append("\n\tPolyphase | Tuner SR:").append(FREQUENCY_FORMAT.format(pcs.getTunerSampleRate() / 1E6d));
+ sb.append(" CF:").append(FREQUENCY_FORMAT.format(pcs.getTunerCenterFrequency() / 1E6d));
+ sb.append(" BW: ").append(FREQUENCY_FORMAT.format(sampleRate / 1E6d));
+ sb.append(" | Channel CF: ").append(FREQUENCY_FORMAT.format(indexCenterFrequency / 1E6d));
+ sb.append(" REQUESTED CF: ").append(FREQUENCY_FORMAT.format(requestedCenterFrequency / 1E6d));
+ sb.append(" MIXER:").append(FREQUENCY_FORMAT.format(appliedFrequencyOffset / 1E6d));
+ sb.append(" | Polyphase Indices: ").append(indexes);
+ }
+
+ return sb.toString();
+ }
+
public void stopAllChannels()
{
mRunning = false;
@@ -278,7 +309,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
//no-op
break;
default:
- mLog.info("Unrecognized source event: " + sourceEvent.toString());
+ mLog.info("Unrecognized source event: " + sourceEvent);
break;
}
}
@@ -442,16 +473,18 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
/**
* Processes the incoming buffer stream from the provider and transfers the buffers to the polyphase channelizer.
*
- * This monitor incorporates a queue for receiving source events that affect configuration of the channelizer so
- * that they can be processed on the buffer processor calling thread, avoiding unnecessary locks on the channelizer
- * and/or the channel sources and output processors.
+ * This monitor incorporates a source event handler that queues a center frequency update so that it can be
+ * handled on the buffer processing thread, avoiding having to lock on the output processor thread. Since we
+ * anticipate that these two threads will contend for access to this update required flag, we use an update lock
+ * to protect access to the flag.
*/
public class NativeBufferReceiver implements Listener
{
private boolean mOutputProcessorUpdateRequired = false;
+ private ReentrantLock mUpdateFlagLock = new ReentrantLock();
/**
- * Queues the source event for deferred execution on the buffer processing thread.
+ * Processes tuner center frequency change source events to flag when output processors need updating.
* @param event that affects configuration of the channelizer (frequency or sample rate change events)
*/
public void receive(SourceEvent event)
@@ -460,14 +493,31 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
if(mChannelCalculator.getCenterFrequency() != frequency)
{
- mChannelCalculator.setCenterFrequency(frequency);
- mOutputProcessorUpdateRequired = true;
+ mUpdateFlagLock.lock();
+
+ try
+ {
+ //Update the channel calculator frequency so that it's ready when the output processor update occurs
+ mChannelCalculator.setCenterFrequency(frequency);
+ mOutputProcessorUpdateRequired = true;
+ }
+ finally
+ {
+ mUpdateFlagLock.unlock();
+ }
}
}
+ /**
+ * Process native buffer streams and update polyphase output channels when the parent tuner center
+ * frequency changes.
+ * @param nativeBuffer of sample to process.
+ */
@Override
public void receive(INativeBuffer nativeBuffer)
{
+ mUpdateFlagLock.lock();
+
try
{
if(mOutputProcessorUpdateRequired)
@@ -475,21 +525,28 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
updateOutputProcessors();
mOutputProcessorUpdateRequired = false;
}
+ }
+ finally
+ {
+ mUpdateFlagLock.unlock();
+ }
- if(mPolyphaseChannelizer != null)
+ if(mPolyphaseChannelizer != null)
+ {
+ Iterator iterator = nativeBuffer.iteratorInterleaved();
+
+ while(iterator.hasNext())
{
- Iterator iterator = nativeBuffer.iteratorInterleaved();
-
- while(iterator.hasNext())
+ try
{
mPolyphaseChannelizer.receive(iterator.next());
}
+ catch(Throwable throwable)
+ {
+ mLog.error("Error", throwable);
+ }
}
}
- catch(Throwable throwable)
- {
- mLog.error("Error", throwable);
- }
}
}
}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
index a2b00c7b..70a82569 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
@@ -71,6 +71,14 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
updateOutputProcessor(channelCalculator, filterManager);
}
+ /**
+ * Description of the state or configuration of the output processor.
+ */
+ public String getStateDescription()
+ {
+ return mPolyphaseChannelOutputProcessor.getStateDescription();
+ }
+
/**
* Current output processor indexes.
* @return indexes
@@ -174,7 +182,7 @@ public class PolyphaseChannelSource extends TunerChannelSource implements Listen
{
try
{
- float[] filter = filterManager.getFilter(channelCalculator.getSampleRate(),
+ float[] filter = filterManager.getFilter(channelCalculator.getChannelSampleRate(),
channelCalculator.getChannelBandwidth(), indexes.size());
mPolyphaseChannelOutputProcessor.setSynthesisFilter(filter);
}
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java
index f1929860..4d3a2ea5 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/TwoChannelSynthesizerM2.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -19,6 +19,7 @@
package io.github.dsheirer.dsp.filter.channelizer;
import io.github.dsheirer.sample.complex.ComplexSamples;
+import java.util.Arrays;
import org.apache.commons.math3.util.FastMath;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
@@ -65,6 +66,20 @@ public class TwoChannelSynthesizerM2
init(filter);
}
+ /**
+ * Description of the state/configuration of this synthesizer to support debugging.
+ * @return state description.
+ */
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Two-Channel Synthesizer");
+ sb.append("\n\t\t Interleaved Filter: " + Arrays.toString(mIQInterleavedFilter));
+ sb.append("\n\t\t Serpentine Buffer Length: ").append(mSerpentineDataBuffer.length);
+ sb.append("\n\t\t Filter Vector Product Length: ").append(mFilterVectorProduct.length);
+ return sb.toString();
+ }
+
/**
* Initializes the synthesizer filter and data buffers for operation.
*
@@ -73,7 +88,6 @@ public class TwoChannelSynthesizerM2
private void init(float[] filter)
{
int tapsPerChannel = (int) FastMath.ceil(filter.length / 2);
-
mIQInterleavedFilter = getInterleavedFilter(filter, tapsPerChannel);
mSerpentineDataBuffer = new float[mIQInterleavedFilter.length];
mFilterVectorProduct = new float[mIQInterleavedFilter.length];
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java
index 1bfd7836..6da0c0bf 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -24,6 +24,11 @@ import java.util.List;
public interface IPolyphaseChannelOutputProcessor
{
+ /**
+ * Description of the state/configuration of this output processor
+ */
+ String getStateDescription();
+
/**
* Start processing channel results
*/
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java
index 73137095..9eda378a 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -45,6 +45,16 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
mMixerAssembler.getMixer().setSampleRate(sampleRate);
}
+ @Override
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("One Channel Output Processor");
+ sb.append("\n\tIndex (doubled) [").append(mChannelOffset).append("]");
+ sb.append("\n\tMixer Assembler: none");
+ return sb.toString();
+ }
+
@Override
public void setSynthesisFilter(float[] filter)
{
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java
index d216ccf9..d8c7ff9d 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelMixerAssembler.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -42,6 +42,19 @@ public class TwoChannelMixerAssembler extends MixerAssembler
super(gain);
}
+ /**
+ * Description of the state/configuration of this mixer assembler.
+ */
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Two Channel Mixer Assembler - State Description:");
+ sb.append("\n\tTwo-Channel Synthesizer: ").append(mTwoChannelSynthesizer.getStateDescription());
+ sb.append("\n\tMixer Frequency: ").append(getMixer().getFrequency());
+ sb.append("\n\tGain: ").append(getGain().getGain());
+ return sb.toString();
+ }
+
/**
* Sets the filter coefficients to use for two-channel synthesis
* @param filter
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java
index 98c2fe31..b1ed6cda 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -49,6 +49,16 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
setSynthesisFilter(filter);
}
+ @Override
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Two Channel Output Processor");
+ sb.append("\n\tIndices (doubled) 1 [").append(mChannelOffset1).append("] 2 [").append(mChannelOffset2).append("]");
+ sb.append("\n\tMixer Assembler: ").append(mMixerAssembler.getStateDescription());
+ return sb.toString();
+ }
+
/**
* Sets the frequency offset to apply to the incoming samples to mix the desired signal to baseband.
*
diff --git a/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java b/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java
index 037993b4..22427e51 100644
--- a/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java
+++ b/src/main/java/io/github/dsheirer/dsp/gain/complex/ComplexGain.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -41,7 +41,7 @@ public abstract class ComplexGain
* Gain value that will be applied to complex samples
* @return gain value
*/
- protected float getGain()
+ public float getGain()
{
return mGain;
}
diff --git a/src/main/java/io/github/dsheirer/gui/power/ChannelPowerPanel.java b/src/main/java/io/github/dsheirer/gui/power/ChannelPowerPanel.java
index 85fce2eb..8de8071d 100644
--- a/src/main/java/io/github/dsheirer/gui/power/ChannelPowerPanel.java
+++ b/src/main/java/io/github/dsheirer/gui/power/ChannelPowerPanel.java
@@ -190,6 +190,7 @@ public class ChannelPowerPanel extends JPanel implements Listener hbtcs)
{
diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
index 78cdb12e..cf9935a9 100644
--- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java
+++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
@@ -74,9 +74,11 @@ import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
import io.github.dsheirer.source.heartbeat.IHeartbeatProvider;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -115,12 +117,15 @@ public class ProcessingChain implements Listener
private Broadcaster mMessageBroadcaster = new Broadcaster<>();
private Broadcaster mSquelchStateEventBroadcaster = new Broadcaster<>();
private AtomicBoolean mRunning = new AtomicBoolean();
- private List mModules = new ArrayList<>();
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(500);
private MessageHistory mMessageHistory = new MessageHistory(500);
private AbstractChannelState mChannelState;
private EventBus mEventBus;
protected Source mSource;
+ //Lock to protect access to the modules list.
+ private ReentrantLock mModuleLock = new ReentrantLock();
+ private List mModules = new ArrayList<>();
+
/**
* Creates a processing chain for managing a set of modules
@@ -195,30 +200,46 @@ public class ProcessingChain implements Listener
*/
public void removeTrafficChannelManager()
{
- Iterator it = mModules.iterator();
+ mModuleLock.lock();
- while(it.hasNext())
+ try
{
- if(it.next() instanceof TrafficChannelManager)
+ Iterator it = mModules.iterator();
+
+ while(it.hasNext())
{
- it.remove();
+ if(it.next() instanceof TrafficChannelManager)
+ {
+ it.remove();
+ }
}
}
+ finally
+ {
+ mModuleLock.unlock();
+ }
}
public void dispose()
{
stop();
- List modules = new ArrayList<>(mModules);
+ mModuleLock.lock();
- for(Module module : modules)
+ try
{
- removeModule(module);
- module.dispose();
- }
+ List modulesToRemove = new ArrayList<>(mModules);
- mModules.clear();
+ for(Module module : modulesToRemove)
+ {
+ removeModule(module);
+ module.dispose();
+ }
+ }
+ finally
+ {
+ mModuleLock.unlock();
+ }
mAudioSegmentBroadcaster.dispose();
mDecodeEventBroadcaster.dispose();
@@ -282,7 +303,7 @@ public class ProcessingChain implements Listener
*/
public List getModules()
{
- return mModules;
+ return Collections.unmodifiableList(mModules);
}
/**
@@ -292,13 +313,22 @@ public class ProcessingChain implements Listener
{
List decoderStates = new ArrayList<>();
- for(Module module : mModules)
+ mModuleLock.lock();
+
+ try
{
- if(module instanceof DecoderState)
+ for(Module module : mModules)
{
- decoderStates.add((DecoderState)module);
+ if(module instanceof DecoderState)
+ {
+ decoderStates.add((DecoderState)module);
+ }
}
}
+ finally
+ {
+ mModuleLock.unlock();
+ }
return decoderStates;
}
@@ -330,7 +360,17 @@ public class ProcessingChain implements Listener
*/
public void addModule(Module module)
{
- mModules.add(module);
+ mModuleLock.lock();
+
+ try
+ {
+ mModules.add(module);
+ }
+ finally
+ {
+ mModuleLock.unlock();
+ }
+
module.setInterModuleEventBus(getEventBus());
registerListeners(module);
registerProviders(module);
@@ -345,7 +385,17 @@ public class ProcessingChain implements Listener
unregisterListeners(module);
unregisterProviders(module);
module.setInterModuleEventBus(null);
- mModules.remove(module);
+
+ mModuleLock.lock();
+
+ try
+ {
+ mModules.remove(module);
+ }
+ finally
+ {
+ mModuleLock.unlock();
+ }
}
/**
@@ -664,18 +714,27 @@ public class ProcessingChain implements Listener
+ "sample type - cannot start processing chain");
}
- /* Start each of the modules */
- for(Module module : mModules)
+ mModuleLock.lock();
+
+ try
{
- try
+ /* Start each of the modules */
+ for(Module module : mModules)
{
- module.start();
- }
- catch(Exception e)
- {
- mLog.error("Error starting module", e);
+ try
+ {
+ module.start();
+ }
+ catch(Exception e)
+ {
+ mLog.error("Error starting module", e);
+ }
}
}
+ finally
+ {
+ mModuleLock.unlock();
+ }
}
else
{
@@ -716,15 +775,28 @@ public class ProcessingChain implements Listener
}
/* Stop each of the remaining modules */
- for(Module module : mModules)
- {
- module.stop();
- }
+ mModuleLock.lock();
- //Reset each of the modules
- for(Module module : mModules)
+ try
{
- module.reset();
+ for(Module module : mModules)
+ {
+ module.stop();
+ }
+
+ //Reset each of the modules
+ for(Module module : mModules)
+ {
+ module.reset();
+ }
+ }
+ catch(Exception e)
+ {
+ mLog.error("Error stopping or resetting modules", e);
+ }
+ finally
+ {
+ mModuleLock.unlock();
}
}
}
@@ -736,13 +808,22 @@ public class ProcessingChain implements Listener
{
List eventLoggingModules = new ArrayList<>();
- for(Module module : mModules)
+ mModuleLock.lock();
+
+ try
{
- if(module instanceof EventLogger)
+ for(Module module : mModules)
{
- eventLoggingModules.add(module);
+ if(module instanceof EventLogger)
+ {
+ eventLoggingModules.add(module);
+ }
}
}
+ finally
+ {
+ mModuleLock.unlock();
+ }
for(Module eventLoggingModule : eventLoggingModules)
{
@@ -757,21 +838,30 @@ public class ProcessingChain implements Listener
{
List recordingModules = new ArrayList<>();
- for(Module module : mModules)
+ mModuleLock.lock();
+
+ try
{
- if(module instanceof ComplexSamplesWaveRecorder)
+ for(Module module : mModules)
{
- recordingModules.add(module);
- }
- else if(module instanceof MBECallSequenceRecorder)
- {
- recordingModules.add(module);
- }
- else if(module instanceof BinaryRecorder)
- {
- recordingModules.add(module);
+ if(module instanceof ComplexSamplesWaveRecorder)
+ {
+ recordingModules.add(module);
+ }
+ else if(module instanceof MBECallSequenceRecorder)
+ {
+ recordingModules.add(module);
+ }
+ else if(module instanceof BinaryRecorder)
+ {
+ recordingModules.add(module);
+ }
}
}
+ finally
+ {
+ mModuleLock.unlock();
+ }
for(Module recordingModule : recordingModules)
{
diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java
index ba001ce7..91be9a0f 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/manager/ChannelSourceManager.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -52,6 +52,12 @@ public abstract class ChannelSourceManager implements ISourceEventProcessor
*/
public abstract SortedSet getTunerChannels();
+ /**
+ * State of this channel source manager for logging and debug purposes
+ * @return description of the state or configuration of this manager
+ */
+ public abstract String getStateDescription();
+
/**
* Count of tuner channels being sourced by this source manager.
* @return
diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java
index dfd577f7..bd8fd3db 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/manager/DiscoveredTuner.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -55,6 +55,27 @@ public abstract class DiscoveredTuner implements ITunerErrorListener
return mTunerStatus;
}
+ /**
+ * Logs current state of the tuner
+ */
+ public void logState()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Discovered Tuner: ").append(getId());
+
+ if(hasTuner())
+ {
+ sb.append("\n\tTuner - Frequency:").append(getTuner().getTunerController().getFrequency());
+ sb.append("\n\tChannel Manager:").append(getTuner().getChannelSourceManager().getStateDescription());
+ }
+ else
+ {
+ sb.append("\n\tTuner - no tuner");
+ }
+
+ mLog.info(sb.toString());
+ }
+
/**
* Sets the status of the discovered tuner and notifies registered listeners of the status change.
* @param tunerStatus to set
diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java
index 029aef87..d6c0de8c 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/manager/HeterodyneChannelSourceManager.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -60,6 +60,23 @@ public class HeterodyneChannelSourceManager extends ChannelSourceManager
mTunerController.addListener(this);
}
+ @Override
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Heterodyne Channel Source Manager Providing [").append(mTunerChannels.size()).append("] Channels");
+ sb.append("\n\tTuner Controller Frequency: ").append(mTunerController.getFrequency());
+ for(HalfBandTunerChannelSource channelSource: mChannelSources)
+ {
+ sb.append("\n\tChannel [").append(channelSource.getTunerChannel())
+ .append("] Frequency [").append(channelSource.getFrequency())
+ .append("] Mixer [").append(channelSource.getMixerFrequency())
+ .append("]");
+ }
+
+ return sb.toString();
+ }
+
@Override
public void stopAllChannels()
{
diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java
index a1584eb2..a1981e2d 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/manager/PassThroughSourceManager.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -54,6 +54,19 @@ public class PassThroughSourceManager extends ChannelSourceManager
mTunerController = tunerController;
}
+ @Override
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Pass-Through Channel Source Manager Providing [").append(mTunerChannels.size()).append("] Channels");
+ for(TunerChannel channel: mTunerChannels)
+ {
+ sb.append("\n\tChannel: ").append(channel);
+ }
+
+ return sb.toString();
+ }
+
@Override
public void stopAllChannels()
{
diff --git a/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java b/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java
index fc490b53..2f3c7601 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/manager/PolyphaseChannelSourceManager.java
@@ -55,6 +55,17 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
mTunerController.addListener(mPolyphaseChannelManager);
}
+ @Override
+ public String getStateDescription()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Polyphase Channel Source Manager");
+ sb.append("\n\tTuner Controller Frequency: ").append(mTunerController.getFrequency());
+ sb.append("\n\t").append(mPolyphaseChannelManager.getStateDescription());
+
+ return sb.toString();
+ }
+
@Override
public void stopAllChannels()
{
@@ -454,8 +465,13 @@ public class PolyphaseChannelSourceManager extends ChannelSourceManager
}
catch(IllegalArgumentException iae)
{
+ mLog.error("Center frequency calculation failed", iae);
//Center frequency calculation failed
}
+ catch(Exception e)
+ {
+ mLog.error("Error getting source", e);
+ }
}
}
}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java
index 615a1410..b8159f90 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/ui/DiscoveredTunerModel.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -189,6 +189,7 @@ public class DiscoveredTunerModel extends AbstractTableModel implements Listener
*/
public void removeDiscoveredTuner(DiscoveredTuner discoveredTuner)
{
+ mLog.info("Removing discovered tuner: " + discoveredTuner.getId());
mLock.lock();
try
diff --git a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java
index b78351b6..99863df4 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/ui/TunerViewPanel.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2023 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
@@ -39,7 +39,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JButton;
+import javax.swing.JMenuItem;
import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
@@ -115,6 +117,32 @@ public class TunerViewPanel extends JPanel
}
});
+ //Add support for right-click context menu to the tuner table
+ JPopupMenu popupMenu = new JPopupMenu();
+ JMenuItem logStateMenuItem = new JMenuItem("Log Tuner State");
+ logStateMenuItem.addActionListener(e -> {
+ int viewRow = mTunerTable.getSelectedRow();
+
+ DiscoveredTuner selected = null;
+
+ if(viewRow >= 0)
+ {
+ int modelRow = mTunerTable.convertRowIndexToModel(viewRow);
+ selected = mDiscoveredTunerModel.getDiscoveredTuner(modelRow);
+ }
+
+ if(selected != null)
+ {
+ selected.logState();
+ }
+ else
+ {
+ mLog.error("Can't log state - tuner not selected");
+ }
+ });
+ popupMenu.add(logStateMenuItem);
+ mTunerTable.setComponentPopupMenu(popupMenu);
+
//Monitor for tuner removal events so we can update the editor when our selected tuner is removed
mDiscoveredTunerModel.addTableModelListener(e ->
{