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