diff --git a/build.gradle b/build.gradle index 800e4ca0..a047bec5 100644 --- a/build.gradle +++ b/build.gradle @@ -52,11 +52,6 @@ plugins { id 'org.beryx.runtime' version '2.0.1' } -repositories { - mavenCentral() - maven { url = "https://jitpack.io" } -} - //Version is defined in gradle.properties so that we can CLI override it for nightly builds. version = project.findProperty('projectVersion') @@ -83,11 +78,11 @@ dependencies { //Jitpack imports implementation 'com.github.dnault:libresample4j:master' - implementation 'com.github.DSheirer.jmbe:jmbe-api:1.0.0' + implementation 'com.github.DSheirer.jmbe:api:v2.0.0' implementation 'com.github.nwaldispuehl:java-lame:v3.98.4' //mavenCentral/jcenter imports - implementation 'ch.qos.logback:logback-classic:1.5.38' + implementation 'ch.qos.logback:logback-classic:1.6.3' implementation 'ch.qos.logback:logback-core:1.5.38' implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.22.1' implementation 'com.fazecast:jSerialComm:2.11.4' diff --git a/settings.gradle b/settings.gradle index 62a13f1d..ad996325 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,3 +2,11 @@ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } rootProject.name = 'sdr-trunk' + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenCentral() + maven { url 'https://jitpack.io' } + } +} diff --git a/src/main/java/io/github/dsheirer/audio/codec/mbe/JmbeAudioModule.java b/src/main/java/io/github/dsheirer/audio/codec/mbe/JmbeAudioModule.java index dca5530f..cc737fbd 100644 --- a/src/main/java/io/github/dsheirer/audio/codec/mbe/JmbeAudioModule.java +++ b/src/main/java/io/github/dsheirer/audio/codec/mbe/JmbeAudioModule.java @@ -35,9 +35,11 @@ import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import jmbe.iface.IAudioCodec; import jmbe.iface.IAudioCodecLibrary; +import jmbe.iface.IAudioCodecV2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +49,7 @@ import org.slf4j.LoggerFactory; public abstract class JmbeAudioModule extends AbstractAudioModule implements Listener, IMessageListener, ISquelchStateListener { - private static final Logger mLog = LoggerFactory.getLogger(JmbeAudioModule.class); + private static final Logger LOG = LoggerFactory.getLogger(JmbeAudioModule.class); private static final String JMBE_AUDIO_LIBRARY = "JMBE"; private static final List mLibraryLoadStatusLogged = new ArrayList<>(); private IAudioCodec mAudioCodec; @@ -62,6 +64,37 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis loadConverter(); } + /** + * List of features supported by the JMBE audio library. Note: features are only supported in Version 2.0+ + * @return list of features or an empty list if the current library version doesn't support features. + */ + public List getFeatures() + { + if(mAudioCodec instanceof IAudioCodecV2 v2) + { + return v2.getFeatures(); + } + + return Collections.emptyList(); + } + + /** + * Sets the JMBE feature + * @param name of the feature + * @param value for the feature + */ + public void setFeature(String name, Object value) + { + if(mAudioCodec instanceof IAudioCodecV2 v2) + { + v2.setFeature(name, value); + } + else + { + LOG.info("Feature [" + name + "] is not supported by the current JMBE audio codec library version"); + } + } + @Override protected void closeAudioSegment() { @@ -135,7 +168,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.info("Loading JMBE library from [" + path + "]"); + LOG.info("Loading JMBE library from [" + path + "]"); } try @@ -149,7 +182,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY + getCodecName())) { - mLog.error("Couldn't load JMBE audio conversion library - " + iae.getMessage()); + LOG.error("Couldn't load JMBE audio conversion library - " + iae.getMessage()); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY + getCodecName()); } } @@ -157,7 +190,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.error("Couldn't load JMBE audio conversion library from path [" + path + "]"); + LOG.error("Couldn't load JMBE audio conversion library from path [" + path + "]"); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -165,7 +198,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.error("Couldn't load JMBE audio conversion library - class not found"); + LOG.error("Couldn't load JMBE audio conversion library - class not found"); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -187,7 +220,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.info("JMBE audio conversion library loaded: " + library.getVersion()); + LOG.info("JMBE audio conversion library loaded: " + library.getVersion()); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -195,7 +228,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.warn("JMBE library version 1.0.0 or higher is required - found: " + library.getVersion()); + LOG.warn("JMBE library version 1.0.0 or higher is required - found: " + library.getVersion()); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -204,7 +237,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.info("JMBE audio conversion library NOT FOUND"); + LOG.info("JMBE audio conversion library NOT FOUND"); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -213,7 +246,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.error("Couldn't load JMBE audio conversion library - invocation target exception", ite); + LOG.error("Couldn't load JMBE audio conversion library - invocation target exception", ite); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -221,7 +254,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.error("Couldn't load JMBE audio conversion library - instantiation exception", e1); + LOG.error("Couldn't load JMBE audio conversion library - instantiation exception", e1); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -229,7 +262,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.error("Couldn't load JMBE audio conversion library - security restrictions"); + LOG.error("Couldn't load JMBE audio conversion library - security restrictions"); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -237,7 +270,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.error("Couldn't load JMBE audio conversion library - no such method exception"); + LOG.error("Couldn't load JMBE audio conversion library - no such method exception"); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } @@ -246,7 +279,7 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis { if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY)) { - mLog.warn("JMBE audio library path is NOT SET in your User Preferences."); + LOG.warn("JMBE audio library path is NOT SET in your User Preferences."); mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY); } } 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 0d242bfa..cf4448a9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java @@ -22,6 +22,7 @@ import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.alias.action.AliasActionManager; import io.github.dsheirer.audio.*; +import io.github.dsheirer.audio.codec.mbe.JmbeAudioModule; import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.channel.state.State; import io.github.dsheirer.controller.channel.Channel; @@ -40,6 +41,7 @@ import io.github.dsheirer.module.decode.am.AMDecoder; import io.github.dsheirer.module.decode.am.AMDecoderState; import io.github.dsheirer.module.decode.am.DecodeConfigAM; import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration; +import io.github.dsheirer.module.decode.config.DecodeConfigJMBE; import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.nbfm.DeemphasisMode; import io.github.dsheirer.module.decode.squelch.dcs.DCSDecoder; @@ -116,6 +118,7 @@ import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; import io.github.dsheirer.source.tuner.channel.rotation.ChannelRotationMonitor; import java.util.ArrayList; import java.util.List; +import jmbe.iface.IAudioCodecV2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -256,8 +259,16 @@ public class DecoderFactory decoderState2.setCurrentChannel(channelDescriptor); modules.add(decoderState1); modules.add(decoderState2); - modules.add(new P25P2AudioModule(userPreferences, P25P2Message.TIMESLOT_1, aliasList)); - modules.add(new P25P2AudioModule(userPreferences, P25P2Message.TIMESLOT_2, aliasList)); + + if(channel.getDecodeConfiguration() instanceof DecodeConfigP25Phase2 configP25P2) + { + P25P2AudioModule audioModule1 = new P25P2AudioModule(userPreferences, P25P2Message.TIMESLOT_1, aliasList); + P25P2AudioModule audioModule2 = new P25P2AudioModule(userPreferences, P25P2Message.TIMESLOT_2, aliasList); + configureJMBEAudioModule(audioModule1, configP25P2); + configureJMBEAudioModule(audioModule2, configP25P2); + modules.add(audioModule1); + modules.add(audioModule2); + } //Add a channel rotation monitor when we have multiple control channel frequencies specified if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency sctmf && @@ -280,9 +291,9 @@ public class DecoderFactory AliasList aliasList, TrafficChannelManager trafficChannelManager, IChannelDescriptor channelDescriptor) { - if(channel.getDecodeConfiguration() instanceof DecodeConfigP25Phase1 p1) + if(channel.getDecodeConfiguration() instanceof DecodeConfigP25Phase1 configP25P1) { - switch(p1.getModulation()) + switch(configP25P1.getModulation()) { case C4FM: modules.add(new P25P1DecoderC4FM()); @@ -291,36 +302,38 @@ public class DecoderFactory modules.add(new P25P1DecoderLSM()); break; default: - throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + p1.getModulation() + "]"); + throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + configP25P1.getModulation() + "]"); } - } - if(channel.getChannelType() == ChannelType.STANDARD) - { - P25TrafficChannelManager primaryTCM = new P25TrafficChannelManager(channel); - modules.add(primaryTCM); - modules.add(new P25P1DecoderState(channel, primaryTCM)); - } - else if(trafficChannelManager instanceof P25TrafficChannelManager parentTCM) - { - P25P1DecoderState decoderState = new P25P1DecoderState(channel, parentTCM); - decoderState.setCurrentChannel(channelDescriptor); - modules.add(decoderState); - } - else - { - mLog.warn("Expected non-null traffic channel manager for channel " + channel.getName()); - } + if(channel.getChannelType() == ChannelType.STANDARD) + { + P25TrafficChannelManager primaryTCM = new P25TrafficChannelManager(channel); + modules.add(primaryTCM); + modules.add(new P25P1DecoderState(channel, primaryTCM)); + } + else if(trafficChannelManager instanceof P25TrafficChannelManager parentTCM) + { + P25P1DecoderState decoderState = new P25P1DecoderState(channel, parentTCM); + decoderState.setCurrentChannel(channelDescriptor); + modules.add(decoderState); + } + else + { + mLog.warn("Expected non-null traffic channel manager for channel " + channel.getName()); + } - modules.add(new P25P1AudioModule(userPreferences, aliasList)); + P25P1AudioModule audioModule = new P25P1AudioModule(userPreferences, aliasList); + configureJMBEAudioModule(audioModule, configP25P1); + modules.add(audioModule); - //Add a channel rotation monitor when we have multiple control channel frequencies specified - if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency sctmf && - sctmf.hasMultipleFrequencies()) - { - List activeStates = new ArrayList<>(); - activeStates.add(State.CONTROL); - modules.add(new ChannelRotationMonitor(activeStates, sctmf.getFrequencyRotationDelay(), userPreferences)); + //Add a channel rotation monitor when we have multiple control channel frequencies specified + if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency sctmf && + sctmf.hasMultipleFrequencies()) + { + List activeStates = new ArrayList<>(); + activeStates.add(State.CONTROL); + modules.add(new ChannelRotationMonitor(activeStates, sctmf.getFrequencyRotationDelay(), userPreferences)); + } } } @@ -520,7 +533,10 @@ public class DecoderFactory } modules.add(new NXDNDecoder(configNXDN)); - modules.add(new NXDNAudioModule(userPreferences, aliasList)); + + NXDNAudioModule audioModule = new NXDNAudioModule(userPreferences, aliasList); + configureJMBEAudioModule(audioModule, configNXDN); + modules.add(audioModule); if(channel.getChannelType() == ChannelType.STANDARD) { @@ -546,6 +562,31 @@ public class DecoderFactory } } + /** + * Configures the JMBE audio module with library version 2.0+ features. + * @param audioModule with optional supported features + * @param config with JMBE feature values + */ + private static void configureJMBEAudioModule(JmbeAudioModule audioModule, DecodeConfigJMBE config) + { + List features = audioModule.getFeatures(); + + if(features.contains(IAudioCodecV2.FEATURE_AUTOMATIC_GAIN_CONTROL)) + { + audioModule.setFeature(IAudioCodecV2.FEATURE_AUTOMATIC_GAIN_CONTROL, config.isAGC()); + } + + if(features.contains(IAudioCodecV2.FEATURE_NOISE_GENERATOR_GAIN)) + { + audioModule.setFeature(IAudioCodecV2.FEATURE_NOISE_GENERATOR_GAIN, config.getNoiseGain()); + } + + if(features.contains(IAudioCodecV2.FEATURE_TONE_GENERATOR_GAIN)) + { + audioModule.setFeature(IAudioCodecV2.FEATURE_TONE_GENERATOR_GAIN, config.getToneGain()); + } + } + /** * Creates modules for DMR decoder setup. * @@ -638,8 +679,13 @@ public class DecoderFactory modules.add(state1); modules.add(state2); - modules.add(new DMRAudioModule(userPreferences, aliasList, DMRMessage.TIMESLOT_1)); - modules.add(new DMRAudioModule(userPreferences, aliasList, DMRMessage.TIMESLOT_2)); + + DMRAudioModule audioModule1 = new DMRAudioModule(userPreferences, aliasList, DMRMessage.TIMESLOT_1); + DMRAudioModule audioModule2 = new DMRAudioModule(userPreferences, aliasList, DMRMessage.TIMESLOT_2); + configureJMBEAudioModule(audioModule1, decodeConfig); + configureJMBEAudioModule(audioModule2, decodeConfig); + modules.add(audioModule1); + modules.add(audioModule2); //Add a channel rotation monitor when we have multiple control channel frequencies specified if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency sctmf && diff --git a/src/main/java/io/github/dsheirer/module/decode/config/DecodeConfigJMBE.java b/src/main/java/io/github/dsheirer/module/decode/config/DecodeConfigJMBE.java new file mode 100644 index 00000000..edacb468 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/config/DecodeConfigJMBE.java @@ -0,0 +1,89 @@ +package io.github.dsheirer.module.decode.config; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +public abstract class DecodeConfigJMBE extends DecodeConfiguration +{ + private boolean mAGC; + private float mToneGain = 1.0f; + private float mNoiseGain = 1.0f; + + /** + * Automatic Gain Control (AGC) for the JMBE audio codec + * @return is AGC enabled. + */ + @JacksonXmlProperty(isAttribute = true, localName = "jmbeAGC", namespace = "http://www.w3.org/2001/XMLSchema-instance") + public boolean isAGC() + { + return mAGC; + } + + /** + * Sets the AGC enabled state for the JMBE audio codec + * @param enabled true to enable AGC + */ + public void setAGC(boolean enabled) + { + mAGC = enabled; + } + + /** + * Tone generation gain (1.0 default) for the JMBE audio codec + * @return gain in range 0.0 (no tones) to 1.0 (default gain). + */ + @JacksonXmlProperty(isAttribute = true, localName = "jmbeToneGain", namespace = "http://www.w3.org/2001/XMLSchema-instance") + public float getToneGain() + { + return mToneGain; + } + + /** + * Sets the tone generation gain for the JMBE audio codec + * @param gain in range 0.0 (no tones) to 1.0 (default gain) + */ + public void setToneGain(float gain) + { + if(gain > 1.0f) + { + mToneGain = 1.0f; + } + else if(gain < 0) + { + mToneGain = 0; + } + else + { + mToneGain = gain; + } + } + + /** + * Sets the comfort noise insertion gain for the JMBE audio codec + * @return noise gain in range 0.0 (no noise) to 1.0 (default noise) + */ + @JacksonXmlProperty(isAttribute = true, localName = "jmbeNoiseGain", namespace = "http://www.w3.org/2001/XMLSchema-instance") + public float getNoiseGain() + { + return mNoiseGain; + } + + /** + * Sets the comfort noise insertion gain for the JMBE audio codec + * @param gain noise gain in range 0.0 (no noise) to 1.0 (default noise) + */ + public void setNoiseGain(float gain) + { + if(gain > 1.0f) + { + mNoiseGain = 1.0f; + } + else if(gain < 0) + { + mNoiseGain = 0; + } + else + { + mNoiseGain = gain; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java index 65e586d9..b1f26bdb 100644 --- a/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DecodeConfigDMR.java @@ -21,6 +21,7 @@ package io.github.dsheirer.module.decode.dmr; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.module.decode.config.DecodeConfigJMBE; import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency; import io.github.dsheirer.module.decode.dmr.message.DMRMessage; @@ -32,7 +33,7 @@ import java.util.List; /** * DMR Decoder Configuration */ -public class DecodeConfigDMR extends DecodeConfiguration +public class DecodeConfigDMR extends DecodeConfigJMBE { public static final int CHANNEL_ROTATION_DELAY_MINIMUM_MS = 200; public static final int CHANNEL_ROTATION_DELAY_DEFAULT_MS = 500; diff --git a/src/main/java/io/github/dsheirer/module/decode/nxdn/DecodeConfigNXDN.java b/src/main/java/io/github/dsheirer/module/decode/nxdn/DecodeConfigNXDN.java index 7a907ef7..e40f81ba 100644 --- a/src/main/java/io/github/dsheirer/module/decode/nxdn/DecodeConfigNXDN.java +++ b/src/main/java/io/github/dsheirer/module/decode/nxdn/DecodeConfigNXDN.java @@ -22,6 +22,7 @@ package io.github.dsheirer.module.decode.nxdn; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.module.decode.config.DecodeConfigJMBE; import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.nxdn.channel.ChannelFrequency; import io.github.dsheirer.module.decode.nxdn.layer3.proprietary.Encoding; @@ -33,7 +34,7 @@ import java.util.List; /** * NXDN decoder configuration */ -public class DecodeConfigNXDN extends DecodeConfiguration +public class DecodeConfigNXDN extends DecodeConfigJMBE { public static final int CHANNEL_ROTATION_DELAY_MINIMUM_MS = 200; public static final int CHANNEL_ROTATION_DELAY_DEFAULT_MS = 500; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/DecodeConfigP25.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/DecodeConfigP25.java index bab3c8be..c6b915dd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/DecodeConfigP25.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/DecodeConfigP25.java @@ -22,6 +22,7 @@ package io.github.dsheirer.module.decode.p25.phase1; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.github.dsheirer.module.decode.config.DecodeConfigJMBE; import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2; @@ -30,7 +31,7 @@ import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2; @JsonSubTypes.Type(value = DecodeConfigP25Phase1.class, name = "decodeConfigP25Phase1"), @JsonSubTypes.Type(value = DecodeConfigP25Phase2.class, name = "decodeConfigP25Phase2"), }) -public abstract class DecodeConfigP25 extends DecodeConfiguration +public abstract class DecodeConfigP25 extends DecodeConfigJMBE { private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT; private boolean mIgnoreDataCalls = false;