From 952d930b5b51d10e8650d273be6af4f6b465c717 Mon Sep 17 00:00:00 2001 From: Rachel Olivero Date: Fri, 3 Feb 2017 10:19:52 -0500 Subject: [PATCH 01/20] Adds a screen reader accessible label to the mute button. Label toggles between mute and unmute to match the action that will be taken when the button is next pressed. --- src/audio/AudioPanel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/audio/AudioPanel.java b/src/audio/AudioPanel.java index 8f95b88d..e91ecacf 100644 --- a/src/audio/AudioPanel.java +++ b/src/audio/AudioPanel.java @@ -383,6 +383,7 @@ public class AudioPanel extends JPanel implements Listener { setIcon(UNMUTED_ICON); setBorderPainted(false); + getAccessibleContext().setAccessibleName("Mute"); addActionListener(new ActionListener() { @@ -402,6 +403,7 @@ public class AudioPanel extends JPanel implements Listener public void run() { setIcon(mMuted ? MUTED_ICON : UNMUTED_ICON); + getAccessibleContext().setAccessibleName(mMuted ? "Unmute" : "Mute"); } }); } From 767b302ed708169078eda93ac40636a25326a8f3 Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 4 Feb 2017 07:32:22 -0500 Subject: [PATCH 02/20] Adds additional unresolved address exception handler to metadata updater --- .idea/dictionaries/denny.xml | 1 + src/sample/OverflowableTransferQueue.java | 40 ++++++++++++------- src/source/tuner/TunerChannelSource.java | 6 +-- .../tuner/usb/USBTransferProcessor.java | 2 +- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/.idea/dictionaries/denny.xml b/.idea/dictionaries/denny.xml index 24e59925..7f72f826 100644 --- a/.idea/dictionaries/denny.xml +++ b/.idea/dictionaries/denny.xml @@ -7,6 +7,7 @@ fleetsync icecast ident + overflowable polyphase remez samplerate diff --git a/src/sample/OverflowableTransferQueue.java b/src/sample/OverflowableTransferQueue.java index 24f4b949..d70308ca 100644 --- a/src/sample/OverflowableTransferQueue.java +++ b/src/sample/OverflowableTransferQueue.java @@ -18,20 +18,27 @@ ******************************************************************************/ package sample; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import source.tuner.TunerChannelSource; + import java.util.Collection; import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public class OverflowableTransferQueue { + private final static Logger mLog = LoggerFactory.getLogger(OverflowableTransferQueue.class); + public enum State {NORMAL, OVERFLOW}; private Listener mStateListener; private LinkedTransferQueue mQueue = new LinkedTransferQueue(); private AtomicInteger mCounter = new AtomicInteger(); + private AtomicBoolean mOverflow = new AtomicBoolean(); private int mMaximumSize; private int mResetThreshold; - private boolean mOverflow = false; /** * Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for @@ -53,7 +60,7 @@ public class OverflowableTransferQueue */ public void offer(E e) { - if(!mOverflow) + if(!mOverflow.get()) { mQueue.offer(e); @@ -73,14 +80,16 @@ public class OverflowableTransferQueue { int drainCount = mQueue.drainTo(collection, maxElements); - if(drainCount > 0) - { - int size = mCounter.addAndGet(-drainCount); + int size = mCounter.addAndGet(-drainCount); - if(mOverflow && size <= mResetThreshold) - { - setOverflow(false); - } + if(mOverflow.get()) + { + mLog.debug("Overflow - current size:" + size + " threshold:" + mResetThreshold); + } + + if(mOverflow.get() && size <= mResetThreshold) + { + setOverflow(false); } return drainCount; @@ -100,7 +109,7 @@ public class OverflowableTransferQueue */ public State getState() { - return mOverflow ? State.OVERFLOW : State.NORMAL; + return mOverflow.get() ? State.OVERFLOW : State.NORMAL; } /** @@ -108,11 +117,12 @@ public class OverflowableTransferQueue */ private void setOverflow(boolean overflow) { - mOverflow = overflow; - - if(mStateListener != null) + if(mOverflow.compareAndSet(!overflow, overflow)) { - mStateListener.receive(getState()); + if(mStateListener != null) + { + mStateListener.receive(overflow ? State.OVERFLOW : State.NORMAL); + } } } @@ -125,7 +135,7 @@ public class OverflowableTransferQueue { mQueue.clear(); mCounter.set(0); - mOverflow = false; + mOverflow.set(false); } } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index c50fd267..369b6079 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -51,7 +51,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang private static final int BUFFER_MAX_CAPACITY = 300; //Threshold for resetting buffer overflow condition - private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 200; + private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 100; private static int CHANNEL_RATE = 48000; private static int CHANNEL_PASS_FREQUENCY = 12000; @@ -108,11 +108,11 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang { if(state == OverflowableTransferQueue.State.NORMAL) { - mLog.debug("Channel [" + mTunerFrequency + "] buffer overflow - temporary pause until processing catches up"); + mLog.debug("Channel [" + mTunerChannel.getFrequency() + "] buffer overflow - temporary pause until processing catches up"); } else { - mLog.debug("Channel [" + mTunerFrequency + "] buffer overflow cleared - resuming normal processing"); + mLog.debug("Channel [" + mTunerChannel.getFrequency() + "] buffer overflow cleared - resuming normal processing"); } } }); diff --git a/src/source/tuner/usb/USBTransferProcessor.java b/src/source/tuner/usb/USBTransferProcessor.java index 053250f3..5039a0d4 100644 --- a/src/source/tuner/usb/USBTransferProcessor.java +++ b/src/source/tuner/usb/USBTransferProcessor.java @@ -56,7 +56,7 @@ public class USBTransferProcessor implements TransferCallback private static final int FILLED_BUFFER_MAX_CAPACITY = 300; //Threshold for resetting buffer overflow condition - private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 200; + private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 100; private String mDeviceName; From 183eb2f7c78f37682d4ab98af9869449885d8f2c Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 06:36:57 -0500 Subject: [PATCH 03/20] Resolves #165 - P25 decoding channel stops arbitrarily and never recovers. Issue identified with Patch Group management and circular dependencies that can develop because of stale patch groups. Refactored all patch group management into a new PatchGroupManager. Patch groups are now automatically deleted if we don't receive any update messages within 5 seconds. The PatchGroupAlias was also updated to not roll-up attributes from subordinate patched group aliases when those aliases are also patch group aliases -- this eliminates the circular dependency chaining. Added IOverflowListener interface and updated Sources to support overflow listeners. Updated ChannelState to register as an overflow listener so that it can visually depict to the user when the channel enters/exits source overflow state. Added new color pattern for OVERFLOW with yellow text on red. --- CHANGELOG | 4 +- src/alias/PatchGroupAlias.java | 32 ++- .../metadata/ChannelMetadataPanel.java | 4 + .../metadata/ChannelMetadataViewer.java | 2 +- src/channel/state/ChannelState.java | 26 +- src/channel/state/DecoderState.java | 8 + src/channel/state/State.java | 180 +++++++------ src/controller/NamingThreadFactory.java | 32 ++- src/module/ProcessingChain.java | 4 + src/module/decode/p25/P25DecoderState.java | 205 +-------------- src/module/decode/p25/PatchGroup.java | 102 ++++++++ src/module/decode/p25/PatchGroupManager.java | 243 ++++++++++++++++++ src/sample/OverflowableTransferQueue.java | 32 +-- src/sample/real/IOverflowListener.java | 28 ++ src/source/Source.java | 23 ++ src/source/tuner/TunerChannelSource.java | 37 +-- .../tuner/usb/USBTransferProcessor.java | 7 +- 17 files changed, 615 insertions(+), 354 deletions(-) create mode 100644 src/module/decode/p25/PatchGroup.java create mode 100644 src/module/decode/p25/PatchGroupManager.java create mode 100644 src/sample/real/IOverflowListener.java diff --git a/CHANGELOG b/CHANGELOG index b040d09a..5fee2ff5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,10 @@ 0.3.0-beta1 20170129 + - REQUIRES: Java 8 (or newer) for compatibility and eventual changeover to JavaFX - FEATURE: audio streaming - adds capability to stream to multiple remote broadcast servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each stream can be independently configured with a delay. Audio calls are directed to one or more streams by attaching one or more stream identifiers to each alias. - - Requires Java 8 (or newer) for compatibility and eventual changeover to JavaFX - Extensive GUI updates for tuner, channel and alias management. Includes new 'Now Playing' channel view and 'Detail' view. - Updated alias list management of alias id values and wildcards. Weighting is now @@ -17,7 +17,7 @@ - Implemented bounded queues for both tuner and individual channel IQ sample buffers. Bounded queues have ability to enter/exit overflow state when queue size reaches overflow threshold and reset when queue size falls below the reset - threshold. + threshold. Channel state will show OVERFLOW in red/yellow. - Updated airspy controller for airspy-mini specific sample rate handling. - New developer remez filter designer tool and internal usage diff --git a/src/alias/PatchGroupAlias.java b/src/alias/PatchGroupAlias.java index b4788a86..b2c0d2fa 100644 --- a/src/alias/PatchGroupAlias.java +++ b/src/alias/PatchGroupAlias.java @@ -168,11 +168,14 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - for(AliasAction action: alias.getAction()) + if(!(alias instanceof PatchGroupAlias)) { - if(!aliasActions.contains(action)) + for(AliasAction action: alias.getAction()) { - aliasActions.add(action); + if(!aliasActions.contains(action)) + { + aliasActions.add(action); + } } } } @@ -185,7 +188,7 @@ public class PatchGroupAlias extends Alias { for(Alias alias: mPatchedAliases) { - if(alias.hasActions()) + if(!(alias instanceof PatchGroupAlias) && alias.hasActions()) { return true; } @@ -201,7 +204,9 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - if(alias.hasCallPriority() && alias.getCallPriority() < highestPriority) + if(!(alias instanceof PatchGroupAlias) && + alias.hasCallPriority() && + alias.getCallPriority() < highestPriority) { highestPriority = alias.getCallPriority(); } @@ -220,7 +225,7 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - if(alias.hasCallPriority()) + if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority()) { return true; } @@ -239,7 +244,7 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - if(alias.isRecordable()) + if(!(alias instanceof PatchGroupAlias) && alias.isRecordable()) { return true; } @@ -258,7 +263,7 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - if(alias.isStreamable()) + if(!(alias instanceof PatchGroupAlias) && alias.isStreamable()) { return true; } @@ -279,11 +284,14 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels()) + if(!(alias instanceof PatchGroupAlias)) { - if(!broadcastChannels.contains(broadcastChannel)) + for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels()) { - broadcastChannels.add(broadcastChannel); + if(!broadcastChannels.contains(broadcastChannel)) + { + broadcastChannels.add(broadcastChannel); + } } } } @@ -301,7 +309,7 @@ public class PatchGroupAlias extends Alias for(Alias alias: mPatchedAliases) { - if(alias.hasBroadcastChannel(channel)) + if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel)) { return true; } diff --git a/src/channel/metadata/ChannelMetadataPanel.java b/src/channel/metadata/ChannelMetadataPanel.java index e9ecef00..5fa80231 100644 --- a/src/channel/metadata/ChannelMetadataPanel.java +++ b/src/channel/metadata/ChannelMetadataPanel.java @@ -210,6 +210,10 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE); foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE); break; + case OVERFLOW: + background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "overflow", Color.RED); + foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "overflow", Color.YELLOW); + break; case RESET: background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK); foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW); diff --git a/src/channel/metadata/ChannelMetadataViewer.java b/src/channel/metadata/ChannelMetadataViewer.java index 53996650..e7efc2c2 100644 --- a/src/channel/metadata/ChannelMetadataViewer.java +++ b/src/channel/metadata/ChannelMetadataViewer.java @@ -51,7 +51,7 @@ public class ChannelMetadataViewer extends JPanel mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager); mCallEventPanel = new CallEventPanel(iconManager); mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager); - mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager); +// mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager); mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager); init(); diff --git a/src/channel/state/ChannelState.java b/src/channel/state/ChannelState.java index 66a22984..768c1062 100644 --- a/src/channel/state/ChannelState.java +++ b/src/channel/state/ChannelState.java @@ -35,6 +35,7 @@ import module.decode.event.ICallEventProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; +import sample.real.IOverflowListener; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; @@ -42,7 +43,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener, - IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener + IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener, IOverflowListener { private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class); @@ -63,6 +64,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder private boolean mSquelchLocked = false; private boolean mSelected = false; + private boolean mSourceOverflow = false; private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY; private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; @@ -123,6 +125,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder mState = State.IDLE; mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); + mSourceOverflow = false; } @Override @@ -350,15 +353,26 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder break; } - mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, mState)); - } - else - { -// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" ); + mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, + mSourceOverflow ? State.OVERFLOW : mState)); } } } + /** + * This method is invoked if the source buffer provider goes into overflow state. Since this is an external state, + * we use the mSourceOverflow variable to override the internal state reported to external listeners. + * @param overflow true to indicate an overflow state + */ + @Override + public void sourceOverflow(boolean overflow) + { + mSourceOverflow = overflow; + + mMutableMetadata.receive(new AttributeChangeRequest(Attribute.CHANNEL_STATE, + mSourceOverflow ? State.OVERFLOW : mState)); + } + /** * Sets the state and processes related actions */ diff --git a/src/channel/state/DecoderState.java b/src/channel/state/DecoderState.java index a27dd7be..8f7ecbf8 100644 --- a/src/channel/state/DecoderState.java +++ b/src/channel/state/DecoderState.java @@ -51,6 +51,14 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv return mAliasList != null; } + /** + * Provides subclass reference to the call event broadcaster + */ + protected Broadcaster getCallEventBroadcaster() + { + return mCallEventBroadcaster; + } + /** * Reset the decoder state to prepare for processing a different sample * source diff --git a/src/channel/state/State.java b/src/channel/state/State.java index 83336d2c..f2c75767 100644 --- a/src/channel/state/State.java +++ b/src/channel/state/State.java @@ -1,17 +1,17 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014-2016 Dennis Sheirer - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ @@ -23,98 +23,94 @@ import java.util.EnumSet; * Details the set of states for a channel and the allowable transition states */ public enum State -{ - IDLE( "IDLE" ) - { - @Override - public boolean canChangeTo( State state ) +{ + IDLE("IDLE") { - return state != TEARDOWN && - state != RESET; - } - }, - CALL( "CALL" ) - { - @Override - public boolean canChangeTo( State state ) + @Override + public boolean canChangeTo(State state) + { + return state != TEARDOWN && state != RESET && state != OVERFLOW; + } + }, + CALL("CALL") { - return state == CONTROL || - state == DATA || - state == ENCRYPTED || - state == FADE || - state == TEARDOWN; - } - }, - DATA( "DATA" ) - { - @Override - public boolean canChangeTo( State state ) + @Override + public boolean canChangeTo(State state) + { + return state == CONTROL || state == DATA || state == ENCRYPTED || state == FADE || state == TEARDOWN; + } + }, + DATA("DATA") { - return state == CALL || - state == CONTROL || - state == ENCRYPTED || - state == FADE || - state == TEARDOWN; - } - }, - ENCRYPTED( "ENCRYPTED" ) + @Override + public boolean canChangeTo(State state) + { + return state == CALL || state == CONTROL || state == ENCRYPTED || state == FADE || state == TEARDOWN; + } + }, + ENCRYPTED("ENCRYPTED") + { + @Override + public boolean canChangeTo(State state) + { + return state == FADE || state == TEARDOWN; + } + }, + CONTROL("CONTROL") + { + @Override + public boolean canChangeTo(State state) + { + return state == IDLE || state == FADE; + } + }, + FADE("FADE") + { + @Override + public boolean canChangeTo(State state) + { + return state != FADE && state != RESET && state != OVERFLOW; + } + }, + OVERFLOW("OVERFLOW") + { + @Override + public boolean canChangeTo(State state) + { + return false; + } + }, + RESET("RESET") + { + @Override + public boolean canChangeTo(State state) + { + return state == IDLE; + } + }, + TEARDOWN("TEARDOWN") + { + @Override + public boolean canChangeTo(State state) + { + return state == RESET; + } + }; + + private String mDisplayValue; + + public static final EnumSet CALL_STATES = EnumSet.of(CALL, CONTROL, DATA, ENCRYPTED); + public static final EnumSet IDLE_STATES = EnumSet.of(IDLE, FADE); + + private State(String displayValue) { - @Override - public boolean canChangeTo( State state ) - { - return state == FADE || state == TEARDOWN; - } - }, - CONTROL( "CONTROL" ) + mDisplayValue = displayValue; + } + + public abstract boolean canChangeTo(State state); + + public String getDisplayValue() { - @Override - public boolean canChangeTo( State state ) - { - return state == IDLE || - state == FADE; - } - }, - FADE( "FADE" ) - { - @Override - public boolean canChangeTo( State state ) - { - return state != FADE && - state != RESET; - } - }, - RESET( "RESET" ) - { - @Override - public boolean canChangeTo(State state) - { - return state == IDLE; - } - }, - - TEARDOWN( "TEARDOWN" ) - { - @Override - public boolean canChangeTo(State state) - { - return state == RESET; - } - }; - - private String mDisplayValue; - - public static final EnumSet CALL_STATES = EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); - public static final EnumSet IDLE_STATES = EnumSet.of( IDLE, FADE ); - - private State( String displayValue ) - { - mDisplayValue = displayValue; - } - - public abstract boolean canChangeTo( State state ); - - public String getDisplayValue() - { - return mDisplayValue; - } + return mDisplayValue; + } } \ No newline at end of file diff --git a/src/controller/NamingThreadFactory.java b/src/controller/NamingThreadFactory.java index ae3302b1..ed7bd0dc 100644 --- a/src/controller/NamingThreadFactory.java +++ b/src/controller/NamingThreadFactory.java @@ -1,5 +1,9 @@ package controller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import util.ThreadPool; + import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; @@ -8,24 +12,23 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class NamingThreadFactory implements ThreadFactory { - static final AtomicInteger poolNumber = new AtomicInteger(1); + private final static Logger mLog = LoggerFactory.getLogger(ThreadPool.class); + + private static final AtomicInteger mPoolNumber = new AtomicInteger(1); - final ThreadGroup mThreadGroup; + private final ThreadGroup mThreadGroup; - final AtomicInteger mThreadNumber = new AtomicInteger(1); + private final AtomicInteger mThreadNumber = new AtomicInteger(1); - final String mNamePrefix; + private final String mNamePrefix; public NamingThreadFactory( String prefix ) { SecurityManager s = System.getSecurityManager(); - mThreadGroup = (s != null)? s.getThreadGroup() : - Thread.currentThread().getThreadGroup(); + mThreadGroup = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - mNamePrefix = prefix + " pool-" + - poolNumber.getAndIncrement() + - "-thread-"; + mNamePrefix = prefix + " pool-" + mPoolNumber.getAndIncrement() + "-thread-"; } public Thread newThread( Runnable runnable ) @@ -42,7 +45,16 @@ public class NamingThreadFactory implements ThreadFactory { thread.setPriority( Thread.NORM_PRIORITY ); } - + + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() + { + @Override + public void uncaughtException(Thread t, Throwable e) + { + mLog.error("Error while executing runnable in scheduled thread pool [" + t.getName() + "]", e); + } + }); + return thread; } } \ No newline at end of file diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index d8a13e9a..fdf60e86 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -538,6 +538,9 @@ public class ProcessingChain implements IChannelEventListener module.reset(); } + //Setup the channel state to monitor source overflow conditions + mSource.setOverflowListener(mChannelState); + /* Register with the source to receive sample data. Setup a * timer task to process the buffer queues 50 times a second * (every 20 ms) */ @@ -606,6 +609,7 @@ public class ProcessingChain implements IChannelEventListener if(mSource != null) { mSource.stop(); + mSource.setOverflowListener(null); switch(mSource.getSampleType()) { diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 156c4fdd..2b835de3 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -19,9 +19,7 @@ package module.decode.p25; import alias.Alias; import alias.AliasList; -import alias.PatchGroupAlias; import alias.id.AliasIDType; -import alias.id.talkgroup.TalkgroupID; import channel.metadata.AliasedStringAttributeMonitor; import channel.metadata.Attribute; import channel.metadata.AttributeChangeRequest; @@ -163,7 +161,6 @@ public class P25DecoderState extends DecoderState private Map mBands = new HashMap<>(); private Map mRegistrations = new HashMap<>(); private Map mNeighborMap = new HashMap<>(); - private Map> mPatchGroupMap = new HashMap<>(); private String mLastCommandEventID; private String mLastPageEventID; @@ -188,6 +185,7 @@ public class P25DecoderState extends DecoderState private P25CallEvent mCurrentCallEvent; private List mCallDetectTalkgroups = new ArrayList<>(); private Map mChannelCallMap = new HashMap<>(); + private PatchGroupManager mPatchGroupManager; public P25DecoderState(AliasList aliasList, ChannelType channelType, @@ -199,6 +197,7 @@ public class P25DecoderState extends DecoderState mModulation = modulation; mIgnoreDataCalls = ignoreDataCalls; + mPatchGroupManager = new PatchGroupManager(aliasList, getCallEventBroadcaster()); mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2, getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE); mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, @@ -2719,6 +2718,9 @@ public class P25DecoderState extends DecoderState switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode()) { case PATCH_GROUP_CHANNEL_GRANT: + //Cleanup patch groups - auto-expire any patch groups before we allocate a channel + mPatchGroupManager.cleanupPatchGroups(); + PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk; channel = pgvcg.getChannel(); @@ -2754,6 +2756,9 @@ public class P25DecoderState extends DecoderState mChannelCallMap.get(channel))); break; case PATCH_GROUP_CHANNEL_GRANT_UPDATE: + //Cleanup patch groups - auto-expire any patch groups before we allocate a channel + mPatchGroupManager.cleanupPatchGroups(); + PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk; channel = gvcgu.getChannel1(); @@ -2784,35 +2789,11 @@ public class P25DecoderState extends DecoderState break; case PATCH_GROUP_ADD: PatchGroupAdd pga = (PatchGroupAdd)tsbk; - - if(addPatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups())) - { - StringBuilder sb = new StringBuilder(); - - sb.append("PATCH GROUP:").append(pga.getPatchGroupAddress()); - sb.append(" ").append(pga.getPatchedTalkgroups()); - - broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_ADD) - .aliasList(getAliasList()) - .details(sb.toString()) - .build()); - } + mPatchGroupManager.updatePatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups()); break; case PATCH_GROUP_DELETE: PatchGroupDelete pgd = (PatchGroupDelete)tsbk; - - if(removePatchGroup(pgd.getPatchGroupAddress(), pgd.getPatchedTalkgroups())) - { - StringBuilder sbpgd = new StringBuilder(); - - sbpgd.append("PATCH GROUP:").append(pgd.getPatchGroupAddress()); - sbpgd.append(" ").append(pgd.getPatchedTalkgroups()); - - broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_DELETE) - .aliasList(getAliasList()) - .details(sbpgd.toString()) - .build()); - } + mPatchGroupManager.removePatchGroup(pgd.getPatchGroupAddress()); break; case CCH_PLANNED_SHUTDOWN: if(!mControlChannelShutdownLogged) @@ -2827,174 +2808,14 @@ public class P25DecoderState extends DecoderState } } - /** - * Creates a new patch group or updates an existing patch group with the patched talkgroup entries. - * - * @param patchGroupID for the patch group - * @param talkgroupsToAdd to include in the patch group - * @return true if the patch group or the patch group map was updated - */ - private boolean addPatchGroup(String patchGroupID, List talkgroupsToAdd) - { - boolean updated = false; - - if(patchGroupID != null && !talkgroupsToAdd.isEmpty()) - { - List patchedTalkgroups = mPatchGroupMap.get(patchGroupID); - - if(patchedTalkgroups == null) - { - patchedTalkgroups = new ArrayList<>(); - } - - for(String talkgroupToAdd : talkgroupsToAdd) - { - //Exclude the patch group ID if it is included in the patched talkgroup list so that we don't have - //an infinite loop situation - if(talkgroupToAdd.equals(patchGroupID)) - { - continue; - } - - if(!patchedTalkgroups.contains(talkgroupToAdd)) - { - patchedTalkgroups.add(talkgroupToAdd); - updated = true; - } - } - - if(updated) - { - mPatchGroupMap.put(patchGroupID, patchedTalkgroups); - updatePatchAlias(patchGroupID, patchedTalkgroups); - } - } - - return updated; - } - - /** - * Removes the patched talkgroups from the patch group and removes the patch group from the patch group map if - * there are no more patched talkgroups in the patch group. - * - * @param patchGroupID for the patch group - * @param talkgroupsToRemove to remove from the patch group - * @return true if the patch group or the patch group map was updated - */ - private boolean removePatchGroup(String patchGroupID, List talkgroupsToRemove) - { - boolean updated = false; - - if(patchGroupID != null && !talkgroupsToRemove.isEmpty()) - { - List patchedTalkgroups = mPatchGroupMap.get(patchGroupID); - - if(patchedTalkgroups != null) - { - for(String talkgroupToRemove : talkgroupsToRemove) - { - if(patchedTalkgroups.contains(talkgroupToRemove)) - { - patchedTalkgroups.remove(talkgroupToRemove); - updated = true; - } - } - - if(patchedTalkgroups.isEmpty()) - { - mPatchGroupMap.remove(patchGroupID); - removePatchGroupAlias(patchGroupID); - updated = true; - } - else - { - updatePatchAlias(patchGroupID, patchedTalkgroups); - } - } - } - - return updated; - } - - /** - * Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the - * patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and - * replaced with the aliases corresponding to the patched talkgroup aliases. - * - * @param patchGroupID for the patch group - * @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups - */ - private void updatePatchAlias(String patchGroupID, List patchedTalkgroups) - { - PatchGroupAlias patchGroupAlias = null; - - //Check for an existing alias for the patch talkgroup - do not include wildcard aliases - Alias existingAlias = getAliasList().getTalkgroupAlias(patchGroupID, false); - - if(existingAlias instanceof PatchGroupAlias) - { - patchGroupAlias = (PatchGroupAlias)existingAlias; - } - else - { - patchGroupAlias = new PatchGroupAlias(); - - patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID)); - - if(existingAlias != null) - { - getAliasList().removeAlias(existingAlias); - - patchGroupAlias.setPatchGroupAlias(existingAlias); - } - - getAliasList().addAlias(patchGroupAlias); - } - - if(patchGroupAlias != null) - { - patchGroupAlias.setPatchedTalkgroupIDs(patchedTalkgroups); - - patchGroupAlias.clearPatchedAliases(); - - for(String patchedTalkgroup : patchedTalkgroups) - { - Alias patchedAlias = getAliasList().getTalkgroupAlias(patchedTalkgroup); - - if(patchedAlias != null) - { - patchGroupAlias.addPatchedAlias(patchedAlias); - } - } - } - } - - /** - * Removes the temporary patch group alias from the alias list - */ - private void removePatchGroupAlias(String patchGroupID) - { - Alias alias = getAliasList().getTalkgroupAlias(patchGroupID); - - if(alias instanceof PatchGroupAlias) - { - PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias; - - getAliasList().removeAlias(patchGroupAlias); - - //Replace our temporary patch group alias with the original alias for the patch group ID - if(patchGroupAlias.hasPatchGroupAlias()) - { - getAliasList().addAlias(patchGroupAlias.getPatchGroupAlias()); - } - } - } - /** * Process a traffic channel allocation message */ private void processTSBKChannelGrant(TSBKMessage message) { + //Cleanup patch groups - auto-expire any patch groups before we allocate a channel + mPatchGroupManager.cleanupPatchGroups(); + String channel = null; String from = null; String to = null; diff --git a/src/module/decode/p25/PatchGroup.java b/src/module/decode/p25/PatchGroup.java new file mode 100644 index 00000000..525602ad --- /dev/null +++ b/src/module/decode/p25/PatchGroup.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ +package module.decode.p25; + +import java.util.ArrayList; +import java.util.List; + +public class PatchGroup +{ + private long mExpireThreshold = 5000; //5 seconds + private long mLastUpdated; + private String mPatchGroupID; + private List mPatchedGroups = new ArrayList<>(); + + /** + * Constructs a patch group with the specified patch group ID + * @param patchGroupID that identifies this patch group + */ + public PatchGroup(String patchGroupID) + { + mPatchGroupID = patchGroupID; + mLastUpdated = System.currentTimeMillis(); + } + + /** + * Patch group ID for this patch group + */ + public String getPatchGroupID() + { + return mPatchGroupID; + } + + /** + * Adds the patched group to this patch group. Duplicate patched groups are ignored. + * + * Note: the patch group ID will not be added as a patched group, to avoid circular dependencies. + * + * @return true if the patched group was added to this patch group + */ + public boolean addPatchedGroup(String patchedGroup) + { + if(patchedGroup != null && !patchedGroup.equals(mPatchGroupID) && !mPatchedGroups.contains(patchedGroup)) + { + mPatchedGroups.add(patchedGroup); + return true; + } + + return false; + } + + /** + * Patched talkgroups that are part of this patch group. + */ + public List getPatchedGroups() + { + return mPatchedGroups; + } + + /** + * Updates the timestamp for this patch group. + */ + public void updateTimestamp() + { + mLastUpdated = System.currentTimeMillis(); + } + + /** + * Indicates if this patch group is expired, meaning that it hasn't been updated in the past (expire + * threshold) seconds. + */ + public boolean isExpired() + { + return System.currentTimeMillis() - mLastUpdated > mExpireThreshold; + } + + /** + * Sets the patch group expiration threshold (default = 5000 ms). A patch group is considered expired if + * no new updates have been received within the past (expire threshold) milliseconds + * + * @param expireThreshold in milliseconds + */ + public void setExpireThreshold(long expireThreshold) + { + mExpireThreshold = expireThreshold; + } +} diff --git a/src/module/decode/p25/PatchGroupManager.java b/src/module/decode/p25/PatchGroupManager.java new file mode 100644 index 00000000..79955038 --- /dev/null +++ b/src/module/decode/p25/PatchGroupManager.java @@ -0,0 +1,243 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ +package module.decode.p25; + +import alias.Alias; +import alias.AliasList; +import alias.PatchGroupAlias; +import alias.id.talkgroup.TalkgroupID; +import module.decode.event.CallEvent; +import sample.Broadcaster; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class PatchGroupManager +{ + private AliasList mAliasList; + private List mPatchGroups = new ArrayList<>(); + private Broadcaster mCallEventBroadcaster; + + /** + * Manages active P25 patch groups and auto-expires patch groups if not updated every 5 seconds. + * @param aliasList to keep updated with patch group changes + * @param broadcaster for rebroadcasting patch group related call events + */ + public PatchGroupManager(AliasList aliasList, Broadcaster broadcaster) + { + mAliasList = aliasList; + mCallEventBroadcaster = broadcaster; + } + + /** + * Removes the patch group. + * + * @param patchGroupID of the patch group to remove + */ + public void removePatchGroup(String patchGroupID) + { + removePatchGroup(patchGroupID, false); + } + + /** + * Removes the patch group. + * + * @param patchGroupID of the patch group to remove + * @param expired true if the patch group is being removed because it expired, false if we receive a + * message commanding commanding a patch group delete. + */ + private void removePatchGroup(String patchGroupID, boolean expired) + { + PatchGroup patchGroup = getPatchGroup(patchGroupID); + + if(patchGroup != null) + { + //Cleanup the alias list + if(mAliasList != null) + { + Alias alias = mAliasList.getTalkgroupAlias(patchGroupID); + + if(alias instanceof PatchGroupAlias) + { + PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias; + + mAliasList.removeAlias(patchGroupAlias); + + //Replace our temporary patch group alias with the original alias for the patch group ID + if(patchGroupAlias.hasPatchGroupAlias()) + { + mAliasList.addAlias(patchGroupAlias.getPatchGroupAlias()); + } + } + } + + mPatchGroups.remove(patchGroup); + + if(mCallEventBroadcaster != null) + { + StringBuilder sb = new StringBuilder(); + + sb.append((expired ? "EXPIRED " : "DELETED ")); + sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID()); + sb.append(" ").append(patchGroup.getPatchedGroups()); + + + mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_DELETE) + .to(patchGroup.getPatchGroupID()) + .details(sb.toString()) + .build()); + } + } + } + + /** + * Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the + * patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and + * replaced with the aliases corresponding to the patched talkgroup aliases. + * + * @param patchGroupID for the patch group + * @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups + */ + public void updatePatchGroup(String patchGroupID, List patchedTalkgroups) + { + boolean updated = false; + + PatchGroup patchGroup = getPatchGroup(patchGroupID); + + if(patchGroup == null) + { + //Remove any expired patch groups before we add a new one + cleanupPatchGroups(); + patchGroup = new PatchGroup(patchGroupID); + mPatchGroups.add(patchGroup); + } + else + { + patchGroup.updateTimestamp(); + } + + for(String patchedGroup: patchedTalkgroups) + { + if(patchGroup.addPatchedGroup(patchedGroup)) + { + updated = true; + } + } + + //If we make any changes to the patch group, update the patch group alias + if(updated) + { + PatchGroupAlias patchGroupAlias = null; + + //Check for an existing alias for the patch talkgroup - do not include wildcard aliases + if(mAliasList != null) + { + Alias existingAlias = mAliasList.getTalkgroupAlias(patchGroupID, false); + + if(existingAlias instanceof PatchGroupAlias) + { + patchGroupAlias = (PatchGroupAlias)existingAlias; + } + else + { + patchGroupAlias = new PatchGroupAlias(); + + patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID)); + + if(existingAlias != null) + { + mAliasList.removeAlias(existingAlias); + + patchGroupAlias.setPatchGroupAlias(existingAlias); + } + + mAliasList.addAlias(patchGroupAlias); + } + + if(patchGroupAlias != null) + { + patchGroupAlias.setPatchedTalkgroupIDs(patchGroup.getPatchedGroups()); + + patchGroupAlias.clearPatchedAliases(); + + for(String patchedTalkgroup : patchGroup.getPatchedGroups()) + { + Alias patchedAlias = mAliasList.getTalkgroupAlias(patchedTalkgroup); + + if(patchedAlias != null) + { + patchGroupAlias.addPatchedAlias(patchedAlias); + } + } + } + } + + if(mCallEventBroadcaster != null) + { + StringBuilder sb = new StringBuilder(); + + sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID()); + sb.append(" ").append(patchGroup.getPatchedGroups()); + + mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_ADD) + .aliasList(mAliasList) + .details(sb.toString()) + .build()); + } + } + } + + /** + * Returns the patch group matching the patch group ID or null + */ + private PatchGroup getPatchGroup(String patchGroupID) + { + for(PatchGroup patchGroup: mPatchGroups) + { + if(patchGroup.getPatchGroupID().equals(patchGroupID)) + { + return patchGroup; + } + } + + return null; + } + + /** + * Checks the last updated timestamp on all patch groups and removes any patch groups that have not been + * updated in the past 5 seconds + */ + public void cleanupPatchGroups() + { + Iterator it = mPatchGroups.iterator(); + + while(it.hasNext()) + { + PatchGroup patchGroup = it.next(); + + if(patchGroup.isExpired()) + { + it.remove(); + + removePatchGroup(patchGroup.getPatchGroupID(), true); + } + } + } +} diff --git a/src/sample/OverflowableTransferQueue.java b/src/sample/OverflowableTransferQueue.java index d70308ca..f9c9c190 100644 --- a/src/sample/OverflowableTransferQueue.java +++ b/src/sample/OverflowableTransferQueue.java @@ -20,6 +20,7 @@ package sample; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sample.real.IOverflowListener; import source.tuner.TunerChannelSource; import java.util.Collection; @@ -32,7 +33,7 @@ public class OverflowableTransferQueue private final static Logger mLog = LoggerFactory.getLogger(OverflowableTransferQueue.class); public enum State {NORMAL, OVERFLOW}; - private Listener mStateListener; + private IOverflowListener mOverflowListener; private LinkedTransferQueue mQueue = new LinkedTransferQueue(); private AtomicInteger mCounter = new AtomicInteger(); @@ -82,11 +83,6 @@ public class OverflowableTransferQueue int size = mCounter.addAndGet(-drainCount); - if(mOverflow.get()) - { - mLog.debug("Overflow - current size:" + size + " threshold:" + mResetThreshold); - } - if(mOverflow.get() && size <= mResetThreshold) { setOverflow(false); @@ -96,32 +92,24 @@ public class OverflowableTransferQueue } /** - * Sets a listener to receive state change events + * Sets a listener to receive overflow state change events */ - public void setStateListener(Listener listener) + public void setOverflowListener(IOverflowListener listener) { - mStateListener = listener; + mOverflowListener = listener; } /** - * Current state of the queue, normal or overflow - */ - public State getState() - { - return mOverflow.get() ? State.OVERFLOW : State.NORMAL; - } - - /** - * Toggles the overflow state + * Toggles the overflow state and broadcast state change to listener */ private void setOverflow(boolean overflow) { if(mOverflow.compareAndSet(!overflow, overflow)) { - if(mStateListener != null) + if(mOverflowListener != null) { - mStateListener.receive(overflow ? State.OVERFLOW : State.NORMAL); + mOverflowListener.sourceOverflow(overflow); } } } @@ -136,6 +124,10 @@ public class OverflowableTransferQueue mQueue.clear(); mCounter.set(0); mOverflow.set(false); + if(mOverflowListener != null) + { + mOverflowListener.sourceOverflow(false); + } } } } diff --git a/src/sample/real/IOverflowListener.java b/src/sample/real/IOverflowListener.java new file mode 100644 index 00000000..61202ea3 --- /dev/null +++ b/src/sample/real/IOverflowListener.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ +package sample.real; + +public interface IOverflowListener +{ + /** + * Indicates that the source is in overflow state (true) or out of overflow state (false) + * @param overflow true to indicate an overflow state + */ + void sourceOverflow(boolean overflow); +} diff --git a/src/source/Source.java b/src/source/Source.java index 217def00..2de310b8 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -19,6 +19,7 @@ package source; import module.Module; import sample.SampleType; +import sample.real.IOverflowListener; /** * Abstract class to define the minimum functionality of a sample data provider. @@ -26,6 +27,7 @@ import sample.SampleType; public abstract class Source extends Module { protected SampleType mSampleType; + protected IOverflowListener mOverflowListener; public Source( SampleType sampleType ) { @@ -47,4 +49,25 @@ public abstract class Source extends Module { mSampleType = sampleType; } + + /** + * Registers the listener to receive overflow state changes. Use null argument to clear the listener + */ + public void setOverflowListener(IOverflowListener listener) + { + mOverflowListener = listener; + } + + /** + * Broadcasts an overflow state + * + * @param overflow true if overlow, false if normal + */ + protected void broadcastOverflowState(boolean overflow) + { + if(mOverflowListener != null) + { + mOverflowListener.sourceOverflow(overflow); + } + } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 369b6079..13be8ba9 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -29,6 +29,7 @@ import sample.Listener; import sample.OverflowableTransferQueue; import sample.complex.Complex; import sample.complex.ComplexBuffer; +import sample.real.IOverflowListener; import source.ComplexSource; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; @@ -101,21 +102,6 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang mTunerFrequency = mTuner.getTunerController().getFrequency(); mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD); - mBuffer.setStateListener(new Listener() - { - @Override - public void receive(OverflowableTransferQueue.State state) - { - if(state == OverflowableTransferQueue.State.NORMAL) - { - mLog.debug("Channel [" + mTunerChannel.getFrequency() + "] buffer overflow - temporary pause until processing catches up"); - } - else - { - mLog.debug("Channel [" + mTunerChannel.getFrequency() + "] buffer overflow cleared - resuming normal processing"); - } - } - }); /* Setup the frequency translator to the current source frequency */ long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency(); @@ -126,6 +112,14 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getTunerController().getSampleRate())); } + /** + * Overrides the default source overflow listener management to delegate responsibility to the overflow buffer + */ + @Override + public void setOverflowListener(IOverflowListener listener) + { + mBuffer.setOverflowListener(listener); + } public void start(ScheduledExecutorService executor) { @@ -175,7 +169,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang } else { - mLog.warn("Attempt to stop() and already stopped tuner channel source was ignored"); + mLog.warn("Attempt to stop() an already stopped tuner channel source was ignored"); } } @@ -316,6 +310,11 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang public void shutdown() { + if(getTunerChannel().getFrequency() == 460500000) + { + mLog.debug("************** Shutdown has been invoked on the P25 primary control channel: 460500000"); + } + mProcessing = false; } @@ -373,12 +372,18 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang } catch(Exception e) { + mLog.error("Error encountered during decimation process", e); + /* Only log the stack trace if we're still processing */ if(mProcessing) { mLog.error("Error encountered during decimation process", e); } } + catch(Throwable throwable) + { + mLog.error("Bad Error", throwable); + } /* Check to see if we've been shutdown */ if(!mProcessing) diff --git a/src/source/tuner/usb/USBTransferProcessor.java b/src/source/tuner/usb/USBTransferProcessor.java index 5039a0d4..aeb56bb9 100644 --- a/src/source/tuner/usb/USBTransferProcessor.java +++ b/src/source/tuner/usb/USBTransferProcessor.java @@ -30,6 +30,7 @@ import sample.Listener; import sample.OverflowableTransferQueue; import sample.adapter.ISampleAdapter; import sample.complex.ComplexBuffer; +import sample.real.IOverflowListener; import source.tuner.TunerManager; import util.ThreadPool; @@ -101,12 +102,12 @@ public class USBTransferProcessor implements TransferCallback mBufferSize = bufferSize; mFilledBuffers = new OverflowableTransferQueue<>(FILLED_BUFFER_MAX_CAPACITY, FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD); - mFilledBuffers.setStateListener(new Listener() + mFilledBuffers.setOverflowListener(new IOverflowListener() { @Override - public void receive(OverflowableTransferQueue.State state) + public void sourceOverflow(boolean overflow) { - if(state == OverflowableTransferQueue.State.NORMAL) + if(overflow) { mLog.debug(mDeviceName + " - buffer overflow - temporary pause until processing catches up"); } From 6a340050c6fbb6a134564d7430cd3df59e4d7beb Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 06:41:45 -0500 Subject: [PATCH 04/20] #165 - cleanup of logging and comments in TunerChannelSource --- src/source/tuner/TunerChannelSource.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 13be8ba9..fa0cef46 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -310,11 +310,6 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang public void shutdown() { - if(getTunerChannel().getFrequency() == 460500000) - { - mLog.debug("************** Shutdown has been invoked on the P25 primary control channel: 460500000"); - } - mProcessing = false; } @@ -372,8 +367,6 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang } catch(Exception e) { - mLog.error("Error encountered during decimation process", e); - /* Only log the stack trace if we're still processing */ if(mProcessing) { @@ -382,7 +375,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang } catch(Throwable throwable) { - mLog.error("Bad Error", throwable); + mLog.error("Code error encountered during decimation process - channel thread will probably die", throwable); } /* Check to see if we've been shutdown */ From 8db47c25bc3c028c96fdc6394e171356fbbe0050 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 09:45:32 -0500 Subject: [PATCH 05/20] #165 - cleanup of logging and comments in TunerChannelSource --- src/module/decode/p25/PatchGroupManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/module/decode/p25/PatchGroupManager.java b/src/module/decode/p25/PatchGroupManager.java index 79955038..8642923e 100644 --- a/src/module/decode/p25/PatchGroupManager.java +++ b/src/module/decode/p25/PatchGroupManager.java @@ -37,6 +37,7 @@ public class PatchGroupManager /** * Manages active P25 patch groups and auto-expires patch groups if not updated every 5 seconds. + * * @param aliasList to keep updated with patch group changes * @param broadcaster for rebroadcasting patch group related call events */ @@ -127,13 +128,14 @@ public class PatchGroupManager cleanupPatchGroups(); patchGroup = new PatchGroup(patchGroupID); mPatchGroups.add(patchGroup); + updated = true; } else { patchGroup.updateTimestamp(); } - for(String patchedGroup: patchedTalkgroups) + for(String patchedGroup : patchedTalkgroups) { if(patchGroup.addPatchedGroup(patchedGroup)) { @@ -209,7 +211,7 @@ public class PatchGroupManager */ private PatchGroup getPatchGroup(String patchGroupID) { - for(PatchGroup patchGroup: mPatchGroups) + for(PatchGroup patchGroup : mPatchGroups) { if(patchGroup.getPatchGroupID().equals(patchGroupID)) { From 4c27d0412098d45fc472a71621d599d2e2a38451 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 10:08:52 -0500 Subject: [PATCH 06/20] Resolves #165 - adds ARM64 (aarch64) linux libusb support --- .idea/libraries/imports.xml | 36 ++------------------ imports/libusb4java-1.2.0-linux-aarch64.jar | Bin 0 -> 99757 bytes 2 files changed, 3 insertions(+), 33 deletions(-) create mode 100644 imports/libusb4java-1.2.0-linux-aarch64.jar diff --git a/.idea/libraries/imports.xml b/.idea/libraries/imports.xml index c2d94bbd..91ffaa83 100644 --- a/.idea/libraries/imports.xml +++ b/.idea/libraries/imports.xml @@ -27,39 +27,9 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + \ No newline at end of file diff --git a/imports/libusb4java-1.2.0-linux-aarch64.jar b/imports/libusb4java-1.2.0-linux-aarch64.jar new file mode 100644 index 0000000000000000000000000000000000000000..61a2e74200ceb0b63d41c8779af6a88acb21557a GIT binary patch literal 99757 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̊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 be^|Hzl;xmd{zDD*brFMuf%$}d0kHoIm;xX- literal 0 HcmV?d00001 From 281be147f8143960e3d0ea0cab9d1f09266cb828 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 10:30:35 -0500 Subject: [PATCH 07/20] Resolves #166 - removes traffic channel timeout from traffic channel manager --- src/channel/traffic/TrafficChannelManager.java | 3 --- src/module/decode/DecoderFactory.java | 9 ++------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/channel/traffic/TrafficChannelManager.java b/src/channel/traffic/TrafficChannelManager.java index 1a706cf0..16cdec10 100644 --- a/src/channel/traffic/TrafficChannelManager.java +++ b/src/channel/traffic/TrafficChannelManager.java @@ -84,8 +84,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider, * @param system label to use to describe the system * @param site label to use to describe the site * @param aliasListName designated for the channel - * @param trafficChannelTimeout - millisecond call timer limit used when an - * end of call signalling event is not decoded. * @param trafficChannelPoolSize - maximum number of allocated traffic channels * in the pool */ @@ -96,7 +94,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider, String system, String site, String aliasListName, - long trafficChannelTimeout, int trafficChannelPoolSize) { mChannelModel = channelModel; diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 19471fe9..645af4bc 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -222,11 +222,9 @@ public class DecoderFactory if(channelType == ChannelType.STANDARD) { - long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds - modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig, channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), - (aliasList != null ? aliasList.getName() : null), timeout, mptConfig.getTrafficChannelPoolSize())); + (aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize())); } modules.add(new FMDemodulatorModule(iqPass, iqStop)); @@ -264,12 +262,9 @@ public class DecoderFactory if(channelType == ChannelType.STANDARD) { - //Set call timeout to 3 seconds for P25 traffic channels - long timeout = 3000; - modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig, channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), - (aliasList != null ? aliasList.getName() : null), timeout, p25Config.getTrafficChannelPoolSize())); + (aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize())); } modules.add(new P25AudioModule(metadata)); From 34dd2161ef69320e79c84bf1624ce96b354de2b5 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 17:05:28 -0500 Subject: [PATCH 08/20] Resolves #176 - 2 issues: A) streaming connection errors - detects errors and if currently connected, resets the connection. B)duplicate calls being streamed - issue with comparison of broadcast channels allowed the same broadcast channel to be queued two or more times for the list of channels to stream a recording. --- src/alias/id/broadcast/BroadcastChannel.java | 149 ++++++++++-------- src/audio/broadcast/BroadcastModel.java | 4 +- .../icecast/IcecastTCPAudioBroadcaster.java | 10 +- src/record/AudioRecorder.java | 23 ++- 4 files changed, 115 insertions(+), 71 deletions(-) diff --git a/src/alias/id/broadcast/BroadcastChannel.java b/src/alias/id/broadcast/BroadcastChannel.java index f0baf630..59a32090 100644 --- a/src/alias/id/broadcast/BroadcastChannel.java +++ b/src/alias/id/broadcast/BroadcastChannel.java @@ -25,76 +25,99 @@ import javax.xml.bind.annotation.XmlAttribute; public class BroadcastChannel extends AliasID implements Comparable { - private String mChannelName; + private String mChannelName; - public BroadcastChannel() - { - //JAXB Constructor - } + public BroadcastChannel() + { + //JAXB Constructor + } - @Override - public int compareTo(BroadcastChannel other) - { - if(mChannelName != null && other.getChannelName() != null) - { - return mChannelName.compareTo(other.getChannelName()); - } - else if(mChannelName != null) - { - return -1; - } - else - { - return 1; - } - } + @Override + public int compareTo(BroadcastChannel other) + { + if(mChannelName != null && other.getChannelName() != null) + { + return mChannelName.compareTo(other.getChannelName()); + } + else if(mChannelName != null) + { + return -1; + } + else + { + return 1; + } + } - /** - * Creates a named broadcast channel - */ - public BroadcastChannel(String channelName) - { - mChannelName = channelName; - } + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(!(o instanceof BroadcastChannel)) + { + return false; + } - /** - * Name of the broadcastAudio channel configuration - */ - @XmlAttribute(name="channel") - public String getChannelName() - { - return mChannelName; - } + BroadcastChannel that = (BroadcastChannel)o; - /** - * Sets the name of the broadcastAudio channel configuration - */ - public void setChannelName(String channel) - { - mChannelName = channel; - } + return getChannelName() != null ? getChannelName().equals(that.getChannelName()) : that.getChannelName() == null; + } - @Override - public AliasIDType getType() - { - return AliasIDType.BROADCAST_CHANNEL; - } + @Override + public int hashCode() + { + return getChannelName() != null ? getChannelName().hashCode() : 0; + } - @Override - public boolean isValid() - { - return mChannelName != null; - } + /** + * Creates a named broadcast channel + */ + public BroadcastChannel(String channelName) + { + mChannelName = channelName; + } - @Override - public boolean matches( AliasID id ) - { - return false; - } - - @Override - public String toString() - { + /** + * Name of the broadcastAudio channel configuration + */ + @XmlAttribute(name = "channel") + public String getChannelName() + { + return mChannelName; + } + + /** + * Sets the name of the broadcastAudio channel configuration + */ + public void setChannelName(String channel) + { + mChannelName = channel; + } + + @Override + public AliasIDType getType() + { + return AliasIDType.BROADCAST_CHANNEL; + } + + @Override + public boolean isValid() + { + return mChannelName != null; + } + + @Override + public boolean matches(AliasID id) + { + return false; + } + + @Override + public String toString() + { if(isValid()) { return "Broadcast Channel: " + mChannelName; @@ -103,5 +126,5 @@ public class BroadcastChannel extends AliasID implements Comparable broadcastChannels = metadata.getBroadcastChannels(); + + for(BroadcastChannel broadcastChannel : broadcastChannels) { String channelName = broadcastChannel.getChannelName(); diff --git a/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java b/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java index 846bd92c..a0db228d 100644 --- a/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java +++ b/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java @@ -248,6 +248,12 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster { String reason = ioe.getMessage(); + if(connected()) + { + mLog.info("Streaming connection error detected - resetting connection - " + reason); + disconnect(); + connect(); + } if(reason.startsWith("Connection reset")) { mLog.info("Streaming connection reset by remote server - reestablishing connection"); @@ -264,14 +270,14 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster { setBroadcastState(BroadcastState.ERROR); disconnect(); - mLog.error("Unrecognized IO error: " + reason + ". Streaming halted."); + mLog.error("Unrecognized IO error: " + reason + ". Streaming halted.", cause); } } else { setBroadcastState(BroadcastState.ERROR); disconnect(); - mLog.error("Unspecified IO error - streaming halted."); + mLog.error("Unspecified IO error - streaming halted.", cause); } } else diff --git a/src/record/AudioRecorder.java b/src/record/AudioRecorder.java index 4d118351..15723eba 100644 --- a/src/record/AudioRecorder.java +++ b/src/record/AudioRecorder.java @@ -163,7 +163,10 @@ public abstract class AudioRecorder extends Module implements Listener listener) { @@ -171,8 +174,14 @@ public abstract class AudioRecorder extends Module implements Listener Date: Fri, 10 Feb 2017 17:09:54 -0500 Subject: [PATCH 09/20] Incrementing build to 0.3.0-beta3 --- build/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.xml b/build/build.xml index 3f28086d..ccf283d9 100644 --- a/build/build.xml +++ b/build/build.xml @@ -7,7 +7,7 @@ - + From 7c930deefe670ad2538e24b9435319352545dab2 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 19:31:09 -0500 Subject: [PATCH 10/20] Resolves #181 - patch group audio priority will assume the highest (listenable) audio priority and override a 'Do Not Monitor' setting if any of the other aliases have an audio priority defined. --- src/alias/PatchGroupAlias.java | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/alias/PatchGroupAlias.java b/src/alias/PatchGroupAlias.java index b2c0d2fa..5831d9ea 100644 --- a/src/alias/PatchGroupAlias.java +++ b/src/alias/PatchGroupAlias.java @@ -197,22 +197,44 @@ public class PatchGroupAlias extends Alias return hasPatchGroupAlias() && getPatchGroupAlias().hasActions(); } + /** + * Returns the highest listenable audio priority defined among the aliases in the group, or returns 'Do Not Monitor' + * if that is the only priority defined. Otherwise, returns default (100) priority. + */ @Override public int getCallPriority() { - int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY); + boolean hasDoNotMonitor = false; + + int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY + 1); for(Alias alias: mPatchedAliases) { - if(!(alias instanceof PatchGroupAlias) && - alias.hasCallPriority() && - alias.getCallPriority() < highestPriority) + if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority() && alias.getCallPriority() < highestPriority) { - highestPriority = alias.getCallPriority(); + if(alias.getCallPriority() == Priority.DO_NOT_MONITOR) + { + hasDoNotMonitor = true; + } + else + { + highestPriority = alias.getCallPriority(); + } } } - return highestPriority; + if(highestPriority <= Priority.DEFAULT_PRIORITY) + { + return highestPriority; + } + else if(hasDoNotMonitor) + { + return Priority.DO_NOT_MONITOR; + } + else + { + return Priority.DEFAULT_PRIORITY; + } } @Override From 2ca9e5d5cea18b56535cc24a74269303af8c0fb4 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 10 Feb 2017 19:34:50 -0500 Subject: [PATCH 11/20] Resolves #183 - alias list throws NPE when created with a null or empty name and receives alias updates. --- src/alias/AliasList.java | 57 ++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index 8c9afa98..44a2b2e9 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -631,36 +631,47 @@ public class AliasList implements Listener return mName; } + /** + * Indicates if this alias list has a non-null, non-empty name + */ + private boolean hasName() + { + return mName != null && !mName.isEmpty(); + } + /** * Receive alias change event notifications and modify this list accordingly */ @Override public void receive(AliasEvent event) { - Alias alias = event.getAlias(); - - switch(event.getEvent()) + if(hasName()) { - case ADD: - if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) - { - addAlias(alias); - } - break; - case CHANGE: - if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) - { - addAlias(alias); - } - break; - case DELETE: - if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) - { - removeAlias(alias); - } - break; - default: - break; + Alias alias = event.getAlias(); + + switch(event.getEvent()) + { + case ADD: + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + { + addAlias(alias); + } + break; + case CHANGE: + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + { + addAlias(alias); + } + break; + case DELETE: + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + { + removeAlias(alias); + } + break; + default: + break; + } } } } From fce3d5c4f3dd5b89711b8360912898234d064268 Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 11 Feb 2017 07:17:23 -0500 Subject: [PATCH 12/20] Resolves #186 - p25 C4FM decoder wasn't working (at all). --- src/dsp/filter/design/FilterViewer.java | 136 +++++++++--------- src/module/decode/DecoderFactory.java | 35 ++--- src/module/decode/p25/P25_C4FMDecoder.java | 25 +--- .../demodulate/fm/FMDemodulatorModule.java | 7 + 4 files changed, 95 insertions(+), 108 deletions(-) diff --git a/src/dsp/filter/design/FilterViewer.java b/src/dsp/filter/design/FilterViewer.java index 3044ae3c..85c710ca 100644 --- a/src/dsp/filter/design/FilterViewer.java +++ b/src/dsp/filter/design/FilterViewer.java @@ -1,65 +1,64 @@ package dsp.filter.design; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import dsp.filter.fir.FIRFilterSpecification; import dsp.filter.fir.remez.RemezFIRFilterDesigner; import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FilterViewer extends Application { - private final static Logger mLog = LoggerFactory.getLogger( FilterViewer.class ); + private final static Logger mLog = LoggerFactory.getLogger(FilterViewer.class); - /** - * Developer tool to visualize filter designs - */ - public FilterViewer() - { - } - - @Override - public void start( Stage primaryStage ) throws Exception - { - Scene scene = new Scene( new FilterView( getFilter() ) ); - - primaryStage.setTitle( "Filter Viewer" ); - primaryStage.setScene( scene ); - primaryStage.show(); - } - - public static void main( String[] args ) - { - launch( args ); - } + /** + * Developer tool to visualize filter designs + */ + public FilterViewer() + { + } - /** - * Provides the filter to visualize. Modify this method to visualize your filter design. - */ - private float[] getFilter() - { -// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() -// .sampleRate( 48000 ) -// .gridDensity( 16 ) -// .passBandCutoff( 3200 ) -// .passBandAmplitude( 1.0 ) -// .passBandRipple( 0.02 ) -// .stopBandStart( 4000 ) -// .stopBandAmplitude( 0.0 ) -// .stopBandRipple( 0.03 ) -// .build(); - - FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder() - .sampleRate( 24000 ) - .stopBandCutoff( 800 ) - .stopBandAmplitude( 0.0 ) - .stopBandRipple( 0.03 ) - .passBandStart( 1000 ) - .passBandAmplitude( 1.0 ) - .passBandRipple( 0.08 ) - .build(); + @Override + public void start(Stage primaryStage) throws Exception + { + Scene scene = new Scene(new FilterView(getFilter())); + + primaryStage.setTitle("Filter Viewer"); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public static void main(String[] args) + { + launch(args); + } + + /** + * Provides the filter to visualize. Modify this method to visualize your filter design. + */ + private float[] getFilter() + { + FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() + .sampleRate(48000) + .gridDensity(16) + .passBandCutoff(2500) + .passBandAmplitude(1.0) + .passBandRipple(0.01) + .stopBandStart(4000) + .stopBandAmplitude(0.0) + .stopBandRipple(0.008) + .build(); + +// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder() +// .sampleRate(24000) +// .stopBandCutoff(800) +// .stopBandAmplitude(0.0) +// .stopBandRipple(0.03) +// .passBandStart(1000) +// .passBandAmplitude(1.0) +// .passBandRipple(0.08) +// .build(); // FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder() // .sampleRate( 48000 ) @@ -71,24 +70,25 @@ public class FilterViewer extends Application // .passRipple( 0.008) // .build(); - float[] taps = null; + float[] taps = null; -// taps = FilterFactory.getLowPass( 16000, 2400, 73, WindowType.HANNING ); +// taps = FilterFactory.getLowPass(48000, 6750, 7500, 60, +// Window.WindowType.HAMMING, true); - try - { - RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner( specification ); - - if( designer.isValid() ) - { - taps = designer.getImpulseResponse(); - } - } - catch( FilterDesignException fde ) - { - mLog.error( "Filter design error", fde ); - } - - return taps; - } + try + { + RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification); + + if(designer.isValid()) + { + taps = designer.getImpulseResponse(); + } + } + catch(FilterDesignException fde) + { + mLog.error("Filter design error", fde); + } + + return taps; + } } diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 645af4bc..cce17e65 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -103,35 +103,35 @@ public class DecoderFactory //Low-pass filter with ~60 dB attenuation and 89 taps private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.lowPassBuilder() - .sampleRate( 48000 ) - .gridDensity( 16 ) - .passBandCutoff( 3200 ) - .passBandAmplitude( 1.0 ) - .passBandRipple( 0.02 ) - .stopBandStart( 4000 ) - .stopBandAmplitude( 0.0 ) - .stopBandRipple( 0.03 ) - .build(); + .sampleRate( 48000 ).gridDensity( 16 ).passBandCutoff( 3200 ).passBandAmplitude( 1.0 ).passBandRipple( 0.02 ) + .stopBandStart( 4000 ).stopBandAmplitude( 0.0 ).stopBandRipple( 0.03 ).build(); + + private static final FIRFilterSpecification P25_C4FM_IQ_SPECIFICATION = FIRFilterSpecification.lowPassBuilder() + .sampleRate(48000).gridDensity(16).passBandCutoff(6750).passBandAmplitude(1.0).passBandRipple(0.01) + .stopBandStart(7000).stopBandAmplitude(0.0).stopBandRipple(0.008).build(); + + private static final FIRFilterSpecification P25_C4FM_DEMOD_SPECIFICATION = FIRFilterSpecification.lowPassBuilder() + .sampleRate(48000).gridDensity(16).passBandCutoff(2500).passBandAmplitude(1.0).passBandRipple(0.01) + .stopBandStart(4000).stopBandAmplitude(0.0).stopBandRipple(0.008).build(); private static float[] MPT1327_LOWPASS_FILTER; + private static float[] P25_C4FM_IQ_FILTER; + private static float[] P25_C4FM_DEMOD_FILTER; static { try { MPT1327_LOWPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION); + P25_C4FM_IQ_FILTER = FilterFactory.getTaps(P25_C4FM_IQ_SPECIFICATION); + P25_C4FM_DEMOD_FILTER = FilterFactory.getTaps(P25_C4FM_DEMOD_SPECIFICATION); } catch(Exception e) { - mLog.error("Couldn't design MPT1327 bandpass filter"); + mLog.error("Couldn't design startup filter(s)"); } } - public static final boolean NO_REMOVE_DC = false; - public static final boolean REMOVE_DC = true; - - public static final int NO_FREQUENCY_CORRECTION = 0; - /** * Returns a list of one primary decoder and any auxiliary decoders, as * specified in the configurations. @@ -228,7 +228,7 @@ public class DecoderFactory } modules.add(new FMDemodulatorModule(iqPass, iqStop)); - modules.add(new DemodulatedAudioFilterModule(MPT1327_LOWPASS_FILTER, 1.0f)); + modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f)); modules.add(new AudioModule(metadata)); break; case PASSPORT: @@ -247,7 +247,8 @@ public class DecoderFactory switch(modulation) { case C4FM: - modules.add(new FMDemodulatorModule(6750, 7500)); + modules.add(new FMDemodulatorModule(P25_C4FM_IQ_FILTER)); + modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f)); modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection())); modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM, p25Config.getIgnoreDataCalls())); diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index 650c0cc2..94f2be56 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -47,12 +47,10 @@ public class P25_C4FMDecoder extends P25Decoder private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); /* Instrumentation Taps */ - private static final String INSTRUMENT_FILTER_OUTPUT = "Tap Point: Pre-Filter Output"; private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; private List mAvailableTaps; - private RealFIRFilter_RB_RB mC4FMPreFilter; private C4FMSymbolFilter mSymbolFilter; private C4FMSlicer mC4FMSlicer; private P25MessageFramer mMessageFramer; @@ -74,15 +72,9 @@ public class P25_C4FMDecoder extends P25Decoder { super( aliasList ); - /* Filter demodulated sample buffers */ - float[] filter = FilterFactory.getLowPass( 48000, 2500, 4000, 80, WindowType.HANNING, true ); - - mC4FMPreFilter = new RealFIRFilter_RB_RB( filter, 1.0f ); - /* Shape gain and frequency offsets to optimize sample stream */ mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum ); - mC4FMPreFilter.setListener( mSymbolFilter ); - + /* Convert samples to symbols */ mC4FMSlicer = new C4FMSlicer(); mSymbolFilter.setListener( mC4FMSlicer ); @@ -100,9 +92,6 @@ public class P25_C4FMDecoder extends P25Decoder { super.dispose(); - mC4FMPreFilter.dispose(); - mC4FMPreFilter = null; - mSymbolFilter.dispose(); mSymbolFilter = null; @@ -121,7 +110,7 @@ public class P25_C4FMDecoder extends P25Decoder @Override public Listener getFilteredRealBufferListener() { - return mC4FMPreFilter; + return mSymbolFilter; } /** @@ -136,8 +125,6 @@ public class P25_C4FMDecoder extends P25Decoder TapGroup group = new TapGroup( "P25 C4FM Decoder" ); - group.add( new FloatBufferTap( INSTRUMENT_FILTER_OUTPUT, 0, 1.0f ) ); - group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); @@ -165,11 +152,6 @@ public class P25_C4FMDecoder extends P25Decoder switch( tap.getName() ) { - case INSTRUMENT_FILTER_OUTPUT: - FloatBufferTap filterOutput = (FloatBufferTap)tap; - mC4FMPreFilter.setListener( filterOutput ); - filterOutput.setListener( mSymbolFilter ); - break; case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: FloatTap symbolTap = (FloatTap)tap; mSymbolFilter.setListener( symbolTap ); @@ -201,9 +183,6 @@ public class P25_C4FMDecoder extends P25Decoder switch( tap.getName() ) { - case INSTRUMENT_FILTER_OUTPUT: - mC4FMPreFilter.setListener( mSymbolFilter ); - break; case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: mSymbolFilter.setListener( mC4FMSlicer ); break; diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java index 8bac1bc4..8b7646de 100644 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -62,6 +62,13 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene mIQFilter.setListener(mDemodulator); } + public FMDemodulatorModule(float[] filter) + { + mIQFilter = new ComplexFIRFilter_CB_CB(filter, 1.0f); + mDemodulator = new FMDemodulator_CB(1.0f); + mIQFilter.setListener(mDemodulator); + } + @Override public Listener getComplexBufferListener() { From cf0018e6bae7079297213bf4e68f2c70233a8231 Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 11 Feb 2017 07:22:54 -0500 Subject: [PATCH 13/20] Incrementing build to 0.3.0-beta4 --- build/build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.xml b/build/build.xml index ccf283d9..bce263e7 100644 --- a/build/build.xml +++ b/build/build.xml @@ -7,7 +7,7 @@ - + From a3e50acfe361af3e42d3782a8dddacbf219c46d8 Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 11 Feb 2017 09:02:35 -0500 Subject: [PATCH 14/20] Incrementing build to 0.3.0-beta4 --- .idea/dictionaries/denny.xml | 1 + src/channel/details/ChannelDetailPanel.java | 12 +- src/module/decode/p25/P25DecoderState.java | 165 +++++++++----------- 3 files changed, 82 insertions(+), 96 deletions(-) diff --git a/.idea/dictionaries/denny.xml b/.idea/dictionaries/denny.xml index 7f72f826..223a1c26 100644 --- a/.idea/dictionaries/denny.xml +++ b/.idea/dictionaries/denny.xml @@ -18,6 +18,7 @@ talkgroup tgid ultravox + wacn xtea diff --git a/src/channel/details/ChannelDetailPanel.java b/src/channel/details/ChannelDetailPanel.java index 3ed53b2f..90e19874 100644 --- a/src/channel/details/ChannelDetailPanel.java +++ b/src/channel/details/ChannelDetailPanel.java @@ -38,7 +38,7 @@ public class ChannelDetailPanel extends JPanel implements Listener

Activity Summary - Decoder:P25 ").append(getModulation().name()).append("

"); + sb.append("
"); + sb.append("SITE: "); if(mNetworkStatus != null) { - sb.append("\nNAC:\t" + mNetworkStatus.getNAC()); - sb.append("\nWACN-SYS:\t" + mNetworkStatus.getWACN()); - sb.append("-" + mNetworkStatus.getSystemID()); - sb.append(" ["); - sb.append(mNetworkStatus.getNetworkCallsign()); - sb.append("]"); - sb.append("\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea()); + sb.append("NAC:" + mNetworkStatus.getNAC()); + sb.append("h WACN:" + mNetworkStatus.getWACN()); + sb.append("h SYS:" + mNetworkStatus.getSystemID()); + sb.append("h [").append(mNetworkStatus.getNetworkCallsign()).append("]"); + sb.append(" LRA:" + mNetworkStatus.getLocationRegistrationArea()); } else if(mNetworkStatusExtended != null) { - sb.append("\nNAC:\t" + mNetworkStatusExtended.getNAC()); - sb.append("\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN()); - sb.append("-" + mNetworkStatusExtended.getSystemID()); - sb.append(" ["); - sb.append(mNetworkStatusExtended.getNetworkCallsign()); - sb.append("]"); - sb.append("\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea()); + sb.append("NAC:" + mNetworkStatusExtended.getNAC()); + sb.append("h WACN:" + mNetworkStatusExtended.getWACN()); + sb.append("h SYS:" + mNetworkStatusExtended.getSystemID()); + sb.append("h [").append(mNetworkStatusExtended.getNetworkCallsign()).append("]"); + sb.append(" LRA:" + mNetworkStatusExtended.getLocationRegistrationArea()); } String site = null; @@ -3376,11 +3372,10 @@ public class P25DecoderState extends DecoderState } else if(mRFSSStatusMessageExtended != null) { - site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + - mRFSSStatusMessageExtended.getSiteID(); + site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + mRFSSStatusMessageExtended.getSiteID(); } - sb.append("\nRFSS-SITE:\t" + site); + sb.append("h RFSS-SITE:").append(site).append("h"); if(hasAliasList()) { @@ -3394,95 +3389,81 @@ public class P25DecoderState extends DecoderState if(mNetworkStatus != null) { - sb.append("\nSERVICES:\t" + SystemService.toString( - mNetworkStatus.getSystemServiceClass())); - sb.append("\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)mNetworkStatus.getDownlinkFrequency() / 1E6d) + - " [" + mNetworkStatus.getIdentifier() + "-" + - mNetworkStatus.getChannel() + "]\n"); - sb.append("\tUPLINK " + mFrequencyFormatter.format( - (double)mNetworkStatus.getUplinkFrequency() / 1E6d) + " [" + - mNetworkStatus.getIdentifier() + "-" + - mNetworkStatus.getChannel() + "]\n"); + sb.append("
SERVICES:" + SystemService.toString(mNetworkStatus.getSystemServiceClass())); + sb.append("
PCCH:DOWNLINK ") + .append(mFrequencyFormatter.format((double)mNetworkStatus.getDownlinkFrequency() / 1E6d)) + .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) + .append("] UPLINK ") + .append(mFrequencyFormatter.format((double)mNetworkStatus.getUplinkFrequency() / 1E6d)) + .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) + .append("]
"); } else if(mNetworkStatusExtended != null) { - sb.append("\nSERVICES:\t" + SystemService.toString( - mNetworkStatusExtended.getSystemServiceClass())); - sb.append("\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d) + - " [" + mNetworkStatusExtended.getTransmitIdentifier() + "-" + - mNetworkStatusExtended.getTransmitChannel() + "]\n"); - sb.append("\tUPLINK " + mFrequencyFormatter.format( - (double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d) + - " [" + mNetworkStatusExtended.getReceiveIdentifier() + "-" + - mNetworkStatusExtended.getReceiveChannel() + "]"); + sb.append("
SERVICES:").append(SystemService.toString(mNetworkStatusExtended.getSystemServiceClass())); + sb.append("
PCCH: DOWNLINK ") + .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d)) + .append(" [").append(mNetworkStatusExtended.getTransmitIdentifier()).append("-") + .append(mNetworkStatusExtended.getTransmitChannel()).append("] UPLINK ") + .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d)) + .append(" [").append(mNetworkStatusExtended.getReceiveIdentifier()).append("-") + .append(mNetworkStatusExtended.getReceiveChannel()).append("]"); } if(mSecondaryControlChannels.isEmpty()) { - sb.append("\nSCCH:\tNONE"); + sb.append("
SCCH: NONE"); } else { - for(module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast - sec : mSecondaryControlChannels) + for(module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sec : mSecondaryControlChannels) { - sb.append("\nSCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)sec.getDownlinkFrequency1() / 1E6d) + - " [" + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n"); - sb.append("\tUPLINK " + mFrequencyFormatter.format( - (double)sec.getUplinkFrequency1() / 1E6d) + " [" + - sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n"); + sb.append("
SCCH: DOWNLINK ") + .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency1() / 1E6d)) + .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("] UPLINK ") + .append(mFrequencyFormatter.format((double)sec.getUplinkFrequency1() / 1E6d)) + .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("]"); if(sec.hasChannel2()) { - sb.append("\nSCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)sec.getDownlinkFrequency2() / 1E6d) + - " [" + sec.getIdentifier2() + "-" + sec.getChannel2() + "]\n"); - sb.append("\tUPLINK " + mFrequencyFormatter.format( - (double)sec.getUplinkFrequency2() / 1E6d) + " [" + - sec.getIdentifier2() + "-" + sec.getChannel2() + "]"); + sb.append("
SCCH: DOWNLINK ") + .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency2() / 1E6d)) + .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()) + .append("] UPLINK ").append(mFrequencyFormatter.format((double)sec.getUplinkFrequency2() / 1E6d)) + .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()).append("]"); } } } if(mSNDCPDataChannel != null) { - sb.append("\nSNDCP:"); - sb.append("\tDNLINK " + mFrequencyFormatter.format( - (double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D) + - " [" + mSNDCPDataChannel.getTransmitChannel() + "]"); - sb.append("\tUPLINK " + mFrequencyFormatter.format( - (double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D) + - " [" + mSNDCPDataChannel.getReceiveChannel() + "]"); + sb.append("
SNDCP: DOWNLINK ") + .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D)) + .append(" [").append(mSNDCPDataChannel.getTransmitChannel()).append("]").append(" UPLINK ") + .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D)) + .append(" [").append(mSNDCPDataChannel.getReceiveChannel()).append("]"); } if(mProtectionParameterBroadcast != null) { - sb.append("\nENCRYPTION:"); - sb.append("\nTYPE:\t" + mProtectionParameterBroadcast - .getEncryptionType().name()); - sb.append("\nALGORITHM:\t" + mProtectionParameterBroadcast - .getAlgorithmID()); - sb.append("\nKEY:\t" + mProtectionParameterBroadcast.getKeyID()); - sb.append("\nINBOUND IV:\t" + mProtectionParameterBroadcast - .getInboundInitializationVector()); - sb.append("\nOUTBOUND IV:\t" + mProtectionParameterBroadcast - .getOutboundInitializationVector()); + sb.append("
ENCRYPTION: TYPE:").append(mProtectionParameterBroadcast.getEncryptionType().name()); + sb.append(" ALGORITHM:").append(mProtectionParameterBroadcast.getAlgorithmID()); + sb.append(" KEY:").append(mProtectionParameterBroadcast.getKeyID()); + sb.append(" INBOUND IV:").append(mProtectionParameterBroadcast.getInboundInitializationVector()); + sb.append(" OUTBOUND IV:").append(mProtectionParameterBroadcast.getOutboundInitializationVector()); } List identifiers = new ArrayList<>(mBands.keySet()); Collections.sort(identifiers); - sb.append("\nFREQUENCY BANDS:"); + sb.append("
FREQUENCY BANDS:"); for(Integer id : identifiers) { IBandIdentifier band = mBands.get(id); - sb.append("\n\t" + id); - sb.append("- BASE: " + mFrequencyFormatter.format( + sb.append("
  ").append(id).append(""); + sb.append(" - BASE: " + mFrequencyFormatter.format( (double)band.getBaseFrequency() / 1E6d)); sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format( (double)band.getChannelSpacing() / 1E6d)); @@ -3490,23 +3471,24 @@ public class P25DecoderState extends DecoderState (double)band.getTransmitOffset() / 1E6D)); } - sb.append("\n\n=================== NEIGHBORS ======================"); + sb.append("
NEIGHBORS:"); if(mNeighborMap.isEmpty()) { - sb.append("\n\tNONE\n"); - sb.append("\n----------------------------------------------------"); + sb.append("
  • NONE
  • "); } else { for(IAdjacentSite neighbor : mNeighborMap.values()) { - sb.append("\nNAC:\t" + ((P25Message)neighbor).getNAC()); - sb.append("\nSYSTEM:\t" + neighbor.getSystemID()); - sb.append("\nLRA:\t" + neighbor.getLRA()); + sb.append("
  • NAC:").append(((P25Message)neighbor).getNAC()); + sb.append("h SYSTEM:" + neighbor.getSystemID()); + sb.append("h LRA:" + neighbor.getLRA()); String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); - sb.append("\nRFSS-SITE:\t" + neighborID); + sb.append("h RFSS-SITE:" + neighborID); + + sb.append("h "); if(hasAliasList()) { @@ -3514,21 +3496,22 @@ public class P25DecoderState extends DecoderState if(siteAlias != null) { - sb.append(" " + siteAlias.getName()); + sb.append(siteAlias.getName()); } } - sb.append("\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)neighbor.getDownlinkFrequency() / 1E6d) + - " [" + neighbor.getDownlinkChannel() + "]"); - sb.append("\n\tUPLINK:" + mFrequencyFormatter.format( - (double)neighbor.getUplinkFrequency() / 1E6d) + - " [" + neighbor.getDownlinkChannel() + "]\n"); - sb.append("\nSERVICES:\t" + neighbor.getSystemServiceClass()); - sb.append("\n----------------------------------------------------"); + sb.append("
      PCCH: DOWNLINK ") + .append(mFrequencyFormatter.format((double)neighbor.getDownlinkFrequency() / 1E6d)) + .append(" [").append(neighbor.getDownlinkChannel()).append("] UPLINK:") + .append(mFrequencyFormatter.format((double)neighbor.getUplinkFrequency() / 1E6d)) + .append(" [").append(neighbor.getDownlinkChannel()).append("] SERVICES: ") + .append(neighbor.getSystemServiceClass()); + sb.append(""); + return sb.toString(); } From 372640156374301375f5120916b051691629665c Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 12 Feb 2017 07:30:32 -0500 Subject: [PATCH 15/20] Resolves #188 - cleanup of the P25 and MPT activity summaries. Resolves an issue in the SNDCP downlink/uplink channel frequencies. --- src/channel/details/ChannelDetailPanel.java | 6 +- src/channel/state/DecoderState.java | 3 + .../decode/mpt1327/MPT1327DecoderState.java | 32 +- src/module/decode/p25/P25DecoderState.java | 72 +++-- .../osp/control/IdentifierUpdateNonVUHF.java | 16 +- .../osp/control/IdentifierUpdateTDMA.java | 28 +- .../osp/control/IdentifierUpdateVUHF.java | 16 +- .../SecondaryControlChannelBroadcast.java | 298 ++++++++++-------- .../SNDCPDataChannelAnnouncementExplicit.java | 21 +- 9 files changed, 273 insertions(+), 219 deletions(-) diff --git a/src/channel/details/ChannelDetailPanel.java b/src/channel/details/ChannelDetailPanel.java index 90e19874..4f41e267 100644 --- a/src/channel/details/ChannelDetailPanel.java +++ b/src/channel/details/ChannelDetailPanel.java @@ -38,7 +38,7 @@ public class ChannelDetailPanel extends JPanel implements Listener mCallEventBroadcaster = new Broadcaster<>(); private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index 00e85efe..a70b96fa 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -389,9 +389,9 @@ public class MPT1327DecoderState extends DecoderState { StringBuilder sb = new StringBuilder(); - sb.append("Activity Summary\n"); - sb.append("Decoder:\tMPT-1327\n"); - sb.append("Site:\t"); + sb.append("Activity Summary - Decoder: MPT-1327\n"); + sb.append(DIVIDER1); + sb.append("Site: "); if(mSite != null) { @@ -403,9 +403,7 @@ public class MPT1327DecoderState extends DecoderState if(siteAlias != null) { - sb.append(" "); - sb.append(siteAlias.getName()); - sb.append("\n"); + sb.append(" ").append(siteAlias.getName()).append("\n"); } } } @@ -414,13 +412,11 @@ public class MPT1327DecoderState extends DecoderState sb.append("Unknown\n"); } - sb.append("\n\n"); - - sb.append("Talkgroups\n\n"); + sb.append(DIVIDER2).append("Talkgroups: "); if(mGroups.isEmpty()) { - sb.append(" None\n"); + sb.append("None\n"); } else { @@ -429,8 +425,7 @@ public class MPT1327DecoderState extends DecoderState for(String talkgroup : talkgroups) { - sb.append(" "); - sb.append(talkgroup); + sb.append("\n ").append(talkgroup); if(hasAliasList()) { @@ -450,7 +445,7 @@ public class MPT1327DecoderState extends DecoderState for(String member : members) { - sb.append(" >"); + sb.append(" >"); sb.append(member); if(hasAliasList()) @@ -466,27 +461,26 @@ public class MPT1327DecoderState extends DecoderState sb.append("\n"); } - - sb.append("\n"); } } - sb.append("\n"); + sb.append(DIVIDER2).append("All Idents: "); - sb.append("Individual Idents\n"); if(mIdents.isEmpty()) { - sb.append(" None\n"); + sb.append("None\n"); } else { + sb.append("\n"); + Iterator it = mIdents.iterator(); while(it.hasNext()) { String ident = it.next(); - sb.append(" "); + sb.append(""); sb.append(ident); sb.append(" "); diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 3ccd0487..76666ccd 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -148,6 +148,7 @@ public class P25DecoderState extends DecoderState private static final DecimalFormat mFrequencyFormatter = new DecimalFormat("0.000000"); + private module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast mNetworkStatus; private NetworkStatusBroadcastExtended mNetworkStatusExtended; private ProtectionParameterBroadcast mProtectionParameterBroadcast; @@ -3343,9 +3344,9 @@ public class P25DecoderState extends DecoderState { StringBuilder sb = new StringBuilder(); - sb.append("

    Activity Summary - Decoder:P25 ").append(getModulation().name()).append("

    "); - sb.append("
    "); - sb.append("SITE: "); + sb.append("Activity Summary - Decoder:P25 ").append(getModulation().getLabel()).append("\n"); + sb.append(DIVIDER1); + sb.append("SITE "); if(mNetworkStatus != null) { @@ -3387,38 +3388,43 @@ public class P25DecoderState extends DecoderState } } + sb.append("\n").append(DIVIDER2); + if(mNetworkStatus != null) { - sb.append("
    SERVICES:" + SystemService.toString(mNetworkStatus.getSystemServiceClass())); - sb.append("
    PCCH:DOWNLINK ") + sb.append("SERVICES: " + SystemService.toString(mNetworkStatus.getSystemServiceClass())).append("\n"); + sb.append(DIVIDER2); + sb.append("PCCH DOWNLINK ") .append(mFrequencyFormatter.format((double)mNetworkStatus.getDownlinkFrequency() / 1E6d)) .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) .append("] UPLINK ") .append(mFrequencyFormatter.format((double)mNetworkStatus.getUplinkFrequency() / 1E6d)) .append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel()) - .append("]
    "); + .append("]\n"); } else if(mNetworkStatusExtended != null) { - sb.append("
    SERVICES:").append(SystemService.toString(mNetworkStatusExtended.getSystemServiceClass())); - sb.append("
    PCCH: DOWNLINK ") + sb.append("\nSERVICES:").append(SystemService.toString(mNetworkStatusExtended.getSystemServiceClass())) + .append("\n"); + sb.append(DIVIDER2); + sb.append("PCCH DOWNLINK ") .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d)) .append(" [").append(mNetworkStatusExtended.getTransmitIdentifier()).append("-") .append(mNetworkStatusExtended.getTransmitChannel()).append("] UPLINK ") .append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d)) .append(" [").append(mNetworkStatusExtended.getReceiveIdentifier()).append("-") - .append(mNetworkStatusExtended.getReceiveChannel()).append("]"); + .append(mNetworkStatusExtended.getReceiveChannel()).append("]\n"); } if(mSecondaryControlChannels.isEmpty()) { - sb.append("
    SCCH: NONE"); + sb.append("SCCH: NONE\n"); } else { for(module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sec : mSecondaryControlChannels) { - sb.append("
    SCCH: DOWNLINK ") + sb.append("SCCH DOWNLINK ") .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency1() / 1E6d)) .append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("] UPLINK ") .append(mFrequencyFormatter.format((double)sec.getUplinkFrequency1() / 1E6d)) @@ -3426,62 +3432,69 @@ public class P25DecoderState extends DecoderState if(sec.hasChannel2()) { - sb.append("
    SCCH: DOWNLINK ") + sb.append(" SCCH 2 DOWNLINK ") .append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency2() / 1E6d)) .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()) .append("] UPLINK ").append(mFrequencyFormatter.format((double)sec.getUplinkFrequency2() / 1E6d)) .append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()).append("]"); } + + sb.append("\n"); } } + if(mSNDCPDataChannel != null) { - sb.append("
    SNDCP: DOWNLINK ") + sb.append("SNDCP DOWNLINK ") .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D)) .append(" [").append(mSNDCPDataChannel.getTransmitChannel()).append("]").append(" UPLINK ") .append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D)) - .append(" [").append(mSNDCPDataChannel.getReceiveChannel()).append("]"); + .append(" [").append(mSNDCPDataChannel.getReceiveChannel()).append("]\n"); } if(mProtectionParameterBroadcast != null) { - sb.append("
    ENCRYPTION: TYPE:").append(mProtectionParameterBroadcast.getEncryptionType().name()); + sb.append(DIVIDER2); + sb.append("ENCRYPTION TYPE:").append(mProtectionParameterBroadcast.getEncryptionType().name()); sb.append(" ALGORITHM:").append(mProtectionParameterBroadcast.getAlgorithmID()); sb.append(" KEY:").append(mProtectionParameterBroadcast.getKeyID()); sb.append(" INBOUND IV:").append(mProtectionParameterBroadcast.getInboundInitializationVector()); sb.append(" OUTBOUND IV:").append(mProtectionParameterBroadcast.getOutboundInitializationVector()); + sb.append("\n"); } List identifiers = new ArrayList<>(mBands.keySet()); Collections.sort(identifiers); - sb.append("
    FREQUENCY BANDS:"); + sb.append(DIVIDER2).append("FREQUENCY BANDS:\n"); for(Integer id : identifiers) { IBandIdentifier band = mBands.get(id); - - sb.append("
      ").append(id).append(""); - sb.append(" - BASE: " + mFrequencyFormatter.format( - (double)band.getBaseFrequency() / 1E6d)); - sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format( - (double)band.getChannelSpacing() / 1E6d)); - sb.append(" UPLINK OFFSET: " + mFrequencyFormatter.format( - (double)band.getTransmitOffset() / 1E6D)); + sb.append(band.toString()).append("\n"); +// sb.append(" ").append(id); +// sb.append(" - BASE: " + mFrequencyFormatter.format( +// (double)band.getBaseFrequency() / 1E6d)); +// sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format( +// (double)band.getChannelSpacing() / 1E6d)); +// sb.append(" UPLINK OFFSET: " + mFrequencyFormatter.format( +// (double)band.getTransmitOffset() / 1E6D)); +// sb.append("\n"); } - sb.append("
    NEIGHBORS:"); + sb.append(DIVIDER2).append("NEIGHBOR SITES: "); if(mNeighborMap.isEmpty()) { - sb.append("
  • NONE
  • "); + sb.append("NONE\n"); } else { for(IAdjacentSite neighbor : mNeighborMap.values()) { - sb.append("
  • NAC:").append(((P25Message)neighbor).getNAC()); + sb.append("\n"); + sb.append("NAC:").append(((P25Message)neighbor).getNAC()); sb.append("h SYSTEM:" + neighbor.getSystemID()); sb.append("h LRA:" + neighbor.getLRA()); @@ -3500,18 +3513,15 @@ public class P25DecoderState extends DecoderState } } - sb.append("
      PCCH: DOWNLINK ") + sb.append("\n PCCH: DOWNLINK ") .append(mFrequencyFormatter.format((double)neighbor.getDownlinkFrequency() / 1E6d)) .append(" [").append(neighbor.getDownlinkChannel()).append("] UPLINK:") .append(mFrequencyFormatter.format((double)neighbor.getUplinkFrequency() / 1E6d)) .append(" [").append(neighbor.getDownlinkChannel()).append("] SERVICES: ") .append(neighbor.getSystemServiceClass()); - sb.append(""); - return sb.toString(); } diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java index e4719edb..0022f082 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java @@ -31,17 +31,21 @@ public class IdentifierUpdateNonVUHF extends IdentifierUpdate StringBuilder sb = new StringBuilder(); sb.append( getMessageStub() ); + sb.append(toString()); - sb.append( " IDEN:" + getIdentifier() ); + return sb.toString(); + } + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( " ID:" + getIdentifier() ); sb.append( " BASE:" + getBaseFrequency() ); - - sb.append( " BW:" + getBandwidth() ); - + sb.append( " BANDWIDTH:" + getBandwidth() ); sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - + return sb.toString(); } diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java index 4fb02ca0..7524ce7d 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java @@ -30,18 +30,26 @@ public class IdentifierUpdateTDMA extends IdentifierUpdate StringBuilder sb = new StringBuilder(); sb.append( getMessageStub() ); + sb.append(toString()); - sb.append( " IDEN:" + getIdentifier() ); - sb.append( " BASE:" + getBaseFrequency() ); - sb.append( " BW:" + getBandwidth() ); - sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - sb.append( " TYPE:" + getChannelType().toString() ); - return sb.toString(); } - - public ChannelType getChannelType() + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( " ID:" + getIdentifier() ); + sb.append( " BASE:" + getBaseFrequency() ); + sb.append( " BW:" + getBandwidth() ); + sb.append( " SPACING:" + getChannelSpacing() ); + sb.append( " OFFSET:" + getTransmitOffset() ); + sb.append( " TYPE:" + getChannelType().toString() ); + + return sb.toString(); + } + + public ChannelType getChannelType() { return ChannelType.fromValue( mMessage.getInt( CHANNEL_TYPE) ); } @@ -109,7 +117,7 @@ public class IdentifierUpdateTDMA extends IdentifierUpdate } sb.append( mAccessType.name() ); - sb.append( " BW:" + mBandwidth ); + sb.append( " BANDWIDTH:" + mBandwidth ); sb.append( " SLOT/CARRIER:" + mSlotsPerCarrier ); sb.append( " VOCODER:" + mVocoder.name() ); diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java index 3dffa8cd..fb7c7098 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java @@ -32,17 +32,21 @@ public class IdentifierUpdateVUHF extends IdentifierUpdate StringBuilder sb = new StringBuilder(); sb.append( getMessageStub() ); + sb.append(toString()); - sb.append( " IDEN:" + getIdentifier() ); + return sb.toString(); + } + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( " ID:" + getIdentifier() ); sb.append( " BASE:" + getBaseFrequency() ); - - sb.append( " BW:" + getBandwidth() ); - + sb.append( " BANDWIDTH:" + getBandwidth() ); sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - + return sb.toString(); } diff --git a/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java index 48b61992..25cf1044 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java @@ -1,44 +1,49 @@ package module.decode.p25.message.tsbk.osp.control; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import alias.AliasList; -import bits.BinaryMessage; - -public class SecondaryControlChannelBroadcast extends TSBKMessage - implements IdentifierReceiver, Comparable +public class SecondaryControlChannelBroadcast extends TSBKMessage + implements IdentifierReceiver, Comparable { - private final static Logger mLog = - LoggerFactory.getLogger( SecondaryControlChannelBroadcast.class ); + private final static Logger mLog = + LoggerFactory.getLogger(SecondaryControlChannelBroadcast.class); + + public static final int[] RFSS_ID = {80, 81, 82, 83, 84, 85, 86, 87}; + public static final int[] SITE_ID = {88, 89, 90, 91, 92, 93, 94, 95}; + public static final int[] IDENTIFIER_1 = {96, 97, 98, 99}; + public static final int[] CHANNEL_1 = {100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111}; + public static final int[] SYSTEM_SERVICE_CLASS_1 = {112, 113, 114, 115, 116, + 117, 118, 119}; + public static final int[] IDENTIFIER_2 = {120, 121, 122, 123}; + public static final int[] CHANNEL_2 = {124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135}; + public static final int[] SYSTEM_SERVICE_CLASS_2 = {136, 137, 138, 139, 140, + 141, 142, 143}; - public static final int[] RFSS_ID = { 80,81,82,83,84,85,86,87 }; - public static final int[] SITE_ID = { 88,89,90,91,92,93,94,95 }; - public static final int[] IDENTIFIER_1 = { 96,97,98,99 }; - public static final int[] CHANNEL_1 = { 100,101,102,103,104,105,106,107,108, - 109,110,111 }; - public static final int[] SYSTEM_SERVICE_CLASS_1 = { 112,113,114,115,116, - 117,118,119 }; - public static final int[] IDENTIFIER_2 = { 120,121,122,123 }; - public static final int[] CHANNEL_2 = { 124,125,126,127,128,129,130,131,132, - 133,134,135 }; - public static final int[] SYSTEM_SERVICE_CLASS_2 = { 136,137,138,139,140, - 141,142,143 }; - private IBandIdentifier mIdentifierUpdate1; private IBandIdentifier mIdentifierUpdate2; - - public SecondaryControlChannelBroadcast( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + + private int mRFSS; + private int mSite; + private int mIdent1; + private int mIdent2; + private int mChan1; + private int mChan2; + + public SecondaryControlChannelBroadcast(BinaryMessage message, + DataUnitID duid, + AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } @Override @@ -46,162 +51,197 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage { return Opcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " CHAN1:" + getIdentifier1() + "-" + getChannel1() ); - - sb.append( " DN1:" + getDownlinkFrequency1() ); - - sb.append( " UP1:" + getUplinkFrequency1() ); - - sb.append( " SVC1:" + - SystemService.toString( getSystemServiceClass1() ) ); - if( hasChannel2() ) + sb.append(getMessageStub()); + + sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); + + sb.append(" CHAN1:" + getIdentifier1() + "-" + getChannel1()); + + sb.append(" DN1:" + getDownlinkFrequency1()); + + sb.append(" UP1:" + getUplinkFrequency1()); + + sb.append(" SVC1:" + + SystemService.toString(getSystemServiceClass1())); + + if(hasChannel2()) { - sb.append( " CHAN2:" + getIdentifier2() + "-" + getChannel2() ); - - sb.append( " DN2:" + getDownlinkFrequency2() ); - - sb.append( " UP2:" + getUplinkFrequency2() ); - - sb.append( " SVC2:" + - SystemService.toString( getSystemServiceClass2() ) ); + sb.append(" CHAN2:" + getIdentifier2() + "-" + getChannel2()); + + sb.append(" DN2:" + getDownlinkFrequency2()); + + sb.append(" UP2:" + getUplinkFrequency2()); + + sb.append(" SVC2:" + + SystemService.toString(getSystemServiceClass2())); } - + return sb.toString(); } - + public String getRFSS() { - return mMessage.getHex( RFSS_ID, 2 ); + return mMessage.getHex(RFSS_ID, 2); } - + public String getSiteID() { - return mMessage.getHex( SITE_ID, 2 ); + return mMessage.getHex(SITE_ID, 2); } - + public int getIdentifier1() { - return mMessage.getInt( IDENTIFIER_1 ); + return mMessage.getInt(IDENTIFIER_1); } - + public int getChannel1() { - return mMessage.getInt( CHANNEL_1 ); + return mMessage.getInt(CHANNEL_1); } - + public int getSystemServiceClass1() { - return mMessage.getInt( SYSTEM_SERVICE_CLASS_1 ); + return mMessage.getInt(SYSTEM_SERVICE_CLASS_1); } public int getIdentifier2() { - return mMessage.getInt( IDENTIFIER_2 ); + return mMessage.getInt(IDENTIFIER_2); } - + public int getChannel2() { - return mMessage.getInt( CHANNEL_2 ); + return mMessage.getInt(CHANNEL_2); } - + public int getSystemServiceClass2() { - return mMessage.getInt( SYSTEM_SERVICE_CLASS_2 ); + return mMessage.getInt(SYSTEM_SERVICE_CLASS_2); } public boolean hasChannel2() { - return getSystemServiceClass2() != 0; + return getSystemServiceClass2() != 0; } - - + + public long getDownlinkFrequency1() { - return calculateDownlink( mIdentifierUpdate1, getChannel1() ); + return calculateDownlink(mIdentifierUpdate1, getChannel1()); } - + public long getUplinkFrequency1() { - return calculateUplink( mIdentifierUpdate1, getChannel1() ); + return calculateUplink(mIdentifierUpdate1, getChannel1()); } public long getDownlinkFrequency2() { - return calculateDownlink( mIdentifierUpdate2, getChannel2() ); + return calculateDownlink(mIdentifierUpdate2, getChannel2()); } - + public long getUplinkFrequency2() { - return calculateUplink( mIdentifierUpdate2, getChannel2() ); + return calculateUplink(mIdentifierUpdate2, getChannel2()); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - if( identifier == getIdentifier1() ) - { - mIdentifierUpdate1 = message; - } - - if( identifier == getIdentifier2() ) - { - mIdentifierUpdate2 = message; - } - } - - public int[] getIdentifiers() - { - int[] idens; - - if( hasChannel2() ) - { - idens = new int[ 2 ]; - - idens[ 0 ] = getIdentifier1(); - idens[ 1 ] = getIdentifier2(); - } - else - { - idens = new int[ 1 ]; - - idens[ 0 ] = getIdentifier1(); - } - - return idens; - } + if(identifier == getIdentifier1()) + { + mIdentifierUpdate1 = message; + } - @Override - public int compareTo( SecondaryControlChannelBroadcast other ) + if(identifier == getIdentifier2()) + { + mIdentifierUpdate2 = message; + } + } + + public int[] getIdentifiers() { - if( other.getSiteID().contentEquals( getSiteID() ) && - other.getIdentifier1() == getIdentifier1() && - other.getChannel1() == getChannel1() && - other.getChannel2() == getChannel2() && - other.getIdentifier2() == getIdentifier2() && - other.getDownlinkFrequency1() == getDownlinkFrequency1() && - other.getUplinkFrequency1() == getUplinkFrequency1() && - other.getDownlinkFrequency2() == getDownlinkFrequency2() && - other.getUplinkFrequency2() == getUplinkFrequency2() ) - { - return 0; - } - else if( other.getIdentifier1() < getIdentifier1() ) - { - return -1; - } - else - { - return 1; - } + int[] idens; + + if(hasChannel2()) + { + idens = new int[2]; + + idens[0] = getIdentifier1(); + idens[1] = getIdentifier2(); + } + else + { + idens = new int[1]; + + idens[0] = getIdentifier1(); + } + + return idens; + } + + @Override + public int compareTo(SecondaryControlChannelBroadcast other) + { + if(other.getRFSS().contentEquals(getRFSS()) && + other.getSiteID().contentEquals(getSiteID()) && + other.getIdentifier1() == getIdentifier1() && + other.getChannel1() == getChannel1()) + { + return 0; + } + else if(other.getIdentifier1() < getIdentifier1()) + { + return -1; + } + else + { + return 1; + } + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(!(o instanceof SecondaryControlChannelBroadcast)) + { + return false; + } + + SecondaryControlChannelBroadcast that = (SecondaryControlChannelBroadcast)o; + + if(getRFSS() != that.getRFSS()) + { + return false; + } + if(getSiteID() != that.getSiteID()) + { + return false; + } + if(getIdentifier1() != that.getIdentifier1()) + { + return false; + } + + return getChannel1() == that.getChannel1(); + } + + @Override + public int hashCode() + { + int result = getRFSS() != null ? getRFSS().hashCode() : 0; + result = 31 * result + getSiteID() != null ? getSiteID().hashCode() : 0; + result = 31 * result + getIdentifier1(); + result = 31 * result + getChannel1(); + return result; } } diff --git a/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java index e7a9f5c1..1753e030 100644 --- a/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java @@ -6,28 +6,21 @@ import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData - implements IdentifierReceiver +public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData implements IdentifierReceiver { public static final int AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR = 88; public static final int REQUESTED_ACCESS_AVAILABLE_INDICATOR = 89; public static final int[] TRANSMIT_IDENTIFIER = { 96,97,98,99 }; - public static final int[] TRANSMIT_NUMBER = { 100,101,102,103,104,105,106, - 107,108,109,110,111 }; + public static final int[] TRANSMIT_NUMBER = { 100,101,102,103,104,105,106, 107,108,109,110,111 }; public static final int[] RECEIVE_IDENTIFIER = { 112,113,114,115 }; - public static final int[] RECEIVE_NUMBER = { 116,117,118,119,120,121,122, - 123,124,125,126,127 }; - - public static final int[] DATA_ACCESS_CONTROL = { 128,129,130,131,132,133, - 134,135,136,137,138,139,140,141,142,143 }; + public static final int[] RECEIVE_NUMBER = { 116,117,118,119,120,121,122, 123,124,125,126,127 }; + public static final int[] DATA_ACCESS_CONTROL = { 128,129,130,131,132,133, 134,135,136,137,138,139,140,141,142,143 }; private IBandIdentifier mIdentifierUpdateTransmit; private IBandIdentifier mIdentifierUpdateReceive; - public SNDCPDataChannelAnnouncementExplicit( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + public SNDCPDataChannelAnnouncementExplicit( BinaryMessage message, DataUnitID duid, AliasList aliasList ) { super( message, duid, aliasList ); } @@ -148,11 +141,11 @@ public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdateTransmit, getReceiveChannelNumber() ); + return calculateDownlink( mIdentifierUpdateTransmit, getTransmitChannelNumber() ); } public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdateReceive, getTransmitChannelNumber() ); + return calculateUplink( mIdentifierUpdateReceive, getReceiveChannelNumber() ); } } From 9f01678b00cfe3eaa9690232b0479d9729c4c73f Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 12 Feb 2017 07:43:51 -0500 Subject: [PATCH 16/20] Resolves #189 - add UTF-8 encoding to shoutcast v2 (ultravox) XML metadata update values. --- .../v2/ShoutcastV2AudioBroadcaster.java | 58 +++++++++++-------- .../v2/ultravox/UltravoxMetadata.java | 7 ++- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java b/src/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java index 786f6728..cd4cadaf 100644 --- a/src/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java +++ b/src/audio/broadcast/shoutcast/v2/ShoutcastV2AudioBroadcaster.java @@ -50,6 +50,7 @@ import properties.SystemProperties; import util.ThreadPool; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.ConnectException; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -244,43 +245,50 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr sb.append(""); - sb.append(UltravoxMetadata.ALBUM_TITLE.asXML(getConfiguration().getName())); - - sb.append(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML(SystemProperties.getInstance().getApplicationName())); - - if(getConfiguration().hasGenre()) + try { - sb.append(UltravoxMetadata.GENRE.asXML(getConfiguration().getGenre())); - } + sb.append(UltravoxMetadata.ALBUM_TITLE.asXML(getConfiguration().getName())); - if(getConfiguration().hasURL()) - { - sb.append(UltravoxMetadata.URL.asXML(getConfiguration().getURL())); - } + sb.append(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML(SystemProperties.getInstance().getApplicationName())); - if(metadata != null) - { - if(metadata.getPrimaryAddressTo().hasAlias()) + if(getConfiguration().hasGenre()) { - sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getAlias().getName())); - } - else if(metadata.getPrimaryAddressTo().hasIdentifier()) - { - sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getIdentifier())); + sb.append(UltravoxMetadata.GENRE.asXML(getConfiguration().getGenre())); } - if(metadata.getPrimaryAddressFrom().hasAlias()) + if(getConfiguration().hasURL()) { - sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + metadata.getPrimaryAddressFrom().getAlias().getName())); + sb.append(UltravoxMetadata.URL.asXML(getConfiguration().getURL())); } - else if(metadata.getPrimaryAddressFrom().hasIdentifier()) + + if(metadata != null) { - sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + metadata.getPrimaryAddressFrom().getIdentifier())); + if(metadata.getPrimaryAddressTo().hasAlias()) + { + sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getAlias().getName())); + } + else if(metadata.getPrimaryAddressTo().hasIdentifier()) + { + sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getIdentifier())); + } + + if(metadata.getPrimaryAddressFrom().hasAlias()) + { + sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + metadata.getPrimaryAddressFrom().getAlias().getName())); + } + else if(metadata.getPrimaryAddressFrom().hasIdentifier()) + { + sb.append(UltravoxMetadata.TITLE_3.asXML("From:" + metadata.getPrimaryAddressFrom().getIdentifier())); + } + } + else + { + sb.append(UltravoxMetadata.TITLE_2.asXML("Scanning ...")); } } - else + catch(UnsupportedEncodingException uee) { - sb.append(UltravoxMetadata.TITLE_2.asXML("Scanning ...")); + mLog.error("UTF-8 Encoding is not supported - shoutcast/ultravox metadata will not be updated"); } sb.append(""); diff --git a/src/audio/broadcast/shoutcast/v2/ultravox/UltravoxMetadata.java b/src/audio/broadcast/shoutcast/v2/ultravox/UltravoxMetadata.java index 73d21cb3..6a124355 100644 --- a/src/audio/broadcast/shoutcast/v2/ultravox/UltravoxMetadata.java +++ b/src/audio/broadcast/shoutcast/v2/ultravox/UltravoxMetadata.java @@ -18,6 +18,9 @@ ******************************************************************************/ package audio.broadcast.shoutcast.v2.ultravox; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + public enum UltravoxMetadata { ALBUM_TITLE("TALB"), @@ -58,11 +61,11 @@ public enum UltravoxMetadata * @param value to enclose in XML tags * @return xml tag wrapped value */ - public String asXML(String value) + public String asXML(String value) throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); sb.append("<").append(getXMLTag()).append(">"); - sb.append(value); + sb.append(URLEncoder.encode(value, "UTF-8")); sb.append(""); return sb.toString(); From c3d60dc1741d80965f3d88bb08f6bb42dfdde50e Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 12 Feb 2017 08:04:27 -0500 Subject: [PATCH 17/20] Resolves #193 - adds handling for MINA WriteTimeoutException error. Resets connection when this occurs. --- .../broadcast/icecast/IcecastTCPAudioBroadcaster.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java b/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java index a0db228d..e240ddea 100644 --- a/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java +++ b/src/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java @@ -25,6 +25,7 @@ import org.apache.mina.core.RuntimeIoException; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; +import org.apache.mina.core.write.WriteTimeoutException; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; import org.slf4j.Logger; @@ -273,6 +274,12 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster mLog.error("Unrecognized IO error: " + reason + ". Streaming halted.", cause); } } + else if(ioe instanceof WriteTimeoutException) + { + mLog.info("Network write timeout error - resetting connection"); + disconnect(); + connect(); + } else { setBroadcastState(BroadcastState.ERROR); From 6916d95cdfbb9643a65b9acd888f89c52f0dfaf3 Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 12 Feb 2017 10:20:45 -0500 Subject: [PATCH 18/20] Resolves #173 - logging/management of P25 TDMA channels. There is no Phase II decoder support yet. --- CHANGELOG | 1 + src/channel/metadata/AttributeMonitor.java | 39 +-- src/module/decode/p25/P25DecoderState.java | 151 +++++--- .../decode/p25/message/IBandIdentifier.java | 32 ++ src/module/decode/p25/message/P25Message.java | 294 +++++++--------- .../ldu/lc/AdjacentSiteStatusBroadcast.java | 250 +++++++------ .../AdjacentSiteStatusBroadcastExplicit.java | 286 ++++++++------- .../ldu/lc/ChannelIdentifierUpdate.java | 127 ++++--- .../lc/ChannelIdentifierUpdateExplicit.java | 21 +- .../ldu/lc/GroupVoiceChannelUpdate.java | 229 ++++++------ .../lc/GroupVoiceChannelUpdateExplicit.java | 226 ++++++------ .../ldu/lc/NetworkStatusBroadcast.java | 183 +++++----- .../lc/NetworkStatusBroadcastExplicit.java | 223 ++++++------ .../message/ldu/lc/RFSSStatusBroadcast.java | 219 ++++++------ .../ldu/lc/RFSSStatusBroadcastExplicit.java | 256 +++++++------- .../lc/SecondaryControlChannelBroadcast.java | 269 +++++++------- ...ondaryControlChannelBroadcastExplicit.java | 240 +++++++------ .../pdu/UnitToUnitChannelGrantExtended.java | 325 +++++++++-------- .../AdjacentStatusBroadcastExtended.java | 300 ++++++++-------- .../NetworkStatusBroadcastExtended.java | 277 ++++++++------- .../control/RFSSStatusBroadcastExtended.java | 287 ++++++++------- .../data/GroupDataChannelGrantExtended.java | 264 +++++++------- .../voice/GroupVoiceChannelGrantExplicit.java | 270 +++++++------- ...phoneInterconnectChannelGrantExplicit.java | 271 ++++++++------- ...ToUnitVoiceChannelGrantUpdateExtended.java | 329 ++++++++++-------- .../tdu/lc/AdjacentSiteStatusBroadcast.java | 248 +++++++------ .../AdjacentSiteStatusBroadcastExplicit.java | 287 ++++++++------- .../tdu/lc/ChannelIdentifierUpdate.java | 129 ++++--- .../lc/ChannelIdentifierUpdateExplicit.java | 126 ++++--- .../tdu/lc/GroupVoiceChannelUpdate.java | 263 ++++++++------ .../lc/GroupVoiceChannelUpdateExplicit.java | 226 ++++++------ .../tdu/lc/NetworkStatusBroadcast.java | 184 +++++----- .../lc/NetworkStatusBroadcastExplicit.java | 223 ++++++------ .../message/tdu/lc/RFSSStatusBroadcast.java | 222 ++++++------ .../tdu/lc/RFSSStatusBroadcastExplicit.java | 263 +++++++------- .../lc/SecondaryControlChannelBroadcast.java | 275 ++++++++------- ...ondaryControlChannelBroadcastExplicit.java | 240 +++++++------ .../decode/p25/message/tsbk/ChannelGrant.java | 112 ++++-- .../tsbk/GroupChannelGrantExplicit.java | 207 ++++++----- .../message/tsbk/GroupMultiChannelGrant.java | 236 ++++++++----- .../p25/message/tsbk/UnitChannelGrant.java | 77 ++-- ...ntrolChannelBaseStationIdentification.java | 170 +++++---- .../motorola/PatchGroupVoiceChannelGrant.java | 182 ++++++---- .../PatchGroupVoiceChannelGrantUpdate.java | 201 +++++++---- .../osp/control/AdjacentStatusBroadcast.java | 243 +++++++------ .../tsbk/osp/control/IdentifierUpdate.java | 39 ++- .../osp/control/IdentifierUpdateTDMA.java | 21 +- .../osp/control/NetworkStatusBroadcast.java | 169 +++++---- .../tsbk/osp/control/RFSSStatusBroadcast.java | 188 +++++----- .../SecondaryControlChannelBroadcast.java | 79 +++-- .../tsbk/osp/data/GroupDataChannelGrant.java | 28 +- .../SNDCPDataChannelAnnouncementExplicit.java | 195 ++++++----- .../tsbk/osp/data/SNDCPDataChannelGrant.java | 179 ++++++---- 53 files changed, 5780 insertions(+), 4601 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5fee2ff5..7ab1b516 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ buffers. Bounded queues have ability to enter/exit overflow state when queue size reaches overflow threshold and reset when queue size falls below the reset threshold. Channel state will show OVERFLOW in red/yellow. + - Improves handling of P25 TDMA channels (NO Phase 2 decoder support yet) - Updated airspy controller for airspy-mini specific sample rate handling. - New developer remez filter designer tool and internal usage diff --git a/src/channel/metadata/AttributeMonitor.java b/src/channel/metadata/AttributeMonitor.java index 81bb477e..013bdde5 100644 --- a/src/channel/metadata/AttributeMonitor.java +++ b/src/channel/metadata/AttributeMonitor.java @@ -19,8 +19,6 @@ package channel.metadata; import alias.Alias; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import sample.Listener; import java.util.ArrayList; @@ -30,8 +28,6 @@ import java.util.Map; public class AttributeMonitor { - private final static Logger mLog = LoggerFactory.getLogger(AttributeMonitor.class); - private Attribute mAttribute; private Listener mListener; private boolean mHeuristicsEnabled = true; @@ -50,8 +46,8 @@ public class AttributeMonitor * an internal mapping of values to occurrence counts is updated and a change is emitted once a value that is * different from the current value, has a higher occurrence count than the current value. If heuristics are * disabled, then a change is emitted at each change in the value. - * @param t * + * @param t type * @return true if the value was changed after processing */ public boolean process(T t) @@ -83,7 +79,7 @@ public class AttributeMonitor { int count = mOccurrenceCounts.get(mCurrentValue); - for(Map.Entry entry: mOccurrenceCounts.entrySet()) + for(Map.Entry entry : mOccurrenceCounts.entrySet()) { if(entry.getValue() > count) { @@ -155,35 +151,4 @@ public class AttributeMonitor { return mCurrentValue != null; } - - public static void main(String[] args) - { - AttributeMonitor monitor = new AttributeMonitor<>(Attribute.PRIMARY_ADDRESS_TO, new Listener() - { - @Override - public void receive(AttributeChangeRequest attributeChangeRequest) - { - mLog.debug("Attribute Changed - value:" + attributeChangeRequest.getValue()); - } - }); - monitor.addIllegalValue("0000"); - monitor.addIllegalValue("000000"); - - monitor.process("ABCD"); - monitor.process("EFGH"); - monitor.process("0000"); - monitor.process("0000"); - monitor.process("ABCD"); - monitor.reset(); - - monitor.process("EFGH"); - monitor.process("ABCD"); - monitor.process("ABCD"); - monitor.process("EFGH"); - monitor.process("EFGH"); - monitor.process("000000"); - monitor.process("000000"); - monitor.process("000000"); - monitor.process("000000"); - } } diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 76666ccd..dbd7e596 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -2742,7 +2742,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (pgvcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(pgvcg.getDownlinkFrequency()) .from(from) @@ -2753,8 +2753,10 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!pgvcg.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; case PATCH_GROUP_CHANNEL_GRANT_UPDATE: //Cleanup patch groups - auto-expire any patch groups before we allocate a channel @@ -2773,10 +2775,8 @@ public class P25DecoderState extends DecoderState { P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) .aliasList(getAliasList()) - .channel(channel) - .details((gvcgu.isEncrypted() ? "ENCRYPTED " : "") + - "PATCH UPDATE - GROUP 2:" + gvcgu.getPatchGroupAddress2() + - " DN:" + gvcgu.getDownlinkFrequency2()) + .channel(channel + (gvcgu.isTDMAChannel1() ? " TDMA" : "")) + .details((gvcgu.isEncrypted() ? "ENCRYPTED " : "")) .frequency(gvcgu.getDownlinkFrequency1()) .to(to) .build(); @@ -2785,8 +2785,37 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!gvcgu.isTDMAChannel1()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } + + String channel2 = gvcgu.getChannel2(); + String to2 = gvcgu.getPatchGroupAddress2(); + + if(hasCallEvent(channel2, null, to2)) + { + updateCallEvent(channel2, null, to2); + } + else + { + P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) + .aliasList(getAliasList()) + .channel(channel2 + (gvcgu.isTDMAChannel2() ? " TDMA" : "")) + .details((gvcgu.isEncrypted() ? "ENCRYPTED " : "")) + .frequency(gvcgu.getDownlinkFrequency2()) + .to(to2) + .build(); + + registerCallEvent(event); + broadcast(event); + } + + if(!gvcgu.isTDMAChannel2()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel2))); + } + break; case PATCH_GROUP_ADD: PatchGroupAdd pga = (PatchGroupAdd)tsbk; @@ -2844,7 +2873,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (gdcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(gdcg.getDownlinkFrequency()) .from(from) @@ -2855,7 +2884,7 @@ public class P25DecoderState extends DecoderState broadcast(event); } - if(!mIgnoreDataCalls) + if(!mIgnoreDataCalls && !gdcg.isTDMAChannel()) { broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); @@ -2882,7 +2911,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (gvcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(gvcg.getDownlinkFrequency()) .from(from) @@ -2893,8 +2922,10 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!gvcg.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; case GROUP_VOICE_CHANNEL_GRANT_UPDATE: GroupVoiceChannelGrantUpdate gvcgu = @@ -2918,8 +2949,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) .aliasList(getAliasList()) - .channel(channel) - .channel(gvcgu.getChannel1()) + .channel(gvcgu.getChannel1() + (gvcgu.isTDMAChannel1() ? " TDMA" : "")) .details(details.toString()) .frequency(gvcgu.getDownlinkFrequency1()) .from(from) @@ -2930,8 +2960,10 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!gvcgu.isTDMAChannel1()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } if(gvcgu.hasChannelNumber2()) { @@ -2952,7 +2984,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event2 = new P25CallEvent.Builder(CallEventType.GROUP_CALL) .aliasList(getAliasList()) - .channel(gvcgu.getChannel2()) + .channel(gvcgu.getChannel2() + (gvcgu.isTDMAChannel2() ? " TDMA" : "")) .details(details.toString()) .frequency(gvcgu.getDownlinkFrequency2()) .to(gvcgu.getGroupAddress2()) @@ -2962,8 +2994,10 @@ public class P25DecoderState extends DecoderState broadcast(event2); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!gvcgu.isTDMAChannel2()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } } break; case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: @@ -2989,7 +3023,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (gvcgue.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(gvcgue.getDownlinkFrequency()) .from(from) @@ -3000,8 +3034,10 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!gvcgue.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; case INDIVIDUAL_DATA_CHANNEL_GRANT: IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; @@ -3024,7 +3060,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (idcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(idcg.getDownlinkFrequency()) .from(from) @@ -3035,10 +3071,9 @@ public class P25DecoderState extends DecoderState broadcast(event); } - if(!mIgnoreDataCalls) + if(!mIgnoreDataCalls && !idcg.isTDMAChannel()) { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); } break; case SNDCP_DATA_CHANNEL_GRANT: @@ -3061,7 +3096,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (sdcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(sdcg.getDownlinkFrequency()) .from(from) @@ -3072,15 +3107,13 @@ public class P25DecoderState extends DecoderState broadcast(event); } - if(!mIgnoreDataCalls) + if(!mIgnoreDataCalls && !sdcg.isTDMAChannel()) { - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); } break; case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectVoiceChannelGrant tivcg = - (TelephoneInterconnectVoiceChannelGrant)message; + TelephoneInterconnectVoiceChannelGrant tivcg = (TelephoneInterconnectVoiceChannelGrant)message; channel = tivcg.getChannel(); from = null; @@ -3104,7 +3137,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (tivcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(tivcg.getDownlinkFrequency()) .from(from) @@ -3115,15 +3148,15 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!tivcg.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = - (TelephoneInterconnectVoiceChannelGrantUpdate)message; + TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = (TelephoneInterconnectVoiceChannelGrantUpdate)message; - channel = tivcgu.getChannelIdentifier() + "-" + - tivcgu.getChannelNumber(); + channel = tivcgu.getChannelIdentifier() + "-" + tivcgu.getChannelNumber(); from = null; /* Address is ambiguous and could mean either source or target, @@ -3146,7 +3179,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (tivcgu.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(tivcgu.getDownlinkFrequency()) .from(from) @@ -3157,15 +3190,15 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!tivcgu.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - UnitToUnitVoiceChannelGrant uuvcg = - (UnitToUnitVoiceChannelGrant)message; + UnitToUnitVoiceChannelGrant uuvcg = (UnitToUnitVoiceChannelGrant)message; - channel = uuvcg.getChannelIdentifier() + "-" + - uuvcg.getChannelNumber(); + channel = uuvcg.getChannelIdentifier() + "-" + uuvcg.getChannelNumber(); from = uuvcg.getSourceAddress(); to = uuvcg.getTargetAddress(); @@ -3184,7 +3217,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (uuvcg.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(uuvcg.getDownlinkFrequency()) .from(from) @@ -3195,15 +3228,15 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!uuvcg.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - UnitToUnitVoiceChannelGrantUpdate uuvcgu = - (UnitToUnitVoiceChannelGrantUpdate)message; + UnitToUnitVoiceChannelGrantUpdate uuvcgu = (UnitToUnitVoiceChannelGrantUpdate)message; - channel = uuvcgu.getChannelIdentifier() + "-" + - uuvcgu.getChannelNumber(); + channel = uuvcgu.getChannelIdentifier() + "-" + uuvcgu.getChannelNumber(); from = uuvcgu.getSourceAddress(); to = uuvcgu.getTargetAddress(); @@ -3222,7 +3255,7 @@ public class P25DecoderState extends DecoderState P25CallEvent event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL) .aliasList(getAliasList()) - .channel(channel) + .channel(channel + (uuvcgu.isTDMAChannel() ? " TDMA" : "")) .details(details.toString()) .frequency(uuvcgu.getDownlinkFrequency()) .from(from) @@ -3233,8 +3266,10 @@ public class P25DecoderState extends DecoderState broadcast(event); } - broadcast(new TrafficChannelAllocationEvent(this, - mChannelCallMap.get(channel))); + if(!uuvcgu.isTDMAChannel()) + { + broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel))); + } break; default: break; diff --git a/src/module/decode/p25/message/IBandIdentifier.java b/src/module/decode/p25/message/IBandIdentifier.java index a2588c42..1f3524eb 100644 --- a/src/module/decode/p25/message/IBandIdentifier.java +++ b/src/module/decode/p25/message/IBandIdentifier.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message; public interface IBandIdentifier @@ -29,4 +47,18 @@ public interface IBandIdentifier */ public abstract long getTransmitOffset(); + /** + * Downlink (tower to mobile) Frequency for the specified channel + */ + public abstract long getDownlinkFrequency(int channelNumber); + + /** + * Uplink (mobile to tower) Frequency for the specified channel + */ + public abstract long getUplinkFrequency(int channelNumber); + + /** + * Indicates if this band is an FDMA (false) or TDMA (true) band + */ + public boolean isTDMA(); } \ No newline at end of file diff --git a/src/module/decode/p25/message/P25Message.java b/src/module/decode/p25/message/P25Message.java index e1e6fe29..730c8acf 100644 --- a/src/module/decode/p25/message/P25Message.java +++ b/src/module/decode/p25/message/P25Message.java @@ -1,214 +1,188 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message; -import java.text.SimpleDateFormat; - -import map.Plottable; -import message.Message; -import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; import edac.CRC; +import map.Plottable; +import message.Message; +import module.decode.p25.reference.DataUnitID; + +import java.text.SimpleDateFormat; public class P25Message extends Message { - public enum DuplexMode { HALF, FULL }; - public enum SessionMode { PACKET, CIRCUIT }; - - public static final int[] NAC = { 0,1,2,3,4,5,6,7,8,9,10,11 }; - public static final int[] DUID = { 12,13,14,15 }; - public static final int[] BCH = { 16,17,18,19,20,21,22,23,24,25,26,27,28,29, - 30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53, - 54,55,56,57,58,59,60,61,62,63 }; - - protected SimpleDateFormat mTimeDurationFormat = - new SimpleDateFormat( "HH:mm:ss.SSS"); - - protected BinaryMessage mMessage; - protected DataUnitID mDUID; - protected AliasList mAliasList; - - protected CRC[] mCRC; - - public P25Message( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super(); - - mMessage = message; - mDUID = duid; - mAliasList = aliasList; - } - - public CRC[] getCRCResults() - { - return mCRC; - } - - public BinaryMessage getSourceMessage() - { - return mMessage; - } - - public AliasList getAliasList() - { - return mAliasList; - } - - public String getNAC() - { - return mMessage.getHex( NAC, 3 ); - } - - public DataUnitID getDUID() - { - return mDUID; - } - - @Override + public enum DuplexMode {HALF, FULL}; + public enum SessionMode {PACKET, CIRCUIT}; + + public static final int[] NAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + public static final int[] DUID = {12, 13, 14, 15}; + public static final int[] BCH = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + + protected SimpleDateFormat mTimeDurationFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + + protected BinaryMessage mMessage; + protected DataUnitID mDUID; + protected AliasList mAliasList; + + protected CRC[] mCRC; + + public P25Message(BinaryMessage message, DataUnitID duid, AliasList aliasList) + { + super(); + + mMessage = message; + mDUID = duid; + mAliasList = aliasList; + } + + public CRC[] getCRCResults() + { + return mCRC; + } + + public BinaryMessage getSourceMessage() + { + return mMessage; + } + + public AliasList getAliasList() + { + return mAliasList; + } + + public String getNAC() + { + return mMessage.getHex(NAC, 3); + } + + public DataUnitID getDUID() + { + return mDUID; + } + + @Override public String getErrorStatus() { - if( mCRC == null ) - { - return "UNKNOWN"; - } - - return CRC.format( mCRC ); + if(mCRC == null) + { + return "UNKNOWN"; + } + + return CRC.format(mCRC); } - @Override + @Override public String getMessage() { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " " ); - sb.append( getDUID().getLabel() ); - sb.append( " DATA UNIT ID:" + mMessage.getInt( DUID ) ); - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append("NAC:"); + sb.append(getNAC()); + sb.append(" "); + sb.append(getDUID().getLabel()); + sb.append(" DATA UNIT ID:" + mMessage.getInt(DUID)); + + return sb.toString(); } - @Override + @Override public String getBinaryMessage() { - return mMessage.toString(); + return mMessage.toString(); } - @Override + @Override public String getProtocol() { - return "P25 Phase 1"; + return "P25 Phase 1"; } - @Override + @Override public String getEventType() { - return mDUID.name(); + return mDUID.name(); } - @Override + @Override public String getFromID() { - return null; - } - - @Override - public Alias getFromIDAlias() - { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getFromID() ); - } - return null; } - @Override + @Override + public Alias getFromIDAlias() + { + if(mAliasList != null) + { + return mAliasList.getTalkgroupAlias(getFromID()); + } + + return null; + } + + @Override public String getToID() { - return null; + return null; } - @Override + @Override public Alias getToIDAlias() { - if( mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( getToID() ); - } - - return null; + if(mAliasList != null) + { + return mAliasList.getTalkgroupAlias(getToID()); + } + + return null; } - @Override + @Override public Plottable getPlottable() { - return null; + return null; } - @Override + @Override public String toString() { - return getMessage(); + return getMessage(); } - @Override + @Override public boolean isValid() { - if( mCRC != null ) - { - for( CRC crc: mCRC ) - { - if( crc == CRC.FAILED_CRC ) - { - return false; - } - } - } - - return true; - } + if(mCRC != null) + { + for(CRC crc : mCRC) + { + if(crc == CRC.FAILED_CRC) + { + return false; + } + } + } - /** - * Calculates the frequency of the uplink channel using the channel number - * and the IdentifierUpdate message. - * - * @param iden - Identifier Update message - * @param channel - channel number - * @return - frequency in Hertz - */ - protected static long calculateUplink( IBandIdentifier iden, int channel ) - { - long downlink = calculateDownlink( iden, channel ); - - if( downlink > 0 && iden != null ) - { - return downlink + iden.getTransmitOffset(); - } - - return 0; - } - - /** - * Calculates the frequency of the downlink channel using the channel number - * and the IdentifierUpdate message. - * - * @param iden - Identifier Update message - * @param channel - channel number - * @return - frequency in Hertz - */ - protected static long calculateDownlink( IBandIdentifier iden, int channel ) - { - if( iden != null ) - { - return iden.getBaseFrequency() + - ( channel * iden.getChannelSpacing() ); - } - - return 0; - } - + return true; + } } diff --git a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java index 089912b8..a8dca0b9 100644 --- a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IAdjacentSite; @@ -7,142 +25,148 @@ import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -public class AdjacentSiteStatusBroadcast extends LDU1Message - implements IdentifierReceiver, IAdjacentSite +public class AdjacentSiteStatusBroadcast extends LDU1Message implements IdentifierReceiver, IAdjacentSite { - public static final int[] LRA = { 364,365,366,367,372,373,374,375 }; - public static final int[] SYSTEM_ID = { 384,385,386,387,536,537,538,539,540, - 541,546,547 }; - public static final int[] RFSS_ID = { 548,549,550,551,556,557,558,559 }; - public static final int[] SITE_ID = { 560,561,566,567,568,569,570,571 }; - public static final int[] IDENTIFIER = { 720,721,722,723 }; - public static final int[] CHANNEL = { 724,725,730,731,732,733,734,735,740, - 741,742,743 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753, - 754,755 }; - - private IBandIdentifier mIdentifierUpdate; - - public AdjacentSiteStatusBroadcast( LDU1Message message ) - { - super( message ); - } - + public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; + public static final int[] SYSTEM_ID = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; + public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; + public static final int[] IDENTIFIER = {720, 721, 722, 723}; + public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; + public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; + + private IBandIdentifier mIdentifierUpdate; + + public AdjacentSiteStatusBroadcast(LDU1Message message) + { + super(message); + } + @Override public String getEventType() { return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLRA() ); + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - sb.append( " SYS:" + getSystemID() ); - - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " CHAN:" + getChannel() ); + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLRA()); + + sb.append(" SYS:" + getSystemID()); + + sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); + + sb.append(" CHAN:" + getChannel()); + + sb.append(" " + getSystemServiceClass()); + + return sb.toString(); + } - sb.append( " " + getSystemServiceClass() ); - - return sb.toString(); - } - public String getUniqueID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getRFSS() ); - sb.append( ":" ); - sb.append( getSiteID() ); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getRFSS() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getChannelNumber() - { - return mMessage.getInt( CHANNEL ); - } - - public String getSystemServiceClass() - { - return SystemService.toString( mMessage.getInt( SYSTEM_SERVICE_CLASS ) ); - } + StringBuilder sb = new StringBuilder(); - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + sb.append(getSystemID()); + sb.append(":"); + sb.append(getRFSS()); + sb.append(":"); + sb.append(getSiteID()); + + return sb.toString(); + } + + public String getLRA() { - mIdentifierUpdate = message; + return mMessage.getHex(LRA, 2); } - @Override + public String getSystemID() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getRFSS() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + public String getChannel() + { + return getIdentifier() + "-" + getChannelNumber(); + } + + public int getChannelNumber() + { + return mMessage.getInt(CHANNEL); + } + + public String getSystemServiceClass() + { + return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + mIdentifierUpdate = message; + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } - + @Override - public String getDownlinkChannel() - { - return getChannel(); - } - - @Override - public String getUplinkChannel() - { - return getChannel(); - } - - public long getDownlinkFrequency() + public String getDownlinkChannel() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + return getChannel(); } - + + @Override + public String getUplinkChannel() + { + return getChannel(); + } + + public long getDownlinkFrequency() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; + } + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java index 893c2e5c..c798598c 100644 --- a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IAdjacentSite; @@ -6,154 +24,160 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message - implements IdentifierReceiver, IAdjacentSite +public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver, IAdjacentSite { - public static final int[] LRA = { 364,365,366,367,372,373,374,375 }; - public static final int[] TRANSMIT_IDENTIFIER = { 376,377,382,383 }; - public static final int[] TRANSMIT_CHANNEL = { 384,385,386,387,536,537,538, - 539,540,541,546,547 }; - public static final int[] RFSS_ID = { 548,549,550,551,556,557,558,559 }; - public static final int[] SITE_ID = { 560,561,566,567,568,569,570,571 }; - - public static final int[] RECEIVE_IDENTIFIER = { 720,721,722,723 }; - public static final int[] RECEIVE_CHANNEL = { 724,725,730,731,732,733,734, - 735,740,741,742,743 }; - - private IBandIdentifier mTransmitBand; - private IBandIdentifier mReceiveBand; - - public AdjacentSiteStatusBroadcastExplicit( LDU1Message message ) - { - super( message ); - } - + public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; + public static final int[] TRANSMIT_IDENTIFIER = {376, 377, 382, 383}; + public static final int[] TRANSMIT_CHANNEL = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; + public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; + public static final int[] RECEIVE_IDENTIFIER = {720, 721, 722, 723}; + public static final int[] RECEIVE_CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; + + private IBandIdentifier mTransmitBand; + private IBandIdentifier mReceiveBand; + + public AdjacentSiteStatusBroadcastExplicit(LDU1Message message) + { + super(message); + } + @Override public String getEventType() { return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLRA() ); - - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " DNLINK:" + getDownlinkChannel() ); - - sb.append( " UPLINK:" + getUplinkChannel() ); - - return sb.toString(); - } - + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLRA()); + + sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); + + sb.append(" DNLINK:" + getDownlinkChannel()); + + sb.append(" UPLINK:" + getUplinkChannel()); + + return sb.toString(); + } + public String getUniqueID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getRFSS() ); - sb.append( ":" ); - sb.append( getSiteID() ); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getRFSS() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSystemID() - { - return "***"; - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannel() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getDownlinkChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannel(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannel() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getUplinkChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannel(); - } + StringBuilder sb = new StringBuilder(); - @Override - public String getSystemServiceClass() - { - return "[]"; - } + sb.append(getSystemID()); + sb.append(":"); + sb.append(getRFSS()); + sb.append(":"); + sb.append(getSiteID()); + + return sb.toString(); + } + + public String getLRA() + { + return mMessage.getHex(LRA, 2); + } + + public String getRFSS() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSystemID() + { + return "***"; + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannel() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getDownlinkChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannel(); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannel() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getUplinkChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannel(); + } + + @Override + public String getSystemServiceClass() + { + return "[]"; + } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitBand, getTransmitChannel() ); + if(mTransmitBand != null) + { + return mTransmitBand.getDownlinkFrequency(getTransmitChannel()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveBand, getReceiveChannel() ); + if(mReceiveBand != null) + { + return mReceiveBand.getUplinkFrequency(getReceiveChannel()); + } + + return 0; } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getTransmitIdentifier() ) - { - mTransmitBand = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveBand = message; - } - } + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitBand = message; + } + + if(identifier == getReceiveIdentifier()) + { + mReceiveBand = message; + } + } + + @Override + public int[] getIdentifiers() + { + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; + } - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; - } - } diff --git a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java index 42cf0e78..9c8ef923 100644 --- a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java @@ -1,83 +1,118 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -public class ChannelIdentifierUpdate extends LDU1Message - implements IBandIdentifier +public class ChannelIdentifierUpdate extends LDU1Message implements IBandIdentifier { - public static final int[] IDENTIFIER = { 364,365,366,367 }; - public static final int[] BANDWIDTH = { 372,373,374,375,376,377,382,383,384 }; - public static final int[] TRANSMIT_OFFSET = { 385,386,387,536,537,538,539, - 540,541 }; - public static final int[] CHANNEL_SPACING = { 546,547,548,549,550,551,556, - 557,558,559 }; - public static final int[] BASE_FREQUENCY = { 560,561,566,567,568,569,570, - 571,720,721,722,723,724,725,730,731,732,733,734,735,740,741,742,743,744, - 745,750,751,752,753,754,755 }; + public static final int[] IDENTIFIER = {364, 365, 366, 367}; + public static final int[] BANDWIDTH = {372, 373, 374, 375, 376, 377, 382, 383, 384}; + public static final int[] TRANSMIT_OFFSET = {385, 386, 387, 536, 537, 538, 539, + 540, 541}; + public static final int[] CHANNEL_SPACING = {546, 547, 548, 549, 550, 551, 556, + 557, 558, 559}; + public static final int[] BASE_FREQUENCY = {560, 561, 566, 567, 568, 569, 570, + 571, 720, 721, 722, 723, 724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743, 744, + 745, 750, 751, 752, 753, 754, 755}; + + public ChannelIdentifierUpdate(LDU1Message message) + { + super(message); + } - public ChannelIdentifierUpdate( LDU1Message message ) - { - super( message ); - } - @Override public String getEventType() { return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " IDEN:" + getIdentifier() ); + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - sb.append( " BASE:" + getBaseFrequency() ); - - sb.append( " BW:" + getBandwidth() ); - - sb.append( " SPACING:" + getChannelSpacing() ); - - sb.append( " OFFSET:" + getTransmitOffset() ); - - return sb.toString(); - } + sb.append(getMessageStub()); - @Override - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } + sb.append(" IDEN:" + getIdentifier()); - /** + sb.append(" BASE:" + getBaseFrequency()); + + sb.append(" BW:" + getBandwidth()); + + sb.append(" SPACING:" + getChannelSpacing()); + + sb.append(" OFFSET:" + getTransmitOffset()); + + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + /** * Channel bandwidth in hertz */ @Override public int getBandwidth() { - return mMessage.getInt( BANDWIDTH ) * 125; + return mMessage.getInt(BANDWIDTH) * 125; } - + @Override public long getChannelSpacing() { - return mMessage.getLong( CHANNEL_SPACING ) * 125l; + return mMessage.getLong(CHANNEL_SPACING) * 125l; } @Override public long getBaseFrequency() { - return mMessage.getLong( BASE_FREQUENCY ) * 5l; + return mMessage.getLong(BASE_FREQUENCY) * 5l; } - @Override + @Override public long getTransmitOffset() { - return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l; + return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (channelNumber * getChannelSpacing()); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + @Override + public boolean isTDMA() + { + return false; } } diff --git a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java index c857754f..a1532abf 100644 --- a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java @@ -4,8 +4,7 @@ import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -public class ChannelIdentifierUpdateExplicit extends LDU1Message - implements IBandIdentifier +public class ChannelIdentifierUpdateExplicit extends LDU1Message implements IBandIdentifier { public static final int[] IDENTIFIER = { 364,365,366,367 }; public static final int[] BANDWIDTH = { 372,373,374,375 }; @@ -80,4 +79,22 @@ public class ChannelIdentifierUpdateExplicit extends LDU1Message { return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l; } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + ( channelNumber * getChannelSpacing() ); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + @Override + public boolean isTDMA() + { + return false; + } } diff --git a/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java index 4091664b..d9b54839 100644 --- a/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,156 +23,167 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupVoiceChannelUpdate extends LDU1Message - implements IdentifierReceiver +public class GroupVoiceChannelUpdate extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( GroupVoiceChannelUpdate.class ); + public static final int[] IDENTIFIER_A = {364, 365, 366, 367}; + public static final int[] CHANNEL_A = {372, 373, 374, 375, 376, 377, 382, 383, 384, 385, 386, 387}; + public static final int[] GROUP_ADDRESS_A = {536, 537, 538, 539, 540, 541, 546, 547, 548, 549, 550, 551, 556, 557, + 558, 559}; + public static final int[] IDENTIFIER_B = {560, 561, 566, 567}; + public static final int[] CHANNEL_B = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; + public static final int[] GROUP_ADDRESS_B = {732, 733, 734, 735, 740, 741, 742, 743, 744, 745, 750, 751, 752, 753, + 754, 755}; + + private IBandIdentifier mIdentifierUpdateA; + private IBandIdentifier mIdentifierUpdateB; + + public GroupVoiceChannelUpdate(LDU1Message message) + { + super(message); + } - public static final int[] IDENTIFIER_A = { 364,365,366,367 }; - public static final int[] CHANNEL_A = { 372,373,374,375,376,377,382,383,384, - 385,386,387 }; - public static final int[] GROUP_ADDRESS_A = { 536,537,538,539,540,541,546, - 547,548,549,550,551,556,557,558,559 }; - public static final int[] IDENTIFIER_B = { 560,561,566,567 }; - public static final int[] CHANNEL_B = { 568,569,570,571,720,721,722,723,724, - 725,730,731 }; - public static final int[] GROUP_ADDRESS_B = { 732,733,734,735,740,741,742, - 743,744,745,750,751,752,753,754,755 }; - - private IBandIdentifier mIdentifierUpdateA; - private IBandIdentifier mIdentifierUpdateB; - - public GroupVoiceChannelUpdate( LDU1Message message ) - { - super( message ); - } - @Override public String getEventType() { return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " CHAN A:" + getChannelA() ); - - sb.append( " GRP A:" + getGroupAddressA() ); - - if( hasChannelB() ) - { - sb.append( " CHAN B:" + getChannelA() ); - - sb.append( " GRP B:" + getGroupAddressA() ); - } - - return sb.toString(); - } + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" CHAN A:" + getChannelA()); + + sb.append(" GRP A:" + getGroupAddressA()); + + if(hasChannelB()) + { + sb.append(" CHAN B:" + getChannelA()); + + sb.append(" GRP B:" + getGroupAddressA()); + } + + return sb.toString(); + } + + public int getChannelIdentifierA() + { + return mMessage.getInt(IDENTIFIER_A); + } - public int getChannelIdentifierA() - { - return mMessage.getInt( IDENTIFIER_A ); - } - public int getChannelNumberA() { - return mMessage.getInt( CHANNEL_A ); + return mMessage.getInt(CHANNEL_A); } - + public String getChannelA() { - return getChannelIdentifierA() + "-" + getChannelNumberA(); + return getChannelIdentifierA() + "-" + getChannelNumberA(); } - - public int getChannelIdentifierB() - { - return mMessage.getInt( IDENTIFIER_B ); - } - + + public int getChannelIdentifierB() + { + return mMessage.getInt(IDENTIFIER_B); + } + public int getChannelNumberB() { - return mMessage.getInt( CHANNEL_B ); + return mMessage.getInt(CHANNEL_B); } - + public String getChannelB() { - return getChannelIdentifierB() + "-" + getChannelNumberB(); + return getChannelIdentifierB() + "-" + getChannelNumberB(); } - + public boolean hasChannelB() { - return getChannelNumberB() != 0; + return getChannelNumberB() != 0; } - + public String getGroupAddressA() { - return mMessage.getHex( GROUP_ADDRESS_A, 4 ); + return mMessage.getHex(GROUP_ADDRESS_A, 4); } public String getGroupAddressB() { - return mMessage.getHex( GROUP_ADDRESS_B, 4 ); + return mMessage.getHex(GROUP_ADDRESS_B, 4); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - if( identifier == getChannelIdentifierA() ) - { - mIdentifierUpdateA = message; - } - if( identifier == getChannelIdentifierB() ) - { - mIdentifierUpdateB = message; - } + if(identifier == getChannelIdentifierA()) + { + mIdentifierUpdateA = message; + } + if(identifier == getChannelIdentifierB()) + { + mIdentifierUpdateB = message; + } } - @Override + @Override public int[] getIdentifiers() { - if( hasChannelB() ) - { - int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getChannelIdentifierA(); - identifiers[ 1 ] = getChannelIdentifierB(); - - return identifiers; - } - else - { - int[] identifiers = new int[ 1 ]; - identifiers[ 0 ] = getChannelIdentifierA(); - - return identifiers; - } + if(hasChannelB()) + { + int[] identifiers = new int[2]; + identifiers[0] = getChannelIdentifierA(); + identifiers[1] = getChannelIdentifierB(); + + return identifiers; + } + else + { + int[] identifiers = new int[1]; + identifiers[0] = getChannelIdentifierA(); + + return identifiers; + } } public long getDownlinkFrequencyA() { - return calculateDownlink( mIdentifierUpdateA, getChannelNumberA() ); + if(mIdentifierUpdateA != null) + { + return mIdentifierUpdateA.getDownlinkFrequency(getChannelNumberA()); + } + + return 0; } - + public long getUplinkFrequencyA() { - return calculateUplink( mIdentifierUpdateA, getChannelNumberA() ); + if(mIdentifierUpdateA != null) + { + return mIdentifierUpdateA.getUplinkFrequency(getChannelNumberA()); + } + + return 0; } public long getDownlinkFrequencyB() { - return calculateDownlink( mIdentifierUpdateB, getChannelNumberA() ); + if(mIdentifierUpdateB != null) + { + return mIdentifierUpdateB.getDownlinkFrequency(getChannelNumberB()); + } + + return 0; } - + public long getUplinkFrequencyB() { - return calculateUplink( mIdentifierUpdateB, getChannelNumberA() ); + if(mIdentifierUpdateB != null) + { + return mIdentifierUpdateB.getUplinkFrequency(getChannelNumberB()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java index f3aa25f3..be2dfda5 100644 --- a/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,151 +23,149 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GroupVoiceChannelUpdateExplicit extends LDU1Message - implements IdentifierReceiver +public class GroupVoiceChannelUpdateExplicit extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( GroupVoiceChannelUpdateExplicit.class ); - public static final int EMERGENCY_FLAG = 376; public static final int ENCRYPTED_CHANNEL_FLAG = 377; public static final int DUPLEX_MODE = 382; public static final int SESSION_MODE = 383; - public static final int[] GROUP_ADDRESS = { 536,537,538,539,540,541,546,547, - 548,549,550,551,556,557,558,559 }; - public static final int[] TRANSMIT_IDENTIFIER = { 560,561,566,567 }; - public static final int[] TRANSMIT_CHANNEL = { 568,569,570,571,720,721,722, - 723,724,725,730,731 }; - public static final int[] RECEIVE_IDENTIFIER = { 732,733,734,735 }; - public static final int[] RECEIVE_CHANNEL = { 740,741,742,743,744,745,750, - 751,752,753,754,755 }; + public static final int[] GROUP_ADDRESS = {536, 537, 538, 539, 540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567}; + public static final int[] TRANSMIT_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; + public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735}; + public static final int[] RECEIVE_CHANNEL = {740, 741, 742, 743, 744, 745, 750, 751, 752, 753, 754, 755}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public GroupVoiceChannelUpdateExplicit(LDU1Message message) + { + super(message); + } - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupVoiceChannelUpdateExplicit( LDU1Message message ) - { - super( message ); - } - @Override public String getEventType() { return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - - return sb.toString(); - } + sb.append(getMessageStub()); + + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + if(isEncryptedChannel()) + { + sb.append(" ENCRYPTED CHANNEL"); + } + + + return sb.toString(); + } public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncryptedChannel() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getInt(TRANSMIT_IDENTIFIER); } - @Override + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public String getGroupAddress() + { + return mMessage.getHex(GROUP_ADDRESS, 4); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitChannelIdentifier(); - identifiers[ 1 ] = getReceiveChannelIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitChannelIdentifier(); + identifiers[1] = getReceiveChannelIdentifier(); + + return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, - getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, - getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java index ec22b3ff..ae8091d9 100644 --- a/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -6,105 +24,106 @@ import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; import module.decode.p25.reference.Service; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class NetworkStatusBroadcast extends LDU1Message - implements IdentifierReceiver +public class NetworkStatusBroadcast extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( NetworkStatusBroadcast.class ); - public static final int[] NETWORK_ID = { 376,377,382,383,384,385,386,387, - 536,537,538,539,540,541,546,547,548,549,550,551 }; - public static final int[] SYSTEM_ID = { 556,557,558,559,560,561,566,567,568, - 569,570,571 }; - public static final int[] IDENTIFIER = { 720,721,722,723 }; - public static final int[] CHANNEL = { 724,725,730,731,732,733,734,735,740, - 741,742,743 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753, - 754,755 }; - - private IBandIdentifier mIdentifierUpdate; - - public NetworkStatusBroadcast( LDU1Message source ) - { - super( source ); - } - + public static final int[] NETWORK_ID = {376, 377, 382, 383, 384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, + 547, 548, 549, 550, 551}; + public static final int[] SYSTEM_ID = {556, 557, 558, 559, 560, 561, 566, 567, 568, 569, 570, 571}; + public static final int[] IDENTIFIER = {720, 721, 722, 723}; + public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; + public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; + + private IBandIdentifier mIdentifierUpdate; + + public NetworkStatusBroadcast(LDU1Message source) + { + super(source); + } + @Override public String getEventType() { return LinkControlOpcode.NETWORK_STATUS_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " NETWORK:" + getNetworkID() ); - sb.append( " SYS:" + getSystem() ); - sb.append( " CHAN:" + getChannel() ); - sb.append( " " + Service.getServices( getSystemServiceClass() ).toString() ); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex( NETWORK_ID, 5 ); - } - - public String getSystem() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - public int getChannelNumber() - { - return mMessage.getInt( CHANNEL ); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - mIdentifierUpdate = message; + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" NETWORK:" + getNetworkID()); + sb.append(" SYS:" + getSystem()); + sb.append(" CHAN:" + getChannel()); + sb.append(" " + Service.getServices(getSystemServiceClass()).toString()); + + return sb.toString(); } - @Override + public String getNetworkID() + { + return mMessage.getHex(NETWORK_ID, 5); + } + + public String getSystem() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + public int getChannelNumber() + { + return mMessage.getInt(CHANNEL); + } + + public String getChannel() + { + return getIdentifier() + "-" + getChannelNumber(); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + mIdentifierUpdate = message; + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java index 682882ff..abfa7c92 100644 --- a/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java @@ -1,131 +1,150 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class NetworkStatusBroadcastExplicit extends LDU1Message - implements IdentifierReceiver +public class NetworkStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( NetworkStatusBroadcastExplicit.class ); - public static final int[] NETWORK_ID = { 364,365,366,367,372,373,374,375, - 376,377,382,383,384,385,386,387,536,537,538,539 }; - public static final int[] SYSTEM_ID = { 540,541,546,547,548,549,550,551,556, - 557,558,559 }; - public static final int[] TRANSMIT_IDENTIFIER = { 560,561,566,567 }; - public static final int[] TRANSMIT_CHANNEL = { 568,569,570,571,720,721,722, - 723,724,725,730,731 }; - public static final int[] RECEIVE_IDENTIFIER = { 732,733,734,735 }; - public static final int[] RECEIVE_CHANNEL = { 740,741,742,743,744,745,750, - 751,752,753,754,755 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public NetworkStatusBroadcastExplicit( LDU1Message message ) - { - super( message ); - } - + public static final int[] NETWORK_ID = {364, 365, 366, 367, 372, 373, 374, 375, 376, 377, 382, 383, 384, 385, 386, + 387, 536, 537, 538, 539}; + public static final int[] SYSTEM_ID = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567}; + public static final int[] TRANSMIT_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; + public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735}; + public static final int[] RECEIVE_CHANNEL = {740, 741, 742, 743, 744, 745, 750, 751, 752, 753, 754, 755}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public NetworkStatusBroadcastExplicit(LDU1Message message) + { + super(message); + } + @Override public String getEventType() { return LinkControlOpcode.NETWORK_STATUS_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " NETWORK:" + getNetworkID() ); - sb.append( " SYS:" + getSystem() ); - sb.append( " TRANSMIT:" + getTransmitChannel() ); - sb.append( " RECEIVE:" + getReceiveChannel() ); - - return sb.toString(); - } - - public String getNetworkID() - { - return mMessage.getHex( NETWORK_ID, 5 ); - } - - public String getSystem() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" NETWORK:" + getNetworkID()); + sb.append(" SYS:" + getSystem()); + sb.append(" TRANSMIT:" + getTransmitChannel()); + sb.append(" RECEIVE:" + getReceiveChannel()); + + return sb.toString(); } - @Override + public String getNetworkID() + { + return mMessage.getHex(NETWORK_ID, 5); + } + + public String getSystem() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getTransmitChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getReceiveChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, - getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, - getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java index 07edbef1..02b6017d 100644 --- a/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -6,125 +24,124 @@ import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RFSSStatusBroadcast extends LDU1Message - implements IdentifierReceiver +public class RFSSStatusBroadcast extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( RFSSStatusBroadcast.class ); + public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; + public static final int[] SYSTEM_ID = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; + public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; + public static final int[] IDENTIFIER = {720, 721, 722, 723}; + public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; + public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; + + private IBandIdentifier mIdentifierUpdate; + + public RFSSStatusBroadcast(LDU1Message message) + { + super(message); + } - public static final int[] LRA = { 364,365,366,367,372,373,374,375 }; - public static final int[] SYSTEM_ID = { 384,385,386,387,536,537,538,539,540, - 541,546,547 }; - public static final int[] RFSS_ID = { 548,549,550,551,556,557,558,559 }; - public static final int[] SITE_ID = { 560,561,566,567,568,569,570,571 }; - public static final int[] IDENTIFIER = { 720,721,722,723 }; - public static final int[] CHANNEL = { 724,725,730,731,732,733,734,735,740, - 741,742,743 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753, - 754,755 }; - - private IBandIdentifier mIdentifierUpdate; - - public RFSSStatusBroadcast( LDU1Message message ) - { - super( message ); - } - @Override public String getEventType() { return LinkControlOpcode.RFSS_STATUS_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " SYS:" + getSystem() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " CHAN:" + getChannelNumber() ); - - sb.append( " " + SystemService.toString( getSystemServiceClass() ) ); - - sb.append( " " + mMessage.toString() ); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getSystem() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getChannelNumber() - { - return mMessage.getInt( CHANNEL ); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - mIdentifierUpdate = message; + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" SYS:" + getSystem()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" CHAN:" + getChannelNumber()); + + sb.append(" " + SystemService.toString(getSystemServiceClass())); + + sb.append(" " + mMessage.toString()); + + return sb.toString(); } - @Override + public String getLocationRegistrationArea() + { + return mMessage.getHex(LRA, 2); + } + + public String getSystem() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + public String getChannel() + { + return getIdentifier() + "-" + getChannelNumber(); + } + + public int getChannelNumber() + { + return mMessage.getInt(CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + mIdentifierUpdate = message; + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } - } diff --git a/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java index b3b7b7ec..54b813d5 100644 --- a/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,146 +23,148 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RFSSStatusBroadcastExplicit extends LDU1Message - implements IdentifierReceiver +public class RFSSStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( RFSSStatusBroadcastExplicit.class ); + public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375}; + public static final int[] RECEIVE_IDENTIFIER = {376, 377, 382, 383}; + public static final int[] RECEIVE_CHANNEL = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547}; + public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571}; + public static final int[] TRANSMIT_IDENTIFIER = {720, 721, 722, 723}; + public static final int[] TRANSMIT_CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; + public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755}; - public static final int[] LRA = { 364,365,366,367,372,373,374,375 }; - public static final int[] RECEIVE_IDENTIFIER = { 376,377,382,383 }; - public static final int[] RECEIVE_CHANNEL = { 384,385,386,387,536,537,538, - 539,540,541,546,547 }; - public static final int[] RFSS_ID = { 548,549,550,551,556,557,558,559 }; - public static final int[] SITE_ID = { 560,561,566,567,568,569,570,571 }; - public static final int[] TRANSMIT_IDENTIFIER = { 720,721,722,723 }; - public static final int[] TRANSMIT_CHANNEL = { 724,725,730,731,732,733,734, - 735,740,741,742,743 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753,754,755 }; + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public RFSSStatusBroadcastExplicit(LDU1Message source) + { + super(source); + } - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public RFSSStatusBroadcastExplicit( LDU1Message source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.RFSS_STATUS_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " TRANSMIT:" + getTransmitChannelNumber() ); - - sb.append( " RECEIVE:" + getReceiveChannelNumber() ); - - sb.append( " " + SystemService.toString( getSystemServiceClass() ) ); - - sb.append( " " + mMessage.toString() ); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" TRANSMIT:" + getTransmitChannelNumber()); + + sb.append(" RECEIVE:" + getReceiveChannelNumber()); + + sb.append(" " + SystemService.toString(getSystemServiceClass())); + + sb.append(" " + mMessage.toString()); + + return sb.toString(); } - @Override + public String getLocationRegistrationArea() + { + return mMessage.getHex(LRA, 2); + } + + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public String getTransmitChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public String getReceiveChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } - } diff --git a/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java index e154f8d5..e275d0fa 100644 --- a/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -6,151 +24,162 @@ import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SecondaryControlChannelBroadcast extends LDU1Message - implements IdentifierReceiver +public class SecondaryControlChannelBroadcast extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( SecondaryControlChannelBroadcast.class ); + public static final int[] RFSS_ID = {364, 365, 366, 367, 372, 373, 374, 375}; + public static final int[] SITE_ID = {376, 377, 382, 383, 384, 385, 386, 387}; + public static final int[] IDENTIFIER_A = {536, 537, 538, 539}; + public static final int[] CHANNEL_A = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] SYSTEM_SERVICE_CLASS_A = {560, 561, 566, 567, 568, 569, 570, 571}; + public static final int[] IDENTIFIER_B = {720, 721, 722, 723}; + public static final int[] CHANNEL_B = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743}; + public static final int[] SYSTEM_SERVICE_CLASS_B = {744, 745, 750, 751, 752, 753, 754, 755}; - public static final int[] RFSS_ID = { 364,365,366,367,372,373,374,375 }; - public static final int[] SITE_ID = { 376,377,382,383,384,385,386,387 }; - public static final int[] IDENTIFIER_A = { 536,537,538,539 }; - public static final int[] CHANNEL_A = { 540,541,546,547,548,549,550,551, - 556,557,558,559 }; - public static final int[] SYSTEM_SERVICE_CLASS_A = { 560,561,566,567,568,569,570,571 }; - public static final int[] IDENTIFIER_B = { 720,721,722,723 }; - public static final int[] CHANNEL_B = { 724,725,730,731,732,733,734,735,740, - 741,742,743 }; - public static final int[] SYSTEM_SERVICE_CLASS_B = { 744,745,750,751,752,753,754,755 }; + private IBandIdentifier mIdentifierProviderA; + private IBandIdentifier mIdentifierProviderB; + + public SecondaryControlChannelBroadcast(LDU1Message message) + { + super(message); + } - private IBandIdentifier mIdentifierProviderA; - private IBandIdentifier mIdentifierProviderB; - - public SecondaryControlChannelBroadcast( LDU1Message message ) - { - super( message ); - } - @Override public String getEventType() { return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " CHAN A:" + getChannelA() ); - - sb.append( " " + SystemService.toString( getSystemServiceClassA() ) ); - - sb.append( " CHAN B:" + getChannelB() ); - - sb.append( " " + SystemService.toString( getSystemServiceClassB() ) ); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getIdentifierA() - { - return mMessage.getInt( IDENTIFIER_A ); - } - - public int getChannelA() - { - return mMessage.getInt( CHANNEL_A ); - } - - public String getChannelNumberA() - { - return getIdentifierA() + "-" + getChannelA(); - } - - public int getIdentifierB() - { - return mMessage.getInt( IDENTIFIER_B ); - } - - public int getChannelB() - { - return mMessage.getInt( CHANNEL_B ); - } - - public String getChannelNumberB() - { - return getIdentifierB() + "-" + getChannelB(); - } - - public int getSystemServiceClassA() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS_A ); - } - - public int getSystemServiceClassB() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS_B ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getIdentifierA() ) - { - mIdentifierProviderA = message; - } - if( identifier == getIdentifierB() ) - { - mIdentifierProviderB = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" CHAN A:" + getChannelA()); + + sb.append(" " + SystemService.toString(getSystemServiceClassA())); + + sb.append(" CHAN B:" + getChannelB()); + + sb.append(" " + SystemService.toString(getSystemServiceClassB())); + + return sb.toString(); } - @Override + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getIdentifierA() + { + return mMessage.getInt(IDENTIFIER_A); + } + + public int getChannelA() + { + return mMessage.getInt(CHANNEL_A); + } + + public String getChannelNumberA() + { + return getIdentifierA() + "-" + getChannelA(); + } + + public int getIdentifierB() + { + return mMessage.getInt(IDENTIFIER_B); + } + + public int getChannelB() + { + return mMessage.getInt(CHANNEL_B); + } + + public String getChannelNumberB() + { + return getIdentifierB() + "-" + getChannelB(); + } + + public int getSystemServiceClassA() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS_A); + } + + public int getSystemServiceClassB() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS_B); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getIdentifierA()) + { + mIdentifierProviderA = message; + } + if(identifier == getIdentifierB()) + { + mIdentifierProviderB = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getIdentifierA(); - identifiers[ 1 ] = getIdentifierB(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getIdentifierA(); + identifiers[1] = getIdentifierB(); + + return identifiers; } - + public long getDownlinkFrequencyA() { - return calculateDownlink( mIdentifierProviderA, getChannelA() ); + if(mIdentifierProviderA != null) + { + return mIdentifierProviderA.getDownlinkFrequency(getChannelA()); + } + + return 0; } - + public long getUplinkFrequencyA() { - return calculateUplink( mIdentifierProviderA, getChannelA() ); + if(mIdentifierProviderA != null) + { + return mIdentifierProviderA.getUplinkFrequency(getChannelA()); + } + + return 0; } - + public long getDownlinkFrequencyB() { - return calculateDownlink( mIdentifierProviderB, getChannelB() ); + if(mIdentifierProviderB != null) + { + return mIdentifierProviderB.getDownlinkFrequency(getChannelB()); + } + + return 0; } - + public long getUplinkFrequencyB() { - return calculateUplink( mIdentifierProviderB, getChannelB() ); + if(mIdentifierProviderB != null) + { + return mIdentifierProviderB.getUplinkFrequency(getChannelB()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java index 299531b2..00ae9b8c 100644 --- a/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.IBandIdentifier; @@ -6,136 +24,134 @@ import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SecondaryControlChannelBroadcastExplicit - extends LDU1Message implements IdentifierReceiver +public class SecondaryControlChannelBroadcastExplicit extends LDU1Message implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( SecondaryControlChannelBroadcastExplicit.class ); + public static final int[] RFSS_ID = {364, 365, 366, 367, 372, 373, 374, 375}; + public static final int[] SITE_ID = {376, 377, 382, 383, 384, 385, 386, 387}; + public static final int[] TRANSMIT_IDENTIFIER = {536, 537, 538, 539}; + public static final int[] TRANSMIT_CHANNEL = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559}; + public static final int[] RECEIVE_IDENTIFIER = {560, 561, 566, 567}; + public static final int[] RECEIVE_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731}; + public static final int[] SYSTEM_SERVICE_CLASS = {732, 733, 734, 735, 740, 741, 742, 743}; + + private IBandIdentifier mTransmitIdentifierProvider; + private IBandIdentifier mReceiveIdentifierProvider; + + public SecondaryControlChannelBroadcastExplicit(LDU1Message message) + { + super(message); + } - public static final int[] RFSS_ID = { 364,365,366,367,372,373,374,375 }; - public static final int[] SITE_ID = { 376,377,382,383,384,385,386,387 }; - public static final int[] TRANSMIT_IDENTIFIER = { 536,537,538,539 }; - public static final int[] TRANSMIT_CHANNEL = { 540,541,546,547,548,549,550, - 551,556,557,558,559 }; - public static final int[] RECEIVE_IDENTIFIER = { 560,561,566,567 }; - public static final int[] RECEIVE_CHANNEL = { 568,569,570,571,720,721,722, - 723,724,725,730,731 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 732,733,734,735,740,741, - 742,743 }; - - private IBandIdentifier mTransmitIdentifierProvider; - private IBandIdentifier mReceiveIdentifierProvider; - - public SecondaryControlChannelBroadcastExplicit( LDU1Message message ) - { - super( message ); - } - @Override public String getEventType() { return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " TRANSMIT:" + getTransmitChannel() ); - - sb.append( " RECEIVE:" + getReceiveChannel() ); - - sb.append( " " + SystemService.toString( getSystemServiceClass() ) ); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierProvider = message; - } - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierProvider = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" TRANSMIT:" + getTransmitChannel()); + + sb.append(" RECEIVE:" + getReceiveChannel()); + + sb.append(" " + SystemService.toString(getSystemServiceClass())); + + return sb.toString(); } - @Override + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getTransmitChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getReceiveChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierProvider = message; + } + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierProvider = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierProvider, - getTransmitChannelNumber() ); + if(mTransmitIdentifierProvider != null) + { + return mTransmitIdentifierProvider.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierProvider, - getReceiveChannelNumber() ); + if(mReceiveIdentifierProvider != null) + { + return mReceiveIdentifierProvider.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java b/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java index ac8a1f30..29bd9bc4 100644 --- a/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java @@ -1,199 +1,220 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu; -import module.decode.p25.message.IBandIdentifier; -import module.decode.p25.message.IdentifierReceiver; -import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; import edac.CRCP25; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; -public abstract class UnitToUnitChannelGrantExtended extends PDUMessage - implements IdentifierReceiver +public abstract class UnitToUnitChannelGrantExtended extends PDUMessage implements IdentifierReceiver { /* Service Options */ public static final int EMERGENCY_FLAG = 128; public static final int ENCRYPTED_CHANNEL_FLAG = 129; public static final int DUPLEX_MODE = 130; public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] SOURCE_WACN = { 160,161,162,163,164,165,166,167, - 168,169,170,171,172,173,174,175,176,177,178,179 }; - public static final int[] SOURCE_SYSTEM_ID = { 180,181,182,183,184,185,186, - 187,188,189,190,191 }; - public static final int[] SOURCE_ID = { 192,193,194,195,196,197,198,199,200, - 201,202,203,204,205,206,207,208,209,210,211,212,213,214,215 }; - public static final int[] TARGET_ADDRESS = { 216,217,218,219,220,221,222, - 223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239 }; - public static final int[] TRANSMIT_IDENTIFIER = { 240,241,242,243 }; - public static final int[] TRANSMIT_NUMBER = { 244,245,246,247,248,249,250, - 251,252,253,254,255 }; - public static final int[] RECEIVE_IDENTIFIER = { 256,257,258,259 }; - public static final int[] RECEIVE_NUMBER = { 260,261,262,263,264,265,266, - 267,268,269,270,271 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, - 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, - 343,344,345,346,347,348,349,350,351 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public UnitToUnitChannelGrantExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + + public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SOURCE_WACN = {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179}; + public static final int[] SOURCE_SYSTEM_ID = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; + public static final int[] SOURCE_ID = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215}; + public static final int[] TARGET_ADDRESS = {216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239}; + public static final int[] TRANSMIT_IDENTIFIER = {240, 241, 242, 243}; + public static final int[] TRANSMIT_NUMBER = {244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + public static final int[] RECEIVE_IDENTIFIER = {256, 257, 258, 259}; + public static final int[] RECEIVE_NUMBER = {260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271}; + public static final int[] MULTIPLE_BLOCK_CRC = {320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public UnitToUnitChannelGrantExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU2( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU2(mMessage); + mCRC[1] = mMessage.getCRC(); } public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - if( isEmergency() ) + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " FROM ADDR:" ); - sb.append( getSourceAddress() ); - - sb.append( " ID:" ); - sb.append( getSourceID() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " WACN:" ); - sb.append( getSourceWACN() ); - - sb.append( " SYS:" ); - sb.append( getSourceSystemID() ); - sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - sb.append( " " + getDownlinkFrequency() ); - - sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - sb.append( " " + getUplinkFrequency() ); - + sb.append(" FROM ADDR:"); + sb.append(getSourceAddress()); + + sb.append(" ID:"); + sb.append(getSourceID()); + + sb.append(" TO:"); + sb.append(getTargetAddress()); + + sb.append(" WACN:"); + sb.append(getSourceWACN()); + + sb.append(" SYS:"); + sb.append(getSourceSystemID()); + + sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + sb.append(" " + getDownlinkFrequency()); + + sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + sb.append(" " + getUplinkFrequency()); + return sb.toString(); } - + public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncrypted() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public String getSourceAddress() { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getSourceWACN() - { - return mMessage.getHex( SOURCE_WACN, 5 ); - } - - public String getSourceSystemID() - { - return mMessage.getHex( SOURCE_SYSTEM_ID, 3 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_NUMBER ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_NUMBER ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + return mMessage.getHex(SOURCE_ADDRESS, 6); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public String getSourceID() { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getHex(SOURCE_ID, 6); + } + + public String getSourceWACN() + { + return mMessage.getHex(SOURCE_WACN, 5); + } + + public String getSourceSystemID() + { + return mMessage.getHex(SOURCE_SYSTEM_ID, 3); + } + + public String getTargetAddress() + { + return mMessage.getHex(TARGET_ADDRESS, 6); + } + + public int getTransmitChannelIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_NUMBER); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_NUMBER); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitChannelIdentifier(); + idens[1] = getReceiveChannelIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitChannelIdentifier(); - idens[ 1 ] = getReceiveChannelIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java index de1183cd..3fd3dce1 100644 --- a/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java @@ -1,5 +1,26 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.control; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IAdjacentSite; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; @@ -7,43 +28,32 @@ import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class AdjacentStatusBroadcastExtended extends PDUMessage - implements IdentifierReceiver, IAdjacentSite +public class AdjacentStatusBroadcastExtended extends PDUMessage implements IdentifierReceiver, IAdjacentSite { - public static final int[] LRA = { 88,89,90,91,92,93,94,95 }; - public static final int[] SYSTEM_ID = { 100,101,102,103,104,105,106,107,108, - 109,110,111 }; - public static final int[] RF_SUBSYSTEM_ID = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] SITE_ID = { 136,137,138,139,140,141,142,143 }; - public static final int[] TRANSMIT_IDENTIFIER = { 160,161,162,163 }; - public static final int[] TRANSMIT_CHANNEL = { 164,165,166,167,168,169,170, - 171,172,173,174,175 }; - public static final int[] RECEIVE_IDENTIFIER = { 176,177,178,179 }; - public static final int[] RECEIVE_CHANNEL = { 180,181,182,183,184,185,186, - 187,188,189,190,191 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 192,193,194,195,196,197, - 198,199 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 }; - - private IBandIdentifier mTransmitIdentifierProvider; - private IBandIdentifier mReceiveIdentifierProvider; - - public AdjacentStatusBroadcastExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + public static final int[] LRA = {88, 89, 90, 91, 92, 93, 94, 95}; + public static final int[] SYSTEM_ID = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] RF_SUBSYSTEM_ID = {128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SITE_ID = {136, 137, 138, 139, 140, 141, 142, 143}; + public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; + public static final int[] TRANSMIT_CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}; + public static final int[] RECEIVE_IDENTIFIER = {176, 177, 178, 179}; + public static final int[] RECEIVE_CHANNEL = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; + public static final int[] SYSTEM_SERVICE_CLASS = {192, 193, 194, 195, 196, 197, 198, 199}; + public static final int[] MULTIPLE_BLOCK_CRC = {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + + private IBandIdentifier mTransmitIdentifierProvider; + private IBandIdentifier mReceiveIdentifierProvider; + + public AdjacentStatusBroadcastExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU1(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -51,138 +61,148 @@ public class AdjacentStatusBroadcastExtended extends PDUMessage { return Opcode.ADJACENT_STATUS_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " LRA:" + getLRA() ); - - sb.append( " SYS:" + getSystemID() ); - - sb.append( " RFSS:" + getRFSS() ); - - sb.append( " SITE:" + getSiteID() ); + sb.append(getMessageStub()); - sb.append( " CTRL CHAN:" + getTransmitIdentifier() + "-" + getTransmitChannel() ); + sb.append(" LRA:" + getLRA()); - sb.append( " DN:" + getDownlinkFrequency() ); + sb.append(" SYS:" + getSystemID()); - sb.append( " " + getReceiveIdentifier() + "-" + getReceiveChannel() ); + sb.append(" RFSS:" + getRFSS()); + + sb.append(" SITE:" + getSiteID()); + + sb.append(" CTRL CHAN:" + getTransmitIdentifier() + "-" + getTransmitChannel()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" " + getReceiveIdentifier() + "-" + getReceiveChannel()); + + sb.append(" UP:" + getUplinkFrequency()); + + sb.append(" SYS SVC CLASS:" + getSystemServiceClass()); - sb.append( " UP:" + getUplinkFrequency() ); - - sb.append( " SYS SVC CLASS:" + getSystemServiceClass() ); - return sb.toString(); } - + public String getUniqueID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getRFSS() ); - sb.append( ":" ); - sb.append( getSiteID() ); - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(getSystemID()); + sb.append(":"); + sb.append(getRFSS()); + sb.append(":"); + sb.append(getSiteID()); + + return sb.toString(); } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannel() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannel() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - + + public String getSystemID() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannel() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannel() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierProvider, getTransmitChannel() ); + if(mTransmitIdentifierProvider != null) + { + return mTransmitIdentifierProvider.getDownlinkFrequency(getTransmitChannel()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierProvider, getReceiveChannel() ); + if(mReceiveIdentifierProvider != null) + { + return mReceiveIdentifierProvider.getUplinkFrequency(getReceiveChannel()); + } + + return 0; } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierProvider = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierProvider = message; - } + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierProvider = message; + } + + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierProvider = message; + } } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); - - return idens; - } - @Override - public String getRFSS() - { - return mMessage.getHex( RF_SUBSYSTEM_ID, 2 ); - } + public int[] getIdentifiers() + { + int[] idens = new int[2]; - @Override - public String getLRA() - { - return mMessage.getHex( LRA, 2 ); - } + idens[0] = getTransmitIdentifier(); + idens[1] = getReceiveIdentifier(); - @Override - public String getSystemServiceClass() - { - return SystemService.toString( mMessage.getInt( SYSTEM_SERVICE_CLASS ) ); - } + return idens; + } - @Override - public String getDownlinkChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannel(); - } + @Override + public String getRFSS() + { + return mMessage.getHex(RF_SUBSYSTEM_ID, 2); + } - @Override - public String getUplinkChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannel(); - } + @Override + public String getLRA() + { + return mMessage.getHex(LRA, 2); + } + + @Override + public String getSystemServiceClass() + { + return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); + } + + @Override + public String getDownlinkChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannel(); + } + + @Override + public String getUplinkChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannel(); + } } diff --git a/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java index f9f812a5..fb6b7fbf 100644 --- a/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java @@ -1,5 +1,26 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.control; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.pdu.PDUMessage; @@ -7,42 +28,32 @@ import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; import module.decode.p25.reference.P25NetworkCallsign; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class NetworkStatusBroadcastExtended extends PDUMessage - implements IdentifierReceiver +public class NetworkStatusBroadcastExtended extends PDUMessage implements IdentifierReceiver { - public static final int[] LRA = { 88,89,90,91,92,93,94,95 }; - public static final int[] SYSTEM_ID = { 100,101,102,103,104,105,106,107,108, - 109,110,111 }; - public static final int[] WACN = { 160,161,162,163,164,165,166,167,168,169, - 170,171,172,173,174,175,176,177,178,179 }; - public static final int[] TRANSMIT_IDENTIFIER = { 184,185,186,187 }; - public static final int[] TRANSMIT_CHANNEL = { 188,189,190,191,192,193,194, - 195,196,197,198,199 }; - public static final int[] RECEIVE_IDENTIFIER = { 200,201,202,203 }; - public static final int[] RECEIVE_CHANNEL = { 204,205,206,207,208,209,210, - 211,212,213,214,215 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 216,217,218,219,220,221, - 222,223 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public NetworkStatusBroadcastExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + public static final int[] LRA = {88, 89, 90, 91, 92, 93, 94, 95}; + public static final int[] SYSTEM_ID = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] WACN = {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179}; + public static final int[] TRANSMIT_IDENTIFIER = {184, 185, 186, 187}; + public static final int[] TRANSMIT_CHANNEL = {188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199}; + public static final int[] RECEIVE_IDENTIFIER = {200, 201, 202, 203}; + public static final int[] RECEIVE_CHANNEL = {204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215}; + public static final int[] SYSTEM_SERVICE_CLASS = {216, 217, 218, 219, 220, 221, 222, 223}; + public static final int[] MULTIPLE_BLOCK_CRC = {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public NetworkStatusBroadcastExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU1(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -50,111 +61,121 @@ public class NetworkStatusBroadcastExtended extends PDUMessage { return Opcode.NETWORK_STATUS_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " WACN:" + getWACN() ); - - sb.append( " SYS:" + getSystemID() ); - - sb.append( " CTRL CHAN:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " " + getTransmitIdentifier() + "-" + getTransmitChannel() ); - - sb.append( " UP:" + getUplinkFrequency() ); - - sb.append( " SYS SVC CLASS:" + - SystemService.toString( getSystemServiceClass() ) ); - + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" WACN:" + getWACN()); + + sb.append(" SYS:" + getSystemID()); + + sb.append(" CTRL CHAN:" + getReceiveIdentifier() + "-" + getReceiveChannel()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" " + getTransmitIdentifier() + "-" + getTransmitChannel()); + + sb.append(" UP:" + getUplinkFrequency()); + + sb.append(" SYS SVC CLASS:" + + SystemService.toString(getSystemServiceClass())); + return sb.toString(); } - - - public String getLocationRegistrationArea() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getNetworkCallsign() - { - return P25NetworkCallsign.getCallsign( mMessage.getInt( WACN ), - mMessage.getInt( SYSTEM_ID ) ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannel() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannel() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public int getSystemServiceClass() + + + public String getLocationRegistrationArea() { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return mMessage.getHex(LRA, 2); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public String getSystemID() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getWACN() + { + return mMessage.getHex(WACN, 5); + } + + public String getNetworkCallsign() + { + return P25NetworkCallsign.getCallsign(mMessage.getInt(WACN), + mMessage.getInt(SYSTEM_ID)); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannel() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannel() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannel()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannel()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitIdentifier(); + idens[1] = getReceiveIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java index a8b6a03f..94ee8608 100644 --- a/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java @@ -1,48 +1,59 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.control; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class RFSSStatusBroadcastExtended extends PDUMessage - implements IdentifierReceiver +public class RFSSStatusBroadcastExtended extends PDUMessage implements IdentifierReceiver { - public static final int[] LRA = { 88,89,90,91,92,93,94,95 }; - public static final int ACTIVE_NETWORK_CONNECTION_INDICATOR = 99; - public static final int[] SYSTEM_ID = { 100,101,102,103,104,105,106,107,108, - 109,110,111 }; - public static final int[] RFSS_ID = { 160,161,162,163,164,165,166,167 }; - public static final int[] SITE_ID = { 168,169,170,171,172,173,174,175 }; - public static final int[] TRANSMIT_IDENTIFIER = { 184,185,186,187 }; - public static final int[] TRANSMIT_CHANNEL = { 188,189,190,191,192,193,194, - 195,196,197,198,199 }; - public static final int[] RECEIVE_IDENTIFIER = { 200,201,202,203 }; - public static final int[] RECEIVE_CHANNEL = { 204,205,206,207,208,209,210, - 211,212,213,214,215 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 216,217,218,219,220,221, - 222,223 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public RFSSStatusBroadcastExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + public static final int[] LRA = {88, 89, 90, 91, 92, 93, 94, 95}; + public static final int ACTIVE_NETWORK_CONNECTION_INDICATOR = 99; + public static final int[] SYSTEM_ID = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] RFSS_ID = {160, 161, 162, 163, 164, 165, 166, 167}; + public static final int[] SITE_ID = {168, 169, 170, 171, 172, 173, 174, 175}; + public static final int[] TRANSMIT_IDENTIFIER = {184, 185, 186, 187}; + public static final int[] TRANSMIT_CHANNEL = {188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199}; + public static final int[] RECEIVE_IDENTIFIER = {200, 201, 202, 203}; + public static final int[] RECEIVE_CHANNEL = {204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215}; + public static final int[] SYSTEM_SERVICE_CLASS = {216, 217, 218, 219, 220, 221, 222, 223}; + public static final int[] MULTIPLE_BLOCK_CRC = {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public RFSSStatusBroadcastExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU1(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -50,115 +61,125 @@ public class RFSSStatusBroadcastExtended extends PDUMessage { return Opcode.NETWORK_STATUS_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " SYS:" + getSystemID() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " CTRL CHAN:" + getReceiveIdentifier() + "-" + getReceiveChannel() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " " + getTransmitIdentifier() + "-" + getTransmitChannel() ); - - sb.append( " UP:" + getUplinkFrequency() ); - - sb.append( " SYS SVC CLASS:" + - SystemService.toString( getSystemServiceClass() ) ); - + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" SYS:" + getSystemID()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" CTRL CHAN:" + getReceiveIdentifier() + "-" + getReceiveChannel()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" " + getTransmitIdentifier() + "-" + getTransmitChannel()); + + sb.append(" UP:" + getUplinkFrequency()); + + sb.append(" SYS SVC CLASS:" + + SystemService.toString(getSystemServiceClass())); + return sb.toString(); } - - - public String getLocationRegistrationArea() - { - return mMessage.getHex( LRA, 2 ); - } - - public boolean hasActiveNetworkConnection() - { - return mMessage.get( ACTIVE_NETWORK_CONNECTION_INDICATOR ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannel() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannel() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public int getSystemServiceClass() + + + public String getLocationRegistrationArea() { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannel() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannel() ); + return mMessage.getHex(LRA, 2); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public boolean hasActiveNetworkConnection() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.get(ACTIVE_NETWORK_CONNECTION_INDICATOR); + } + + public String getSystemID() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannel() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannel() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannel()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannel()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitIdentifier(); + idens[1] = getReceiveIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitIdentifier(); - idens[ 1 ] = getReceiveIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java index e196f10a..4ba2eef4 100644 --- a/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java @@ -1,51 +1,63 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.data; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class GroupDataChannelGrantExtended extends PDUMessage - implements IdentifierReceiver +public class GroupDataChannelGrantExtended extends PDUMessage implements IdentifierReceiver { /* Service Options */ public static final int EMERGENCY_FLAG = 128; public static final int ENCRYPTED_CHANNEL_FLAG = 129; public static final int DUPLEX_MODE = 130; public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] TRANSMIT_IDENTIFIER = { 160,161,162,163 }; - public static final int[] TRANSMIT_NUMBER = { 164,165,166,167,168,169,170, - 171,172,173,174,175 }; - public static final int[] RECEIVE_IDENTIFIER = { 176,177,178,179 }; - public static final int[] RECEIVE_NUMBER = { 180,181,182,183,184,185,186, - 187,188,189,190,191 }; - public static final int[] GROUP_ADDRESS = { 192,193,194,195,196,197,198,199, - 200,201,202,203,204,205,206,207 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupDataChannelGrantExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + + public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; + public static final int[] TRANSMIT_NUMBER = {164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}; + public static final int[] RECEIVE_IDENTIFIER = {176, 177, 178, 179}; + public static final int[] RECEIVE_NUMBER = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; + public static final int[] GROUP_ADDRESS = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207}; + public static final int[] MULTIPLE_BLOCK_CRC = {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public GroupDataChannelGrantExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU1(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -53,125 +65,135 @@ public class GroupDataChannelGrantExtended extends PDUMessage { return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - if( isEmergency() ) + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " FROM:" ); - sb.append( getSourceAddress() ); - - sb.append( " TO:" ); - sb.append( getGroupAddress() ); - - sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - sb.append( " " + getDownlinkFrequency() ); - - sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - sb.append( " " + getUplinkFrequency() ); - + + sb.append(" FROM:"); + sb.append(getSourceAddress()); + + sb.append(" TO:"); + sb.append(getGroupAddress()); + + sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + sb.append(" " + getDownlinkFrequency()); + + sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + sb.append(" " + getUplinkFrequency()); + return sb.toString(); } - + public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncrypted() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public String getSourceAddress() { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); + return mMessage.getHex(SOURCE_ADDRESS, 6); } public String getGroupAddress() { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_NUMBER ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_NUMBER ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + return mMessage.getHex(GROUP_ADDRESS, 4); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public int getTransmitChannelIdentifier() { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_NUMBER); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_NUMBER); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitChannelIdentifier(); + idens[1] = getReceiveChannelIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitChannelIdentifier(); - idens[ 1 ] = getReceiveChannelIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java index 7b9105d7..d9f41c8a 100644 --- a/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java @@ -1,51 +1,63 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.voice; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class GroupVoiceChannelGrantExplicit extends PDUMessage - implements IdentifierReceiver +public class GroupVoiceChannelGrantExplicit extends PDUMessage implements IdentifierReceiver { /* Service Options */ public static final int EMERGENCY_FLAG = 128; public static final int ENCRYPTED_CHANNEL_FLAG = 129; public static final int DUPLEX_MODE = 130; public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] TRANSMIT_IDENTIFIER = { 176,177,178,179 }; - public static final int[] TRANSMIT_NUMBER = { 180,181,182,183,184,185,186, - 187,188,189,190,191 }; - public static final int[] RECEIVE_IDENTIFIER = { 192,193,194,195 }; - public static final int[] RECEIVE_NUMBER = { 196,197,198,199,200,201,202, - 203,204,205,206,207 }; - public static final int[] GROUP_ADDRESS = { 208,209,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupVoiceChannelGrantExplicit( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + + public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] TRANSMIT_IDENTIFIER = {176, 177, 178, 179}; + public static final int[] TRANSMIT_NUMBER = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; + public static final int[] RECEIVE_IDENTIFIER = {192, 193, 194, 195}; + public static final int[] RECEIVE_NUMBER = {196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207}; + public static final int[] GROUP_ADDRESS = {208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223}; + public static final int[] MULTIPLE_BLOCK_CRC = {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public GroupVoiceChannelGrantExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU1(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -53,125 +65,135 @@ public class GroupVoiceChannelGrantExplicit extends PDUMessage { return Opcode.GROUP_VOICE_CHANNEL_GRANT.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - if( isEmergency() ) + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " FROM:" ); - sb.append( getSourceAddress() ); - - sb.append( " GRP:" ); - sb.append( getGroupAddress() ); - sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - sb.append( " " + getDownlinkFrequency() ); - - sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - sb.append( " " + getUplinkFrequency() ); - + sb.append(" FROM:"); + sb.append(getSourceAddress()); + + sb.append(" GRP:"); + sb.append(getGroupAddress()); + + sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + sb.append(" " + getDownlinkFrequency()); + + sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + sb.append(" " + getUplinkFrequency()); + return sb.toString(); } - + public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncrypted() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public String getSourceAddress() { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_NUMBER ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_NUMBER ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + return mMessage.getHex(SOURCE_ADDRESS, 6); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public String getGroupAddress() { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getHex(GROUP_ADDRESS, 4); + } + + public int getTransmitChannelIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_NUMBER); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_NUMBER); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitChannelIdentifier(); + idens[1] = getReceiveChannelIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitChannelIdentifier(); - idens[ 1 ] = getReceiveChannelIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java index 48105b9a..0d06ad93 100644 --- a/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java @@ -1,53 +1,64 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.voice; -import java.util.Date; - +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage - implements IdentifierReceiver +import java.util.Date; + +public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage implements IdentifierReceiver { /* Service Options */ public static final int EMERGENCY_FLAG = 128; public static final int ENCRYPTED_CHANNEL_FLAG = 129; public static final int DUPLEX_MODE = 130; public static final int SESSION_MODE = 131; - - public static final int[] ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] TRANSMIT_IDENTIFIER = { 160,161,162,163 }; - public static final int[] TRANSMIT_NUMBER = { 164,165,166,167,168,169,170, - 171,172,173,174,175 }; - public static final int[] RECEIVE_IDENTIFIER = { 176,177,178,179 }; - public static final int[] RECEIVE_NUMBER = { 180,181,182,183,184,185,186, - 187,188,189,190,191 }; - public static final int[] CALL_TIMER = { 192,193,194,195,196,197,198,199, - 200,201,202,203,204,205,206,207 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 224,225,226,227,228,229, - 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public TelephoneInterconnectChannelGrantExplicit( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + + public static final int[] ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111}; + public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; + public static final int[] TRANSMIT_NUMBER = {164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}; + public static final int[] RECEIVE_IDENTIFIER = {176, 177, 178, 179}; + public static final int[] RECEIVE_NUMBER = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; + public static final int[] CALL_TIMER = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207}; + public static final int[] MULTIPLE_BLOCK_CRC = {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public TelephoneInterconnectChannelGrantExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU1( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU1(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -55,57 +66,57 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage { return Opcode.TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - if( isEmergency() ) + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " ADDR:" ); - sb.append( getAddress() ); - - sb.append( " CALL TIMER:" ); - sb.append( mTimeDurationFormat.format( new Date( getCallTimer() ) ) ); - - sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - sb.append( " " + getDownlinkFrequency() ); - - sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - sb.append( " " + getUplinkFrequency() ); - + + sb.append(" ADDR:"); + sb.append(getAddress()); + + sb.append(" CALL TIMER:"); + sb.append(mTimeDurationFormat.format(new Date(getCallTimer()))); + + sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + sb.append(" " + getDownlinkFrequency()); + + sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + sb.append(" " + getUplinkFrequency()); + return sb.toString(); } - + public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncrypted() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public String getAddress() { - return mMessage.getHex( ADDRESS, 6 ); + return mMessage.getHex(ADDRESS, 6); } /* @@ -113,72 +124,82 @@ public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage */ public long getCallTimer() { - int timer = mMessage.getInt( CALL_TIMER ); - - return timer / 100; - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_NUMBER ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_NUMBER ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + int timer = mMessage.getInt(CALL_TIMER); + + return timer / 100; } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public int getTransmitChannelIdentifier() { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_NUMBER); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_NUMBER); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitChannelIdentifier(); + idens[1] = getReceiveChannelIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitChannelIdentifier(); - idens[ 1 ] = getReceiveChannelIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java index a6b3ff06..322cb003 100644 --- a/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java @@ -1,57 +1,68 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.pdu.osp.voice; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRCP25; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRCP25; -public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage - implements IdentifierReceiver +public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage implements IdentifierReceiver { /* Service Options */ public static final int EMERGENCY_FLAG = 128; public static final int ENCRYPTED_CHANNEL_FLAG = 129; public static final int DUPLEX_MODE = 130; public static final int SESSION_MODE = 131; - - public static final int[] SOURCE_ADDRESS = { 88,89,90,91,92,93,94,95,96,97, - 98,99,100,101,102,103,104,105,106,107,108,109,110,111 }; - public static final int[] SERVICE_OPTIONS = { 128,129,130,131,132,133,134, - 135 }; - public static final int[] SOURCE_WACN = { 160,161,162,163,164,165,166,167, - 168,169,170,171,172,173,174,175,176,177,178,179 }; - public static final int[] SOURCE_SYSTEM_ID = { 180,181,182,183,184,185,186, - 187,188,189,190,191 }; - public static final int[] SOURCE_ID = { 192,193,194,195,196,197,198,199,200, - 201,202,203,204,205,206,207,208,209,210,211,212,213,214,215 }; - public static final int[] TARGET_ADDRESS = { 216,217,218,219,220,221,222, - 223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239 }; - public static final int[] TRANSMIT_IDENTIFIER = { 240,241,242,243 }; - public static final int[] TRANSMIT_NUMBER = { 244,245,246,247,248,249,250, - 251,252,253,254,255 }; - public static final int[] RECEIVE_IDENTIFIER = { 256,257,258,259 }; - public static final int[] RECEIVE_NUMBER = { 260,261,262,263,264,265,266, - 267,268,269,270,271 }; - public static final int[] MULTIPLE_BLOCK_CRC = { 320,321,322,323,324,325, - 326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342, - 343,344,345,346,347,348,349,350,351 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public UnitToUnitVoiceChannelGrantUpdateExtended( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + + public static final int[] SOURCE_ADDRESS = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SERVICE_OPTIONS = {128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SOURCE_WACN = {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179}; + public static final int[] SOURCE_SYSTEM_ID = {180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191}; + public static final int[] SOURCE_ID = {192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215}; + public static final int[] TARGET_ADDRESS = {216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239}; + public static final int[] TRANSMIT_IDENTIFIER = {240, 241, 242, 243}; + public static final int[] TRANSMIT_NUMBER = {244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; + public static final int[] RECEIVE_IDENTIFIER = {256, 257, 258, 259}; + public static final int[] RECEIVE_NUMBER = {260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271}; + public static final int[] MULTIPLE_BLOCK_CRC = {320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public UnitToUnitVoiceChannelGrantUpdateExtended(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); - + super(message, duid, aliasList); + /* Header block is already error detected/corrected - perform error - * detection correction on the intermediate and final data blocks */ - mMessage = CRCP25.correctPDU2( mMessage ); - mCRC[ 1 ] = mMessage.getCRC(); + * detection correction on the intermediate and final data blocks */ + mMessage = CRCP25.correctPDU2(mMessage); + mCRC[1] = mMessage.getCRC(); } @Override @@ -59,149 +70,159 @@ public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage { return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - if( isEmergency() ) + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " FROM ADDR:" ); - sb.append( getSourceAddress() ); - - sb.append( " ID:" ); - sb.append( getSourceID() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - - sb.append( " WACN:" ); - sb.append( getSourceWACN() ); - - sb.append( " SYS:" ); - sb.append( getSourceSystemID() ); - sb.append( " CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - sb.append( " " + getDownlinkFrequency() ); - - sb.append( " CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - sb.append( " " + getUplinkFrequency() ); - + sb.append(" FROM ADDR:"); + sb.append(getSourceAddress()); + + sb.append(" ID:"); + sb.append(getSourceID()); + + sb.append(" TO:"); + sb.append(getTargetAddress()); + + sb.append(" WACN:"); + sb.append(getSourceWACN()); + + sb.append(" SYS:"); + sb.append(getSourceSystemID()); + + sb.append(" CHAN DN:" + getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + sb.append(" " + getDownlinkFrequency()); + + sb.append(" CHAN UP:" + getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + sb.append(" " + getUplinkFrequency()); + return sb.toString(); } - + public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncrypted() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public String getSourceAddress() { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); - } - - public String getSourceID() - { - return mMessage.getHex( SOURCE_ID, 6 ); - } - - public String getSourceWACN() - { - return mMessage.getHex( SOURCE_WACN, 5 ); - } - - public String getSourceSystemID() - { - return mMessage.getHex( SOURCE_SYSTEM_ID, 3 ); - } - - public String getTargetAddress() - { - return mMessage.getHex( TARGET_ADDRESS, 6 ); - } - - public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_NUMBER ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_NUMBER ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + return mMessage.getHex(SOURCE_ADDRESS, 6); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public String getSourceID() { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getHex(SOURCE_ID, 6); + } + + public String getSourceWACN() + { + return mMessage.getHex(SOURCE_WACN, 5); + } + + public String getSourceSystemID() + { + return mMessage.getHex(SOURCE_SYSTEM_ID, 3); + } + + public String getTargetAddress() + { + return mMessage.getHex(TARGET_ADDRESS, 6); + } + + public int getTransmitChannelIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_NUMBER); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_NUMBER); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public long getDownlinkFrequency() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + public int[] getIdentifiers() + { + int[] idens = new int[2]; + + idens[0] = getTransmitChannelIdentifier(); + idens[1] = getReceiveChannelIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 2 ]; - - idens[ 0 ] = getTransmitChannelIdentifier(); - idens[ 1 ] = getReceiveChannelIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java index d14e03d6..2259c9d6 100644 --- a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IAdjacentSite; @@ -6,141 +24,147 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -public class AdjacentSiteStatusBroadcast extends TDULinkControlMessage - implements IdentifierReceiver, IAdjacentSite +public class AdjacentSiteStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver, IAdjacentSite { - public static final int[] LRA = { 72,73,74,75,88,89,90,91 }; - public static final int[] SYSTEM_ID = { 96,97,98,99,112,113,114,115, - 116,117,118,119 }; - public static final int[] RFSS_ID = { 120,121,122,123,136,137,138,139 }; - public static final int[] SITE_ID = { 140,141,142,143,144,145,146,147 }; - public static final int[] IDENTIFIER = { 160,161,162,163 }; - public static final int[] CHANNEL = { 164,165,166,167,168,169,170, - 171,184,185,186,187 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 188,189,190,191,192,193, - 194,195 }; - - private IBandIdentifier mIdentifierUpdate; - - public AdjacentSiteStatusBroadcast( TDULinkControlMessage source ) - { - super( source ); - } - + public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; + public static final int[] SYSTEM_ID = {96, 97, 98, 99, 112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; + public static final int[] IDENTIFIER = {160, 161, 162, 163}; + public static final int[] CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; + public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mIdentifierUpdate; + + public AdjacentSiteStatusBroadcast(TDULinkControlMessage source) + { + super(source); + } + @Override public String getEventType() { return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLRA() ); + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - sb.append( " SYS:" + getSystemID() ); - - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " CHAN:" + getChannel() ); + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLRA()); + + sb.append(" SYS:" + getSystemID()); + + sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); + + sb.append(" CHAN:" + getChannel()); + + sb.append(" " + getSystemServiceClass()); + + return sb.toString(); + } - sb.append( " " + getSystemServiceClass() ); - - return sb.toString(); - } - public String getUniqueID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getRFSS() ); - sb.append( ":" ); - sb.append( getSiteID() ); - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(getSystemID()); + sb.append(":"); + sb.append(getRFSS()); + sb.append(":"); + sb.append(getSiteID()); + + return sb.toString(); } - - public String getLRA() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getRFSS() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - @Override - public String getDownlinkChannel() - { - return getChannel(); - } - @Override - public String getUplinkChannel() - { - return getChannel(); - } - - public int getChannelNumber() - { - return mMessage.getInt( CHANNEL ); - } - - public String getSystemServiceClass() - { - return SystemService.toString( mMessage.getInt( SYSTEM_SERVICE_CLASS ) ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public String getLRA() { - mIdentifierUpdate = message; + return mMessage.getHex(LRA, 2); } - @Override + public String getSystemID() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getRFSS() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + public String getChannel() + { + return getIdentifier() + "-" + getChannelNumber(); + } + + @Override + public String getDownlinkChannel() + { + return getChannel(); + } + + @Override + public String getUplinkChannel() + { + return getChannel(); + } + + public int getChannelNumber() + { + return mMessage.getInt(CHANNEL); + } + + public String getSystemServiceClass() + { + return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + mIdentifierUpdate = message; + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java index da0c09d0..5be95844 100644 --- a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IAdjacentSite; @@ -5,153 +23,162 @@ import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.LinkControlOpcode; -public class AdjacentSiteStatusBroadcastExplicit extends TDULinkControlMessage - implements IdentifierReceiver, IAdjacentSite +public class AdjacentSiteStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver, IAdjacentSite { - public static final int[] LRA = { 72,73,74,75,88,89,90,91 }; - public static final int[] TRANSMIT_IDENTIFIER = { 92,93,94,95 }; - public static final int[] TRANSMIT_CHANNEL = { 96,97,98,99,112,113,114,115, - 116,117,118,119 }; - public static final int[] RFSS_ID = { 120,121,122,123,136,137,138,139 }; - public static final int[] SITE_ID = { 140,141,142,143,144,145,146,147 }; - - public static final int[] RECEIVE_IDENTIFIER = { 160,161,162,163 }; - public static final int[] RECEIVE_CHANNEL = { 164,165,166,167,168,169,170, - 171,184,185,186,187 }; - - private IBandIdentifier mTransmitBand; - private IBandIdentifier mReceiveBand; + public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; + public static final int[] TRANSMIT_IDENTIFIER = {92, 93, 94, 95}; + public static final int[] TRANSMIT_CHANNEL = {96, 97, 98, 99, 112, 113, 114, 115, + 116, 117, 118, 119}; + public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; + + public static final int[] RECEIVE_IDENTIFIER = {160, 161, 162, 163}; + public static final int[] RECEIVE_CHANNEL = {164, 165, 166, 167, 168, 169, 170, + 171, 184, 185, 186, 187}; + + private IBandIdentifier mTransmitBand; + private IBandIdentifier mReceiveBand; + + public AdjacentSiteStatusBroadcastExplicit(TDULinkControlMessage source) + { + super(source); + } - public AdjacentSiteStatusBroadcastExplicit( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLRA() ); - - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " DNLINK:" + getDownlinkChannel() ); - - sb.append( " UPLINK:" + getUplinkChannel() ); - - return sb.toString(); - } - + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLRA()); + + sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); + + sb.append(" DNLINK:" + getDownlinkChannel()); + + sb.append(" UPLINK:" + getUplinkChannel()); + + return sb.toString(); + } + public String getUniqueID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getRFSS() ); - sb.append( ":" ); - sb.append( getSiteID() ); - - return sb.toString(); - } - - public String getLRA() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getRFSS() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSystemID() - { - return "***"; - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - @Override - public String getSystemServiceClass() - { - return "[]"; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getSystemID()); + sb.append(":"); + sb.append(getRFSS()); + sb.append(":"); + sb.append(getSiteID()); + + return sb.toString(); + } + + public String getLRA() + { + return mMessage.getHex(LRA, 2); + } + + public String getRFSS() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSystemID() + { + return "***"; + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + @Override + public String getSystemServiceClass() + { + return "[]"; + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannel() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getDownlinkChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannel(); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannel() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getUplinkChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannel(); + } - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannel() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getDownlinkChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannel(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannel() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getUplinkChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannel(); - } - public long getDownlinkFrequency() { - return calculateDownlink( mTransmitBand, getTransmitChannel() ); + if(mTransmitBand != null) + { + return mTransmitBand.getDownlinkFrequency(getTransmitChannel()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveBand, getReceiveChannel() ); + if(mReceiveBand != null) + { + return mReceiveBand.getUplinkFrequency(getReceiveChannel()); + } + + return 0; } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getTransmitIdentifier() ) - { - mTransmitBand = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveBand = message; - } - } + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitBand = message; + } - @Override - public int[] getIdentifiers() - { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; - } + if(identifier == getReceiveIdentifier()) + { + mReceiveBand = message; + } + } + + @Override + public int[] getIdentifiers() + { + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; + } } diff --git a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java index 75e725f8..63440320 100644 --- a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java @@ -1,82 +1,117 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.reference.LinkControlOpcode; -public class ChannelIdentifierUpdate extends TDULinkControlMessage - implements IBandIdentifier +public class ChannelIdentifierUpdate extends TDULinkControlMessage implements IBandIdentifier { - public static final int[] IDENTIFIER = { 72,73,74,75 }; - public static final int[] BANDWIDTH = { 88,89,90,91,92,93,94,95,96 }; - public static final int[] TRANSMIT_OFFSET = { 97,98,99,112,113,114,115,116, - 117 }; - public static final int[] CHANNEL_SPACING = { 118,119,120,121,122,123,136, - 137,138,139 }; - public static final int[] BASE_FREQUENCY = { 140,141,142,143,144,145,146, - 147,160,161,162,163,164,165,166,167,168,169,170,171,184,185,186, - 187,188,189,190,191,192,193,194,195 }; - - public ChannelIdentifierUpdate( TDULinkControlMessage source ) - { - super( source ); - } - + public static final int[] IDENTIFIER = {72, 73, 74, 75}; + public static final int[] BANDWIDTH = {88, 89, 90, 91, 92, 93, 94, 95, 96}; + public static final int[] TRANSMIT_OFFSET = {97, 98, 99, 112, 113, 114, 115, 116, + 117}; + public static final int[] CHANNEL_SPACING = {118, 119, 120, 121, 122, 123, 136, + 137, 138, 139}; + public static final int[] BASE_FREQUENCY = {140, 141, 142, 143, 144, 145, 146, + 147, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195}; + + public ChannelIdentifierUpdate(TDULinkControlMessage source) + { + super(source); + } + @Override public String getEventType() { return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " IDEN:" + getIdentifier() ); + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - sb.append( " BASE:" + getBaseFrequency() ); - - sb.append( " BW:" + getBandwidth() ); - - sb.append( " SPACING:" + getChannelSpacing() ); - - sb.append( " OFFSET:" + getTransmitOffset() ); - - return sb.toString(); - } + sb.append(getMessageStub()); - @Override - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } + sb.append(" IDEN:" + getIdentifier()); - /** + sb.append(" BASE:" + getBaseFrequency()); + + sb.append(" BW:" + getBandwidth()); + + sb.append(" SPACING:" + getChannelSpacing()); + + sb.append(" OFFSET:" + getTransmitOffset()); + + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + /** * Channel bandwidth in hertz */ @Override public int getBandwidth() { - return mMessage.getInt( BANDWIDTH ) * 125; + return mMessage.getInt(BANDWIDTH) * 125; } - + @Override public long getChannelSpacing() { - return mMessage.getLong( CHANNEL_SPACING ) * 125l; + return mMessage.getLong(CHANNEL_SPACING) * 125l; } @Override public long getBaseFrequency() { - return mMessage.getLong( BASE_FREQUENCY ) * 5l; + return mMessage.getLong(BASE_FREQUENCY) * 5l; } - @Override + @Override public long getTransmitOffset() { - return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l; + return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (channelNumber * getChannelSpacing()); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + @Override + public boolean isTDMA() + { + return false; } } diff --git a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java index eb427fe3..a3be9821 100644 --- a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java @@ -1,82 +1,114 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.reference.LinkControlOpcode; -public class ChannelIdentifierUpdateExplicit extends TDULinkControlMessage - implements IBandIdentifier +public class ChannelIdentifierUpdateExplicit extends TDULinkControlMessage implements IBandIdentifier { - public static final int[] IDENTIFIER = { 72,73,74,75 }; - public static final int[] BANDWIDTH = { 88,89,90,91 }; - public static final int[] TRANSMIT_OFFSET = { 92,93,94,95,96,97,98,99,112, - 113,114,115,116,117 }; - public static final int[] CHANNEL_SPACING = { 118,119,120,121,122, - 123,136,137,138,139 }; - public static final int[] BASE_FREQUENCY = { 140,141,142,143,144,145,146, - 147,160,161,162,163,164,165,166,167,168,169,170,171,184,185,186, - 187,188,189,190,191,192,193,194,195 }; - - public ChannelIdentifierUpdateExplicit( TDULinkControlMessage source ) - { - super( source ); - } - + public static final int[] IDENTIFIER = {72, 73, 74, 75}; + public static final int[] BANDWIDTH = {88, 89, 90, 91}; + public static final int[] TRANSMIT_OFFSET = {92, 93, 94, 95, 96, 97, 98, 99, 112, 113, 114, 115, 116, 117}; + public static final int[] CHANNEL_SPACING = {118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] BASE_FREQUENCY = {140, 141, 142, 143, 144, 145, 146, 147, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195}; + + public ChannelIdentifierUpdateExplicit(TDULinkControlMessage source) + { + super(source); + } + @Override public String getEventType() { return LinkControlOpcode.CHANNEL_IDENTIFIER_UPDATE_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " IDEN:" + getIdentifier() ); + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - sb.append( " BASE:" + getBaseFrequency() ); - - sb.append( " BW:" + getBandwidth() ); - - sb.append( " SPACING:" + getChannelSpacing() ); - - sb.append( " OFFSET:" + getTransmitOffset() ); - - return sb.toString(); - } + sb.append(getMessageStub()); - @Override - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } + sb.append(" IDEN:" + getIdentifier()); - /** + sb.append(" BASE:" + getBaseFrequency()); + + sb.append(" BW:" + getBandwidth()); + + sb.append(" SPACING:" + getChannelSpacing()); + + sb.append(" OFFSET:" + getTransmitOffset()); + + return sb.toString(); + } + + @Override + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + /** * Channel bandwidth in hertz */ @Override public int getBandwidth() { - return mMessage.getInt( BANDWIDTH ) * 125; + return mMessage.getInt(BANDWIDTH) * 125; } - + @Override public long getChannelSpacing() { - return mMessage.getLong( CHANNEL_SPACING ) * 125l; + return mMessage.getLong(CHANNEL_SPACING) * 125l; } @Override public long getBaseFrequency() { - return mMessage.getLong( BASE_FREQUENCY ) * 5l; + return mMessage.getLong(BASE_FREQUENCY) * 5l; } - @Override + @Override public long getTransmitOffset() { - return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l; + return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l; + } + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + (channelNumber * getChannelSpacing()); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + @Override + public boolean isTDMA() + { + return false; } } diff --git a/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java index 8f9521ef..3aa54dfc 100644 --- a/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java @@ -1,176 +1,207 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; -import java.util.Set; -import java.util.TreeSet; - import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.LinkControlOpcode; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class GroupVoiceChannelUpdate extends TDULinkControlMessage - implements IdentifierReceiver -{ - private final static Logger mLog = - LoggerFactory.getLogger( GroupVoiceChannelUpdate.class ); +import java.util.Set; +import java.util.TreeSet; + +public class GroupVoiceChannelUpdate extends TDULinkControlMessage implements IdentifierReceiver +{ + public static final int[] IDENTIFIER_A = {72, 73, 74, 75}; + public static final int[] CHANNEL_A = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99}; + public static final int[] GROUP_ADDRESS_A = {112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] IDENTIFIER_B = {140, 141, 142, 143}; + public static final int[] CHANNEL_B = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; + public static final int[] GROUP_ADDRESS_B = {168, 169, 170, 171, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mIdentifierUpdateA; + private IBandIdentifier mIdentifierUpdateB; + + public GroupVoiceChannelUpdate(TDULinkControlMessage source) + { + super(source); + } - public static final int[] IDENTIFIER_A = { 72,73,74,75 }; - public static final int[] CHANNEL_A = { 88,89,90,91,92,93,94,95,96,97,98,99 }; - public static final int[] GROUP_ADDRESS_A = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139 }; - public static final int[] IDENTIFIER_B = { 140,141,142,143 }; - public static final int[] CHANNEL_B = { 144,145,146,147,160,161,162,163,164, - 165,166,167 }; - public static final int[] GROUP_ADDRESS_B = { 168,169,170,171,184,185,186, - 187,188,189,190,191,192,193,194,195 }; - - private IBandIdentifier mIdentifierUpdateA; - private IBandIdentifier mIdentifierUpdateB; - - public GroupVoiceChannelUpdate( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " CHAN A:" + getChannelA() ); - - sb.append( " GRP A:" + getGroupAddressA() ); - - if( hasChannelB() ) - { - sb.append( " CHAN B:" + getChannelA() ); - - sb.append( " GRP B:" + getGroupAddressA() ); - } - - return sb.toString(); - } + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" CHAN A:" + getChannelA()); + + sb.append(" GRP A:" + getGroupAddressA()); + + if(hasChannelB()) + { + sb.append(" CHAN B:" + getChannelA()); + + sb.append(" GRP B:" + getGroupAddressA()); + } + + return sb.toString(); + } + + public int getChannelIdentifierA() + { + return mMessage.getInt(IDENTIFIER_A); + } - public int getChannelIdentifierA() - { - return mMessage.getInt( IDENTIFIER_A ); - } - public int getChannelNumberA() { - return mMessage.getInt( CHANNEL_A ); + return mMessage.getInt(CHANNEL_A); } - + public String getChannelA() { - return getChannelIdentifierA() + "-" + getChannelNumberA(); + return getChannelIdentifierA() + "-" + getChannelNumberA(); } - - public int getChannelIdentifierB() - { - return mMessage.getInt( IDENTIFIER_B ); - } - + + public int getChannelIdentifierB() + { + return mMessage.getInt(IDENTIFIER_B); + } + public int getChannelNumberB() { - return mMessage.getInt( CHANNEL_B ); + return mMessage.getInt(CHANNEL_B); } - + public String getChannelB() { - return getChannelIdentifierB() + "-" + getChannelNumberB(); + return getChannelIdentifierB() + "-" + getChannelNumberB(); } - + public boolean hasChannelB() { - return getChannelNumberB() != 0; + return getChannelNumberB() != 0; } - + public String getGroupAddressA() { - return mMessage.getHex( GROUP_ADDRESS_A, 4 ); + return mMessage.getHex(GROUP_ADDRESS_A, 4); } public String getGroupAddressB() { - return mMessage.getHex( GROUP_ADDRESS_B, 4 ); + return mMessage.getHex(GROUP_ADDRESS_B, 4); } - /** - * Returns a sorted list of addresses contained in the A and B fields - * @return - */ - public Set getAddresses() - { - Set addresses = new TreeSet(); - - addresses.add( getGroupAddressA() ); - addresses.add( getGroupAddressB() ); - - return addresses; - } - - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + /** + * Returns a sorted list of addresses contained in the A and B fields + * + * @return + */ + public Set getAddresses() { - if( identifier == getChannelIdentifierA() ) - { - mIdentifierUpdateA = message; - } - if( identifier == getChannelIdentifierB() ) - { - mIdentifierUpdateB = message; - } + Set addresses = new TreeSet(); + + addresses.add(getGroupAddressA()); + addresses.add(getGroupAddressB()); + + return addresses; } - @Override + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getChannelIdentifierA()) + { + mIdentifierUpdateA = message; + } + if(identifier == getChannelIdentifierB()) + { + mIdentifierUpdateB = message; + } + } + + @Override public int[] getIdentifiers() { - if( hasChannelB() ) - { - int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getChannelIdentifierA(); - identifiers[ 1 ] = getChannelIdentifierB(); - - return identifiers; - } - else - { - int[] identifiers = new int[ 1 ]; - identifiers[ 0 ] = getChannelIdentifierA(); - - return identifiers; - } + if(hasChannelB()) + { + int[] identifiers = new int[2]; + identifiers[0] = getChannelIdentifierA(); + identifiers[1] = getChannelIdentifierB(); + + return identifiers; + } + else + { + int[] identifiers = new int[1]; + identifiers[0] = getChannelIdentifierA(); + + return identifiers; + } } public long getDownlinkFrequencyA() { - return calculateDownlink( mIdentifierUpdateA, getChannelNumberA() ); + if(mIdentifierUpdateA != null) + { + return mIdentifierUpdateA.getDownlinkFrequency(getChannelNumberA()); + } + + return 0; } - + public long getUplinkFrequencyA() { - return calculateUplink( mIdentifierUpdateA, getChannelNumberA() ); + if(mIdentifierUpdateA != null) + { + return mIdentifierUpdateA.getUplinkFrequency(getChannelNumberA()); + } + + return 0; } public long getDownlinkFrequencyB() { - return calculateDownlink( mIdentifierUpdateB, getChannelNumberA() ); + if(mIdentifierUpdateB != null) + { + return mIdentifierUpdateB.getDownlinkFrequency(getChannelNumberB()); + } + + return 0; } - + public long getUplinkFrequencyB() { - return calculateUplink( mIdentifierUpdateB, getChannelNumberA() ); + if(mIdentifierUpdateB != null) + { + return mIdentifierUpdateB.getUplinkFrequency(getChannelNumberB()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java index 174f988f..72c35bcb 100644 --- a/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java @@ -1,154 +1,176 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.LinkControlOpcode; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GroupVoiceChannelUpdateExplicit extends TDULinkControlMessage - implements IdentifierReceiver + implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( GroupVoiceChannelUpdateExplicit.class ); + private final static Logger mLog = + LoggerFactory.getLogger(GroupVoiceChannelUpdateExplicit.class); public static final int EMERGENCY_FLAG = 92; public static final int ENCRYPTED_CHANNEL_FLAG = 93; public static final int DUPLEX_MODE = 94; public static final int SESSION_MODE = 95; - public static final int[] GROUP_ADDRESS = { 112,113,114,115,116,117,118, - 119,120,121,122,123,136,137,138,139 }; - public static final int[] TRANSMIT_IDENTIFIER = { 140,141,142,143 }; - public static final int[] TRANSMIT_CHANNEL = { 144,145,146,147,160,161,162, - 163,164,165,166,167 }; - public static final int[] RECEIVE_IDENTIFIER = { 168,169,170,171 }; - public static final int[] RECEIVE_CHANNEL = { 184,185,186,187,188,189,190, - 191,192,193,194,195,196,197,198,199 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public GroupVoiceChannelUpdateExplicit( TDULinkControlMessage source ) - { - super( source ); - } - + public static final int[] GROUP_ADDRESS = {112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] TRANSMIT_IDENTIFIER = {140, 141, 142, 143}; + public static final int[] TRANSMIT_CHANNEL = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; + public static final int[] RECEIVE_IDENTIFIER = {168, 169, 170, 171}; + public static final int[] RECEIVE_CHANNEL = {184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public GroupVoiceChannelUpdateExplicit(TDULinkControlMessage source) + { + super(source); + } + @Override public String getEventType() { return LinkControlOpcode.GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - if( isEncryptedChannel() ) - { - sb.append( " ENCRYPTED CHANNEL" ); - } + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); - - return sb.toString(); - } + sb.append(getMessageStub()); + + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + if(isEncryptedChannel()) + { + sb.append(" ENCRYPTED CHANNEL"); + } + + + return sb.toString(); + } public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncryptedChannel() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } public int getTransmitChannelIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getInt(TRANSMIT_IDENTIFIER); } - @Override + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public String getGroupAddress() + { + return mMessage.getHex(GROUP_ADDRESS, 4); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitChannelIdentifier(); - identifiers[ 1 ] = getReceiveChannelIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitChannelIdentifier(); + identifiers[1] = getReceiveChannelIdentifier(); + + return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, - getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, - getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java index b298fad6..db403f1c 100644 --- a/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,106 +23,106 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.LinkControlOpcode; import module.decode.p25.reference.Service; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class NetworkStatusBroadcast extends TDULinkControlMessage - implements IdentifierReceiver +public class NetworkStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( NetworkStatusBroadcast.class ); - - public static final int[] WACN = { 72,73,74,75,88,89,90,91,92,93,94,95,96, - 97,98,99,112,113,114,115,116,117,118,119,120,121,122,123 }; - public static final int[] SYSTEM = { 136,137,138,139,140,141,142,143,144, - 145,146,147 }; - public static final int[] IDENTIFIER = { 160,161,162,163 }; - public static final int[] CHANNEL = { 164,165,166,167,168,169,170,171,184, - 185,186,187 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 188,189,190,191,192,193, - 194,195 }; - - private IBandIdentifier mIdentifierUpdate; - - public NetworkStatusBroadcast( TDULinkControlMessage source ) - { - super( source ); - } - + public static final int[] WACN = {72, 73, 74, 75, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123}; + public static final int[] SYSTEM = {136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147}; + public static final int[] IDENTIFIER = {160, 161, 162, 163}; + public static final int[] CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; + public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mIdentifierUpdate; + + public NetworkStatusBroadcast(TDULinkControlMessage source) + { + super(source); + } + @Override public String getEventType() { return LinkControlOpcode.NETWORK_STATUS_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " NETWORK:" + getWACN() ); - sb.append( " SYS:" + getSystem() ); - sb.append( " CHAN:" + getChannel() ); - sb.append( " " + Service.getServices( getSystemServiceClass() ).toString() ); - - return sb.toString(); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystem() - { - return mMessage.getHex( SYSTEM, 3 ); - } - - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - public int getChannelNumber() - { - return mMessage.getInt( CHANNEL ); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - mIdentifierUpdate = message; + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" NETWORK:" + getWACN()); + sb.append(" SYS:" + getSystem()); + sb.append(" CHAN:" + getChannel()); + sb.append(" " + Service.getServices(getSystemServiceClass()).toString()); + + return sb.toString(); } - @Override + public String getWACN() + { + return mMessage.getHex(WACN, 5); + } + + public String getSystem() + { + return mMessage.getHex(SYSTEM, 3); + } + + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + public int getChannelNumber() + { + return mMessage.getInt(CHANNEL); + } + + public String getChannel() + { + return getIdentifier() + "-" + getChannelNumber(); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + mIdentifierUpdate = message; + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java index d31b41ad..a88536ca 100644 --- a/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java @@ -1,131 +1,146 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class NetworkStatusBroadcastExplicit extends TDULinkControlMessage - implements IdentifierReceiver +public class NetworkStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( NetworkStatusBroadcastExplicit.class ); + public static final int[] WACN = {72, 73, 74, 75, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 112, 113, 114, 115}; + public static final int[] SYSTEM = {116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] TRANSMIT_IDENTIFIER = {140, 141, 142, 143}; + public static final int[] TRANSMIT_CHANNEL = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; + public static final int[] RECEIVE_IDENTIFIER = {168, 169, 170, 171}; + public static final int[] RECEIVE_CHANNEL = {184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public NetworkStatusBroadcastExplicit(TDULinkControlMessage source) + { + super(source); + } - public static final int[] WACN = { 72,73,74,75,88,89,90,91,92,93,94,95,96, - 97,98,99,112,113,114,115 }; - public static final int[] SYSTEM = { 116,117,118,119,120,121,122,123,136, - 137,138,139 }; - public static final int[] TRANSMIT_IDENTIFIER = { 140,141,142,143 }; - public static final int[] TRANSMIT_CHANNEL = { 144,145,146,147,160,161,162, - 163,164,165,166,167 }; - public static final int[] RECEIVE_IDENTIFIER = { 168,169,170,171 }; - public static final int[] RECEIVE_CHANNEL = { 184,185,186,187,188,189,190, - 191,192,193,194,195 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public NetworkStatusBroadcastExplicit( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.NETWORK_STATUS_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " NETWORK:" + getWACN() ); - sb.append( " SYS:" + getSystem() ); - sb.append( " TRANSMIT:" + getTransmitChannel() ); - sb.append( " RECEIVE:" + getReceiveChannel() ); - - return sb.toString(); - } - - public String getWACN() - { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystem() - { - return mMessage.getHex( SYSTEM, 3 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" NETWORK:" + getWACN()); + sb.append(" SYS:" + getSystem()); + sb.append(" TRANSMIT:" + getTransmitChannel()); + sb.append(" RECEIVE:" + getReceiveChannel()); + + return sb.toString(); } - @Override + public String getWACN() + { + return mMessage.getHex(WACN, 5); + } + + public String getSystem() + { + return mMessage.getHex(SYSTEM, 3); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getTransmitChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getReceiveChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, - getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, - getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java index 7864b3c8..cc29771b 100644 --- a/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,128 +23,124 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RFSSStatusBroadcast extends TDULinkControlMessage - implements IdentifierReceiver +public class RFSSStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( RFSSStatusBroadcast.class ); + public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; + public static final int[] SYSTEM_ID = {96, 97, 98, 99, 112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; + public static final int[] IDENTIFIER = {160, 161, 162, 163}; + public static final int[] CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; + public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mIdentifierUpdate; + + public RFSSStatusBroadcast(TDULinkControlMessage source) + { + super(source); + } - public static final int[] LRA = { 72,73,74,75,88,89, - 90,91 }; - public static final int[] SYSTEM_ID = { 96,97,98,99,112,113,114,115,116,117, - 118,119 }; - public static final int[] RFSS_ID = { 120,121,122,123,136,137,138, - 139 }; - public static final int[] SITE_ID = { 140,141,142,143,144,145,146,147 }; - - public static final int[] IDENTIFIER = { 160,161,162,163 }; - public static final int[] CHANNEL = { 164,165,166,167,168,169,170, - 171,184,185,186,187 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 188,189,190,191,192,193, - 194,195 }; - - private IBandIdentifier mIdentifierUpdate; - - public RFSSStatusBroadcast( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.RFSS_STATUS_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " SYS:" + getSystem() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " CHAN:" + getChannelNumber() ); - - sb.append( " " + SystemService.toString( getSystemServiceClass() ) ); - - sb.append( " " + mMessage.toString() ); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getSystem() - { - return mMessage.getHex( SYSTEM_ID, 3 ); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getIdentifier() - { - return mMessage.getInt( IDENTIFIER ); - } - - public String getChannel() - { - return getIdentifier() + "-" + getChannelNumber(); - } - - public int getChannelNumber() - { - return mMessage.getInt( CHANNEL ); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - mIdentifierUpdate = message; + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" SYS:" + getSystem()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" CHAN:" + getChannelNumber()); + + sb.append(" " + SystemService.toString(getSystemServiceClass())); + + sb.append(" " + mMessage.toString()); + + return sb.toString(); } - @Override + public String getLocationRegistrationArea() + { + return mMessage.getHex(LRA, 2); + } + + public String getSystem() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getIdentifier() + { + return mMessage.getInt(IDENTIFIER); + } + + public String getChannel() + { + return getIdentifier() + "-" + getChannelNumber(); + } + + public int getChannelNumber() + { + return mMessage.getInt(CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + mIdentifierUpdate = message; + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } - } diff --git a/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java index 452b3d51..0bec65c4 100644 --- a/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java @@ -1,152 +1,171 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RFSSStatusBroadcastExplicit extends TDULinkControlMessage - implements IdentifierReceiver +public class RFSSStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( RFSSStatusBroadcastExplicit.class ); + private final static Logger mLog = LoggerFactory.getLogger(RFSSStatusBroadcastExplicit.class); + + public static final int[] LRA = {72, 73, 74, 75, 88, 89, 90, 91}; + public static final int[] RECEIVE_IDENTIFIER = {92, 93, 94, 95}; + public static final int[] RECEIVE_CHANNEL = {96, 97, 98, 99, 112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] RFSS_ID = {120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] SITE_ID = {140, 141, 142, 143, 144, 145, 146, 147}; + public static final int[] TRANSMIT_IDENTIFIER = {160, 161, 162, 163}; + public static final int[] TRANSMIT_CHANNEL = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; + public static final int[] SYSTEM_SERVICE_CLASS = {188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mTransmitIdentifierUpdate; + private IBandIdentifier mReceiveIdentifierUpdate; + + public RFSSStatusBroadcastExplicit(TDULinkControlMessage source) + { + super(source); + } - public static final int[] LRA = { 72,73,74,75,88,89,90,91 }; - public static final int[] RECEIVE_IDENTIFIER = { 92,93,94,95 }; - public static final int[] RECEIVE_CHANNEL = { 96,97,98,99,112,113,114,115, - 116,117,118,119 }; - public static final int[] RFSS_ID = { 120,121,122,123,136,137,138, - 139 }; - public static final int[] SITE_ID = { 140,141,142,143,144,145,146,147 }; - - public static final int[] TRANSMIT_IDENTIFIER = { 160,161,162,163 }; - public static final int[] TRANSMIT_CHANNEL = { 164,165,166,167,168,169,170, - 171,184,185,186,187 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 188,189,190,191,192,193, - 194,195 }; - - private IBandIdentifier mTransmitIdentifierUpdate; - private IBandIdentifier mReceiveIdentifierUpdate; - - public RFSSStatusBroadcastExplicit( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.RFSS_STATUS_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " TRANSMIT:" + getTransmitChannelNumber() ); - - sb.append( " RECEIVE:" + getReceiveChannelNumber() ); - - sb.append( " " + SystemService.toString( getSystemServiceClass() ) ); - - sb.append( " " + mMessage.toString() ); - - return sb.toString(); - } - - public String getLocationRegistrationArea() - { - return mMessage.getHex( LRA, 2 ); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" TRANSMIT:" + getTransmitChannelNumber()); + + sb.append(" RECEIVE:" + getReceiveChannelNumber()); + + sb.append(" " + SystemService.toString(getSystemServiceClass())); + + sb.append(" " + mMessage.toString()); + + return sb.toString(); } - @Override + public String getLocationRegistrationArea() + { + return mMessage.getHex(LRA, 2); + } + + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public String getTransmitChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public String getReceiveChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } - } diff --git a/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java index 6a3587df..e1cf6342 100644 --- a/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,155 +23,162 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SecondaryControlChannelBroadcast extends TDULinkControlMessage - implements IdentifierReceiver +public class SecondaryControlChannelBroadcast extends TDULinkControlMessage implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( SecondaryControlChannelBroadcast.class ); + public static final int[] RFSS_ID = {72, 73, 74, 75, 88, 89, 90, 91}; + public static final int[] SITE_ID = {92, 93, 94, 95, 96, 97, 98, 99}; + public static final int[] IDENTIFIER_A = {112, 113, 114, 115}; + public static final int[] CHANNEL_A = {116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] SYSTEM_SERVICE_CLASS_A = {140, 141, 142, 143, 144, 145, 146, 147}; + public static final int[] IDENTIFIER_B = {160, 161, 162, 163}; + public static final int[] CHANNEL_B = {164, 165, 166, 167, 168, 169, 170, 171, 184, 185, 186, 187}; + public static final int[] SYSTEM_SERVICE_CLASS_B = {188, 189, 190, 191, 192, 193, 194, 195}; + + private IBandIdentifier mIdentifierProviderA; + private IBandIdentifier mIdentifierProviderB; + + public SecondaryControlChannelBroadcast(TDULinkControlMessage source) + { + super(source); + } - public static final int[] RFSS_ID = { 72,73,74,75,88,89,90,91 }; - public static final int[] SITE_ID = { 92,93,94,95,96,97,98,99 }; - public static final int[] IDENTIFIER_A = { 112,113,114,115 }; - public static final int[] CHANNEL_A = { 116,117,118,119,120,121,122, - 123,136,137,138,139 }; - - public static final int[] SYSTEM_SERVICE_CLASS_A = { 140,141,142,143,144, - 145,146,147 }; - - public static final int[] IDENTIFIER_B = { 160,161,162,163 }; - public static final int[] CHANNEL_B = { 164,165,166,167,168,169,170, - 171,184,185,186,187 }; - public static final int[] SYSTEM_SERVICE_CLASS_B = { 188,189,190,191,192, - 193,194,195 }; - - private IBandIdentifier mIdentifierProviderA; - private IBandIdentifier mIdentifierProviderB; - - public SecondaryControlChannelBroadcast( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " CHAN A:" + getChannelA() ); - - sb.append( " " + SystemService.toString( getSystemServiceClassA() ) ); - - sb.append( " CHAN B:" + getChannelB() ); - - sb.append( " " + SystemService.toString( getSystemServiceClassB() ) ); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getIdentifierA() - { - return mMessage.getInt( IDENTIFIER_A ); - } - - public int getChannelA() - { - return mMessage.getInt( CHANNEL_A ); - } - - public String getChannelNumberA() - { - return getIdentifierA() + "-" + getChannelA(); - } - - public int getIdentifierB() - { - return mMessage.getInt( IDENTIFIER_B ); - } - - public int getChannelB() - { - return mMessage.getInt( CHANNEL_B ); - } - - public String getChannelNumberB() - { - return getIdentifierB() + "-" + getChannelB(); - } - - public int getSystemServiceClassA() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS_A ); - } - - public int getSystemServiceClassB() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS_B ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getIdentifierA() ) - { - mIdentifierProviderA = message; - } - if( identifier == getIdentifierB() ) - { - mIdentifierProviderB = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" CHAN A:" + getChannelA()); + + sb.append(" " + SystemService.toString(getSystemServiceClassA())); + + sb.append(" CHAN B:" + getChannelB()); + + sb.append(" " + SystemService.toString(getSystemServiceClassB())); + + return sb.toString(); } - @Override + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getIdentifierA() + { + return mMessage.getInt(IDENTIFIER_A); + } + + public int getChannelA() + { + return mMessage.getInt(CHANNEL_A); + } + + public String getChannelNumberA() + { + return getIdentifierA() + "-" + getChannelA(); + } + + public int getIdentifierB() + { + return mMessage.getInt(IDENTIFIER_B); + } + + public int getChannelB() + { + return mMessage.getInt(CHANNEL_B); + } + + public String getChannelNumberB() + { + return getIdentifierB() + "-" + getChannelB(); + } + + public int getSystemServiceClassA() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS_A); + } + + public int getSystemServiceClassB() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS_B); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getIdentifierA()) + { + mIdentifierProviderA = message; + } + if(identifier == getIdentifierB()) + { + mIdentifierProviderB = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getIdentifierA(); - identifiers[ 1 ] = getIdentifierB(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getIdentifierA(); + identifiers[1] = getIdentifierB(); + + return identifiers; } - + public long getDownlinkFrequencyA() { - return calculateDownlink( mIdentifierProviderA, getChannelA() ); + if(mIdentifierProviderA != null) + { + return mIdentifierProviderA.getDownlinkFrequency(getChannelA()); + } + + return 0; } - + public long getUplinkFrequencyA() { - return calculateUplink( mIdentifierProviderA, getChannelA() ); + if(mIdentifierProviderA != null) + { + return mIdentifierProviderA.getUplinkFrequency(getChannelA()); + } + + return 0; } - + public long getDownlinkFrequencyB() { - return calculateDownlink( mIdentifierProviderB, getChannelB() ); + if(mIdentifierProviderB != null) + { + return mIdentifierProviderB.getDownlinkFrequency(getChannelB()); + } + + return 0; } - + public long getUplinkFrequencyB() { - return calculateUplink( mIdentifierProviderB, getChannelB() ); + if(mIdentifierProviderB != null) + { + return mIdentifierProviderB.getUplinkFrequency(getChannelB()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java index fb35f425..8032794d 100644 --- a/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; @@ -5,136 +23,134 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SecondaryControlChannelBroadcastExplicit - extends TDULinkControlMessage implements IdentifierReceiver +public class SecondaryControlChannelBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { - private final static Logger mLog = - LoggerFactory.getLogger( SecondaryControlChannelBroadcastExplicit.class ); + public static final int[] RFSS_ID = {72, 73, 74, 75, 88, 89, 90, 91}; + public static final int[] SITE_ID = {92, 93, 94, 95, 96, 97, 98, 99}; + public static final int[] TRANSMIT_IDENTIFIER = {112, 113, 114, 115}; + public static final int[] TRANSMIT_CHANNEL = {116, 117, 118, 119, 120, 121, 122, 123, 136, 137, 138, 139}; + public static final int[] RECEIVE_IDENTIFIER = {140, 141, 142, 143}; + public static final int[] RECEIVE_CHANNEL = {144, 145, 146, 147, 160, 161, 162, 163, 164, 165, 166, 167}; + public static final int[] SYSTEM_SERVICE_CLASS = {168, 169, 170, 171, 184, 185, 186, 187}; + + private IBandIdentifier mTransmitIdentifierProvider; + private IBandIdentifier mReceiveIdentifierProvider; + + public SecondaryControlChannelBroadcastExplicit(TDULinkControlMessage source) + { + super(source); + } - public static final int[] RFSS_ID = { 72,73,74,75,88,89,90,91 }; - public static final int[] SITE_ID = { 92,93,94,95,96,97,98,99 }; - public static final int[] TRANSMIT_IDENTIFIER = { 112,113,114,115 }; - public static final int[] TRANSMIT_CHANNEL = { 116,117,118,119,120,121,122, - 123,136,137,138,139 }; - public static final int[] RECEIVE_IDENTIFIER = { 140,141,142,143 }; - public static final int[] RECEIVE_CHANNEL = { 144,145,146,147,160,161,162, - 163,164,165,166,167 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 168,169,170,171,184, - 185,186,187 }; - - private IBandIdentifier mTransmitIdentifierProvider; - private IBandIdentifier mReceiveIdentifierProvider; - - public SecondaryControlChannelBroadcastExplicit( TDULinkControlMessage source ) - { - super( source ); - } - @Override public String getEventType() { return LinkControlOpcode.SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT.getDescription(); } - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " SITE:" + getRFSubsystemID() + "-" + getSiteID() ); - - sb.append( " TRANSMIT:" + getTransmitChannel() ); - - sb.append( " RECEIVE:" + getReceiveChannel() ); - - sb.append( " " + SystemService.toString( getSystemServiceClass() ) ); - - return sb.toString(); - } - - public String getRFSubsystemID() - { - return mMessage.getHex( RFSS_ID, 2 ); - } - - public String getSiteID() - { - return mMessage.getHex( SITE_ID, 2 ); - } - - public int getTransmitIdentifier() - { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_CHANNEL ); - } - - public String getTransmitChannel() - { - return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_CHANNEL ); - } - - public String getReceiveChannel() - { - return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public String getMessage() { - if( identifier == getTransmitIdentifier() ) - { - mTransmitIdentifierProvider = message; - } - if( identifier == getReceiveIdentifier() ) - { - mReceiveIdentifierProvider = message; - } + StringBuilder sb = new StringBuilder(); + + sb.append(getMessageStub()); + + sb.append(" SITE:" + getRFSubsystemID() + "-" + getSiteID()); + + sb.append(" TRANSMIT:" + getTransmitChannel()); + + sb.append(" RECEIVE:" + getReceiveChannel()); + + sb.append(" " + SystemService.toString(getSystemServiceClass())); + + return sb.toString(); } - @Override + public String getRFSubsystemID() + { + return mMessage.getHex(RFSS_ID, 2); + } + + public String getSiteID() + { + return mMessage.getHex(SITE_ID, 2); + } + + public int getTransmitIdentifier() + { + return mMessage.getInt(TRANSMIT_IDENTIFIER); + } + + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_CHANNEL); + } + + public String getTransmitChannel() + { + return getTransmitIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_CHANNEL); + } + + public String getReceiveChannel() + { + return getReceiveIdentifier() + "-" + getReceiveChannelNumber(); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitIdentifier()) + { + mTransmitIdentifierProvider = message; + } + if(identifier == getReceiveIdentifier()) + { + mReceiveIdentifierProvider = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitIdentifier(); - identifiers[ 1 ] = getReceiveIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + + identifiers[0] = getTransmitIdentifier(); + identifiers[1] = getReceiveIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierProvider, - getTransmitChannelNumber() ); + if(mTransmitIdentifierProvider != null) + { + return mTransmitIdentifierProvider.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierProvider, - getReceiveChannelNumber() ); + if(mReceiveIdentifierProvider != null) + { + return mReceiveIdentifierProvider.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tsbk/ChannelGrant.java b/src/module/decode/p25/message/tsbk/ChannelGrant.java index b0c81548..fa4893c7 100644 --- a/src/module/decode/p25/message/tsbk/ChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/ChannelGrant.java @@ -1,26 +1,53 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; -public abstract class ChannelGrant extends ServiceMessage - implements IdentifierReceiver +public abstract class ChannelGrant extends ServiceMessage implements IdentifierReceiver { - public static final int[] PRIORITY = { 85,86,87 }; - public static final int[] CHANNEL_IDENTIFIER = { 88,89,90,91 }; - public static final int[] CHANNEL_NUMBER = { 92,93,94,95,96,97,98,99,100, - 101,102,103 }; - + public static final int[] PRIORITY = {85, 86, 87}; + public static final int[] CHANNEL_IDENTIFIER = {88, 89, 90, 91}; + public static final int[] CHANNEL_NUMBER = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + private IBandIdentifier mIdentifierUpdate; - - public ChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + + public ChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); + } + + /** + * Indicates if this channel grant is for a TDMA channel + */ + public boolean isTDMAChannel() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.isTDMA(); + } + + return false; } /** @@ -28,47 +55,56 @@ public abstract class ChannelGrant extends ServiceMessage */ public int getPriority() { - return mMessage.getInt( PRIORITY ); + return mMessage.getInt(PRIORITY); } - + public int getChannelIdentifier() { - return mMessage.getInt( CHANNEL_IDENTIFIER ); + return mMessage.getInt(CHANNEL_IDENTIFIER); } - + public int getChannelNumber() { - return mMessage.getInt( CHANNEL_NUMBER ); + return mMessage.getInt(CHANNEL_NUMBER); } public String getChannel() { - return getChannelIdentifier() + "-" + getChannelNumber(); + return getChannelIdentifier() + "-" + getChannelNumber(); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - mIdentifierUpdate = message; + mIdentifierUpdate = message; } - @Override + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - identifiers[ 0 ] = getChannelIdentifier(); - - return identifiers; - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + int[] identifiers = new int[1]; + identifiers[0] = getChannelIdentifier(); + + return identifiers; } + public long getDownlinkFrequency() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; + } } diff --git a/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java b/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java index 5233162f..5a656706 100644 --- a/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java +++ b/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java @@ -1,120 +1,155 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk; -import module.decode.p25.message.IBandIdentifier; -import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.DataUnitID; public abstract class GroupChannelGrantExplicit extends ChannelGrant { - public static final int[] TRANSMIT_IDENTIFIER = { 96,97,98,99 }; - public static final int[] TRANSMIT_NUMBER = { 100,101,102,103, - 104,105,106,107,108,109,110,111 }; - public static final int[] RECEIVE_IDENTIFIER = { 112,113,114,115 }; - public static final int[] RECEIVE_NUMBER = { 116,117,118,119, - 120,121,122,123,124,125,126,127 }; - public static final int[] GROUP_ADDRESS = { 128,129,130,131,132,133,134,135, - 136,137,138,139,140,141,142,143 }; - + public static final int[] TRANSMIT_IDENTIFIER = {96, 97, 98, 99}; + public static final int[] TRANSMIT_NUMBER = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] RECEIVE_IDENTIFIER = {112, 113, 114, 115}; + public static final int[] RECEIVE_NUMBER = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + public static final int[] GROUP_ADDRESS = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143}; + private IBandIdentifier mTransmitIdentifierUpdate; private IBandIdentifier mReceiveIdentifierUpdate; - public GroupChannelGrantExplicit( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + public GroupChannelGrantExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); + } + + @Override + public boolean isTDMAChannel() + { + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.isTDMA(); + } + + return false; } public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) + + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " XMIT:" ); - sb.append( getTransmitChannelIdentifier() + "/" + getTransmitChannelNumber() ); - - sb.append( " RCV:" ); - sb.append( getReceiveChannelIdentifier() + "/" + getReceiveChannelNumber() ); - - sb.append( " GRP:" ); - sb.append( getGroupAddress() ); - + + sb.append(" XMIT:"); + sb.append(getTransmitChannelIdentifier() + "/" + getTransmitChannelNumber()); + + sb.append(" RCV:"); + sb.append(getReceiveChannelIdentifier() + "/" + getReceiveChannelNumber()); + + sb.append(" GRP:"); + sb.append(getGroupAddress()); + return sb.toString(); } - + public int getTransmitChannelIdentifier() { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); - } - - public int getTransmitChannelNumber() - { - return mMessage.getInt( TRANSMIT_NUMBER ); - } - - public String getTransmitChannel() - { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); - } - - public int getReceiveChannelIdentifier() - { - return mMessage.getInt( RECEIVE_IDENTIFIER ); - } - - public int getReceiveChannelNumber() - { - return mMessage.getInt( RECEIVE_NUMBER ); - } - - public String getReceiveChannel() - { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); - } - - public String getGroupAddress() - { - return mMessage.getHex( GROUP_ADDRESS, 4 ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getTransmitChannelIdentifier() ) - { - mTransmitIdentifierUpdate = message; - } - - if( identifier == getReceiveChannelIdentifier() ) - { - mReceiveIdentifierUpdate = message; - } + return mMessage.getInt(TRANSMIT_IDENTIFIER); } - @Override + public int getTransmitChannelNumber() + { + return mMessage.getInt(TRANSMIT_NUMBER); + } + + public String getTransmitChannel() + { + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + } + + public int getReceiveChannelIdentifier() + { + return mMessage.getInt(RECEIVE_IDENTIFIER); + } + + public int getReceiveChannelNumber() + { + return mMessage.getInt(RECEIVE_NUMBER); + } + + public String getReceiveChannel() + { + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + } + + public String getGroupAddress() + { + return mMessage.getHex(GROUP_ADDRESS, 4); + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mTransmitIdentifierUpdate = message; + } + + if(identifier == getReceiveChannelIdentifier()) + { + mReceiveIdentifierUpdate = message; + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getTransmitChannelIdentifier(); - identifiers[ 1 ] = getReceiveChannelIdentifier(); - - return identifiers; + int[] identifiers = new int[2]; + identifiers[0] = getTransmitChannelIdentifier(); + identifiers[1] = getReceiveChannelIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mTransmitIdentifierUpdate, getTransmitChannelNumber() ); + if(mTransmitIdentifierUpdate != null) + { + return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mReceiveIdentifierUpdate, getReceiveChannelNumber() ); + if(mReceiveIdentifierUpdate != null) + { + return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java b/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java index 90446323..8c2bf284 100644 --- a/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java @@ -1,106 +1,116 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; -public abstract class GroupMultiChannelGrant extends ChannelGrant - implements IdentifierReceiver +public abstract class GroupMultiChannelGrant extends ChannelGrant implements IdentifierReceiver { - public static final int[] CHANNEL_IDENTIFIER_1 = { 80,81,82,83 }; - public static final int[] CHANNEL_NUMBER_1 = { 84,85,86,87, - 88,89,90,91,92,93,94,95 }; - public static final int[] GROUP_ADDRESS_1 = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111 }; - - public static final int[] CHANNEL_IDENTIFIER_2 = { 112,113,114,115 }; - public static final int[] CHANNEL_NUMBER_2 = { 116,117,118,119,120,121,122, - 123,124,125,126,127 }; - public static final int[] GROUP_ADDRESS_2 = { 128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143 }; - + public static final int[] CHANNEL_IDENTIFIER_1 = {80, 81, 82, 83}; + public static final int[] CHANNEL_NUMBER_1 = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + public static final int[] GROUP_ADDRESS_1 = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] CHANNEL_IDENTIFIER_2 = {112, 113, 114, 115}; + public static final int[] CHANNEL_NUMBER_2 = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + public static final int[] GROUP_ADDRESS_2 = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdate1; private IBandIdentifier mIdentifierUpdate2; - public GroupMultiChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + public GroupMultiChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " CHAN1:" ); - sb.append( getChannelIdentifier1() + "/" + getChannelNumber1() ); - - sb.append( " GRP1:" ); - sb.append( getGroupAddress1() ); - - if( hasChannelNumber2() ) + + sb.append(getMessageStub()); + + sb.append(" CHAN1:"); + sb.append(getChannelIdentifier1() + "/" + getChannelNumber1()); + + sb.append(" GRP1:"); + sb.append(getGroupAddress1()); + + if(hasChannelNumber2()) { - sb.append( " CHAN2:" ); - sb.append( getChannelIdentifier2() + "/" + getChannelNumber2() ); - - sb.append( " GRP2:" ); - sb.append( getGroupAddress2() ); + sb.append(" CHAN2:"); + sb.append(getChannelIdentifier2() + "/" + getChannelNumber2()); + + sb.append(" GRP2:"); + sb.append(getGroupAddress2()); } - + return sb.toString(); } - + public int getChannelIdentifier1() { - return mMessage.getInt( CHANNEL_IDENTIFIER_1 ); + return mMessage.getInt(CHANNEL_IDENTIFIER_1); } - + public int getChannelNumber1() { - return mMessage.getInt( CHANNEL_NUMBER_1 ); + return mMessage.getInt(CHANNEL_NUMBER_1); } - + public String getChannel1() { - return getChannelIdentifier1() + "-" + getChannelNumber1(); + return getChannelIdentifier1() + "-" + getChannelNumber1(); } - + public String getGroupAddress1() { - return mMessage.getHex( GROUP_ADDRESS_1, 4 ); + return mMessage.getHex(GROUP_ADDRESS_1, 4); } - + public int getChannelIdentifier2() { - return mMessage.getInt( CHANNEL_IDENTIFIER_2 ); + return mMessage.getInt(CHANNEL_IDENTIFIER_2); } - + public int getChannelNumber2() { - return mMessage.getInt( CHANNEL_NUMBER_2 ); + return mMessage.getInt(CHANNEL_NUMBER_2); } - + public String getChannel2() { - return getChannelIdentifier2() + "-" + getChannelNumber2(); + return getChannelIdentifier2() + "-" + getChannelNumber2(); } - + public String getGroupAddress2() { - return mMessage.getHex( GROUP_ADDRESS_2, 4 ); + return mMessage.getHex(GROUP_ADDRESS_2, 4); } - + public boolean hasChannelNumber2() { - return mMessage.getInt( CHANNEL_NUMBER_2 ) != - mMessage.getInt( CHANNEL_NUMBER_1 ); + return mMessage.getInt(CHANNEL_NUMBER_2) != + mMessage.getInt(CHANNEL_NUMBER_1); } - + @Override public String getFromID() { @@ -112,59 +122,99 @@ public abstract class GroupMultiChannelGrant extends ChannelGrant { return getGroupAddress2(); } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getChannelIdentifier1() ) - { - mIdentifierUpdate1 = message; - } - if( hasChannelNumber2() && identifier == getChannelIdentifier2() ) - { - mIdentifierUpdate2 = message; - } + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getChannelIdentifier1()) + { + mIdentifierUpdate1 = message; + } + + if(hasChannelNumber2() && identifier == getChannelIdentifier2()) + { + mIdentifierUpdate2 = message; + } } - @Override + @Override public int[] getIdentifiers() { - int[] identifiers; - - if( hasChannelNumber2() ) - { - identifiers = new int[ 2 ]; - identifiers[ 0 ] = getChannelIdentifier1(); - identifiers[ 1 ] = getChannelIdentifier2(); - } - else - { - identifiers = new int[ 1 ]; - identifiers[ 0 ] = getChannelIdentifier1(); - } - - return identifiers; + int[] identifiers; + + if(hasChannelNumber2()) + { + identifiers = new int[2]; + identifiers[0] = getChannelIdentifier1(); + identifiers[1] = getChannelIdentifier2(); + } + else + { + identifiers = new int[1]; + identifiers[0] = getChannelIdentifier1(); + } + + return identifiers; } - + public long getDownlinkFrequency1() { - return calculateDownlink( mIdentifierUpdate1, getChannelNumber1() ); + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.getDownlinkFrequency(getChannelNumber1()); + } + + return 0; } - + + public boolean isTDMAChannel1() + { + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.isTDMA(); + } + + return false; + } + public long getUplinkFrequency1() { - return calculateUplink( mIdentifierUpdate1, getChannelNumber1() ); + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.getUplinkFrequency(getChannelNumber1()); + } + + return 0; } - + public long getDownlinkFrequency2() { - return calculateDownlink( mIdentifierUpdate2, getChannelNumber2() ); + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.getDownlinkFrequency(getChannelNumber2()); + } + + return 0; } - + + public boolean isTDMAChannel2() + { + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.isTDMA(); + } + + return false; + } + public long getUplinkFrequency2() { - return calculateUplink( mIdentifierUpdate2, getChannelNumber2() ); + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.getUplinkFrequency(getChannelNumber2()); + } + + return 0; } - + } diff --git a/src/module/decode/p25/message/tsbk/UnitChannelGrant.java b/src/module/decode/p25/message/tsbk/UnitChannelGrant.java index 5d49f4b7..90e8b476 100644 --- a/src/module/decode/p25/message/tsbk/UnitChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/UnitChannelGrant.java @@ -1,55 +1,70 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk; -import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; +import module.decode.p25.reference.DataUnitID; public abstract class UnitChannelGrant extends ChannelGrant { - public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 }; - - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - - public UnitChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + public static final int[] TARGET_ADDRESS = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] SOURCE_ADDRESS = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; + + public UnitChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) + + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - sb.append( " FROM:" ); - sb.append( getSourceAddress() ); - - sb.append( " TO:" ); - sb.append( getTargetAddress() ); - + + sb.append(" FROM:"); + sb.append(getSourceAddress()); + + sb.append(" TO:"); + sb.append(getTargetAddress()); + return sb.toString(); } - + public String getTargetAddress() { - return mMessage.getHex( TARGET_ADDRESS, 6 ); + return mMessage.getHex(TARGET_ADDRESS, 6); } - + public String getSourceAddress() { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); + return mMessage.getHex(SOURCE_ADDRESS, 6); } - + @Override public String getFromID() { @@ -59,11 +74,11 @@ public abstract class UnitChannelGrant extends ChannelGrant @Override public Alias getFromIDAlias() { - if( mAliasList != null ) + if(mAliasList != null) { - return mAliasList.getTalkgroupAlias( getFromID() ); + return mAliasList.getTalkgroupAlias(getFromID()); } - + return null; } diff --git a/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java b/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java index 987d84ad..be11983f 100644 --- a/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java +++ b/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java @@ -1,34 +1,48 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.motorola; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; -public class ControlChannelBaseStationIdentification extends MotorolaTSBKMessage - implements IdentifierReceiver +public class ControlChannelBaseStationIdentification extends MotorolaTSBKMessage implements IdentifierReceiver { - public static final int[] CHARACTER_1 = { 80,81,82,83,84,85 }; - public static final int[] CHARACTER_2 = { 86,87,88,89,90,91 }; - public static final int[] CHARACTER_3 = { 92,93,94,95,96,97 }; - public static final int[] CHARACTER_4 = { 98,99,100,101,102,103 }; - public static final int[] CHARACTER_5 = { 104,105,106,107,108,109 }; - public static final int[] CHARACTER_6 = { 110,111,112,113,114,115 }; - public static final int[] CHARACTER_7 = { 116,117,118,119,120,121 }; - public static final int[] CHARACTER_8 = { 122,123,124,125,126,127 }; - - public static final int[] IDENTIFIER = { 128,129,130,131 }; - public static final int[] CHANNEL = { 132,133,134,135,136,137,138,139,140, - 141,142,143 }; - + public static final int[] CHARACTER_1 = {80, 81, 82, 83, 84, 85}; + public static final int[] CHARACTER_2 = {86, 87, 88, 89, 90, 91}; + public static final int[] CHARACTER_3 = {92, 93, 94, 95, 96, 97}; + public static final int[] CHARACTER_4 = {98, 99, 100, 101, 102, 103}; + public static final int[] CHARACTER_5 = {104, 105, 106, 107, 108, 109}; + public static final int[] CHARACTER_6 = {110, 111, 112, 113, 114, 115}; + public static final int[] CHARACTER_7 = {116, 117, 118, 119, 120, 121}; + public static final int[] CHARACTER_8 = {122, 123, 124, 125, 126, 127}; + + public static final int[] IDENTIFIER = {128, 129, 130, 131}; + public static final int[] CHANNEL = {132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdate; - - public ControlChannelBaseStationIdentification( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + + public ControlChannelBaseStationIdentification(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } @Override @@ -36,86 +50,96 @@ public class ControlChannelBaseStationIdentification extends MotorolaTSBKMessage { return MotorolaOpcode.CONTROL_CHANNEL_ID.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " CHAN:" + getIdentifier() + "-" + getChannel() ); - - sb.append( " CWID:" + getCWID() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " UP:" + getUplinkFrequency() ); - + sb.append(getMessageStub()); + + sb.append(" CHAN:" + getIdentifier() + "-" + getChannel()); + + sb.append(" CWID:" + getCWID()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" UP:" + getUplinkFrequency()); + return sb.toString(); } - - + + public String getCWID() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( getCharacter( CHARACTER_1 ) ); - sb.append( getCharacter( CHARACTER_2 ) ); - sb.append( getCharacter( CHARACTER_3 ) ); - sb.append( getCharacter( CHARACTER_4 ) ); - sb.append( getCharacter( CHARACTER_5 ) ); - sb.append( getCharacter( CHARACTER_6 ) ); - sb.append( getCharacter( CHARACTER_7 ) ); - sb.append( getCharacter( CHARACTER_8 ) ); - - return sb.toString(); + sb.append(getCharacter(CHARACTER_1)); + sb.append(getCharacter(CHARACTER_2)); + sb.append(getCharacter(CHARACTER_3)); + sb.append(getCharacter(CHARACTER_4)); + sb.append(getCharacter(CHARACTER_5)); + sb.append(getCharacter(CHARACTER_6)); + sb.append(getCharacter(CHARACTER_7)); + sb.append(getCharacter(CHARACTER_8)); + + return sb.toString(); } - - private String getCharacter( int[] field ) + + private String getCharacter(int[] field) { - int value = mMessage.getInt( field ); - - if( value != 0 ) - { - return String.valueOf( (char)( value + 43 ) ); - } - - return null; + int value = mMessage.getInt(field); + + if(value != 0) + { + return String.valueOf((char)(value + 43)); + } + + return null; } - + public int getIdentifier() { - return mMessage.getInt( IDENTIFIER ); + return mMessage.getInt(IDENTIFIER); } - + public int getChannel() { - return mMessage.getInt( CHANNEL ); + return mMessage.getInt(CHANNEL); } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannel() ); + if(mIdentifierUpdate != null) + { + mIdentifierUpdate.getUplinkFrequency(getChannel()); + } + + return 0; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannel() ); + if(mIdentifierUpdate != null) + { + mIdentifierUpdate.getDownlinkFrequency(getChannel()); + } + + return 0; } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - mIdentifierUpdate = message; + mIdentifierUpdate = message; } - @Override + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - - identifiers[ 0 ] = getIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + + identifiers[0] = getIdentifier(); + + return identifiers; } } diff --git a/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java index d760b2c9..54ab32c3 100644 --- a/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java @@ -1,151 +1,187 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.motorola; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; -public class PatchGroupVoiceChannelGrant extends MotorolaTSBKMessage - implements IdentifierReceiver +public class PatchGroupVoiceChannelGrant extends MotorolaTSBKMessage implements IdentifierReceiver { /* Service Options */ public static final int EMERGENCY_FLAG = 80; public static final int ENCRYPTED_CHANNEL_FLAG = 81; public static final int DUPLEX_MODE = 82; public static final int SESSION_MODE = 83; - - public static final int[] PRIORITY = { 85,86,87 }; - public static final int[] IDENTIFIER = { 88,89,90,91 }; - public static final int[] CHANNEL = { 92,93,94,95,96,97,98,99,100,101,102,103 }; - public static final int[] PATCH_GROUP_ADDRESS = { 104,105,106,107,108,109, - 110,111,112,113,114,115,116,117,118,119 }; - public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; + + public static final int[] PRIORITY = {85, 86, 87}; + public static final int[] IDENTIFIER = {88, 89, 90, 91}; + public static final int[] CHANNEL = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + public static final int[] PATCH_GROUP_ADDRESS = {104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119}; + public static final int[] SOURCE_ADDRESS = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; private IBandIdentifier mIdentifierUpdate; - public PatchGroupVoiceChannelGrant( BinaryMessage message, DataUnitID duid, - AliasList aliasList ) + public PatchGroupVoiceChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } - @Override + public boolean isTDMAChannel() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.isTDMA(); + } + + return false; + } + + @Override public String getEventType() { - return MotorolaOpcode.PATCH_GROUP_CHANNEL_GRANT.getLabel(); + return MotorolaOpcode.PATCH_GROUP_CHANNEL_GRANT.getLabel(); } - @Override + @Override public String getMessage() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( getMessageStub() ); + sb.append(getMessageStub()); - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " PATCH GROUP:" ); - sb.append( getPatchGroupAddress() ); - sb.append( " FROM:" ); - sb.append( getSourceAddress() ); - sb.append( " PRI:" ); - sb.append( getPriority() ); + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } - sb.append( " CHAN:" ); - sb.append( getChannelIdentifier() + "-" + getChannelNumber() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " UP:" + getUplinkFrequency() ); + sb.append(" PATCH GROUP:"); + sb.append(getPatchGroupAddress()); + sb.append(" FROM:"); + sb.append(getSourceAddress()); + sb.append(" PRI:"); + sb.append(getPriority()); - sb.append( " SESSION MODE:"); - sb.append( getSessionMode().name() ); - - return sb.toString(); + sb.append(" CHAN:"); + sb.append(getChannelIdentifier() + "-" + getChannelNumber()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" UP:" + getUplinkFrequency()); + + sb.append(" SESSION MODE:"); + sb.append(getSessionMode().name()); + + return sb.toString(); } - + public String getPatchGroupAddress() { - return mMessage.getHex( PATCH_GROUP_ADDRESS, 4 ); + return mMessage.getHex(PATCH_GROUP_ADDRESS, 4); } - + public String getSourceAddress() { - return mMessage.getHex( SOURCE_ADDRESS, 6 ); + return mMessage.getHex(SOURCE_ADDRESS, 6); } - + public boolean isEmergency() { - return mMessage.get( EMERGENCY_FLAG ); + return mMessage.get(EMERGENCY_FLAG); } - + public boolean isEncryptedChannel() { - return mMessage.get( ENCRYPTED_CHANNEL_FLAG ); + return mMessage.get(ENCRYPTED_CHANNEL_FLAG); } - + public DuplexMode getDuplexMode() { - return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF; + return mMessage.get(DUPLEX_MODE) ? DuplexMode.FULL : DuplexMode.HALF; } public SessionMode getSessionMode() { - return mMessage.get( SESSION_MODE ) ? - SessionMode.CIRCUIT : SessionMode.PACKET; + return mMessage.get(SESSION_MODE) ? + SessionMode.CIRCUIT : SessionMode.PACKET; } - + /** * 1 = Lowest, 4 = Default, 7 = Highest */ public int getPriority() { - return mMessage.getInt( PRIORITY ); + return mMessage.getInt(PRIORITY); } - + public int getChannelIdentifier() { - return mMessage.getInt( IDENTIFIER ); + return mMessage.getInt(IDENTIFIER); } - + public int getChannelNumber() { - return mMessage.getInt( CHANNEL ); + return mMessage.getInt(CHANNEL); } public String getChannel() { - return getChannelIdentifier() + "-" + getChannelNumber(); + return getChannelIdentifier() + "-" + getChannelNumber(); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - mIdentifierUpdate = message; + mIdentifierUpdate = message; } - @Override + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 1 ]; - identifiers[ 0 ] = getChannelIdentifier(); - - return identifiers; + int[] identifiers = new int[1]; + identifiers[0] = getChannelIdentifier(); + + return identifiers; } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannelNumber() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannelNumber()); + } + + return 0; } - + } diff --git a/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java index 0788c2ab..e1ee6556 100644 --- a/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java @@ -1,146 +1,197 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.motorola; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; -public class PatchGroupVoiceChannelGrantUpdate extends MotorolaTSBKMessage - implements IdentifierReceiver +public class PatchGroupVoiceChannelGrantUpdate extends MotorolaTSBKMessage implements IdentifierReceiver { - public static final int[] IDENTIFIER_1 = { 80,81,82,83 }; - public static final int[] CHANNEL_1 = { 84,85,86,87,88,89,90,91,92,93,94, - 95 }; - public static final int[] PATCH_GROUP_ADDRESS_1 = { 96,97,98,99,100,101,102, - 103,104,105,106,107,108,109,110,111 }; - public static final int[] IDENTIFIER_2 = { 112,113,114,115 }; - public static final int[] CHANNEL_2 = { 116,117,118,119,120,121,122,123, - 124,125,126,127 }; - public static final int[] PATCH_GROUP_ADDRESS_2 = { 128,129,130,131,132,133, - 134,135,136,137,138,139,140,141,142,143 }; + public static final int[] IDENTIFIER_1 = {80, 81, 82, 83}; + public static final int[] CHANNEL_1 = {84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}; + public static final int[] PATCH_GROUP_ADDRESS_1 = {96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] IDENTIFIER_2 = {112, 113, 114, 115}; + public static final int[] CHANNEL_2 = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + public static final int[] PATCH_GROUP_ADDRESS_2 = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; private IBandIdentifier mIdentifierUpdate1; private IBandIdentifier mIdentifierUpdate2; - public PatchGroupVoiceChannelGrantUpdate( BinaryMessage message, - DataUnitID duid, AliasList aliasList ) + public PatchGroupVoiceChannelGrantUpdate(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } - @Override + @Override public String getEventType() { - return MotorolaOpcode.PATCH_GROUP_CHANNEL_GRANT_UPDATE.getLabel(); + return MotorolaOpcode.PATCH_GROUP_CHANNEL_GRANT_UPDATE.getLabel(); } - @Override + @Override public String getMessage() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( getMessageStub() ); + sb.append(getMessageStub()); - - sb.append( " PATCH GRP1:" ); - sb.append( getPatchGroupAddress1() ); - sb.append( " GRP2:" ); - sb.append( getPatchGroupAddress2() ); - sb.append( " CHAN 1:" ); - sb.append( getChannel1() ); - sb.append( " DN:" + getDownlinkFrequency1() ); - - sb.append( " CHAN 2:" ); - sb.append( getChannel2() ); - sb.append( " DN:" + getDownlinkFrequency2() ); - - return sb.toString(); + sb.append(" PATCH GRP1:"); + sb.append(getPatchGroupAddress1()); + sb.append(" GRP2:"); + sb.append(getPatchGroupAddress2()); + + sb.append(" CHAN 1:"); + sb.append(getChannel1()); + sb.append(" DN:" + getDownlinkFrequency1()); + + sb.append(" CHAN 2:"); + sb.append(getChannel2()); + sb.append(" DN:" + getDownlinkFrequency2()); + + return sb.toString(); } - + public String getPatchGroupAddress1() { - return mMessage.getHex( PATCH_GROUP_ADDRESS_1, 4 ); + return mMessage.getHex(PATCH_GROUP_ADDRESS_1, 4); } - + public String getPatchGroupAddress2() { - return mMessage.getHex( PATCH_GROUP_ADDRESS_2, 4 ); + return mMessage.getHex(PATCH_GROUP_ADDRESS_2, 4); } - + public int getChannelIdentifier1() { - return mMessage.getInt( IDENTIFIER_1 ); + return mMessage.getInt(IDENTIFIER_1); } - + public int getChannelNumber1() { - return mMessage.getInt( CHANNEL_1 ); + return mMessage.getInt(CHANNEL_1); } public String getChannel1() { - return getChannelIdentifier1() + "-" + getChannelNumber1(); + return getChannelIdentifier1() + "-" + getChannelNumber1(); } public int getChannelIdentifier2() { - return mMessage.getInt( IDENTIFIER_2 ); + return mMessage.getInt(IDENTIFIER_2); } - + public int getChannelNumber2() { - return mMessage.getInt( CHANNEL_2 ); + return mMessage.getInt(CHANNEL_2); } public String getChannel2() { - return getChannelIdentifier2() + "-" + getChannelNumber2(); + return getChannelIdentifier2() + "-" + getChannelNumber2(); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - if( identifier == getChannelIdentifier1() ) - { - mIdentifierUpdate1 = message; - } - - if( identifier == getChannelIdentifier2() ) - { - mIdentifierUpdate2 = message; - } + if(identifier == getChannelIdentifier1()) + { + mIdentifierUpdate1 = message; + } + + if(identifier == getChannelIdentifier2()) + { + mIdentifierUpdate2 = message; + } } - @Override + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - identifiers[ 0 ] = getChannelIdentifier1(); - identifiers[ 1 ] = getChannelIdentifier2(); - - return identifiers; + int[] identifiers = new int[2]; + identifiers[0] = getChannelIdentifier1(); + identifiers[1] = getChannelIdentifier2(); + + return identifiers; } - + public long getDownlinkFrequency1() { - return calculateDownlink( mIdentifierUpdate1, getChannelNumber1() ); + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.getDownlinkFrequency(getChannelNumber1()); + } + + return 0; } - + + public boolean isTDMAChannel1() + { + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.isTDMA(); + } + + return false; + } + public long getUplinkFrequency1() { - return calculateUplink( mIdentifierUpdate1, getChannelNumber1() ); + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.getUplinkFrequency(getChannelNumber1()); + } + + return 0; } - + public long getDownlinkFrequency2() { - return calculateDownlink( mIdentifierUpdate2, getChannelNumber2() ); + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.getDownlinkFrequency(getChannelNumber2()); + } + + return 0; } - + + public boolean isTDMAChannel2() + { + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.isTDMA(); + } + + return false; + } + public long getUplinkFrequency2() { - return calculateUplink( mIdentifierUpdate2, getChannelNumber2() ); + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.getUplinkFrequency(getChannelNumber2()); + } + + return 0; } - } diff --git a/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java index 49744f78..4ff1a876 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java @@ -1,40 +1,51 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.control; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IAdjacentSite; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; -public class AdjacentStatusBroadcast extends TSBKMessage - implements IdentifierReceiver, IAdjacentSite +public class AdjacentStatusBroadcast extends TSBKMessage implements IdentifierReceiver, IAdjacentSite { - public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85, - 86,87 }; + public static final int[] LOCATION_REGISTRATION_AREA = {80, 81, 82, 83, 84, 85, 86, 87}; public static final int CONVENTIONAL_CHANNEL_FLAG = 88; public static final int SITE_FAILURE_CONDITION_FLAG = 89; public static final int VALID_FLAG = 90; public static final int ACTIVE_NETWORK_CONNECTION_FLAG = 91; - public static final int[] SYSTEM_ID = { 92,93,94,95,96,97,98,99,100,101, - 102,103 }; - public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 }; - public static final int[] SITE_ID = { 112,113,114,115,116,117,118,119 }; - public static final int[] IDENTIFIER = { 120,121,122,123 }; - public static final int[] CHANNEL = { 124,125,126,127,128,129,130,131,132, - 133,134,135 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141, - 142,143 }; - + public static final int[] SYSTEM_ID = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + public static final int[] RFSS_ID = {104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SITE_ID = {112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] IDENTIFIER = {120, 121, 122, 123}; + public static final int[] CHANNEL = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SYSTEM_SERVICE_CLASS = {136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdate; - - public AdjacentStatusBroadcast( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + + public AdjacentStatusBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } @Override @@ -42,154 +53,164 @@ public class AdjacentStatusBroadcast extends TSBKMessage { return Opcode.ADJACENT_STATUS_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLRA() ); - sb.append( " SYS:" + getSystemID() ); + sb.append(getMessageStub()); - sb.append( " SITE:" + getRFSS() + "-" + getSiteID() ); - - sb.append( " CHAN:" + getIdentifier() + "-" + getChannel() ); + sb.append(" LRA:" + getLRA()); - sb.append( " DN:" + getDownlinkFrequency() ); + sb.append(" SYS:" + getSystemID()); - sb.append( " UP:" + getUplinkFrequency() ); - - if( isConventionalChannel() ) + sb.append(" SITE:" + getRFSS() + "-" + getSiteID()); + + sb.append(" CHAN:" + getIdentifier() + "-" + getChannel()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" UP:" + getUplinkFrequency()); + + if(isConventionalChannel()) { - sb.append( " CONVENTIONAL" ); + sb.append(" CONVENTIONAL"); } - - if( isSiteFailureCondition() ) + + if(isSiteFailureCondition()) { - sb.append( " FAILURE-CONDITION" ); + sb.append(" FAILURE-CONDITION"); } - - if( isValidSiteInformation() ) + + if(isValidSiteInformation()) { - sb.append( " VALID-INFO" ); + sb.append(" VALID-INFO"); } - - if( hasActiveNetworkConnection() ) + + if(hasActiveNetworkConnection()) { - sb.append( " ACTIVE-NETWORK-CONN" ); + sb.append(" ACTIVE-NETWORK-CONN"); } - - sb.append( getSystemServiceClass() ); - + + sb.append(getSystemServiceClass()); + return sb.toString(); } - + @Override - public String getDownlinkChannel() - { - return getIdentifier() + "-" + getChannel(); - } - - @Override - public String getUplinkChannel() - { - return getIdentifier() + "-" + getChannel(); - } - - public String getLRA() + public String getDownlinkChannel() { - return mMessage.getHex( LOCATION_REGISTRATION_AREA, 2 ); + return getIdentifier() + "-" + getChannel(); } - + + @Override + public String getUplinkChannel() + { + return getIdentifier() + "-" + getChannel(); + } + + public String getLRA() + { + return mMessage.getHex(LOCATION_REGISTRATION_AREA, 2); + } + public boolean isConventionalChannel() { - return mMessage.get( CONVENTIONAL_CHANNEL_FLAG ); + return mMessage.get(CONVENTIONAL_CHANNEL_FLAG); } - + public boolean isSiteFailureCondition() { - return mMessage.get( SITE_FAILURE_CONDITION_FLAG ); + return mMessage.get(SITE_FAILURE_CONDITION_FLAG); } - + public boolean isValidSiteInformation() { - return mMessage.get( VALID_FLAG ); + return mMessage.get(VALID_FLAG); } - + public boolean hasActiveNetworkConnection() { - return mMessage.get( ACTIVE_NETWORK_CONNECTION_FLAG ); + return mMessage.get(ACTIVE_NETWORK_CONNECTION_FLAG); } - + public String getUniqueID() { - StringBuilder sb = new StringBuilder(); - - sb.append( getSystemID() ); - sb.append( ":" ); - sb.append( getRFSS() ); - sb.append( ":" ); - sb.append( getSiteID() ); - - return sb.toString(); + StringBuilder sb = new StringBuilder(); + + sb.append(getSystemID()); + sb.append(":"); + sb.append(getRFSS()); + sb.append(":"); + sb.append(getSiteID()); + + return sb.toString(); } - + public String getSystemID() { - return mMessage.getHex( SYSTEM_ID, 3 ); + return mMessage.getHex(SYSTEM_ID, 3); } - + public String getRFSS() { - return mMessage.getHex( RFSS_ID, 2 ); + return mMessage.getHex(RFSS_ID, 2); } - + public String getSiteID() { - return mMessage.getHex( SITE_ID, 2 ); + return mMessage.getHex(SITE_ID, 2); } - + public int getChannel() { - return mMessage.getInt( CHANNEL ); + return mMessage.getInt(CHANNEL); } - + public int getIdentifier() { - return mMessage.getInt( IDENTIFIER ); + return mMessage.getInt(IDENTIFIER); } - + public String getSystemServiceClass() { - return SystemService.toString( mMessage.getInt( SYSTEM_SERVICE_CLASS ) ); + return SystemService.toString(mMessage.getInt(SYSTEM_SERVICE_CLASS)); } - + public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannel() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannel()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdate, getChannel() ); - } - + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannel()); + } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - /* we're only expecting 1 identifier, so use whatever is received */ - mIdentifierUpdate = message; + return 0; + } + + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + /* we're only expecting 1 identifier, so use whatever is received */ + mIdentifierUpdate = message; + } + + public int[] getIdentifiers() + { + int[] idens = new int[1]; + + idens[0] = getIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 1 ]; - - idens[ 0 ] = getIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java index 1f3e7e5e..d0b64617 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.control; import module.decode.p25.message.IBandIdentifier; @@ -6,8 +24,7 @@ import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -public abstract class IdentifierUpdate extends TSBKMessage - implements IBandIdentifier +public abstract class IdentifierUpdate extends TSBKMessage implements IBandIdentifier { public static final int[] IDENTIFIER = { 80,81,82,83 }; @@ -45,4 +62,22 @@ public abstract class IdentifierUpdate extends TSBKMessage @Override public abstract long getTransmitOffset(); + + @Override + public long getDownlinkFrequency(int channelNumber) + { + return getBaseFrequency() + ( channelNumber * getChannelSpacing() ); + } + + @Override + public long getUplinkFrequency(int channelNumber) + { + return getDownlinkFrequency(channelNumber) + getTransmitOffset(); + } + + @Override + public boolean isTDMA() + { + return false; + } } diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java index 7524ce7d..5a4ab33d 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java @@ -80,7 +80,26 @@ public class IdentifierUpdateTDMA extends IdentifierUpdate return -offset; } } - + + @Override + public long getDownlinkFrequency(int channelNumber) + { + if(isTDMA()) + { + return getBaseFrequency() + ( (channelNumber / getChannelType().getSlotsPerCarrier()) * getChannelSpacing() ); + } + else + { + return super.getDownlinkFrequency(channelNumber); + } + } + + @Override + public boolean isTDMA() + { + return getChannelType().getAccessType() == AccessType.TDMA; + } + public enum ChannelType { TYPE_0( AccessType.FDMA, 12500, 1, Vocoder.HALF_RATE ), diff --git a/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java index 9acd5b99..a911693e 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java @@ -1,35 +1,46 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.control; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; import module.decode.p25.reference.P25NetworkCallsign; -import alias.AliasList; -import bits.BinaryMessage; public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierReceiver { - public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85, - 86,87 }; - public static final int[] WACN = { 88,89,90,91,92,93,94,95,96,97,98,99,100, - 101,102,103,104,105,106,107 }; - public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115,116, - 117,118,119 }; - public static final int[] IDENTIFIER = { 120,121,122,123 }; - public static final int[] CHANNEL = { 124,125,126,127,128,129,130,131,132, - 133,134,135 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141, - 142,143 }; - + public static final int[] LOCATION_REGISTRATION_AREA = {80, 81, 82, 83, 84, 85, 86, 87}; + public static final int[] WACN = {88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107}; + public static final int[] SYSTEM_ID = {108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] IDENTIFIER = {120, 121, 122, 123}; + public static final int[] CHANNEL = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SYSTEM_SERVICE_CLASS = {136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdate; - - public NetworkStatusBroadcast( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + + public NetworkStatusBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } @Override @@ -37,90 +48,100 @@ public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierRec { return Opcode.NETWORK_STATUS_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - sb.append( " LRA:" + getLocationRegistrationArea() ); - - sb.append( " WACN:" + getWACN() ); - - sb.append( " SYS:" + getSystemID() ); - - sb.append( " CTRL CHAN:" + getIdentifier() + "-" + getChannel() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " UP:" + getUplinkFrequency() ); - - sb.append( " SYS SVC CLASS:" + - SystemService.toString( getSystemServiceClass() ) ); - + sb.append(getMessageStub()); + + sb.append(" LRA:" + getLocationRegistrationArea()); + + sb.append(" WACN:" + getWACN()); + + sb.append(" SYS:" + getSystemID()); + + sb.append(" CTRL CHAN:" + getIdentifier() + "-" + getChannel()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" UP:" + getUplinkFrequency()); + + sb.append(" SYS SVC CLASS:" + + SystemService.toString(getSystemServiceClass())); + return sb.toString(); } - + public String getLocationRegistrationArea() { - return mMessage.getHex( LOCATION_REGISTRATION_AREA, 2 ); + return mMessage.getHex(LOCATION_REGISTRATION_AREA, 2); } public String getWACN() { - return mMessage.getHex( WACN, 5 ); - } - - public String getSystemID() - { - return mMessage.getHex( SYSTEM_ID, 3 ); + return mMessage.getHex(WACN, 5); + } + + public String getSystemID() + { + return mMessage.getHex(SYSTEM_ID, 3); + } + + public String getNetworkCallsign() + { + return P25NetworkCallsign.getCallsign(mMessage.getInt(WACN), + mMessage.getInt(SYSTEM_ID)); } - public String getNetworkCallsign() - { - return P25NetworkCallsign.getCallsign( mMessage.getInt( WACN ), - mMessage.getInt( SYSTEM_ID ) ); - } - public int getIdentifier() { - return mMessage.getInt( IDENTIFIER ); + return mMessage.getInt(IDENTIFIER); } - + public int getChannel() { - return mMessage.getInt( CHANNEL ); + return mMessage.getInt(CHANNEL); } public int getSystemServiceClass() { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); + return mMessage.getInt(SYSTEM_SERVICE_CLASS); } public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdate, getChannel() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mIdentifierUpdate, getChannel() ); + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannel()); + } + + return 0; } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public long getUplinkFrequency() { - /* we're only expecting 1 identifier, so use whatever is received */ - mIdentifierUpdate = message; + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannel()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + /* we're only expecting 1 identifier, so use whatever is received */ + mIdentifierUpdate = message; + } + + public int[] getIdentifiers() + { + int[] idens = new int[1]; + + idens[0] = getIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 1 ]; - - idens[ 0 ] = getIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java index b1f95c7a..8e5ed2d3 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java @@ -1,35 +1,47 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.control; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; public class RFSSStatusBroadcast extends TSBKMessage implements IdentifierReceiver { - public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85, - 86,87 }; + public static final int[] LOCATION_REGISTRATION_AREA = {80, 81, 82, 83, 84, 85, 86, 87}; public static final int ACTIVE_NETWORK_CONNECTION_FLAG = 91; - public static final int[] SYSTEM_ID = { 92,93,94,95,96,97,98,99,100,101, - 102,103 }; - public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 }; - public static final int[] SITE_ID = { 112,113,114,115,116,117,118,119 }; - public static final int[] IDENTIFIER = { 120,121,122,123 }; - public static final int[] CHANNEL = { 124,125,126,127,128,129,130,131,132, - 133,134,135 }; - public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141, - 142,143 }; - + public static final int[] SYSTEM_ID = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + public static final int[] RFSS_ID = {104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SITE_ID = {112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] IDENTIFIER = {120, 121, 122, 123}; + public static final int[] CHANNEL = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SYSTEM_SERVICE_CLASS = {136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdate; - - public RFSSStatusBroadcast( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + + public RFSSStatusBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } @Override @@ -37,100 +49,110 @@ public class RFSSStatusBroadcast extends TSBKMessage implements IdentifierReceiv { return Opcode.RFSS_STATUS_BROADCAST.getDescription(); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " LRA:" + getLocationRegistrationArea() ); - sb.append( " SYSID:" + getSystemID() ); + sb.append(getMessageStub()); - sb.append( " RFSS:" + getRFSubsystemID() ); - - sb.append( " SITE:" + getSiteID() ); - - sb.append( " CHAN:" + getIdentifier() + "-" + getChannel() ); - - sb.append( " DN:" + getDownlinkFrequency() ); - - sb.append( " UP:" + getUplinkFrequency() ); + sb.append(" LRA:" + getLocationRegistrationArea()); - if( hasActiveNetworkConnection() ) + sb.append(" SYSID:" + getSystemID()); + + sb.append(" RFSS:" + getRFSubsystemID()); + + sb.append(" SITE:" + getSiteID()); + + sb.append(" CHAN:" + getIdentifier() + "-" + getChannel()); + + sb.append(" DN:" + getDownlinkFrequency()); + + sb.append(" UP:" + getUplinkFrequency()); + + if(hasActiveNetworkConnection()) { - sb.append( " ACTIVE-NETWORK-CONN" ); + sb.append(" ACTIVE-NETWORK-CONN"); } - - sb.append( SystemService.toString( getSystemServiceClass() ) ); - + + sb.append(SystemService.toString(getSystemServiceClass())); + return sb.toString(); } - + public String getLocationRegistrationArea() { - return mMessage.getHex( LOCATION_REGISTRATION_AREA, 2 ); + return mMessage.getHex(LOCATION_REGISTRATION_AREA, 2); } - + public boolean hasActiveNetworkConnection() { - return mMessage.get( ACTIVE_NETWORK_CONNECTION_FLAG ); + return mMessage.get(ACTIVE_NETWORK_CONNECTION_FLAG); } - + public String getSystemID() { - return mMessage.getHex( SYSTEM_ID, 3 ); + return mMessage.getHex(SYSTEM_ID, 3); } - + public String getRFSubsystemID() { - return mMessage.getHex( RFSS_ID, 2 ); + return mMessage.getHex(RFSS_ID, 2); } - + public String getSiteID() { - return mMessage.getHex( SITE_ID, 2 ); + return mMessage.getHex(SITE_ID, 2); } public int getIdentifier() { - return mMessage.getInt( IDENTIFIER ); - } - - public int getChannel() - { - return mMessage.getInt( CHANNEL ); - } - - public int getSystemServiceClass() - { - return mMessage.getInt( SYSTEM_SERVICE_CLASS ); - } - - public long getDownlinkFrequency() - { - return calculateDownlink( mIdentifierUpdate, getChannel() ); - } - - public long getUplinkFrequency() - { - return calculateUplink( mIdentifierUpdate, getChannel() ); + return mMessage.getInt(IDENTIFIER); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + public int getChannel() { - /* we're only expecting 1 identifier, so use whatever is received */ - mIdentifierUpdate = message; + return mMessage.getInt(CHANNEL); + } + + public int getSystemServiceClass() + { + return mMessage.getInt(SYSTEM_SERVICE_CLASS); + } + + public long getDownlinkFrequency() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getDownlinkFrequency(getChannel()); + } + + return 0; + } + + public long getUplinkFrequency() + { + if(mIdentifierUpdate != null) + { + return mIdentifierUpdate.getUplinkFrequency(getChannel()); + } + + return 0; + } + + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + /* we're only expecting 1 identifier, so use whatever is received */ + mIdentifierUpdate = message; + } + + public int[] getIdentifiers() + { + int[] idens = new int[1]; + + idens[0] = getIdentifier(); + + return idens; } - - public int[] getIdentifiers() - { - int[] idens = new int[ 1 ]; - - idens[ 0 ] = getIdentifier(); - - return idens; - } } diff --git a/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java index 25cf1044..e120c785 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.control; import alias.AliasList; @@ -7,38 +25,21 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -public class SecondaryControlChannelBroadcast extends TSBKMessage - implements IdentifierReceiver, Comparable +public class SecondaryControlChannelBroadcast extends TSBKMessage implements IdentifierReceiver, Comparable { - private final static Logger mLog = - LoggerFactory.getLogger(SecondaryControlChannelBroadcast.class); - public static final int[] RFSS_ID = {80, 81, 82, 83, 84, 85, 86, 87}; public static final int[] SITE_ID = {88, 89, 90, 91, 92, 93, 94, 95}; public static final int[] IDENTIFIER_1 = {96, 97, 98, 99}; - public static final int[] CHANNEL_1 = {100, 101, 102, 103, 104, 105, 106, 107, 108, - 109, 110, 111}; - public static final int[] SYSTEM_SERVICE_CLASS_1 = {112, 113, 114, 115, 116, - 117, 118, 119}; + public static final int[] CHANNEL_1 = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] SYSTEM_SERVICE_CLASS_1 = {112, 113, 114, 115, 116, 117, 118, 119}; public static final int[] IDENTIFIER_2 = {120, 121, 122, 123}; - public static final int[] CHANNEL_2 = {124, 125, 126, 127, 128, 129, 130, 131, 132, - 133, 134, 135}; - public static final int[] SYSTEM_SERVICE_CLASS_2 = {136, 137, 138, 139, 140, - 141, 142, 143}; + public static final int[] CHANNEL_2 = {124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135}; + public static final int[] SYSTEM_SERVICE_CLASS_2 = {136, 137, 138, 139, 140, 141, 142, 143}; private IBandIdentifier mIdentifierUpdate1; private IBandIdentifier mIdentifierUpdate2; - private int mRFSS; - private int mSite; - private int mIdent1; - private int mIdent2; - private int mChan1; - private int mChan2; - public SecondaryControlChannelBroadcast(BinaryMessage message, DataUnitID duid, AliasList aliasList) @@ -66,8 +67,7 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage sb.append(" UP1:" + getUplinkFrequency1()); - sb.append(" SVC1:" + - SystemService.toString(getSystemServiceClass1())); + sb.append(" SVC1:" + SystemService.toString(getSystemServiceClass1())); if(hasChannel2()) { @@ -77,8 +77,7 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage sb.append(" UP2:" + getUplinkFrequency2()); - sb.append(" SVC2:" + - SystemService.toString(getSystemServiceClass2())); + sb.append(" SVC2:" + SystemService.toString(getSystemServiceClass2())); } return sb.toString(); @@ -132,22 +131,42 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage public long getDownlinkFrequency1() { - return calculateDownlink(mIdentifierUpdate1, getChannel1()); + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.getDownlinkFrequency(getChannel1()); + } + + return 0; } public long getUplinkFrequency1() { - return calculateUplink(mIdentifierUpdate1, getChannel1()); + if(mIdentifierUpdate1 != null) + { + return mIdentifierUpdate1.getUplinkFrequency(getChannel1()); + } + + return 0; } public long getDownlinkFrequency2() { - return calculateDownlink(mIdentifierUpdate2, getChannel2()); + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.getDownlinkFrequency(getChannel2()); + } + + return 0; } public long getUplinkFrequency2() { - return calculateUplink(mIdentifierUpdate2, getChannel2()); + if(mIdentifierUpdate2 != null) + { + return mIdentifierUpdate2.getUplinkFrequency(getChannel2()); + } + + return 0; } @Override diff --git a/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java index 0873f1e9..42be5c6b 100644 --- a/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java @@ -1,19 +1,35 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.data; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.tsbk.GroupChannelGrant; import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; public class GroupDataChannelGrant extends GroupChannelGrant { - public GroupDataChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + public GroupDataChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } @Override diff --git a/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java index 1753e030..f8aec423 100644 --- a/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java @@ -1,151 +1,178 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.data; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData implements IdentifierReceiver { - public static final int AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR = 88; - public static final int REQUESTED_ACCESS_AVAILABLE_INDICATOR = 89; - - public static final int[] TRANSMIT_IDENTIFIER = { 96,97,98,99 }; - public static final int[] TRANSMIT_NUMBER = { 100,101,102,103,104,105,106, 107,108,109,110,111 }; - public static final int[] RECEIVE_IDENTIFIER = { 112,113,114,115 }; - public static final int[] RECEIVE_NUMBER = { 116,117,118,119,120,121,122, 123,124,125,126,127 }; - public static final int[] DATA_ACCESS_CONTROL = { 128,129,130,131,132,133, 134,135,136,137,138,139,140,141,142,143 }; - + public static final int AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR = 88; + public static final int REQUESTED_ACCESS_AVAILABLE_INDICATOR = 89; + public static final int[] TRANSMIT_IDENTIFIER = {96, 97, 98, 99}; + public static final int[] TRANSMIT_NUMBER = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}; + public static final int[] RECEIVE_IDENTIFIER = {112, 113, 114, 115}; + public static final int[] RECEIVE_NUMBER = {116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + public static final int[] DATA_ACCESS_CONTROL = {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdateTransmit; private IBandIdentifier mIdentifierUpdateReceive; - public SNDCPDataChannelAnnouncementExplicit( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + public SNDCPDataChannelAnnouncementExplicit(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } - + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) + + sb.append(getMessageStub()); + + if(isEmergency()) { - sb.append( " EMERGENCY" ); + sb.append(" EMERGENCY"); } - - if( isAutonomousAccessAvailable() ) + + if(isAutonomousAccessAvailable()) { - sb.append( " AUTONOMOUS ACCESS" ); + sb.append(" AUTONOMOUS ACCESS"); } - - if( isRequestedAccessAvailable() ) + + if(isRequestedAccessAvailable()) { - sb.append( " DOWNLINK:" ); - sb.append( getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - - sb.append( " " + getUplinkFrequency() ); - - sb.append( " UPLINK:" ); - sb.append( getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - - sb.append( " " + getDownlinkFrequency() ); - - sb.append( " DATA ACCESS CONTROL:" ); + sb.append(" DOWNLINK:"); + sb.append(getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + + sb.append(" " + getUplinkFrequency()); + + sb.append(" UPLINK:"); + sb.append(getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + + sb.append(" " + getDownlinkFrequency()); + + sb.append(" DATA ACCESS CONTROL:"); } else { - sb.append( " ACCESS DENIED TO DATA ACCESS CONTROL:" ); + sb.append(" ACCESS DENIED TO DATA ACCESS CONTROL:"); } - - sb.append( getDataAccessControl() ); - + + sb.append(getDataAccessControl()); + return sb.toString(); } - + public boolean isAutonomousAccessAvailable() { - return mMessage.get( AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR ); + return mMessage.get(AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR); } - + public boolean isRequestedAccessAvailable() { - return mMessage.get( REQUESTED_ACCESS_AVAILABLE_INDICATOR ); + return mMessage.get(REQUESTED_ACCESS_AVAILABLE_INDICATOR); } - + public int getTransmitChannelIdentifier() { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); + return mMessage.getInt(TRANSMIT_IDENTIFIER); } - + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_NUMBER ); + return mMessage.getInt(TRANSMIT_NUMBER); } - + public String getTransmitChannel() { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); } - + public int getReceiveChannelIdentifier() { - return mMessage.getInt( RECEIVE_IDENTIFIER ); + return mMessage.getInt(RECEIVE_IDENTIFIER); } - + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_NUMBER ); + return mMessage.getInt(RECEIVE_NUMBER); } - + public String getReceiveChannel() { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } - + public String getDataAccessControl() { - return mMessage.getHex( DATA_ACCESS_CONTROL, 4 ); - } - - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) - { - if( identifier == getTransmitChannelIdentifier() ) - { - mIdentifierUpdateTransmit = message; - } - else if( identifier == getReceiveChannelIdentifier() ) - { - mIdentifierUpdateReceive = message; - } - else - { - throw new IllegalArgumentException( "unexpected channel band " - + "identifier [" + identifier + "]" ); - } + return mMessage.getHex(DATA_ACCESS_CONTROL, 4); } - @Override + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) + { + if(identifier == getTransmitChannelIdentifier()) + { + mIdentifierUpdateTransmit = message; + } + else if(identifier == getReceiveChannelIdentifier()) + { + mIdentifierUpdateReceive = message; + } + else + { + throw new IllegalArgumentException("unexpected channel band " + + "identifier [" + identifier + "]"); + } + } + + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitChannelIdentifier(); - identifiers[ 1 ] = getReceiveChannelIdentifier(); + int[] identifiers = new int[2]; - return identifiers; + identifiers[0] = getTransmitChannelIdentifier(); + identifiers[1] = getReceiveChannelIdentifier(); + + return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdateTransmit, getTransmitChannelNumber() ); + if(mIdentifierUpdateTransmit != null) + { + return mIdentifierUpdateTransmit.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdateReceive, getReceiveChannelNumber() ); + if(mIdentifierUpdateReceive != null) + { + mIdentifierUpdateReceive.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } diff --git a/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java index 88589309..31e62211 100644 --- a/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java @@ -1,131 +1,164 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * + ******************************************************************************/ package module.decode.p25.message.tsbk.osp.data; +import alias.AliasList; +import bits.BinaryMessage; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.DataUnitID; -import alias.AliasList; -import bits.BinaryMessage; public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierReceiver { - public static final int[] TRANSMIT_IDENTIFIER = { 88,89,90,91 }; - public static final int[] TRANSMIT_NUMBER = { 92,93,94,95,96,97,98,99,100, - 101,102,103 }; - public static final int[] RECEIVE_IDENTIFIER = { 104,105,106,107 }; - public static final int[] RECEIVE_NUMBER = { 108,109,110,111,112,113,114, - 115,116,117,118,119 }; - - public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126, - 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 }; - + public static final int[] TRANSMIT_IDENTIFIER = {88, 89, 90, 91}; + public static final int[] TRANSMIT_NUMBER = {92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103}; + public static final int[] RECEIVE_IDENTIFIER = {104, 105, 106, 107}; + public static final int[] RECEIVE_NUMBER = {108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; + public static final int[] TARGET_ADDRESS = {120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}; + private IBandIdentifier mIdentifierUpdateTransmit; private IBandIdentifier mIdentifierUpdateReceive; - public SNDCPDataChannelGrant( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) + public SNDCPDataChannelGrant(BinaryMessage message, DataUnitID duid, AliasList aliasList) { - super( message, duid, aliasList ); + super(message, duid, aliasList); } - + + public boolean isTDMAChannel() + { + if(mIdentifierUpdateTransmit != null) + { + return mIdentifierUpdateTransmit.isTDMA(); + } + + return false; + } + public String getMessage() { StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - if( isEmergency() ) - { - sb.append( " EMERGENCY" ); - } - - sb.append( " UPLINK:" ); - sb.append( getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber() ); - - sb.append( " " + getUplinkFrequency() ); - - sb.append( " DOWNLINK:" ); - sb.append( getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber() ); - - sb.append( " " + getDownlinkFrequency() ); - sb.append( " TGT:" ); - sb.append( getTargetAddress() ); - + sb.append(getMessageStub()); + + if(isEmergency()) + { + sb.append(" EMERGENCY"); + } + + sb.append(" UPLINK:"); + sb.append(getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber()); + + sb.append(" " + getUplinkFrequency()); + + sb.append(" DOWNLINK:"); + sb.append(getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber()); + + sb.append(" " + getDownlinkFrequency()); + + sb.append(" TGT:"); + sb.append(getTargetAddress()); + return sb.toString(); } - + public int getTransmitChannelIdentifier() { - return mMessage.getInt( TRANSMIT_IDENTIFIER ); + return mMessage.getInt(TRANSMIT_IDENTIFIER); } - + public int getTransmitChannelNumber() { - return mMessage.getInt( TRANSMIT_NUMBER ); + return mMessage.getInt(TRANSMIT_NUMBER); } - + public String getTransmitChannel() { - return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); + return getTransmitChannelIdentifier() + "-" + getTransmitChannelNumber(); } public int getReceiveChannelIdentifier() { - return mMessage.getInt( RECEIVE_IDENTIFIER ); + return mMessage.getInt(RECEIVE_IDENTIFIER); } - + public int getReceiveChannelNumber() { - return mMessage.getInt( RECEIVE_NUMBER ); + return mMessage.getInt(RECEIVE_NUMBER); } - + public String getReceiveChannel() { - return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); + return getReceiveChannelIdentifier() + "-" + getReceiveChannelNumber(); } public String getTargetAddress() { - return mMessage.getHex( TARGET_ADDRESS, 6 ); + return mMessage.getHex(TARGET_ADDRESS, 6); } - @Override - public void setIdentifierMessage( int identifier, IBandIdentifier message ) + @Override + public void setIdentifierMessage(int identifier, IBandIdentifier message) { - if( identifier == getTransmitChannelIdentifier() ) - { - mIdentifierUpdateTransmit = message; - } - else if( identifier == getReceiveChannelIdentifier() ) - { - mIdentifierUpdateReceive = message; - } - else - { - throw new IllegalArgumentException( "unexpected channel band " - + "identifier [" + identifier + "]" ); - } + if(identifier == getTransmitChannelIdentifier()) + { + mIdentifierUpdateTransmit = message; + } + else if(identifier == getReceiveChannelIdentifier()) + { + mIdentifierUpdateReceive = message; + } + else + { + throw new IllegalArgumentException("unexpected channel band " + + "identifier [" + identifier + "]"); + } } - @Override + @Override public int[] getIdentifiers() { - int[] identifiers = new int[ 2 ]; - - identifiers[ 0 ] = getTransmitChannelIdentifier(); - identifiers[ 1 ] = getReceiveChannelIdentifier(); + int[] identifiers = new int[2]; - return identifiers; + identifiers[0] = getTransmitChannelIdentifier(); + identifiers[1] = getReceiveChannelIdentifier(); + + return identifiers; } public long getDownlinkFrequency() { - return calculateDownlink( mIdentifierUpdateTransmit, getTransmitChannelNumber() ); + if(mIdentifierUpdateTransmit != null) + { + return mIdentifierUpdateTransmit.getDownlinkFrequency(getTransmitChannelNumber()); + } + + return 0; } - + public long getUplinkFrequency() { - return calculateUplink( mIdentifierUpdateReceive, getReceiveChannelNumber() ); + if(mIdentifierUpdateReceive != null) + { + return mIdentifierUpdateReceive.getUplinkFrequency(getReceiveChannelNumber()); + } + + return 0; } } From cd6025133781924b74e8632f539c14d0e8d5f07f Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 12 Feb 2017 10:59:06 -0500 Subject: [PATCH 19/20] Resolves #173 - duplicate streaming calls for patch groups. --- src/alias/Alias.java | 25 +---------- src/alias/PatchGroupAlias.java | 57 +++++++++++++++++++++---- src/audio/broadcast/BroadcastModel.java | 4 +- src/channel/metadata/Metadata.java | 40 ++++------------- 4 files changed, 60 insertions(+), 66 deletions(-) diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 0747e72d..8f8b5ba5 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -185,27 +185,6 @@ public class Alias */ public void validate() { - //Check for duplicate broadcast channels - List broadcastChannels = getBroadcastChannels(); - - if(broadcastChannels.size() > 0) - { - Set channelNames = new TreeSet<>(); - - for(BroadcastChannel broadcastChannel: broadcastChannels) - { - String channelName = broadcastChannel.getChannelName(); - - if(channelName == null || channelName.isEmpty() || channelNames.contains(channelName)) - { - removeAliasID(broadcastChannel); - } - else - { - channelNames.add(channelName); - } - } - } } /** * Returns the priority level of this alias, if defined, or the default priority @@ -336,9 +315,9 @@ public class Alias * List of broadcast channels specified for this alias. */ @XmlTransient - public List getBroadcastChannels() + public Set getBroadcastChannels() { - List broadcastChannels = new ArrayList<>(); + Set broadcastChannels = new TreeSet<>(); for(AliasID id: getId()) { diff --git a/src/alias/PatchGroupAlias.java b/src/alias/PatchGroupAlias.java index 5831d9ea..bb7e347a 100644 --- a/src/alias/PatchGroupAlias.java +++ b/src/alias/PatchGroupAlias.java @@ -24,12 +24,20 @@ import alias.id.AliasIDType; import alias.id.broadcast.BroadcastChannel; import alias.id.priority.Priority; import alias.id.talkgroup.TalkgroupID; +import gui.SDRTrunk; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import playlist.version1.System; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import java.util.TreeSet; public class PatchGroupAlias extends Alias { + private final static Logger mLog = LoggerFactory.getLogger(PatchGroupAlias.class); + private Alias mPatchGroupAlias; private List mPatchedAliases = new ArrayList<>(); private List mPatchedTalkgroupIDs = new ArrayList<>(); @@ -295,9 +303,9 @@ public class PatchGroupAlias extends Alias } @Override - public List getBroadcastChannels() + public Set getBroadcastChannels() { - List broadcastChannels = new ArrayList<>(); + Set broadcastChannels = new TreeSet<>(); if(hasPatchGroupAlias()) { @@ -308,13 +316,7 @@ public class PatchGroupAlias extends Alias { if(!(alias instanceof PatchGroupAlias)) { - for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels()) - { - if(!broadcastChannels.contains(broadcastChannel)) - { - broadcastChannels.add(broadcastChannel); - } - } + broadcastChannels.addAll(alias.getBroadcastChannels()); } } @@ -339,4 +341,41 @@ public class PatchGroupAlias extends Alias return false; } + + public static void main(String[] args) + { + Alias a1 = new Alias("Alias 1"); + a1.addAliasID(new BroadcastChannel("Channel 1")); + a1.addAliasID(new BroadcastChannel("Channel 2")); + a1.addAliasID(new BroadcastChannel("Channel 3")); + a1.addAliasID(new BroadcastChannel("Channel 4")); + + Alias a2 = new Alias("Alias 1"); + a1.addAliasID(new BroadcastChannel("Channel 1")); + a1.addAliasID(new BroadcastChannel("Channel 2")); + a1.addAliasID(new BroadcastChannel("Channel 3")); + a1.addAliasID(new BroadcastChannel("Channel 4")); + + Alias a3 = new Alias("Alias 1"); + a1.addAliasID(new BroadcastChannel("Channel 1")); + a1.addAliasID(new BroadcastChannel("Channel 2")); + a1.addAliasID(new BroadcastChannel("Channel 3")); + a1.addAliasID(new BroadcastChannel("Channel 4")); + + Alias a4 = new Alias("Alias 1"); + a1.addAliasID(new BroadcastChannel("Channel 1")); + a1.addAliasID(new BroadcastChannel("Channel 2")); + a1.addAliasID(new BroadcastChannel("Channel 3")); + a1.addAliasID(new BroadcastChannel("Channel 4")); + + PatchGroupAlias pga = new PatchGroupAlias(); + pga.setPatchGroupAlias(a1); + pga.addPatchedAlias(a2); + pga.addPatchedAlias(a3); + pga.addPatchedAlias(a4); + + Set bc = pga.getBroadcastChannels(); + + mLog.debug("Channels:" + bc); + } } diff --git a/src/audio/broadcast/BroadcastModel.java b/src/audio/broadcast/BroadcastModel.java index fa9708ed..04f64f76 100644 --- a/src/audio/broadcast/BroadcastModel.java +++ b/src/audio/broadcast/BroadcastModel.java @@ -707,9 +707,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener