Merge pull request #122 from DSheirer/#90-channel_processing_list_and_gui_reformat

#90 channel processing list and gui reformat
This commit is contained in:
Denny Sheirer
2017-01-22 06:51:30 -05:00
committed by GitHub
119 changed files with 13065 additions and 15112 deletions

View File

@@ -8,6 +8,7 @@
<w>icecast</w>
<w>ident</w>
<w>polyphase</w>
<w>remez</w>
<w>samplerate</w>
<w>sdrtrunk</w>
<w>sheirer</w>

View File

@@ -117,9 +117,9 @@ public class Alias
mColor = color;
}
public Color getMapColor()
public Color getDisplayColor()
{
return new Color( mColor );
return new Color( getColor() );
}
@XmlAttribute

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,6 +14,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package alias;
@@ -29,7 +30,6 @@ import alias.id.siteID.SiteID;
import alias.id.status.StatusID;
import alias.id.talkgroup.TalkgroupID;
import alias.id.uniqueID.UniqueID;
import audio.metadata.MetadataType;
import module.decode.lj1200.LJ1200Message.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,16 +48,16 @@ public class AliasList implements Listener<AliasEvent>
public static final String WILDCARD = "*";
public static final String REGEX_WILDCARD = ".";
private Map<String, Alias> mESN = new HashMap<>();
private Map<String, Alias> mFleetsync = new HashMap<>();
private Map<LoJackFunctionAndID, Alias> mLoJack = new HashMap<>();
private Map<String, Alias> mMDC1200 = new HashMap<>();
private Map<String, Alias> mMobileID = new HashMap<>();
private Map<String, Alias> mMPT1327 = new HashMap<>();
private Map<String, Alias> mSiteID = new HashMap<>();
private Map<Integer, Alias> mStatus = new HashMap<>();
private Map<String, Alias> mTalkgroup = new HashMap<>();
private Map<Integer, Alias> mUniqueID = new HashMap<>();
private Map<String,Alias> mESN = new HashMap<>();
private Map<String,Alias> mFleetsync = new HashMap<>();
private Map<LoJackFunctionAndID,Alias> mLoJack = new HashMap<>();
private Map<String,Alias> mMDC1200 = new HashMap<>();
private Map<String,Alias> mMobileID = new HashMap<>();
private Map<String,Alias> mMPT1327 = new HashMap<>();
private Map<String,Alias> mSiteID = new HashMap<>();
private Map<Integer,Alias> mStatus = new HashMap<>();
private Map<String,Alias> mTalkgroup = new HashMap<>();
private Map<Integer,Alias> mUniqueID = new HashMap<>();
private List<WildcardID> mESNWildcards = new ArrayList<>();
private List<WildcardID> mMobileIDWildcards = new ArrayList<>();
@@ -85,9 +85,9 @@ public class AliasList implements Listener<AliasEvent>
*/
public void addAlias(Alias alias)
{
if (alias != null)
if(alias != null)
{
for (AliasID aliasID : alias.getId())
for(AliasID aliasID : alias.getId())
{
addAliasID(aliasID, alias);
}
@@ -99,18 +99,18 @@ public class AliasList implements Listener<AliasEvent>
*/
private void addAliasID(AliasID id, Alias alias)
{
if (id.isValid())
if(id.isValid())
{
try
{
switch (id.getType())
switch(id.getType())
{
case ESN:
String esn = ((Esn)id).getEsn();
String esn = ((Esn) id).getEsn();
if(esn != null)
{
if (esn.contains(WILDCARD))
if(esn.contains(WILDCARD))
{
mESNWildcards.add(new WildcardID(esn));
Collections.sort(mESNWildcards);
@@ -120,11 +120,11 @@ public class AliasList implements Listener<AliasEvent>
}
break;
case FLEETSYNC:
String fleetsync = ((FleetsyncID)id).getIdent();
String fleetsync = ((FleetsyncID) id).getIdent();
if(fleetsync != null)
{
if (fleetsync.contains(WILDCARD))
if(fleetsync.contains(WILDCARD))
{
mFleetsyncWildcards.add(new WildcardID(fleetsync));
Collections.sort(mFleetsyncWildcards);
@@ -137,11 +137,11 @@ public class AliasList implements Listener<AliasEvent>
mLoJack.put((LoJackFunctionAndID) id, alias);
break;
case MDC1200:
String mdc = ((MDC1200ID)id).getIdent();
String mdc = ((MDC1200ID) id).getIdent();
if(mdc != null)
{
if (mdc.contains(WILDCARD))
if(mdc.contains(WILDCARD))
{
mMDC1200Wildcards.add(new WildcardID(mdc));
Collections.sort(mMDC1200Wildcards);
@@ -151,11 +151,11 @@ public class AliasList implements Listener<AliasEvent>
}
break;
case MPT1327:
String mpt = ((MPT1327ID)id).getIdent();
String mpt = ((MPT1327ID) id).getIdent();
if(mpt != null)
{
if (mpt.contains(WILDCARD))
if(mpt.contains(WILDCARD))
{
mMPT1327Wildcards.add(new WildcardID(mpt));
Collections.sort(mMPT1327Wildcards);
@@ -165,11 +165,11 @@ public class AliasList implements Listener<AliasEvent>
}
break;
case MIN:
String min = ((Min)id).getMin();
String min = ((Min) id).getMin();
if(min != null)
{
if (min.contains(WILDCARD))
if(min.contains(WILDCARD))
{
mMobileIDWildcards.add(new WildcardID(min));
Collections.sort(mMobileIDWildcards);
@@ -179,14 +179,14 @@ public class AliasList implements Listener<AliasEvent>
}
break;
case LTR_NET_UID:
mUniqueID.put(((UniqueID)id).getUid(), alias);
mUniqueID.put(((UniqueID) id).getUid(), alias);
break;
case SITE:
String siteID = ((SiteID)id).getSite();
String siteID = ((SiteID) id).getSite();
if(siteID != null)
{
if (siteID.contains(WILDCARD))
if(siteID.contains(WILDCARD))
{
mSiteWildcards.add(new WildcardID(siteID));
Collections.sort(mSiteWildcards);
@@ -199,11 +199,11 @@ public class AliasList implements Listener<AliasEvent>
mStatus.put(((StatusID) id).getStatus(), alias);
break;
case TALKGROUP:
String tgid = ((TalkgroupID)id).getTalkgroup();
String tgid = ((TalkgroupID) id).getTalkgroup();
if(tgid != null)
{
if (tgid.contains(WILDCARD))
if(tgid.contains(WILDCARD))
{
mTalkgroupWildcards.add(new WildcardID(tgid));
Collections.sort(mTalkgroupWildcards);
@@ -222,7 +222,7 @@ public class AliasList implements Listener<AliasEvent>
break;
}
}
catch (Exception e)
catch(Exception e)
{
mLog.error("Couldn't add alias ID " + id + " for alias " + alias);
}
@@ -234,9 +234,9 @@ public class AliasList implements Listener<AliasEvent>
*/
public void removeAlias(Alias alias)
{
if (alias != null)
if(alias != null)
{
for (AliasID aliasID : alias.getId())
for(AliasID aliasID : alias.getId())
{
removeAliasID(aliasID, alias);
}
@@ -247,7 +247,7 @@ public class AliasList implements Listener<AliasEvent>
{
if(value != null)
{
for(WildcardID wildcardID: wildcards)
for(WildcardID wildcardID : wildcards)
{
if(wildcardID.value().equals(value))
{
@@ -262,12 +262,12 @@ public class AliasList implements Listener<AliasEvent>
*/
private void removeAliasID(AliasID id, Alias alias)
{
if (id.isValid())
if(id.isValid())
{
switch (id.getType())
switch(id.getType())
{
case ESN:
String esn = ((Esn)id).getEsn();
String esn = ((Esn) id).getEsn();
if(esn != null)
{
@@ -280,13 +280,13 @@ public class AliasList implements Listener<AliasEvent>
mESN.remove(esn);
break;
case FLEETSYNC:
String fleetsync = ((FleetsyncID)id).getIdent();
String fleetsync = ((FleetsyncID) id).getIdent();
if(fleetsync != null)
{
if(fleetsync.contains(WILDCARD))
{
removeWildcard(fleetsync,mFleetsyncWildcards);
removeWildcard(fleetsync, mFleetsyncWildcards);
}
mFleetsync.remove(fleetsync);
@@ -296,7 +296,7 @@ public class AliasList implements Listener<AliasEvent>
mLoJack.remove((LoJackFunctionAndID) id);
break;
case MDC1200:
String mdc = ((MDC1200ID)id).getIdent();
String mdc = ((MDC1200ID) id).getIdent();
if(mdc != null)
{
@@ -309,7 +309,7 @@ public class AliasList implements Listener<AliasEvent>
}
break;
case MPT1327:
String mpt = ((MPT1327ID)id).getIdent();
String mpt = ((MPT1327ID) id).getIdent();
if(mpt != null)
{
@@ -322,7 +322,7 @@ public class AliasList implements Listener<AliasEvent>
}
break;
case MIN:
String min = ((Min)id).getMin();
String min = ((Min) id).getMin();
if(min != null)
{
@@ -344,7 +344,7 @@ public class AliasList implements Listener<AliasEvent>
mStatus.remove(((StatusID) id).getStatus());
break;
case TALKGROUP:
String tgid = ((TalkgroupID)id).getTalkgroup();
String tgid = ((TalkgroupID) id).getTalkgroup();
if(tgid != null)
{
@@ -378,11 +378,11 @@ public class AliasList implements Listener<AliasEvent>
*/
private String getWildcardMatch(String id, List<WildcardID> wildcards)
{
if (id != null)
if(id != null)
{
for (WildcardID wildcard : wildcards)
for(WildcardID wildcard : wildcards)
{
if (wildcard.matches(id))
if(wildcard.matches(id))
{
return wildcard.value();
}
@@ -399,15 +399,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (siteID != null)
if(siteID != null)
{
alias = mSiteID.get(siteID);
if (alias == null)
if(alias == null)
{
String wildcard = getWildcardMatch(siteID, mSiteWildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mSiteID.get(wildcard);
}
@@ -440,15 +440,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (esn != null)
if(esn != null)
{
alias = mESN.get(esn);
if (alias == null)
if(alias == null)
{
String wildcard = getWildcardMatch(esn, mESNWildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mESN.get(wildcard);
}
@@ -465,15 +465,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (ident != null)
if(ident != null)
{
alias = mFleetsync.get(ident);
if (alias == null)
if(alias == null)
{
String wildcard = getWildcardMatch(ident, mFleetsyncWildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mFleetsync.get(wildcard);
}
@@ -488,11 +488,11 @@ public class AliasList implements Listener<AliasEvent>
*/
public Alias getLoJackAlias(Function function, String id)
{
if (id != null)
if(id != null)
{
for (LoJackFunctionAndID lojack : mLoJack.keySet())
for(LoJackFunctionAndID lojack : mLoJack.keySet())
{
if (lojack.matches(function, id))
if(lojack.matches(function, id))
{
return mLoJack.get(lojack);
}
@@ -509,15 +509,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (ident != null)
if(ident != null)
{
alias = mMDC1200.get(ident);
if (alias == null)
if(alias == null)
{
String wildcard = getWildcardMatch(ident, mMDC1200Wildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mMDC1200.get(wildcard);
}
@@ -534,15 +534,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (ident != null)
if(ident != null)
{
alias = mMPT1327.get(ident);
if (alias == null)
if(alias == null)
{
String wildcard = getWildcardMatch(ident, mMPT1327Wildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mMPT1327.get(wildcard);
}
@@ -559,15 +559,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (ident != null)
if(ident != null)
{
alias = mMobileID.get(ident);
if (alias == null)
if(alias == null)
{
String wildcard = getWildcardMatch(ident, mMobileIDWildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mMobileID.get(wildcard);
}
@@ -588,15 +588,15 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = null;
if (tgid != null)
if(tgid != null)
{
alias = mTalkgroup.get(tgid);
if (alias == null && includeWildcards)
if(alias == null && includeWildcards)
{
String wildcard = getWildcardMatch(tgid, mTalkgroupWildcards);
if (wildcard != null)
if(wildcard != null)
{
alias = mTalkgroup.get(wildcard);
}
@@ -614,9 +614,9 @@ public class AliasList implements Listener<AliasEvent>
return getTalkgroupAlias(tgid, true);
}
/**
* Alias list name
*/
/**
* Alias list name
*/
public String toString()
{
return mName;
@@ -631,61 +631,6 @@ public class AliasList implements Listener<AliasEvent>
return mName;
}
/**
* Lookup an alias by metadata type and string value
*/
public Alias getAlias(String value, MetadataType type)
{
switch (type)
{
case ESN:
return getESNAlias(value);
case FLEETSYNC:
return getFleetsyncAlias(value);
case MDC1200:
return getMDC1200Alias(value);
case MOBILE_ID:
return getMobileIDNumberAlias(value);
case MPT1327:
return getMPT1327Alias(value);
case SITE_ID:
return getSiteID(value);
case STATUS:
if (value != null)
{
try
{
return getStatus(Integer.valueOf(value));
}
catch (Exception e)
{
//do nothing, we couldn't parse the int value
}
}
break;
case FROM:
case TO:
return getTalkgroupAlias(value);
case UNIQUE_ID:
if (value != null)
{
try
{
return getUniqueID(Integer.valueOf(value));
}
catch (Exception e)
{
//do nothing, we couldn't parse the int value
}
}
break;
default:
break;
}
return null;
}
/**
* Receive alias change event notifications and modify this list accordingly
*/
@@ -694,22 +639,22 @@ public class AliasList implements Listener<AliasEvent>
{
Alias alias = event.getAlias();
switch (event.getEvent())
switch(event.getEvent())
{
case ADD:
if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
{
addAlias(alias);
}
break;
case CHANGE:
if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
{
addAlias(alias);
}
break;
case DELETE:
if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
{
removeAlias(alias);
}

View File

@@ -148,7 +148,7 @@ public class AliasNameEditor extends Editor<Alias>
Color newColor = JColorChooser.showDialog(
AliasNameEditor.this,
"Choose color for this alias",
(hasItem() ? getItem().getMapColor() : null));
(hasItem() ? getItem().getDisplayColor() : null));
if(newColor != null)
{
@@ -264,7 +264,7 @@ public class AliasNameEditor extends Editor<Alias>
mGroupCombo.setSelectedItem(alias.getGroup());
Color color = alias.getMapColor();
Color color = alias.getDisplayColor();
mButtonColor.setBackground(color);
mButtonColor.setForeground(color);

View File

@@ -47,6 +47,17 @@ public class PatchGroupAlias extends Alias
{
}
@Override
public int getColor()
{
if(mPatchGroupAlias != null)
{
return mPatchGroupAlias.getColor();
}
return super.getColor();
}
/**
* Sets an alias for this patch group ID
*/

View File

@@ -19,12 +19,13 @@
package audio;
import alias.Alias;
import audio.metadata.AudioMetadata;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import audio.output.AudioOutput;
import channel.metadata.Metadata;
import icon.IconManager;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import sample.Listener;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
@@ -39,24 +40,29 @@ public class AudioChannelPanel extends JPanel
implements Listener<AudioEvent>, SettingChangeListener
{
private static final long serialVersionUID = 1L;
private static final Logger mLog = LoggerFactory.getLogger(AudioChannelPanel.class);
private Font mFont = new Font(Font.MONOSPACED, Font.PLAIN, 12);
public static final String PROPERTY_PREFIX = "audio.channel.panel.color.";
public static final String PROPERTY_COLOR_BACKGROUND = PROPERTY_PREFIX + "background";
public static final String PROPERTY_COLOR_LABEL = PROPERTY_PREFIX + "label";
public static final String PROPERTY_COLOR_MUTED = PROPERTY_PREFIX + "muted";
public static final String PROPERTY_COLOR_VALUE = PROPERTY_PREFIX + "value";
private Font mFont = new Font(Font.MONOSPACED, Font.PLAIN, 16);
private Color mBackgroundColor;
private Color mLabelColor;
private Color mDetailsColor;
private Color mMutedColor;
private Color mValueColor;
private IconManager mIconManager;
private SettingsManager mSettingsManager;
private AudioOutput mAudioOutput;
private JLabel mMutedLabel = new JLabel("M");
private JLabel mChannelName = new JLabel(" ");
private JLabel mToLabel = new JLabel("TO:");
private JLabel mTo = new JLabel("");
private JLabel mToAlias = new JLabel("");
private JLabel mMutedLabel = new JLabel(" ");
private JLabel mFromLabel = new JLabel("FROM:");
private JLabel mFrom = new JLabel("");
private JLabel mFromAlias = new JLabel("");
private JLabel mToAlias = new JLabel(" ");
private JLabel mTo = new JLabel("-----");
private boolean mConfigured = false;
@@ -71,69 +77,39 @@ public class AudioChannelPanel extends JPanel
if(mAudioOutput != null)
{
mAudioOutput.addAudioEventListener(this);
mAudioOutput.setAudioMetadataListener(new AudioMetadataProcessor());
mAudioOutput.setMetadataListener(new AudioMetadataProcessor());
}
mLabelColor = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DECODER).getColor();
mDetailsColor = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS).getColor();
mBackgroundColor = SystemProperties.getInstance().get(PROPERTY_COLOR_BACKGROUND, Color.BLACK);
mLabelColor = SystemProperties.getInstance().get(PROPERTY_COLOR_LABEL, Color.LIGHT_GRAY);
mMutedColor = SystemProperties.getInstance().get(PROPERTY_COLOR_MUTED, Color.RED);
mValueColor = SystemProperties.getInstance().get(PROPERTY_COLOR_VALUE, Color.GREEN);
init();
}
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0", "[][right][][grow,fill]", "[]0[]0[grow,fill]"));
setBackground(Color.BLACK);
setLayout(new MigLayout("align center center, insets 0 0 0 0", "[][][align right]0[grow,fill]", ""));
setBackground(mBackgroundColor);
mMutedLabel.setFont(mFont);
mMutedLabel.setForeground(mMutedColor);
mMutedLabel.setVisible(false);
add(mMutedLabel);
mChannelName = new JLabel(mAudioOutput != null ? mAudioOutput.getChannelName() : " ");
mChannelName.setFont(mFont);
mChannelName.setForeground(mDetailsColor);
mChannelName.setForeground(mLabelColor);
add(mChannelName);
mToLabel.setFont(mFont);
if(mAudioOutput != null)
{
mToLabel.setForeground(mLabelColor);
}
else
{
mToLabel.setForeground(getBackground());
}
add(mToLabel);
mToAlias.setFont(mFont);
mToAlias.setForeground(mValueColor);
add(mToAlias);
mTo.setFont(mFont);
mTo.setForeground(mLabelColor);
mTo.setForeground(mValueColor);
add(mTo);
mToAlias.setFont(mFont);
mToAlias.setForeground(mLabelColor);
add(mToAlias, "wrap");
mMutedLabel.setFont(mFont);
mMutedLabel.setForeground(Color.RED);
add(mMutedLabel);
mFromLabel.setFont(mFont);
if(mAudioOutput != null)
{
mFromLabel.setForeground(mLabelColor);
}
else
{
mFromLabel.setForeground(getBackground());
}
add(mFromLabel);
mFrom.setFont(mFont);
mFrom.setForeground(mLabelColor);
add(mFrom);
mFromAlias.setFont(mFont);
mFromAlias.setForeground(mLabelColor);
add(mFromAlias, "wrap");
}
@Override
@@ -158,7 +134,7 @@ public class AudioChannelPanel extends JPanel
@Override
public void run()
{
mMutedLabel.setText(mAudioOutput.isMuted() ? "M" : " ");
mMutedLabel.setVisible(mAudioOutput.isMuted());
}
});
break;
@@ -173,11 +149,7 @@ public class AudioChannelPanel extends JPanel
*/
private void resetLabels()
{
mFrom.setText("");
updateAlias(mFromAlias, null);
mTo.setText("");
updateAlias(mToAlias, null);
updateLabel(mTo, null, mToAlias, null);
mConfigured = false;
}
@@ -187,27 +159,26 @@ public class AudioChannelPanel extends JPanel
* does not occur on the Swing event thread -- wrap any calls to this
* method with an event thread call.
*/
private void updateAlias(JLabel label, Alias alias)
private void updateLabel(JLabel valueLabel, String value, JLabel aliasLabel, Alias alias)
{
if(alias != null)
if(value != null)
{
label.setText(alias.getName());
String icon = alias.getIconName();
if(icon != null)
if(alias != null)
{
label.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE));
valueLabel.setText(alias.getName());
aliasLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18));
}
else
{
label.setIcon(null);
valueLabel.setText(value);
aliasLabel.setIcon(null);
}
}
else
{
label.setText("");
label.setIcon(null);
valueLabel.setText("-----");
aliasLabel.setIcon(null);
}
}
@@ -215,45 +186,25 @@ public class AudioChannelPanel extends JPanel
/**
* Processes audio metadata to update this panel's display values
*/
public class AudioMetadataProcessor implements Listener<AudioMetadata>
public class AudioMetadataProcessor implements Listener<Metadata>
{
@Override
public void receive(AudioMetadata audioMetadata)
public void receive(final Metadata metadata)
{
if(!mConfigured || audioMetadata.isUpdated())
if(metadata.isUpdated() || !mConfigured)
{
final Metadata from = audioMetadata.getMetadata(MetadataType.FROM);
final Metadata to = audioMetadata.getMetadata(MetadataType.TO);
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
if(from != null)
{
mFrom.setText(from.getValue());
updateAlias(mFromAlias, from.getAlias());
}
else
{
mFrom.setText("-----");
}
updateLabel(mTo, metadata.getPrimaryAddressTo().getIdentifier(),
mToAlias, metadata.getPrimaryAddressTo().getAlias());
if(to != null)
{
mTo.setText(to.getValue());
updateAlias(mToAlias, to.getAlias());
}
else
{
mTo.setText("-----");
}
mConfigured = true;
}
});
mConfigured = true;
}
}
}
@@ -269,14 +220,6 @@ public class AudioChannelPanel extends JPanel
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mFrom != null)
{
mFrom.setForeground(mLabelColor);
}
if(mFromAlias != null)
{
mFromAlias.setForeground(mLabelColor);
}
if(mTo != null)
{
mTo.setForeground(mLabelColor);
@@ -286,16 +229,6 @@ public class AudioChannelPanel extends JPanel
mToAlias.setForeground(mLabelColor);
}
break;
case CHANNEL_STATE_LABEL_DETAILS:
if(mFromLabel != null)
{
mFromLabel.setForeground(mDetailsColor);
}
if(mToLabel != null)
{
mToLabel.setForeground(mDetailsColor);
}
break;
default:
break;
}

View File

@@ -33,15 +33,18 @@ public class AudioChannelsPanel extends JPanel
public AudioChannelsPanel(IconManager iconManager, SettingsManager settingsManager, IAudioController controller)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]"));
// setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill][][grow,fill]", "[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0", "[][sg,grow,fill][][sg,grow,fill]", "[grow,fill]"));
setBackground(Color.BLACK);
List<AudioOutput> outputs = controller.getAudioOutputs();
addSeparator();
for(int x = 0; x < outputs.size(); x++)
{
add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x)), "wrap");
add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x)), "growx");
if(x < outputs.size() - 1)
{
@@ -54,14 +57,14 @@ public class AudioChannelsPanel extends JPanel
if(outputs.size() == 1)
{
addSeparator();
add(new AudioChannelPanel(iconManager, settingsManager, null), "wrap");
add(new AudioChannelPanel(iconManager, settingsManager, null), "growx");
}
}
private void addSeparator()
{
JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
JSeparator separator = new JSeparator(JSeparator.VERTICAL);
separator.setBackground(Color.DARK_GRAY);
add(separator, "span,growx");
add(separator);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,38 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package audio;
import java.util.Arrays;
import java.util.concurrent.ScheduledExecutorService;
import audio.squelch.ISquelchStateListener;
import audio.squelch.SquelchState;
import channel.metadata.Metadata;
import dsp.filter.design.FilterDesignException;
import dsp.filter.fir.FIRFilterSpecification;
import dsp.filter.fir.remez.RemezFIRFilterDesigner;
import dsp.filter.polyphase.PolyphaseFIRDecimatingFilter_RB;
import module.Module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.IFilteredRealBufferListener;
import sample.real.RealBuffer;
import audio.metadata.AudioMetadata;
import audio.metadata.IMetadataListener;
import audio.metadata.Metadata;
import audio.squelch.ISquelchStateListener;
import audio.squelch.SquelchState;
import controller.channel.ChannelEvent;
import controller.channel.IChannelEventListener;
import dsp.filter.Filters;
import dsp.filter.fir.real.RealFIRFilter_R_R;
import java.util.concurrent.ScheduledExecutorService;
/**
* Provides packaging of demodulated audio sample buffers into audio packets for
@@ -33,12 +42,8 @@ import dsp.filter.fir.real.RealFIRFilter_R_R;
* Incorporates audio squelch state listener to control if audio packets are
* broadcast or ignored.
*/
public class AudioModule extends Module implements IAudioPacketProvider,
IChannelEventListener,
IMetadataListener,
IFilteredRealBufferListener,
ISquelchStateListener,
Listener<RealBuffer>
public class AudioModule extends Module implements IAudioPacketProvider, IFilteredRealBufferListener,
ISquelchStateListener, Listener<RealBuffer>
{
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
@@ -48,48 +53,41 @@ public class AudioModule extends Module implements IAudioPacketProvider,
{
//Band-pass filter to both decimate (48kHz to 8kHz) and block 0 - 300 Hz (LTR signalling).
FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
.sampleRate( 48000 )
.stopFrequency1( 150 )
.passFrequencyBegin( 300 )
.passFrequencyEnd( 3500 )
.stopFrequency2( 4000 )
.stopRipple( 0.0004 )
.passRipple( 0.008)
.build();
.sampleRate(48000)
.stopFrequency1(150)
.passFrequencyBegin(300)
.passFrequencyEnd(3500)
.stopFrequency2(4000)
.stopRipple(0.0004)
.passRipple(0.008)
.build();
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
try
{
if (!designer.isValid())
if(!designer.isValid())
{
throw new FilterDesignException("Couldn't design the audio decimation filter");
}
mDecimationCoefficients = designer.getImpulseResponse();
}
catch (FilterDesignException e)
catch(FilterDesignException e)
{
mLog.debug("Error designing filter", e);
}
}
/* Provides a unique identifier for this audio module instance to use as a
* source identifier for all audio packets */
private static int UNIQUE_ID = 0;
private int mSourceID;
private AudioMetadata mAudioMetadata;
private ChannelEventListener mChannelEventListener = new ChannelEventListener();
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
private SquelchState mSquelchState = SquelchState.SQUELCH;
private Listener<AudioPacket> mAudioPacketListener;
private PolyphaseFIRDecimatingFilter_RB mAudioDecimationFilter;
private Metadata mMetadata;
public AudioModule(boolean record)
public AudioModule(Metadata metadata)
{
mSourceID = ++UNIQUE_ID;
mAudioMetadata = new AudioMetadata(mSourceID, record);
mMetadata = metadata;
mAudioDecimationFilter = new PolyphaseFIRDecimatingFilter_RB(mDecimationCoefficients, 6, 2.0f);
mAudioDecimationFilter.setListener(new Listener<RealBuffer>()
@@ -97,9 +95,9 @@ public class AudioModule extends Module implements IAudioPacketProvider,
@Override
public void receive(RealBuffer realBuffer)
{
if (mAudioPacketListener != null)
if(mAudioPacketListener != null)
{
AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mAudioMetadata.copyOf());
AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mMetadata.copyOf());
mAudioPacketListener.receive(packet);
}
}
@@ -116,7 +114,6 @@ public class AudioModule extends Module implements IAudioPacketProvider,
@Override
public void reset()
{
mAudioMetadata.reset();
}
@Override
@@ -128,11 +125,10 @@ public class AudioModule extends Module implements IAudioPacketProvider,
@Override
public void stop()
{
/* Issue an end audio packet in case a recorder is still rolling */
if (mAudioPacketListener != null)
/* Issue an end audio packet in case a recorder is still rolling */
if(mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END,
mAudioMetadata.copyOf()));
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
}
}
@@ -143,15 +139,9 @@ public class AudioModule extends Module implements IAudioPacketProvider,
@Override
public void receive(RealBuffer buffer)
{
if (mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
{
mAudioDecimationFilter.receive(buffer);
// /* We make a copy of the samples, so that we don't affect any other
// * processes that might be concurrently processing the same buffer */
// float[] audio = Arrays.copyOf( buffer.getSamples(), buffer.getSamples().length );
//
// mAudioFilter.filter( audio );
}
}
@@ -161,12 +151,6 @@ public class AudioModule extends Module implements IAudioPacketProvider,
return this;
}
@Override
public Listener<Metadata> getMetadataListener()
{
return mAudioMetadata;
}
@Override
public void setAudioPacketListener(Listener<AudioPacket> listener)
{
@@ -179,41 +163,12 @@ public class AudioModule extends Module implements IAudioPacketProvider,
mAudioPacketListener = null;
}
@Override
public Listener<ChannelEvent> getChannelEventListener()
{
return mChannelEventListener;
}
@Override
public Listener<SquelchState> getSquelchStateListener()
{
return mSquelchStateListener;
}
/**
* Wrapper for channel event listener. Responds to channel state reset
* events to remove/cleanup current audio metadata
*/
public class ChannelEventListener implements Listener<ChannelEvent>
{
@Override
public void receive(ChannelEvent event)
{
switch (event.getEvent())
{
case NOTIFICATION_STATE_RESET:
mAudioMetadata.reset();
break;
case NOTIFICATION_SELECTION_CHANGE:
mAudioMetadata.setSelected(event.getChannel().isSelected());
break;
default:
break;
}
}
}
/**
* Wrapper for squelch state listener
*/
@@ -222,10 +177,9 @@ public class AudioModule extends Module implements IAudioPacketProvider,
@Override
public void receive(SquelchState state)
{
if (state == SquelchState.SQUELCH && mAudioPacketListener != null)
if(state == SquelchState.SQUELCH && mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END,
mAudioMetadata.copyOf()));
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
}
mSquelchState = state;

View File

@@ -1,77 +1,78 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package audio;
import channel.metadata.Metadata;
import sample.real.RealBuffer;
import audio.metadata.AudioMetadata;
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
******************************************************************************/
public class AudioPacket
{
private Type mType;
private RealBuffer mAudioData;
private AudioMetadata mAudioMetadata;
public AudioPacket( Type type, AudioMetadata metadata )
{
mType = type;
mAudioMetadata = metadata;
}
public AudioPacket( float[] audio, AudioMetadata metadata )
{
this( Type.AUDIO, metadata );
private Type mType;
private RealBuffer mAudioData;
private Metadata mMetadata;
mAudioData = new RealBuffer( audio );
}
public boolean hasAudioMetadata()
{
return mAudioMetadata != null;
}
public AudioMetadata getAudioMetadata()
{
return mAudioMetadata;
}
public void setMetadata( AudioMetadata metadata )
{
mAudioMetadata = metadata;
}
public Type getType()
{
return mType;
}
public RealBuffer getAudioBuffer()
{
return mAudioData;
}
public boolean hasAudioBuffer()
{
return mAudioData != null;
}
public enum Type
{
AUDIO,
END;
}
public AudioPacket(Type type, Metadata metadata)
{
mType = type;
mMetadata = metadata;
}
public AudioPacket(float[] audio, Metadata metadata)
{
this(Type.AUDIO, metadata);
mAudioData = new RealBuffer(audio);
}
public boolean hasMetadata()
{
return mMetadata != null;
}
public Metadata getMetadata()
{
return mMetadata;
}
public void setMetadata(Metadata metadata)
{
mMetadata = metadata;
}
public Type getType()
{
return mType;
}
public RealBuffer getAudioBuffer()
{
return mAudioData;
}
public boolean hasAudioBuffer()
{
return mAudioData != null;
}
public enum Type
{
AUDIO,
END;
}
}

View File

@@ -45,9 +45,9 @@ public class AudioPanel extends JPanel implements Listener<AudioEvent>
private static final Logger mLog = LoggerFactory.getLogger(AudioPanel.class);
private static ImageIcon MUTED_ICON = IconManager.getScaledIcon(
new ImageIcon("images/audio_muted.png"), 38);
new ImageIcon("images/audio_muted.png"), 20);
private static ImageIcon UNMUTED_ICON = IconManager.getScaledIcon(
new ImageIcon("images/audio_unmuted.png"), 38);
new ImageIcon("images/audio_unmuted.png"), 20);
private IconManager mIconManager;
private SettingsManager mSettingsManager;

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2016 Dennis Sheirer
* 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
@@ -18,14 +18,11 @@
******************************************************************************/
package audio.broadcast;
import audio.AudioPacket;
import audio.IAudioPacketListener;
import audio.convert.ISilenceGenerator;
import audio.metadata.AudioMetadata;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import sample.Listener;
import java.io.ByteArrayInputStream;
@@ -34,14 +31,13 @@ import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AudioBroadcaster implements Listener<AudioRecording>
{
private final static Logger mLog = LoggerFactory.getLogger( AudioBroadcaster.class );
private final static Logger mLog = LoggerFactory.getLogger(AudioBroadcaster.class);
public static final int PROCESSOR_RUN_INTERVAL_MS = 1000;
@@ -107,9 +103,10 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
/**
* Broadcasts the next song's audio metadata prior to streaming the next song.
*
* @param metadata for the next recording that will be streamed
*/
protected void broadcastMetadata(AudioMetadata metadata)
protected void broadcastMetadata(Metadata metadata)
{
IBroadcastMetadataUpdater metadataUpdater = getMetadataUpdater();
@@ -136,7 +133,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
if(mThreadPoolManager != null)
{
mScheduledTask = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING,
mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS );
mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS);
}
}
}
@@ -160,6 +157,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
/**
* Stream name for the broadcast configuration for this broadcaster
*
* @return stream name or null
*/
public String getStreamName()
@@ -246,7 +244,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
{
if(mBroadcastState != state)
{
mLog.info("[" + getStreamName() + "] status: " + state);
mLog.info("[" + getStreamName() + "] status: " + state);
mBroadcastState = state;
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE));
@@ -261,8 +259,15 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
//Remove all pending audio recordings
while(!mAudioRecordingQueue.isEmpty())
{
AudioRecording recording = mAudioRecordingQueue.get(0);
recording.removePendingReplay();
try
{
AudioRecording recording = mAudioRecordingQueue.get(0);
recording.removePendingReplay();
}
catch(Exception e)
{
//Ignore
}
}
}
}
@@ -386,7 +391,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
{
mStreamedAudioCount++;
broadcast(new BroadcastEvent(AudioBroadcaster.this,
BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE));
BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE));
metadataUpdateRequired = true;
}
@@ -431,7 +436,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
if(connected())
{
broadcastMetadata(nextRecording.getAudioMetadata());
broadcastMetadata(nextRecording.getMetadata());
}
metadataUpdateRequired = false;

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2016 Dennis Sheirer
* 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
@@ -18,7 +18,7 @@
******************************************************************************/
package audio.broadcast;
import audio.metadata.AudioMetadata;
import channel.metadata.Metadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,22 +30,23 @@ public class AudioRecording implements Comparable<AudioRecording>
private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
private Path mPath;
private AudioMetadata mAudioMetadata;
private long mStartTime;
private long mRecordingLength;
private AtomicInteger mPendingReplayCount = new AtomicInteger();
private Metadata mMetadata;
/**
* Audio recording that is ready to be streamed
*
* @param path to the audio recording file
* @param audioMetadata associated with the recording
* @param metadata associated with the recording
* @param start time of recording in milliseconds since epoch
* @param recordingLength in milliseconds
*/
public AudioRecording(Path path, AudioMetadata audioMetadata, long start, long recordingLength)
public AudioRecording(Path path, Metadata metadata, long start, long recordingLength)
{
mPath = path;
mAudioMetadata = audioMetadata;
mMetadata = metadata;
mStartTime = start;
mRecordingLength = recordingLength;
}
@@ -61,9 +62,9 @@ public class AudioRecording implements Comparable<AudioRecording>
/**
* Optional audio metadata for the recording.
*/
public AudioMetadata getAudioMetadata()
public Metadata getMetadata()
{
return mAudioMetadata;
return mMetadata;
}
/**

View File

@@ -20,7 +20,7 @@ package audio.broadcast;
import alias.id.broadcast.BroadcastChannel;
import audio.AudioPacket;
import audio.metadata.AudioMetadata;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import icon.IconManager;
import org.slf4j.Logger;
@@ -48,7 +48,7 @@ import java.util.regex.Pattern;
public class BroadcastModel extends AbstractTableModel implements Listener<AudioPacket>
{
private final static Logger mLog = LoggerFactory.getLogger( BroadcastModel.class );
private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class);
public static final String TEMPORARY_STREAM_DIRECTORY = "streaming";
public static final String TEMPORARY_STREAM_FILE_SUFFIX = "temporary_streaming_file_";
@@ -84,7 +84,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
//Monitor to remove temporary recording files that have been streamed by all audio broadcasters
mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_RECORDING,
new RecordingDeletionMonitor(), 15l, TimeUnit.SECONDS );
new RecordingDeletionMonitor(), 15l, TimeUnit.SECONDS);
removeOrphanedTemporaryRecordings();
}
@@ -96,7 +96,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
{
List<String> names = new ArrayList<>();
for(BroadcastConfiguration configuration: mBroadcastConfigurations)
for(BroadcastConfiguration configuration : mBroadcastConfigurations)
{
names.add(configuration.getName());
}
@@ -117,7 +117,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
*/
public void addBroadcastConfigurations(List<BroadcastConfiguration> configurations)
{
for(BroadcastConfiguration configuration: configurations)
for(BroadcastConfiguration configuration : configurations)
{
addBroadcastConfiguration(configuration);
}
@@ -138,11 +138,11 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
int index = mBroadcastConfigurations.size() - 1;
fireTableRowsInserted( index, index );
fireTableRowsInserted(index, index);
mBroadcastConfigurationMap.put(configuration.getName(), configuration);
process( new BroadcastEvent( configuration, BroadcastEvent.Event.CONFIGURATION_ADD) );
process(new BroadcastEvent(configuration, BroadcastEvent.Event.CONFIGURATION_ADD));
}
}
}
@@ -164,6 +164,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
/**
* Updates the configuration's name so that it is unique among all other broadcast configurations
*
* @param configuration
*/
private void ensureUniqueName(BroadcastConfiguration configuration)
@@ -235,11 +236,11 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
return false;
}
for(BroadcastConfiguration configurationToCompare: mBroadcastConfigurations)
for(BroadcastConfiguration configurationToCompare : mBroadcastConfigurations)
{
if(configurationToCompare != configuration &&
configurationToCompare.getName() != null &&
configurationToCompare.getName().equals(name))
configurationToCompare.getName() != null &&
configurationToCompare.getName().equals(name))
{
return false;
}
@@ -258,9 +259,9 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
mBroadcastConfigurationMap.remove(broadcastConfiguration.getName());
process(new BroadcastEvent( broadcastConfiguration, BroadcastEvent.Event.CONFIGURATION_DELETE));
process(new BroadcastEvent(broadcastConfiguration, BroadcastEvent.Event.CONFIGURATION_DELETE));
fireTableRowsDeleted( index, index );
fireTableRowsDeleted(index, index);
}
}
@@ -275,8 +276,8 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
@Override
public void receive(AudioPacket audioPacket)
{
if (audioPacket.hasAudioMetadata() &&
audioPacket.getAudioMetadata().isStreamable())
if(audioPacket.hasMetadata() &&
audioPacket.getMetadata().isStreamable())
{
mStreamManager.receive(audioPacket);
}
@@ -287,7 +288,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
*/
private void createBroadcaster(BroadcastConfiguration broadcastConfiguration)
{
if (broadcastConfiguration != null &&
if(broadcastConfiguration != null &&
broadcastConfiguration.isEnabled() &&
broadcastConfiguration.isValid() &&
!mBroadcasterMap.containsKey(broadcastConfiguration.getName()))
@@ -295,7 +296,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
AudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(mThreadPoolManager,
broadcastConfiguration);
if (audioBroadcaster != null)
if(audioBroadcaster != null)
{
audioBroadcaster.setListener(new Listener<BroadcastEvent>()
{
@@ -377,7 +378,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
*/
private void broadcast(BroadcastEvent event)
{
mBroadcastEventBroadcaster.broadcast( event );
mBroadcastEventBroadcaster.broadcast(event);
}
/**
@@ -395,7 +396,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
case CONFIGURATION_CHANGE:
BroadcastConfiguration broadcastConfiguration = broadcastEvent.getBroadcastConfiguration();
int index = mBroadcastConfigurations.indexOf(broadcastConfiguration);
fireTableRowsUpdated( index, index );
fireTableRowsUpdated(index, index);
//Delete and recreate the broadcaster for any broadcast configuration changes
String previousChannelName = cleanupMapAssociations(broadcastConfiguration);
@@ -452,7 +453,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
{
String oldName = null;
for(Map.Entry<String,BroadcastConfiguration> entry: mBroadcastConfigurationMap.entrySet())
for(Map.Entry<String,BroadcastConfiguration> entry : mBroadcastConfigurationMap.entrySet())
{
if(entry.getValue() == broadcastConfiguration)
{
@@ -488,7 +489,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
{
try
{
if( rowIndex <= mBroadcastConfigurations.size())
if(rowIndex <= mBroadcastConfigurations.size())
{
BroadcastConfiguration configuration = mBroadcastConfigurations.get(rowIndex);
@@ -584,7 +585,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
switch(column)
{
case COLUMN_SERVER_ICON:
return null;
return "Streaming";
case COLUMN_STREAM_NAME:
return "Name";
case COLUMN_BROADCASTER_STATUS:
@@ -686,19 +687,19 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
@Override
public void receive(AudioRecording audioRecording)
{
AudioMetadata metadata = audioRecording.getAudioMetadata();
Metadata metadata = audioRecording.getMetadata();
if(metadata != null && metadata.isStreamable())
{
for (BroadcastChannel broadcastChannel : metadata.getBroadcastChannels())
for(BroadcastChannel broadcastChannel : metadata.getBroadcastChannels())
{
String channelName = broadcastChannel.getChannelName();
if (channelName != null)
if(channelName != null)
{
AudioBroadcaster audioBroadcaster = getBroadcaster(channelName);
if (audioBroadcaster != null)
if(audioBroadcaster != null)
{
audioRecording.addPendingReplay();
audioBroadcaster.receive(audioRecording);
@@ -739,7 +740,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
if(!recordingsToDelete.isEmpty())
{
for(AudioRecording recordingToDelete: recordingsToDelete)
for(AudioRecording recordingToDelete : recordingsToDelete)
{
mRecordingQueue.remove(recordingToDelete);
removeRecording(recordingToDelete);

View File

@@ -50,7 +50,7 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
private AliasModel mAliasModel;
private IconManager mIconManager;
private JTable mBroadcastConfigurationTable;
private BroadcastStatusPanel mBroadcastStatusPanel;
private JideSplitPane mSplitPane;
private Editor<BroadcastConfiguration> mEmptyEditor = new EmptyEditor<>("Configuration:");
private Editor<BroadcastConfiguration> mEditor;
@@ -72,17 +72,11 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
{
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
mBroadcastConfigurationTable = new JTable(mBroadcastModel);
mBroadcastConfigurationTable.setAutoCreateRowSorter(true);
mBroadcastConfigurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mBroadcastConfigurationTable.getSelectionModel().addListSelectionListener(BroadcastPanel.this);
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
mBroadcastConfigurationTable.getColumnModel().getColumn(BroadcastModel.COLUMN_SERVER_ICON).setPreferredWidth(1);
mBroadcastConfigurationTable.getColumnModel().getColumn(BroadcastModel.COLUMN_STREAM_NAME).setPreferredWidth(25);
mBroadcastConfigurationTable.getColumnModel().getColumn(BroadcastModel.COLUMN_BROADCASTER_STATUS)
.setCellRenderer(new StatusCellRenderer());
JScrollPane scroller = new JScrollPane(mBroadcastConfigurationTable);
mBroadcastStatusPanel.getTable().setAutoCreateRowSorter(true);
mBroadcastStatusPanel.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mBroadcastStatusPanel.getTable().getSelectionModel().addListSelectionListener(BroadcastPanel.this);
JPanel buttonsPanel = new JPanel();
buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]"));
@@ -122,7 +116,7 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
listAndButtonsPanel.add(scroller, "wrap");
listAndButtonsPanel.add(mBroadcastStatusPanel, "wrap");
listAndButtonsPanel.add(buttonsPanel);
mSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
@@ -168,11 +162,11 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
*/
private BroadcastConfiguration getSelectedBroadcastConfiguration()
{
int viewRow = mBroadcastConfigurationTable.getSelectedRow();
int viewRow = mBroadcastStatusPanel.getTable().getSelectedRow();
if(viewRow >= 0)
{
int modelRow = mBroadcastConfigurationTable.convertRowIndexToModel(viewRow);
int modelRow = mBroadcastStatusPanel.getTable().convertRowIndexToModel(viewRow);
if(modelRow >= 0)
{
@@ -255,75 +249,12 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
mBroadcastModel.addBroadcastConfiguration(configuration);
int modelRow = mBroadcastModel.getRowForConfiguration(configuration);
int tableRow = mBroadcastConfigurationTable.convertRowIndexToView(modelRow);
mBroadcastConfigurationTable.changeSelection(tableRow, 0, false, false);
int tableRow = mBroadcastStatusPanel.getTable().convertRowIndexToView(modelRow);
mBroadcastStatusPanel.getTable().changeSelection(tableRow, 0, false, false);
}
}
});
}
}
/**
* Custom cell renderer for the broadcast state column.
*/
public class StatusCellRenderer extends DefaultTableCellRenderer
{
public StatusCellRenderer()
{
setOpaque(true);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column)
{
JLabel component = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(isSelected)
{
setBackground(table.getSelectionBackground());
setForeground(table.getSelectionForeground());
}
else
{
if(value instanceof BroadcastState)
{
BroadcastState state = (BroadcastState)value;
if(state == BroadcastState.CONNECTED)
{
setBackground(Color.GREEN);
setForeground(table.getForeground());
}
else if(state == BroadcastState.DISABLED)
{
setBackground(table.getBackground());
setForeground(Color.LIGHT_GRAY);
}
else if(state == BroadcastState.INVALID_SETTINGS)
{
setBackground(Color.YELLOW);
setForeground(table.getForeground());
}
else if(state.isErrorState())
{
setBackground(Color.RED);
setForeground(table.getForeground());
}
else
{
setBackground(table.getBackground());
setForeground(table.getForeground());
}
}
else
{
setForeground(table.getForeground());
setBackground(table.getBackground());
}
}
return this;
}
}
}

View File

@@ -0,0 +1,125 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package audio.broadcast;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
public class BroadcastStatusPanel extends JPanel
{
private JTable mTable;
private JScrollPane mScrollPane;
private BroadcastModel mBroadcastModel;
public BroadcastStatusPanel(BroadcastModel broadcastModel)
{
mBroadcastModel = broadcastModel;
init();
}
public JTable getTable()
{
return mTable;
}
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
mTable = new JTable(mBroadcastModel);
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)mTable.getDefaultRenderer(String.class);
renderer.setHorizontalAlignment(SwingConstants.CENTER);
mTable.getColumnModel().getColumn(BroadcastModel.COLUMN_BROADCASTER_STATUS).setCellRenderer(new StatusCellRenderer());
mScrollPane = new JScrollPane(mTable);
add(mScrollPane);
}
/**
* Custom cell renderer for the broadcast state column.
*/
public class StatusCellRenderer extends DefaultTableCellRenderer
{
public StatusCellRenderer()
{
setOpaque(true);
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column)
{
JLabel component = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(isSelected)
{
setBackground(table.getSelectionBackground());
setForeground(table.getSelectionForeground());
}
else
{
if(value instanceof BroadcastState)
{
BroadcastState state = (BroadcastState)value;
if(state == BroadcastState.CONNECTED)
{
setBackground(Color.GREEN);
setForeground(table.getForeground());
}
else if(state == BroadcastState.DISABLED)
{
setBackground(table.getBackground());
setForeground(Color.LIGHT_GRAY);
}
else if(state == BroadcastState.INVALID_SETTINGS)
{
setBackground(Color.YELLOW);
setForeground(table.getForeground());
}
else if(state.isErrorState())
{
setBackground(Color.RED);
setForeground(table.getForeground());
}
else
{
setBackground(table.getBackground());
setForeground(table.getForeground());
}
}
else
{
setForeground(table.getForeground());
setBackground(table.getBackground());
}
}
return this;
}
}
}

View File

@@ -18,7 +18,7 @@
******************************************************************************/
package audio.broadcast;
import audio.metadata.AudioMetadata;
import channel.metadata.Metadata;
public interface IBroadcastMetadataUpdater
{
@@ -27,5 +27,5 @@ public interface IBroadcastMetadataUpdater
*
* @param metadata containing metadata attributes
*/
void update(AudioMetadata metadata);
void update(Metadata metadata);
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2016 Dennis Sheirer
* 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
@@ -48,7 +48,7 @@ public class StreamManager implements Listener<AudioPacket>
private Listener<AudioRecording> mAudioRecordingListener;
private BroadcastFormat mBroadcastFormat;
private Path mTempDirectory;
private Map<Integer, AudioRecorder> mStreamRecorders = new HashMap<>();
private Map<Integer,AudioRecorder> mStreamRecorders = new HashMap<>();
private Runnable mRecorderMonitor;
private ScheduledFuture<?> mRecorderMonitorFuture;
private AtomicBoolean mRunning = new AtomicBoolean();
@@ -68,8 +68,8 @@ public class StreamManager implements Listener<AudioPacket>
public StreamManager(ThreadPoolManager threadPoolManager, Listener<AudioRecording> listener,
BroadcastFormat broadcastFormat, Path tempDirectory)
{
assert(tempDirectory != null && Files.isDirectory(tempDirectory));
assert(mThreadPoolManager != null);
assert (tempDirectory != null && Files.isDirectory(tempDirectory));
assert (mThreadPoolManager != null);
mThreadPoolManager = threadPoolManager;
mAudioRecordingListener = listener;
@@ -91,7 +91,7 @@ public class StreamManager implements Listener<AudioPacket>
}
mRecorderMonitorFuture = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING,
mRecorderMonitor, 2, TimeUnit.SECONDS);
mRecorderMonitor, 2, TimeUnit.SECONDS);
}
}
@@ -107,11 +107,11 @@ public class StreamManager implements Listener<AudioPacket>
mRecorderMonitorFuture.cancel(true);
}
synchronized (mStreamRecorders)
synchronized(mStreamRecorders)
{
List<Integer> streamKeys = new ArrayList<>(mStreamRecorders.keySet());
for(Integer streamKey: streamKeys)
for(Integer streamKey : streamKeys)
{
removeRecorder(streamKey);
}
@@ -122,19 +122,19 @@ public class StreamManager implements Listener<AudioPacket>
@Override
public void receive(AudioPacket audioPacket)
{
if(mRunning.get() && audioPacket.hasAudioMetadata())
if(mRunning.get() && audioPacket.hasMetadata())
{
synchronized (mStreamRecorders)
synchronized(mStreamRecorders)
{
int sourceChannelID = audioPacket.getAudioMetadata().getSource();
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
AudioPacket.Type type = audioPacket.getType();
if(type == AudioPacket.Type.AUDIO)
{
if(mStreamRecorders.containsKey(sourceChannelID))
if(mStreamRecorders.containsKey(channelMetadataID))
{
AudioRecorder recorder = mStreamRecorders.get(sourceChannelID);
AudioRecorder recorder = mStreamRecorders.get(channelMetadataID);
if(recorder != null)
{
@@ -144,15 +144,15 @@ public class StreamManager implements Listener<AudioPacket>
else
{
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
mBroadcastFormat);
mBroadcastFormat);
recorder.start(mThreadPoolManager.getScheduledExecutorService());
recorder.receive(audioPacket);
mStreamRecorders.put(sourceChannelID, recorder);
mStreamRecorders.put(channelMetadataID, recorder);
}
}
else if(type == AudioPacket.Type.END)
{
removeRecorder(sourceChannelID);
removeRecorder(channelMetadataID);
}
else
{
@@ -203,8 +203,8 @@ public class StreamManager implements Listener<AudioPacket>
StringBuilder sb = new StringBuilder();
sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
sb.append(sNextRecordingNumber.incrementAndGet()).append("_");
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
sb.append( mBroadcastFormat.getFileExtension() );
sb.append(TimeStamp.getLongTimeStamp("_"));
sb.append(mBroadcastFormat.getFileExtension());
Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString());
@@ -219,19 +219,19 @@ public class StreamManager implements Listener<AudioPacket>
@Override
public void run()
{
synchronized (mStreamRecorders)
synchronized(mStreamRecorders)
{
long now = System.currentTimeMillis();
mStreamRecorders.entrySet().stream()
.filter(entry -> entry.getValue().getTimeRecordingStart() + MAXIMUM_RECORDER_LIFESPAN_MILLIS < now)
.forEach(entry ->
{
mLog.debug("Maximum recording time limit reached - removing recorder: " +
entry.getValue().getPath().toString());
{
mLog.debug("Maximum recording time limit reached - removing recorder: " +
entry.getValue().getPath().toString());
removeRecorder(entry.getKey());
});
removeRecorder(entry.getKey());
});
}
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2016 Dennis Sheirer
* 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
@@ -19,9 +19,7 @@
package audio.broadcast.icecast;
import audio.broadcast.IBroadcastMetadataUpdater;
import audio.metadata.AudioMetadata;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
@@ -58,7 +56,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
private NioSocketConnector mSocketConnector;
private AtomicBoolean mUpdating = new AtomicBoolean();
private Queue<String> mMetadataQueue = new LinkedTransferQueue<>();
private Map<String, String> mHTTPHeaders;
private Map<String,String> mHTTPHeaders;
private boolean mStackTraceLoggingSuppressed;
/**
@@ -78,7 +76,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
*/
private NioSocketConnector getSocketConnector()
{
if (mSocketConnector == null)
if(mSocketConnector == null)
{
mSocketConnector = new NioSocketConnector();
// mSocketConnector.getFilterChain().addLast("logger", new LoggingFilter(IcecastBroadcastMetadataUpdater.class));
@@ -116,19 +114,19 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
* and will remain in the update queue until the next metadata update is requested. However, this is a design
* trade-off to avoid having a scheduled runnable repeatedly processing the update queue.
*/
public void update(AudioMetadata audioMetadata)
public void update(Metadata metadata)
{
mMetadataQueue.offer(getSong(audioMetadata));
mMetadataQueue.offer(getSong(metadata));
if (mUpdating.compareAndSet(false, true))
if(mUpdating.compareAndSet(false, true))
{
String song = mMetadataQueue.poll();
while (song != null)
while(song != null)
{
HttpRequest updateRequest = createUpdateRequest(song);
if (updateRequest != null)
if(updateRequest != null)
{
mThreadPoolManager.scheduleOnce(new Runnable()
{
@@ -143,22 +141,22 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
connectFuture.awaitUninterruptibly();
IoSession session = connectFuture.getSession();
if (session != null)
if(session != null)
{
session.write(updateRequest);
}
}
catch (Exception e)
catch(Exception e)
{
Throwable throwableCause = e.getCause();
if (throwableCause instanceof ConnectException)
if(throwableCause instanceof ConnectException)
{
//Do nothing, the server is unavailable
}
else
{
if (!mStackTraceLoggingSuppressed)
if(!mStackTraceLoggingSuppressed)
{
mLog.error("Error sending metadata update. Future errors will " +
"be suppressed", e);
@@ -182,52 +180,45 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
/**
* Creates the song information for a metadata update
*/
private static String getSong(AudioMetadata audioMetadata)
private static String getSong(Metadata metadata)
{
StringBuilder sb = new StringBuilder();
if(audioMetadata != null)
if(metadata != null)
{
Metadata to = audioMetadata.getMetadata(MetadataType.TO);
String to = metadata.getPrimaryAddressTo().getIdentifier();
sb.append("TO:");
if(to != null)
if(metadata.getPrimaryAddressTo().hasAlias())
{
if(to.hasAlias())
{
sb.append(to.getAlias().getName());
}
else
{
sb.append(to.getValue());
}
sb.append(metadata.getPrimaryAddressTo().getAlias().getName());
}
else if(metadata.getPrimaryAddressTo().hasIdentifier())
{
sb.append(metadata.getPrimaryAddressTo().getIdentifier());
}
else
{
sb.append("UNKNOWN");
}
Metadata from = audioMetadata.getMetadata(MetadataType.FROM);
sb.append(" FROM:");
if(from != null)
if(metadata.getPrimaryAddressFrom().hasAlias())
{
if(from.hasAlias())
{
sb.append(from.getAlias().getName());
}
else
{
sb.append(from.getValue());
}
sb.append(metadata.getPrimaryAddressFrom().getAlias().getName());
}
else if(metadata.getPrimaryAddressFrom().hasIdentifier())
{
sb.append(metadata.getPrimaryAddressFrom().getIdentifier());
}
else
{
sb.append("UNKNOWN");
}
}
else
{
@@ -255,7 +246,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
return request;
}
catch (UnsupportedEncodingException e)
catch(UnsupportedEncodingException e)
{
//This should never happen
mLog.error("UTF-8 encoding is not supported - can't update song metadata");

View File

@@ -19,9 +19,7 @@
package audio.broadcast.shoutcast.v1;
import audio.broadcast.IBroadcastMetadataUpdater;
import audio.metadata.AudioMetadata;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
@@ -57,7 +55,6 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
private NioSocketConnector mSocketConnector;
private AtomicBoolean mUpdating = new AtomicBoolean();
private Queue<String> mMetadataQueue = new LinkedTransferQueue<>();
private Map<String, String> mHTTPHeaders;
private boolean mStackTraceLoggingSuppressed;
/**
@@ -77,7 +74,7 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
*/
private NioSocketConnector getSocketConnector()
{
if (mSocketConnector == null)
if(mSocketConnector == null)
{
mSocketConnector = new NioSocketConnector();
@@ -116,19 +113,19 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
* and will remain in the update queue until the next metadata update is requested. However, this is a design
* trade-off to avoid having a scheduled runnable repeatedly processing the update queue.
*/
public void update(AudioMetadata audioMetadata)
public void update(Metadata metadata)
{
mMetadataQueue.offer(getSong(audioMetadata));
mMetadataQueue.offer(getSong(metadata));
if (mUpdating.compareAndSet(false, true))
if(mUpdating.compareAndSet(false, true))
{
String song = mMetadataQueue.poll();
while (song != null)
while(song != null)
{
HttpRequest updateRequest = createUpdateRequest(song);
if (updateRequest != null)
if(updateRequest != null)
{
mThreadPoolManager.scheduleOnce(new Runnable()
{
@@ -143,22 +140,22 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
connectFuture.awaitUninterruptibly();
IoSession session = connectFuture.getSession();
if (session != null)
if(session != null)
{
session.write(updateRequest);
}
}
catch (Exception e)
catch(Exception e)
{
Throwable throwableCause = e.getCause();
if (throwableCause instanceof ConnectException)
if(throwableCause instanceof ConnectException)
{
//Do nothing, the server is unavailable
}
else
{
if (!mStackTraceLoggingSuppressed)
if(!mStackTraceLoggingSuppressed)
{
mLog.error("Error sending metadata update. Future errors will " +
"be suppressed", e);
@@ -182,52 +179,45 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
/**
* Creates the song information for a metadata update
*/
private static String getSong(AudioMetadata audioMetadata)
private static String getSong(Metadata metadata)
{
StringBuilder sb = new StringBuilder();
if(audioMetadata != null)
if(metadata != null)
{
Metadata to = audioMetadata.getMetadata(MetadataType.TO);
String to = metadata.getPrimaryAddressTo().getIdentifier();
sb.append("TO:");
if(to != null)
if(metadata.getPrimaryAddressTo().hasAlias())
{
if(to.hasAlias())
{
sb.append(to.getAlias().getName());
}
else
{
sb.append(to.getValue());
}
sb.append(metadata.getPrimaryAddressTo().getAlias().getName());
}
else if(metadata.getPrimaryAddressTo().hasIdentifier())
{
sb.append(metadata.getPrimaryAddressTo().getIdentifier());
}
else
{
sb.append("UNKNOWN");
}
Metadata from = audioMetadata.getMetadata(MetadataType.FROM);
sb.append(" FROM:");
if(from != null)
if(metadata.getPrimaryAddressFrom().hasAlias())
{
if(from.hasAlias())
{
sb.append(from.getAlias().getName());
}
else
{
sb.append(from.getValue());
}
sb.append(metadata.getPrimaryAddressFrom().getAlias().getName());
}
else if(metadata.getPrimaryAddressFrom().hasIdentifier())
{
sb.append(metadata.getPrimaryAddressFrom().getIdentifier());
}
else
{
sb.append("UNKNOWN");
}
}
else
{
@@ -256,7 +246,7 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
return request;
}
catch (UnsupportedEncodingException e)
catch(UnsupportedEncodingException e)
{
//This should never happen
mLog.error("UTF-8 encoding is not supported - can't update song metadata");

View File

@@ -37,9 +37,7 @@ import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageFactory;
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageType;
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMetadata;
import audio.broadcast.shoutcast.v2.ultravox.UltravoxProtocolFactory;
import audio.metadata.AudioMetadata;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.future.ConnectFuture;
@@ -62,7 +60,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBroadcastMetadataUpdater
{
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV2AudioBroadcaster.class );
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastV2AudioBroadcaster.class);
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
private int mMaxPayloadSize = 16377;
@@ -93,7 +91,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
public ShoutcastV2Configuration getConfiguration()
{
return (ShoutcastV2Configuration)getBroadcastConfiguration();
return (ShoutcastV2Configuration) getBroadcastConfiguration();
}
/**
@@ -122,7 +120,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
}
@Override
public void update(AudioMetadata metadata)
public void update(Metadata metadata)
{
List<UltravoxMessage> metadataMessages = getMetadataMessages(metadata);
@@ -149,7 +147,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
private boolean connect()
{
if(!connected() && canConnect() &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
mConnecting.compareAndSet(false, true))
{
mLastConnectionAttempt = System.currentTimeMillis();
@@ -242,7 +240,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
* @param metadata containing audio metadata tags and attributes
* @return a sequence of CacheableXMLMetadata messages sufficient to carry the complete set of metadata
*/
private List<UltravoxMessage> getMetadataMessages(AudioMetadata metadata)
private List<UltravoxMessage> getMetadataMessages(Metadata metadata)
{
StringBuilder sb = new StringBuilder();
@@ -264,30 +262,22 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
if(metadata != null)
{
Metadata to = metadata.getMetadata(MetadataType.TO);
if(to != null)
if(metadata.getPrimaryAddressTo().hasAlias())
{
if(to.hasAlias())
{
sb.append(UltravoxMetadata.TITLE_2.asXML(to.getAlias().toString()));
}
else
{
sb.append(UltravoxMetadata.TITLE_2.asXML(to.getValue()));
}
sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getAlias().getName()));
}
else if(metadata.getPrimaryAddressTo().hasIdentifier())
{
sb.append(UltravoxMetadata.TITLE_2.asXML(metadata.getPrimaryAddressTo().getIdentifier()));
}
Metadata from = metadata.getMetadata(MetadataType.FROM);
if(from != null)
if(metadata.getPrimaryAddressFrom().hasAlias())
{
if(from.hasAlias())
{
sb.append(UltravoxMetadata.TITLE_3.asXML("From: " + from.getAlias().toString()));
}
else
{
sb.append(UltravoxMetadata.TITLE_3.asXML("From: " + from.getValue()));
}
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
@@ -301,7 +291,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
int pointer = 0;
int messageCounter = 1;
int messageCount = (int)Math.ceil((double)xml.length / (double)(mMaxPayloadSize - 6));
int messageCount = (int) Math.ceil((double) xml.length / (double) (mMaxPayloadSize - 6));
if(messageCount > 32)
{
@@ -317,9 +307,9 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
byte[] payload = new byte[payloadSize + 6];
payload[1] = (byte)(0x01);
payload[3] = (byte)(messageCount & 0xFF);
payload[5] = (byte)(messageCounter & 0xFF);
payload[1] = (byte) (0x01);
payload[3] = (byte) (messageCount & 0xFF);
payload[5] = (byte) (messageCounter & 0xFF);
System.arraycopy(xml, pointer, payload, 6, payloadSize);
@@ -368,9 +358,9 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
if(cause instanceof IOException && ((IOException)cause).getMessage().startsWith("Connection reset"))
if(cause instanceof IOException && ((IOException) cause).getMessage().startsWith("Connection reset"))
{
IOException ioe = (IOException)cause;
IOException ioe = (IOException) cause;
if(ioe.getMessage() != null && ioe.getMessage().startsWith("Connection reset"))
{
@@ -398,12 +388,12 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
{
if(object instanceof UltravoxMessage)
{
UltravoxMessage ultravoxMessage = (UltravoxMessage)object;
UltravoxMessage ultravoxMessage = (UltravoxMessage) object;
switch (ultravoxMessage.getMessageType())
switch(ultravoxMessage.getMessageType())
{
case REQUEST_CIPHER:
String cipherKey = ((RequestCipher)ultravoxMessage).getCipher();
String cipherKey = ((RequestCipher) ultravoxMessage).getCipher();
AuthenticateBroadcast authenticateBroadcast = new AuthenticateBroadcast();
@@ -476,7 +466,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
}
else
{
mMaxPayloadSize = ((NegotiateMaxPayloadSize)ultravoxMessage).getMaximumPayloadSize();
mMaxPayloadSize = ((NegotiateMaxPayloadSize) ultravoxMessage).getMaximumPayloadSize();
ConfigureIcyPublic configureIcyPublic = new ConfigureIcyPublic();
configureIcyPublic.setPublic(getConfiguration().isPublic());

View File

@@ -1,357 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package audio.metadata;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import alias.id.broadcast.BroadcastChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import alias.Alias;
import alias.id.priority.Priority;
public class AudioMetadata implements Listener<Metadata>
{
protected static final Logger mLog = LoggerFactory.getLogger(AudioMetadata.class);
private int mSource;
private Map<MetadataType, Metadata> mMetadata = new HashMap<>();
private boolean mUpdated = false;
/* Channel Selected == highest priority */
private boolean mSelected = false;
/* Call Priority */
private int mPriority = Priority.DEFAULT_PRIORITY;
/* Indicates if the overall source is recordable */
private boolean mSourceRecordable = false;
/* Indicates the current recordable state */
private boolean mRecordable = false;
/* Indicates the current streamable state */
private boolean mStreamable = false;
/* A unique identifier for this metadata */
private String mIdentifier;
public AudioMetadata(int source, boolean recordable)
{
mSource = source;
setIdentifier(mSource);
mSourceRecordable = recordable;
setRecordable(mSourceRecordable);
}
/**
* Returns a unique identifier for this audio metadata that contains the
* source id and optionally the TO metadata string.
*/
public String getIdentifier()
{
return mIdentifier;
}
public void setIdentifier(int source)
{
setIdentifier(source, null);
}
public void setIdentifier(int source, String id)
{
mIdentifier = "SRC:" + source + " ID:" + (id == null ? "UNKNOWN" : id);
mUpdated = true;
}
/**
* Returns a copy of the current audio metadata.
*/
public AudioMetadata copyOf()
{
AudioMetadata copy = new AudioMetadata(mSource, mSourceRecordable);
copy.mPriority = mPriority;
copy.mSelected = mSelected;
copy.mRecordable = mRecordable;
copy.mStreamable = mStreamable;
copy.mMetadata.putAll(mMetadata);
copy.mUpdated = mUpdated;
copy.mIdentifier = new String(mIdentifier);
mUpdated = false;
return copy;
}
/**
* Removes any accumulated temporal metadata and returns to a default set.
*/
public void reset()
{
//Reset recordable state, identifier and audio call priority levels and reprocess the metadata
mRecordable = mSourceRecordable;
mStreamable = false;
setIdentifier(mSource);
mPriority = Priority.DEFAULT_PRIORITY;
List<MetadataType> toRemove = new ArrayList<>();
for (MetadataType type : mMetadata.keySet())
{
Metadata metadata = mMetadata.get(type);
if (metadata != null)
{
if (metadata.isTemporal())
{
toRemove.add(type);
}
else
{
processMetadata(metadata);
}
}
}
for (MetadataType type : toRemove)
{
mMetadata.remove(type);
}
mUpdated = true;
}
private void processMetadata(Metadata metadata)
{
if (metadata.hasAlias())
{
Alias alias = metadata.getAlias();
if (metadata.getMetadataType() == MetadataType.FROM || metadata.getMetadataType() == MetadataType.TO)
{
if (!alias.isRecordable())
{
mRecordable = false;
}
if (alias.isStreamable())
{
mStreamable = true;
}
}
if (metadata.getMetadataType() == MetadataType.TO)
{
setIdentifier(mSource, metadata.getValue());
}
if (alias != null && alias.getCallPriority() < mPriority)
{
mPriority = alias.getCallPriority();
}
}
}
/**
* Indicates if this audio metadata contains updated information. The flag
* will only be set when new data is added and will be reset by a copyOf()
* method invocation.
*/
public boolean isUpdated()
{
return mUpdated;
}
public void setUpdated(boolean updated)
{
mUpdated = updated;
}
/**
* Adds the metadata. Any temporal metadata will be removed when the
* dispose() method is invoked.
*/
@Override
public void receive(Metadata metadata)
{
if (metadata.isReset())
{
reset();
}
else
{
Metadata existing = mMetadata.get(metadata.getMetadataType());
if (existing == null || !existing.equals(metadata))
{
mMetadata.put(metadata.getMetadataType(), metadata);
processMetadata(metadata);
setUpdated(true);
}
}
mUpdated = true;
}
/**
* Returns the complete set of metadata
*/
public Map<MetadataType, Metadata> getMetadata()
{
return mMetadata;
}
/**
* Returns the metadata object matching the type or null
*/
public Metadata getMetadata(MetadataType type)
{
return mMetadata.get(type);
}
/**
* Source channel ID
*/
public void setSource(int source)
{
mSource = source;
mUpdated = true;
}
public int getSource()
{
return mSource;
}
/**
* Sets the priority of this audio packet within the defined min/max priority range
*/
public void setPriority(int priority)
{
mPriority = priority;
mUpdated = true;
}
/**
* Returns the priority of this audio metadata
*
* @return - priority of audio packet where lower numbers are higher priority
*/
public int getPriority()
{
return mPriority;
}
public boolean isDoNotMonitor()
{
return mPriority == Priority.DO_NOT_MONITOR;
}
/**
* Selected audio channel. A selected channel will override the priority
* level so that the audio can be listened to immediately.
*/
public void setSelected(boolean selected)
{
mSelected = selected;
mUpdated = true;
}
public boolean isSelected()
{
return mSelected;
}
/**
* Indicates if audio associated with this metadata is recordable.
*/
public boolean isRecordable()
{
return mRecordable;
}
public void setRecordable(boolean recordable)
{
mRecordable = recordable;
mUpdated = true;
}
/**
* Indicates if the audio associated with this metadata is streamable
*/
public boolean isStreamable()
{
return mStreamable;
}
public Collection<BroadcastChannel> getBroadcastChannels()
{
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
for(Metadata metadata: mMetadata.values())
{
if(metadata.hasAlias() && metadata.getAlias().isStreamable())
{
Alias alias = metadata.getAlias();
broadcastChannels.addAll(alias.getBroadcastChannels());
}
}
return broadcastChannels;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Audio Metadata - source:");
sb.append(getSource());
sb.append(" priority:");
sb.append(getPriority());
sb.append(" updated:" + isUpdated());
sb.append(" ");
for (Entry<MetadataType, Metadata> entry : getMetadata().entrySet())
{
sb.append(entry.getValue().getKey());
sb.append(":");
sb.append(entry.getValue().getValue());
sb.append(" ");
}
return sb.toString();
}
}

View File

@@ -1,8 +0,0 @@
package audio.metadata;
import sample.Listener;
public interface IMetadataListener
{
public Listener<Metadata> getMetadataListener();
}

View File

@@ -1,9 +0,0 @@
package audio.metadata;
import sample.Listener;
public interface IMetadataProvider
{
public void setMetadataListener( Listener<Metadata> listener );
public void removeMetadataListener();
}

View File

@@ -1,162 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package audio.metadata;
import alias.Alias;
public class Metadata
{
private MetadataType mMetadataType;
private String mValue;
private Alias mValueAlias;
private boolean mTemporal;
/**
* Constucts a metadata object with the temporal settings. Temporal
* indicates that this metadata will be removed at the end of a reset
* event, or call end event.
*/
public Metadata( MetadataType metadataType, String value, Alias alias, boolean temporal )
{
mMetadataType = metadataType;
mValue = value;
mValueAlias = alias;
mTemporal = temporal;
}
/**
* Constucts a metadata object with the temporal settings. Temporal
* indicates that this metadata will be removed at the end of a reset
* event, or call end event.
*/
public Metadata( MetadataType metadataType, String value, boolean temporal )
{
this( metadataType, value, null, temporal );
}
/**
* Constucts a non-temporal metadata object
*/
public Metadata( MetadataType metadataType, String value, Alias alias )
{
this( metadataType, value, alias, false );
}
/**
* Constucts a non-temporal metadata object
*/
public Metadata( MetadataType metadataType, String value )
{
this( metadataType, value, null, false );
}
public MetadataType getMetadataType()
{
return mMetadataType;
}
public String getKey()
{
return mMetadataType.getLabel();
}
public String getValue()
{
return mValue;
}
public Alias getAlias()
{
return mValueAlias;
}
public boolean hasAlias()
{
return mValueAlias != null;
}
/**
* Indicates that this piece of metadata is only valid for the duration of
* a call event and should be removed during a reset
*/
public boolean isTemporal()
{
return mTemporal;
}
public boolean isReset()
{
return false;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( mMetadataType.getLabel() );
sb.append( ":" );
sb.append( mValue );
sb.append( " Alias:" );
sb.append( mValueAlias == null ? "null" : getAlias().getName() );
sb.append( " [" );
sb.append( mMetadataType.name() );
sb.append( "] temporal:" );
sb.append( mTemporal );
return sb.toString();
}
public boolean equals( Metadata other )
{
if( mMetadataType != other.getMetadataType() )
{
return false;
}
if( mTemporal != other.isTemporal() )
{
return false;
}
if( mValue != null && other.getValue() != null &&
!mValue.contentEquals( other.getValue() ))
{
return false;
}
if( ( mValue == null && other.getValue() != null ) ||
( mValue != null && other.getValue() == null ) )
{
return false;
}
if( mValueAlias != null && other.getAlias() != null &&
!mValueAlias.getName().contentEquals( other.getAlias().getName() ))
{
return false;
}
if( ( mValueAlias == null && other.getAlias() != null ) ||
( mValueAlias != null && other.getAlias() == null ) )
{
return false;
}
return true;
}
}

View File

@@ -1,14 +0,0 @@
package audio.metadata;
public class MetadataReset extends Metadata
{
public MetadataReset()
{
super( MetadataType.TEMPORAL_RESET, "reset", true );
}
public boolean isReset()
{
return true;
}
}

View File

@@ -1,40 +0,0 @@
package audio.metadata;
public enum MetadataType
{
CHANNEL_NUMBER( "Channel Number" ),
CHANNEL_NAME( "Channel Name" ),
DETAILS( "Details" ),
ESN( "ESN" ),
FLEETSYNC( "Fleetsync" ),
FREQUENCY( "Frequency" ),
FROM( "From" ),
MDC1200( "MDC-1200" ),
MOBILE_ID( "Mobile ID" ),
MPT1327( "MPT-1327" ),
PROTOCOL( "Protocol" ),
SITE_ID( "Site ID" ),
STATUS( "Status" ),
SYSTEM( "System" ),
TO( "To" ),
UNIQUE_ID( "Unique ID" ),
TEMPORAL_RESET( "Reset" );
private String mLabel;
private MetadataType( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return mLabel;
}
}

View File

@@ -1,29 +1,34 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package audio.output;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import audio.AudioEvent;
import audio.AudioPacket;
import audio.AudioPacket.Type;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import source.mixer.MixerChannel;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.BooleanControl;
@@ -35,418 +40,412 @@ import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import source.mixer.MixerChannel;
import audio.AudioEvent;
import audio.AudioPacket;
import audio.AudioPacket.Type;
import audio.metadata.AudioMetadata;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
{
private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class );
private LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
private int mBufferStartThreshold;
private int mBufferStopThreshold;
private final static Logger mLog = LoggerFactory.getLogger(AudioOutput.class);
private Listener<AudioMetadata> mAudioMetadataListener;
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
private LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
private int mBufferStartThreshold;
private int mBufferStopThreshold;
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture<?> mProcessorTask;
private Listener<Metadata> mMetadataListener;
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
private SourceDataLine mOutput;
private Mixer mMixer;
private MixerChannel mMixerChannel;
private FloatControl mGainControl;
private BooleanControl mMuteControl;
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture<?> mProcessorTask;
private AudioEvent mAudioStartEvent;
private AudioEvent mAudioStopEvent;
private SourceDataLine mOutput;
private Mixer mMixer;
private MixerChannel mMixerChannel;
private FloatControl mGainControl;
private BooleanControl mMuteControl;
private boolean mCanProcessAudio = false;
private long mLastActivity = System.currentTimeMillis();
private AudioEvent mAudioStartEvent;
private AudioEvent mAudioStopEvent;
/**
* Single audio channel playback with automatic starting and stopping of the
* underlying sourcedataline specified by the mixer and mixer channel
* arguments.
*
* Maintains an internal non-blocking audio packet queue and processes this
* queue 25 times a second (every 40 ms).
*
* @param threadPoolManager for assigning buffer processing schedule task
* @param mixer to obtain source data line
* @param mixerChannel either mono or left/right stereo
* @param audioFormat to use during playback
* @param lineInfo to use when obtaining the source data line
* @param requestedBufferSize of approximately 1 second of audio
*/
public AudioOutput( ThreadPoolManager threadPoolManager,
Mixer mixer,
MixerChannel mixerChannel,
AudioFormat audioFormat,
Line.Info lineInfo,
int requestedBufferSize )
{
mThreadPoolManager = threadPoolManager;
mMixer = mixer;
mMixerChannel = mixerChannel;
try
{
mOutput = (SourceDataLine)mMixer.getLine( lineInfo );
if( mOutput != null )
{
mOutput.open( audioFormat, requestedBufferSize );
//Start threshold: buffer is full with 10% or less of capacity remaining
mBufferStartThreshold = (int)( mOutput.getBufferSize() * 0.10 );
private boolean mCanProcessAudio = false;
private long mLastActivity = System.currentTimeMillis();
//Stop threshold: buffer is empty with 90% or more capacity available
mBufferStopThreshold = (int)( mOutput.getBufferSize() * 0.90 );
/**
* Single audio channel playback with automatic starting and stopping of the
* underlying sourcedataline specified by the mixer and mixer channel
* arguments.
*
* Maintains an internal non-blocking audio packet queue and processes this
* queue 25 times a second (every 40 ms).
*
* @param threadPoolManager for assigning buffer processing schedule task
* @param mixer to obtain source data line
* @param mixerChannel either mono or left/right stereo
* @param audioFormat to use during playback
* @param lineInfo to use when obtaining the source data line
* @param requestedBufferSize of approximately 1 second of audio
*/
public AudioOutput(ThreadPoolManager threadPoolManager,
Mixer mixer,
MixerChannel mixerChannel,
AudioFormat audioFormat,
Line.Info lineInfo,
int requestedBufferSize)
{
mThreadPoolManager = threadPoolManager;
mMixer = mixer;
mMixerChannel = mixerChannel;
try
{
mOutput = (SourceDataLine) mMixer.getLine(lineInfo);
if(mOutput != null)
{
mOutput.open(audioFormat, requestedBufferSize);
//Start threshold: buffer is full with 10% or less of capacity remaining
mBufferStartThreshold = (int) (mOutput.getBufferSize() * 0.10);
//Stop threshold: buffer is empty with 90% or more capacity available
mBufferStopThreshold = (int) (mOutput.getBufferSize() * 0.90);
mOutput.addLineListener(this);
if(mOutput != null)
{
try
{
Control gain = mOutput.getControl(FloatControl.Type.MASTER_GAIN);
mGainControl = (FloatControl) gain;
}
catch(IllegalArgumentException iae)
{
mLog.warn("Couldn't obtain MASTER GAIN control for stereo line [" +
mixer.getMixerInfo().getName() + " | " + getChannelName() + "]");
}
try
{
Control mute = mOutput.getControl(BooleanControl.Type.MUTE);
mMuteControl = (BooleanControl) mute;
}
catch(IllegalArgumentException iae)
{
mLog.warn("Couldn't obtain MUTE control for stereo line [" +
mixer.getMixerInfo().getName() + " | " + getChannelName() + "]");
}
mOutput.addLineListener( this );
if( mOutput != null )
{
try
{
Control gain = mOutput.getControl( FloatControl.Type.MASTER_GAIN );
mGainControl = (FloatControl)gain;
}
catch( IllegalArgumentException iae )
{
mLog.warn( "Couldn't obtain MASTER GAIN control for stereo line [" +
mixer.getMixerInfo().getName() + " | " + getChannelName() + "]" );
}
try
{
Control mute = mOutput.getControl( BooleanControl.Type.MUTE );
mMuteControl = (BooleanControl)mute;
}
catch( IllegalArgumentException iae )
{
mLog.warn( "Couldn't obtain MUTE control for stereo line [" +
mixer.getMixerInfo().getName() + " | " + getChannelName() + "]" );
}
/* Run the queue processor task every 40 milliseconds or 25 times a second */
mProcessorTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.AUDIO_PROCESSING, new BufferProcessor(),
40, TimeUnit.MILLISECONDS );
}
mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED,
getChannelName() );
mAudioStopEvent = new AudioEvent( AudioEvent.Type.AUDIO_STOPPED,
getChannelName() );
mProcessorTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.AUDIO_PROCESSING, new BufferProcessor(),
40, TimeUnit.MILLISECONDS);
}
mCanProcessAudio = true;
}
}
catch ( LineUnavailableException e )
{
mLog.error( "Couldn't obtain audio source data line for "
+ "audio output - mixer [" + mMixer.getMixerInfo().getName() + "]" );
}
}
public void reset()
{
broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, getChannelName() ) );
}
public void dispose()
{
mCanProcessAudio = false;
mAudioStartEvent = new AudioEvent(AudioEvent.Type.AUDIO_STARTED,
getChannelName());
mAudioStopEvent = new AudioEvent(AudioEvent.Type.AUDIO_STOPPED,
getChannelName());
if( mProcessorTask != null && mThreadPoolManager != null )
{
mThreadPoolManager.cancel( mProcessorTask );
}
mThreadPoolManager = null;
mProcessorTask = null;
mCanProcessAudio = true;
}
}
catch(LineUnavailableException e)
{
mLog.error("Couldn't obtain audio source data line for "
+ "audio output - mixer [" + mMixer.getMixerInfo().getName() + "]");
}
}
mBuffer.clear();
public void reset()
{
broadcast(new AudioEvent(AudioEvent.Type.AUDIO_STOPPED, getChannelName()));
}
mAudioEventBroadcaster.dispose();
mAudioEventBroadcaster = null;
mAudioMetadataListener = null;
if( mOutput != null )
{
mOutput.close();
}
mOutput = null;
mGainControl = null;
mMuteControl = null;
}
public void dispose()
{
mCanProcessAudio = false;
/**
* Converts the audio packet data into a byte buffer format appropriate for
* the underlying source data line.
*/
protected abstract ByteBuffer convert( AudioPacket packet );
if(mProcessorTask != null && mThreadPoolManager != null)
{
mThreadPoolManager.cancel(mProcessorTask);
}
/**
* Audio output channel name
*/
public String getChannelName()
{
return mMixerChannel.getLabel();
}
/**
* Mixer Channel for this audio output
*/
protected MixerChannel getMixerChannel()
{
return mMixerChannel;
}
mThreadPoolManager = null;
mProcessorTask = null;
/**
* Registers a single listener to receive audio start and audio stop events
*/
public void addAudioEventListener( Listener<AudioEvent> listener )
{
mAudioEventBroadcaster.addListener( listener );
}
public void removeAudioEventListener( Listener<AudioEvent> listener )
{
mAudioEventBroadcaster.removeListener( listener );
}
/**
* Broadcasts an audio event to the registered listener
*/
private void broadcast( AudioEvent audioEvent )
{
mAudioEventBroadcaster.broadcast( audioEvent );
}
/**
* Registers a single listener to receive the audio metadata from each
* audio packet
*/
public void setAudioMetadataListener( Listener<AudioMetadata> listener )
{
mAudioMetadataListener = listener;
}
public void removeAudioMetadataListener()
{
mAudioMetadataListener = null;
}
mBuffer.clear();
/**
* Broadcasts audio metadata to the registered listener
*/
private void broadcast( AudioMetadata metadata )
{
if( mAudioMetadataListener != null )
{
mAudioMetadataListener.receive( metadata );
}
}
mAudioEventBroadcaster.dispose();
mAudioEventBroadcaster = null;
mMetadataListener = null;
/**
* Timestamp of either last buffer received or last buffer processed
*/
public long getLastActivityTimestamp()
{
return mLastActivity;
}
if(mOutput != null)
{
mOutput.close();
}
/**
* Updates the last activity timestamp to current system time
*/
public void updateTimestamp()
{
mLastActivity = System.currentTimeMillis();
}
@Override
public void receive( AudioPacket packet )
{
if( mCanProcessAudio )
{
//Update the activity timestamp so that this audio output doesn't
//get disconnected before it starts processing the audio stream
updateTimestamp();
mBuffer.add( packet );
}
}
public class BufferProcessor implements Runnable
{
private AtomicBoolean mProcessing = new AtomicBoolean();
public BufferProcessor()
{
}
mOutput = null;
mGainControl = null;
mMuteControl = null;
}
@Override
public void run()
{
try
{
/**
* Converts the audio packet data into a byte buffer format appropriate for
* the underlying source data line.
*/
protected abstract ByteBuffer convert(AudioPacket packet);
/**
* Audio output channel name
*/
public String getChannelName()
{
return mMixerChannel.getLabel();
}
/**
* Mixer Channel for this audio output
*/
protected MixerChannel getMixerChannel()
{
return mMixerChannel;
}
/**
* Registers a single listener to receive audio start and audio stop events
*/
public void addAudioEventListener(Listener<AudioEvent> listener)
{
mAudioEventBroadcaster.addListener(listener);
}
public void removeAudioEventListener(Listener<AudioEvent> listener)
{
mAudioEventBroadcaster.removeListener(listener);
}
/**
* Broadcasts an audio event to the registered listener
*/
private void broadcast(AudioEvent audioEvent)
{
mAudioEventBroadcaster.broadcast(audioEvent);
}
/**
* Registers a single listener to receive the audio metadata from each
* audio packet
*/
public void setMetadataListener(Listener<Metadata> listener)
{
mMetadataListener = listener;
}
public void removeAudioMetadataListener()
{
mMetadataListener = null;
}
/**
* Broadcasts audio metadata to the registered listener
*/
private void broadcast(Metadata metadata)
{
if(mMetadataListener != null)
{
mMetadataListener.receive(metadata);
}
}
/**
* Timestamp of either last buffer received or last buffer processed
*/
public long getLastActivityTimestamp()
{
return mLastActivity;
}
/**
* Updates the last activity timestamp to current system time
*/
public void updateTimestamp()
{
mLastActivity = System.currentTimeMillis();
}
@Override
public void receive(AudioPacket packet)
{
if(mCanProcessAudio)
{
//Update the activity timestamp so that this audio output doesn't
//get disconnected before it starts processing the audio stream
updateTimestamp();
mBuffer.add(packet);
}
}
public class BufferProcessor implements Runnable
{
private AtomicBoolean mProcessing = new AtomicBoolean();
public BufferProcessor()
{
}
@Override
public void run()
{
try
{
/* The processing flag ensures that only one instance of the
* processor can run at any given time */
if( mProcessing.compareAndSet( false, true ) )
{
List<AudioPacket> packets = new ArrayList<AudioPacket>();
mBuffer.drainTo( packets );
for( AudioPacket packet: packets )
{
if( packet.getType() == Type.AUDIO )
{
broadcast( packet.getAudioMetadata() );
if(mProcessing.compareAndSet(false, true))
{
List<AudioPacket> packets = new ArrayList<AudioPacket>();
ByteBuffer buffer = convert( packet );
int wrote = 0;
if( !mOutput.isRunning() )
{
int toWrite = mOutput.available();
if( toWrite > buffer.array().length )
{
toWrite = buffer.array().length;
}
//Top off the buffer and check if we can start it
wrote += mOutput.write( buffer.array(), 0, toWrite );
checkStart();
}
mBuffer.drainTo(packets);
if( mOutput.isRunning() && wrote < buffer.array().length )
{
//Blocking write
wrote += mOutput.write( buffer.array(), wrote,
buffer.array().length - wrote );
}
updateTimestamp();
}
}
for(AudioPacket packet : packets)
{
if(packet.getType() == Type.AUDIO)
{
broadcast(packet.getMetadata());
checkStop();
mProcessing.set( false );
}
}
catch( Exception e )
{
mLog.error( "Error while processing audio buffers", e );
}
}
/**
* Starts audio playback once audio buffer is almost full and remaining
* capacity falls below the start threshold.
*/
private void checkStart()
{
if( mCanProcessAudio &&
!mOutput.isRunning() &&
mOutput.available() <= mBufferStartThreshold )
{
mOutput.start();
}
}
ByteBuffer buffer = convert(packet);
/**
* Stops audio playback and drains the audio buffer to empty when the
* audio buffer is mostly empty and the available buffer capacity
* exceeds the stop threshold
*/
private void checkStop()
{
if( mCanProcessAudio &&
mOutput.isRunning() &&
mOutput.available() >= mBufferStopThreshold )
{
mOutput.drain();
mOutput.stop();
}
}
}
/**
* Sets the mute state for this audio output channel
*/
public void setMuted( boolean muted )
{
if( mMuteControl != null )
{
mMuteControl.setValue( muted );
broadcast( new AudioEvent( muted ? AudioEvent.Type.AUDIO_MUTED :
AudioEvent.Type.AUDIO_UNMUTED, getChannelName() ) );
}
}
int wrote = 0;
/**
* Current mute state for this audio output channel
*/
public boolean isMuted()
{
if( mMuteControl != null )
{
return mMuteControl.getValue();
}
return false;
}
if(!mOutput.isRunning())
{
int toWrite = mOutput.available();
/**
* Gain/volume control for this audio output channel, if one is available.
*/
public FloatControl getGainControl()
{
return mGainControl;
}
if(toWrite > buffer.array().length)
{
toWrite = buffer.array().length;
}
public boolean hasGainControl()
{
return mGainControl != null;
}
/**
* Monitors the source data line playback state and broadcasts audio events
* to the registered listener as the state changes
*/
@Override
public void update( LineEvent event )
{
LineEvent.Type type = event.getType();
//Top off the buffer and check if we can start it
wrote += mOutput.write(buffer.array(), 0, toWrite);
if( type == LineEvent.Type.START )
{
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
}
else if( type == LineEvent.Type.STOP )
{
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
}
}
checkStart();
}
if(mOutput.isRunning() && wrote < buffer.array().length)
{
//Blocking write
wrote += mOutput.write(buffer.array(), wrote,
buffer.array().length - wrote);
}
updateTimestamp();
}
}
checkStop();
mProcessing.set(false);
}
}
catch(Exception e)
{
mLog.error("Error while processing audio buffers", e);
}
}
/**
* Starts audio playback once audio buffer is almost full and remaining
* capacity falls below the start threshold.
*/
private void checkStart()
{
if(mCanProcessAudio &&
!mOutput.isRunning() &&
mOutput.available() <= mBufferStartThreshold)
{
mOutput.start();
}
}
/**
* Stops audio playback and drains the audio buffer to empty when the
* audio buffer is mostly empty and the available buffer capacity
* exceeds the stop threshold
*/
private void checkStop()
{
if(mCanProcessAudio &&
mOutput.isRunning() &&
mOutput.available() >= mBufferStopThreshold)
{
mOutput.drain();
mOutput.stop();
}
}
}
/**
* Sets the mute state for this audio output channel
*/
public void setMuted(boolean muted)
{
if(mMuteControl != null)
{
mMuteControl.setValue(muted);
broadcast(new AudioEvent(muted ? AudioEvent.Type.AUDIO_MUTED :
AudioEvent.Type.AUDIO_UNMUTED, getChannelName()));
}
}
/**
* Current mute state for this audio output channel
*/
public boolean isMuted()
{
if(mMuteControl != null)
{
return mMuteControl.getValue();
}
return false;
}
/**
* Gain/volume control for this audio output channel, if one is available.
*/
public FloatControl getGainControl()
{
return mGainControl;
}
public boolean hasGainControl()
{
return mGainControl != null;
}
/**
* Monitors the source data line playback state and broadcasts audio events
* to the registered listener as the state changes
*/
@Override
public void update(LineEvent event)
{
LineEvent.Type type = event.getType();
if(type == LineEvent.Type.START)
{
mAudioEventBroadcaster.broadcast(mAudioStartEvent);
}
else if(type == LineEvent.Type.STOP)
{
mAudioEventBroadcaster.broadcast(mAudioStopEvent);
}
}
}

View File

@@ -0,0 +1,111 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
public class AliasedIdentifier
{
private String mIdentifier;
private Alias mAlias;
/**
* Identifier and corresponding optional alias
*/
public AliasedIdentifier()
{
}
/**
* Resets alias and value to null
*
* Package Private - should only be changed within MutableMetadata
*/
void reset()
{
mIdentifier = null;
mAlias = null;
}
/**
* Identifier
*/
public String getIdentifier()
{
return mIdentifier;
}
/**
* Sets the identifier to the argument value
*
* Package Private - should only be changed within MutableMetadata
*/
void setIdentifier(String identifier)
{
mIdentifier = identifier;
}
/**
* Indicates if there is a non-null, non-empty identifier
*/
public boolean hasIdentifier()
{
return mIdentifier != null && !mIdentifier.isEmpty();
}
/**
* Alias for the identifier.
*/
public Alias getAlias()
{
return mAlias;
}
/**
* Sets the alias to the argument
*
* Package Private - should only be changed within MutableMetadata
*/
void setAlias(Alias alias)
{
mAlias = alias;
}
/**
* Indicates if there is an alias corresponding to the identifier
*/
public boolean hasAlias()
{
return mAlias != null;
}
public AliasedIdentifier copyOf()
{
AliasedIdentifier copy = new AliasedIdentifier();
copy.setAlias(mAlias);
if(mIdentifier != null)
{
copy.setIdentifier(new String(mIdentifier));
}
return copy;
}
}

View File

@@ -0,0 +1,99 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import sample.Listener;
import static alias.id.AliasIDType.*;
public class AliasedIntegerAttributeMonitor extends AttributeMonitor<Integer>
{
private static final AliasIDType[] VALID_ID_TYPES = {STATUS, LTR_NET_UID};
private AliasList mAliasList;
private AliasIDType mAliasIDType;
/**
* Provides monitoring of a String attribute's values with occurrence heuristics enabled and alias lookup with each
* change in the attribute's value.
*
* @param attribute to monitor
* @param listener to receive change requests
* @param aliasList to lookup alias from
* @param aliasIDType to determine correct alias lookup method in alias list
*/
public AliasedIntegerAttributeMonitor(Attribute attribute, Listener<AttributeChangeRequest> listener, AliasList aliasList,
AliasIDType aliasIDType)
{
super(attribute, listener);
if(!isValidIDType(aliasIDType))
{
throw new IllegalArgumentException("Alias ID Type [" + aliasIDType.name() + "] is not supported");
}
mAliasList = aliasList;
mAliasIDType = aliasIDType;
}
/**
* Indicates if the alias ID type is a supported/valid type for this monitor.
*/
public static boolean isValidIDType(AliasIDType type)
{
for(AliasIDType validType: VALID_ID_TYPES)
{
if(type == validType)
{
return true;
}
}
return false;
}
/**
* Lookup of the alias that corresponds to the value's alias ID type
*/
@Override
protected Alias getAlias()
{
Alias alias = null;
if(mAliasList != null)
{
if(hasValue())
{
switch(mAliasIDType)
{
case STATUS:
alias = mAliasList.getStatus(getValue());
break;
case LTR_NET_UID:
alias = mAliasList.getUniqueID(getValue());
break;
}
}
}
return alias;
}
}

View File

@@ -0,0 +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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import sample.Listener;
import static alias.id.AliasIDType.*;
public class AliasedStringAttributeMonitor extends AttributeMonitor<String>
{
private static final AliasIDType[] VALID_ID_TYPES = {ESN, FLEETSYNC, MDC1200, MIN, MPT1327, SITE, TALKGROUP};
private AliasList mAliasList;
private AliasIDType mAliasIDType;
/**
* Provides monitoring of a String attribute's values with occurrence heuristics enabled and alias lookup with each
* change in the attribute's value.
*
* @param attribute to monitor
* @param listener to receive change requests
* @param aliasList to lookup alias from
* @param aliasIDType to determine correct alias lookup method in alias list
*/
public AliasedStringAttributeMonitor(Attribute attribute, Listener<AttributeChangeRequest> listener, AliasList aliasList,
AliasIDType aliasIDType)
{
super(attribute, listener);
if(!isValidIDType(aliasIDType))
{
throw new IllegalArgumentException("Alias ID Type [" + aliasIDType.name() + "] is not supported");
}
mAliasList = aliasList;
mAliasIDType = aliasIDType;
}
/**
* Indicates if the alias ID type is a supported/valid type for this monitor.
*/
public static boolean isValidIDType(AliasIDType type)
{
for(AliasIDType validType: VALID_ID_TYPES)
{
if(type == validType)
{
return true;
}
}
return false;
}
/**
* Lookup of the alias that corresponds to the value's alias ID type
*/
@Override
protected Alias getAlias()
{
Alias alias = null;
if(mAliasList != null)
{
if(hasValue())
{
switch(mAliasIDType)
{
case ESN:
alias = mAliasList.getESNAlias(getValue());
break;
case FLEETSYNC:
alias = mAliasList.getFleetsyncAlias(getValue());
break;
case MDC1200:
alias = mAliasList.getMDC1200Alias(getValue());
break;
case MIN:
alias = mAliasList.getMobileIDNumberAlias(getValue());
break;
case MPT1327:
alias = mAliasList.getMPT1327Alias(getValue());
break;
case SITE:
alias = mAliasList.getSiteID(getValue());
break;
case TALKGROUP:
alias = mAliasList.getTalkgroupAlias(getValue());
break;
}
}
}
return alias;
}
}

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
public enum Attribute
{
CHANNEL_CONFIGURATION_NAME,
CHANNEL_CONFIGURATION_SITE,
CHANNEL_CONFIGURATION_SYSTEM,
CHANNEL_FREQUENCY,
CHANNEL_FREQUENCY_LABEL,
CHANNEL_STATE,
MESSAGE,
MESSAGE_TYPE,
NETWORK_ID_1,
NETWORK_ID_2,
PRIMARY_ADDRESS_FROM,
PRIMARY_ADDRESS_TO,
PRIMARY_DECODER_TYPE,
SECONDARY_ADDRESS_FROM,
SECONDARY_ADDRESS_TO,
}

View File

@@ -0,0 +1,131 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import module.decode.DecoderType;
public class AttributeChangeRequest<T>
{
private Attribute mAttribute;
private T mValue;
private Alias mAlias;
/**
* Request to change the value of an attribute
* @param attribute to change
* @param value to change for the attribute
* @param alias (optional) for the value
*/
public AttributeChangeRequest(Attribute attribute, T value, Alias alias)
{
mAttribute = attribute;
mValue = value;
mAlias = alias;
}
/**
* Request to change the value of an attribute
* @param attribute to change
* @param value to change for the attribute
*/
public AttributeChangeRequest(Attribute attribute, T value)
{
this(attribute, value, null);
}
/**
* Attribute to change
*/
public Attribute getAttribute()
{
return mAttribute;
}
/**
* Value to set for the attribute
*/
public T getValue()
{
return mValue;
}
/**
* Indicates if this change request has a non-null value for the attribute
*/
public boolean hasValue()
{
return mValue != null;
}
/**
* Alias for the corresponding attribute value
*/
public Alias getAlias()
{
return mAlias;
}
/**
* Indicates if this change request contains an alias that corresponds to the attribute value
*/
public boolean hasAlias()
{
return mAlias != null;
}
/**
* Returns the value as a String or null if the value is not a String instance.
*/
public String getStringValue()
{
if(mValue instanceof String)
{
return (String)mValue;
}
return null;
}
/**
* Returns the value as a Long or null if the value is not a Long instance.
*/
public Long getLongValue()
{
if(mValue instanceof Long)
{
return (Long)mValue;
}
return null;
}
/**
* Returns the value as a DecoderType or null if the value is not a DecoderType instance.
*/
public DecoderType getDecoderTypeValue()
{
if(mValue instanceof DecoderType)
{
return (DecoderType)mValue;
}
return null;
}
}

View File

@@ -0,0 +1,189 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AttributeMonitor<T>
{
private final static Logger mLog = LoggerFactory.getLogger(AttributeMonitor.class);
private Attribute mAttribute;
private Listener<AttributeChangeRequest> mListener;
private boolean mHeuristicsEnabled = true;
private Map<T,Integer> mOccurrenceCounts = new HashMap<>();
private T mCurrentValue;
private List<T> mIllegalValues = new ArrayList<>();
public AttributeMonitor(Attribute attribute, Listener<AttributeChangeRequest> listener)
{
mAttribute = attribute;
mListener = listener;
}
/**
* Processes the value and emits a change request if the value needs to be changed. If heuristics are enabled,
* 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
*
* @return true if the value was changed after processing
*/
public boolean process(T t)
{
boolean changed = false;
if(t != null && !mIllegalValues.contains(t))
{
if(mHeuristicsEnabled)
{
if(mOccurrenceCounts.containsKey(t))
{
mOccurrenceCounts.put(t, mOccurrenceCounts.get(t) + 1);
}
else
{
mOccurrenceCounts.put(t, 1);
}
}
if(mCurrentValue == null)
{
mCurrentValue = t;
changed = true;
}
else if(!mCurrentValue.equals(t))
{
if(mHeuristicsEnabled)
{
int count = mOccurrenceCounts.get(mCurrentValue);
for(Map.Entry<T,Integer> entry: mOccurrenceCounts.entrySet())
{
if(entry.getValue() > count)
{
count = entry.getValue();
mCurrentValue = entry.getKey();
changed = true;
}
}
}
else
{
mCurrentValue = t;
changed = true;
}
}
if(changed && mListener != null)
{
mListener.receive(new AttributeChangeRequest(mAttribute, getValue(), getAlias()));
}
}
return changed;
}
/**
* Alias for the current value. This class does not return an alias, but subclasses can return an alias that
* is appropriate for the monitored attribute value.
*
* @return an optional alias for the current value
*/
protected Alias getAlias()
{
return null;
}
/**
* Resets the attribute value to null and resets occurrence heuristics.
*/
public void reset()
{
mCurrentValue = null;
mOccurrenceCounts.clear();
}
/**
* Adds a value to the internal list of illegal values. The monitor will not allow the attribute to be changed to
* any values contained in this list.
*
* @param illegalValue to prevent for the monitored alias
*/
public void addIllegalValue(T illegalValue)
{
mIllegalValues.add(illegalValue);
}
/**
* Returns the current value or null if the monitor is newly started or reset.
*/
public T getValue()
{
return mCurrentValue;
}
/**
* Indicates if the current value is non-null
*/
public boolean hasValue()
{
return mCurrentValue != null;
}
public static void main(String[] args)
{
AttributeMonitor<String> monitor = new AttributeMonitor<>(Attribute.PRIMARY_ADDRESS_TO, new Listener<AttributeChangeRequest>()
{
@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");
}
}

View File

@@ -0,0 +1,275 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import channel.state.State;
import controller.channel.Channel;
import sample.Listener;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MutableMetadataChangeEvent>
{
private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" );
public static final int COLUMN_STATE = 0;
public static final int COLUMN_DECODER = 1;
public static final int COLUMN_CHANNEL = 2;
public static final int COLUMN_FREQUENCY = 3;
public static final int COLUMN_PRIMARY_FROM = 4;
public static final int COLUMN_PRIMARY_TO = 5;
public static final int COLUMN_SECONDARY_FROM = 6;
public static final int COLUMN_SECONDARY_TO = 7;
public static final int COLUMN_CONFIGURATION_NAME = 8;
public static final int COLUMN_MESSAGE = 9;
private static final String[] COLUMNS = {"State", "Decoder", "Channel", "Frequency", "Primary From", "Primary To",
"Secondary From", "Secondary To", "Channel Name", "Message"};
private List<MutableMetadata> mChannelMetadata = new ArrayList();
private Map<MutableMetadata,Channel> mMetadataChannelMap = new HashMap();
public void add(MutableMetadata metadata, Channel channel)
{
//Execute on the swing thread to avoid threading issues
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
mChannelMetadata.add(metadata);
mMetadataChannelMap.put(metadata, channel);
int index = mChannelMetadata.indexOf(metadata);
fireTableRowsInserted(index, index);
metadata.addListener(ChannelMetadataModel.this);
}
});
}
public void remove(MutableMetadata metadata)
{
//Execute on the swing thread to avoid threading issues
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
metadata.removeListener(ChannelMetadataModel.this);
int index = mChannelMetadata.indexOf(metadata);
mChannelMetadata.remove(metadata);
mMetadataChannelMap.remove(metadata);
fireTableRowsDeleted(index, index);
}
});
}
/**
* Get the channel metadata at the specified model row index
*/
public Metadata getMetadata(int row)
{
if(row < mChannelMetadata.size())
{
return mChannelMetadata.get(row);
}
return null;
}
/**
* Returns the channel that matches the metadata or null
*/
public Channel getChannelFromMetadata(Metadata metadata)
{
return mMetadataChannelMap.get(metadata);
}
@Override
public int getRowCount()
{
return mChannelMetadata.size();
}
@Override
public int getColumnCount()
{
return COLUMNS.length;
}
@Override
public String getColumnName(int column)
{
return COLUMNS[column];
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
switch(columnIndex)
{
case COLUMN_STATE:
return State.class;
case COLUMN_DECODER:
case COLUMN_CHANNEL:
case COLUMN_FREQUENCY:
case COLUMN_MESSAGE:
case COLUMN_CONFIGURATION_NAME:
return String.class;
case COLUMN_PRIMARY_TO:
case COLUMN_PRIMARY_FROM:
case COLUMN_SECONDARY_TO:
case COLUMN_SECONDARY_FROM:
return MutableMetadata.class;
default:
return String.class;
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(rowIndex <= mChannelMetadata.size())
{
Metadata metadata = mChannelMetadata.get(rowIndex);
switch(columnIndex)
{
case COLUMN_STATE:
return metadata.getState();
case COLUMN_DECODER:
if(metadata.hasPrimaryDecoderType())
{
return metadata.getPrimaryDecoderType().getShortDisplayString();
}
return null;
case COLUMN_CHANNEL:
return metadata.getChannelFrequencyLabel();
case COLUMN_FREQUENCY:
if(metadata.hasChannelFrequency())
{
return FREQUENCY_FORMATTER.format((double)metadata.getChannelFrequency() / 1E6d);
}
return null;
case COLUMN_MESSAGE:
if(metadata.hasMessageType() | metadata.hasMessage())
{
StringBuilder sb = new StringBuilder();
if(metadata.hasMessageType())
{
sb.append(metadata.getMessageType()).append(" ");
}
if(metadata.hasMessage())
{
sb.append(metadata.getMessage());
}
return sb.toString();
}
return null;
case COLUMN_CONFIGURATION_NAME:
return metadata.getChannelConfigurationName();
case COLUMN_PRIMARY_TO:
case COLUMN_PRIMARY_FROM:
case COLUMN_SECONDARY_TO:
case COLUMN_SECONDARY_FROM:
return metadata;
default:
return String.class;
}
}
return null;
}
@Override
public void receive(MutableMetadataChangeEvent mutableMetadataChangeEvent)
{
//Execute on the swing thread to avoid threading issues
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
int rowIndex = mChannelMetadata.indexOf(mutableMetadataChangeEvent.getMetadata());
if(rowIndex >= 0)
{
switch(mutableMetadataChangeEvent.getAttribute())
{
case CHANNEL_CONFIGURATION_SYSTEM:
break;
case CHANNEL_CONFIGURATION_SITE:
break;
case CHANNEL_CONFIGURATION_NAME:
fireTableCellUpdated(rowIndex, COLUMN_CONFIGURATION_NAME);
break;
case CHANNEL_FREQUENCY:
fireTableCellUpdated(rowIndex, COLUMN_FREQUENCY);
break;
case CHANNEL_FREQUENCY_LABEL:
fireTableCellUpdated(rowIndex, COLUMN_CHANNEL);
break;
case CHANNEL_STATE:
fireTableCellUpdated(rowIndex, COLUMN_STATE);
break;
case MESSAGE:
case MESSAGE_TYPE:
fireTableCellUpdated(rowIndex, COLUMN_MESSAGE);
break;
case PRIMARY_ADDRESS_FROM:
fireTableCellUpdated(rowIndex, COLUMN_PRIMARY_FROM);
break;
case PRIMARY_ADDRESS_TO:
fireTableCellUpdated(rowIndex, COLUMN_PRIMARY_TO);
break;
case PRIMARY_DECODER_TYPE:
fireTableCellUpdated(rowIndex, COLUMN_DECODER);
break;
case SECONDARY_ADDRESS_FROM:
fireTableCellUpdated(rowIndex, COLUMN_SECONDARY_FROM);
break;
case SECONDARY_ADDRESS_TO:
fireTableCellUpdated(rowIndex, COLUMN_SECONDARY_TO);
break;
}
}
}
});
}
}

View File

@@ -0,0 +1,227 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import channel.state.State;
import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
import controller.channel.Channel;
import controller.channel.ChannelProcessingManager;
import icon.IconManager;
import module.ProcessingChain;
import net.miginfocom.swing.MigLayout;
import properties.SystemProperties;
import sample.Broadcaster;
import sample.Listener;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
public class ChannelMetadataPanel extends JPanel implements ListSelectionListener
{
private static final String PROPERTY_PREFIX_BACKGROUND = "channel.metadata.panel.state.color.";
private static final String PROPERTY_PREFIX_FOREGROUND = "channel.metadata.panel.text.color.";
private ChannelProcessingManager mChannelProcessingManager;
private IconManager mIconManager;
private JTable mTable;
private Broadcaster<ProcessingChain> mSelectedProcessingChainBroadcaster = new Broadcaster<>();
/**
* Table view for currently decoding channel metadata
*/
public ChannelMetadataPanel(ChannelProcessingManager channelProcessingManager, IconManager iconManager)
{
mIconManager = iconManager;
mChannelProcessingManager = channelProcessingManager;
init();
}
/**
* Initializes the panel
*/
private void init()
{
setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]") );
mTable = new JTable(mChannelProcessingManager.getChannelMetadataModel());
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)mTable.getDefaultRenderer(String.class);
renderer.setHorizontalAlignment(SwingConstants.CENTER);
mTable.getSelectionModel().addListSelectionListener(this);
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_STATE)
.setCellRenderer(new ColoredStateCellRenderer());
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_PRIMARY_TO)
.setCellRenderer(new AliasedValueCellRenderer(Attribute.PRIMARY_ADDRESS_TO));
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_PRIMARY_FROM)
.setCellRenderer(new AliasedValueCellRenderer(Attribute.PRIMARY_ADDRESS_FROM));
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_SECONDARY_TO)
.setCellRenderer(new AliasedValueCellRenderer(Attribute.SECONDARY_ADDRESS_TO));
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_SECONDARY_FROM)
.setCellRenderer(new AliasedValueCellRenderer(Attribute.SECONDARY_ADDRESS_FROM));
JScrollPane scrollPane = new JScrollPane(mTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
add(scrollPane);
// new TableColumnWidthMonitor(mTable, PROPERTY_PREFIX, new int[] {15,15,15,15,25,25,25,25,15,40});
}
@Override
public void valueChanged(ListSelectionEvent e)
{
if(!mTable.getSelectionModel().getValueIsAdjusting())
{
ProcessingChain processingChain = null;
int selectedViewRow = mTable.getSelectedRow();
if(selectedViewRow >= 0)
{
int selectedModelRow = mTable.convertRowIndexToModel(selectedViewRow);
Metadata selectedMetadata = mChannelProcessingManager.getChannelMetadataModel()
.getMetadata(selectedModelRow);
if(selectedMetadata != null)
{
Channel selectedChannel = mChannelProcessingManager.getChannelMetadataModel()
.getChannelFromMetadata(selectedMetadata);
processingChain = mChannelProcessingManager.getProcessingChain(selectedChannel);
}
}
mSelectedProcessingChainBroadcaster.broadcast(processingChain);
}
}
/**
* Adds the listener to receive the processing chain associated with the metadata selected in the
* metadata table.
*/
public void addProcessingChainSelectionListener(Listener<ProcessingChain> listener)
{
mSelectedProcessingChainBroadcaster.addListener(listener);
}
public class AliasedValueCellRenderer extends DefaultTableCellRenderer
{
private Attribute mAttribute;
public AliasedValueCellRenderer(Attribute attribute)
{
assert(attribute != null);
mAttribute = attribute;
setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Metadata metadata = (Metadata)value;
String value1 = metadata.getValue(mAttribute);
Alias alias = metadata.getAlias(mAttribute);
label.setText(alias != null ? alias.getName() : value1);
label.setIcon(alias != null ? mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE) : null);
label.setForeground(alias != null ? alias.getDisplayColor() : table.getForeground());
return label;
}
}
public class ColoredStateCellRenderer extends DefaultTableCellRenderer
{
public ColoredStateCellRenderer()
{
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column)
{
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color background = table.getBackground();
Color foreground = table.getForeground();
if(value instanceof State)
{
State state = (State)value;
label.setText(state.getDisplayValue());
switch(state)
{
case CALL:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "call", Color.BLUE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "call", Color.YELLOW);
break;
case CONTROL:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "control", Color.ORANGE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "control", Color.BLUE);
break;
case DATA:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "data", Color.GREEN);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "data", Color.BLUE);
break;
case ENCRYPTED:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "encrypted", Color.MAGENTA);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "encrypted", Color.WHITE);
break;
case FADE:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "fade", Color.LIGHT_GRAY.BLUE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "fade", Color.YELLOW);
break;
case IDLE:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
break;
case RESET:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
break;
case TEARDOWN:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "teardown", Color.DARK_GRAY);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "teardown", Color.WHITE);
break;
}
}
else
{
setText("");
}
setBackground(background);
setForeground(foreground);
return label;
}
}
}

View File

@@ -0,0 +1,77 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import com.jidesoft.swing.JideSplitPane;
import com.jidesoft.swing.JideTabbedPane;
import controller.channel.ChannelProcessingManager;
import icon.IconManager;
import module.decode.event.CallEventPanel;
import module.decode.event.MessageActivityPanel;
import net.miginfocom.swing.MigLayout;
import settings.SettingsManager;
import spectrum.ChannelSpectrumPanel;
import javax.swing.*;
import java.awt.*;
public class ChannelMetadataViewer extends JPanel
{
private CallEventPanel mCallEventPanel;
private MessageActivityPanel mMessageActivityPanel;
private ChannelSpectrumPanel mChannelSpectrumPanel;
private ChannelMetadataPanel mChannelMetadataPanel;
/**
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
* messages, events, and spectral view.
*/
public ChannelMetadataViewer(ChannelProcessingManager channelProcessingManager, IconManager iconManager,
SettingsManager settingsManager)
{
mCallEventPanel = new CallEventPanel(iconManager);
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager);
mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager);
init();
}
private void init()
{
setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]") );
JideTabbedPane tabbedPane = new JideTabbedPane();
// tabbedPane.addTab("Details", new JPanel());
tabbedPane.addTab("Events", mCallEventPanel);
tabbedPane.addTab("Messages", mMessageActivityPanel);
// tabbedPane.addTab("Spectrum", mChannelSpectrumPanel);
tabbedPane.setFont(this.getFont());
tabbedPane.setForeground(Color.BLACK);
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
splitPane.setShowGripper(true);
splitPane.add(mChannelMetadataPanel);
splitPane.add(tabbedPane);
add(splitPane);
mChannelMetadataPanel.addProcessingChainSelectionListener(mCallEventPanel);
mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel);
}
}

View File

@@ -0,0 +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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import sample.Listener;
public interface IAttributeChangeRequestListener
{
public Listener<AttributeChangeRequest> getAttributeChangeRequestListener();
}

View File

@@ -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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import sample.Listener;
public interface IAttributeChangeRequestProvider
{
public void setAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener);
public void removeAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener);
}

View File

@@ -0,0 +1,662 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import alias.id.broadcast.BroadcastChannel;
import alias.id.priority.Priority;
import channel.state.State;
import module.decode.DecoderType;
import java.util.ArrayList;
import java.util.List;
public class Metadata
{
// Static unique metadata identifier tracking
private static int UNIQUE_METADATA_ID_GENERATOR = 0;
private int mMetadataID;
protected boolean mUpdated;
protected DecoderType mPrimaryDecoderType;
protected boolean mSelected;
protected State mState = State.IDLE;
protected String mChannelConfigurationSystem;
protected String mChannelConfigurationSite;
protected String mChannelConfigurationName;
protected String mChannelFrequencyLabel;
protected long mChannelFrequency;
protected AliasedIdentifier mNetworkID1 = new AliasedIdentifier();
protected AliasedIdentifier mNetworkID2 = new AliasedIdentifier();
protected String mMessage;
protected String mMessageType;
protected AliasedIdentifier mPrimaryAddressFrom = new AliasedIdentifier();
protected AliasedIdentifier mPrimaryAddressTo = new AliasedIdentifier();
protected AliasedIdentifier mSecondaryAddressFrom = new AliasedIdentifier();
protected AliasedIdentifier mSecondaryAddressTo = new AliasedIdentifier();
//Lazily constructed member variables.
private Integer mAudioPriority;
protected Boolean mRecordable;
private List<BroadcastChannel> mBroadcastChannels;
/**
* Channel metadata. Contains all attributes that reflect the state and current attribute values for a channel
* that is currently decoding. This metadata is intended to support any decoding channel gui components to
* graphically convey the current state of a decoding channel and to provide audio metadata
*/
public Metadata()
{
mMetadataID = UNIQUE_METADATA_ID_GENERATOR++;
}
/**
* Protected constructor. Primarily used by the copyOf() method to create a metadata copy with the same ID
*/
protected Metadata(int metadataID)
{
mMetadataID = metadataID;
}
/**
* Unique string identifier for this metadata that is comprised of the channel ID and the primary TO address.
*
* This identifier can be used to uniquely identify a channel audio stream and the primary communicant.
*/
public String getUniqueIdentifier()
{
return "SRC:" + mMetadataID +
" ID:" + (mPrimaryAddressTo.hasIdentifier() ? mPrimaryAddressTo.getIdentifier() : "UNKNOWN");
}
/**
* Indicates if any of the fields of this metadata have been updated since the last time a copy was made from this
* metadata. This method is primarily used by downstream audio playback and audio recording to signal when changes
* are made to the metadata that requires the downstream component to reinspect the metadata.
*
* This flag is reset to false immediately after a copy is made of this metadata via the copyOf() method.
*/
public boolean isUpdated()
{
return mUpdated;
}
/**
* Audio Priority as the highest audio priority value from across the primary and secondary identifier aliases.
*
* A default priority will be used if there are no aliased values. If there is a conflict between audio priority
* and 'Do Not Monitor' among the aliases, the highest audio priority will be used (prefer to monitor).
*
* -1 Do Not Monitor
* 0 Channel Selected for immediate monitor
* 1 Highest Priority
* 100 Lowest Priority (default)
*
* @return audio priority
*/
public int getAudioPriority()
{
if(isSelected())
{
return Priority.SELECTED_PRIORITY;
}
if(mAudioPriority == null)
{
determineAudioPriority();
}
return mAudioPriority;
}
/**
* Indicates if the audio priority is set to 'Do Not Monitor'
*/
public boolean isDoNotMonitor()
{
return getAudioPriority() == Priority.DO_NOT_MONITOR;
}
/**
* Indicates if any of the primary or secondary TO/FROM aliases are identified as recordable
*/
public boolean isRecordable()
{
if(mRecordable == null)
{
determineRecordable();
}
return mRecordable;
}
/**
* Indicates that this metadata has been selected by the user and any audio produced by this channel will be set
* to the highest playback priority (immediate monitor).
*/
public boolean isSelected()
{
return mSelected;
}
/**
* List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases.
*/
public List<BroadcastChannel> getBroadcastChannels()
{
if(mBroadcastChannels == null)
{
determineBroadcastChannels();
}
return mBroadcastChannels;
}
/**
* Indicates if this metadata is streamable, meaning that it contains one ore more broadcast channels
*/
public boolean isStreamable()
{
return !getBroadcastChannels().isEmpty();
}
/**
* Unique identifier for the source channel that produces this metadata
*/
public int getMetadataID()
{
return mMetadataID;
}
/**
* Decoder type (ie protocol) for the primary decoder
*/
public DecoderType getPrimaryDecoderType()
{
return mPrimaryDecoderType;
}
/**
* Indicates if this metadata has a primary decoder type defined
*/
public boolean hasPrimaryDecoderType()
{
return mPrimaryDecoderType != null;
}
/**
* Returns the alias associated with the attribute or null
*/
public Alias getAlias(Attribute attribute)
{
switch(attribute)
{
case NETWORK_ID_1:
return getNetworkID1().getAlias();
case NETWORK_ID_2:
return getNetworkID2().getAlias();
case PRIMARY_ADDRESS_FROM:
return getPrimaryAddressFrom().getAlias();
case PRIMARY_ADDRESS_TO:
return getPrimaryAddressTo().getAlias();
case SECONDARY_ADDRESS_FROM:
return getSecondaryAddressFrom().getAlias();
case SECONDARY_ADDRESS_TO:
return getSecondaryAddressTo().getAlias();
}
return null;
}
/**
* Returns the string value for the specified attribute or null
*/
public String getValue(Attribute attribute)
{
switch(attribute)
{
case CHANNEL_CONFIGURATION_SYSTEM:
return mChannelConfigurationSystem;
case CHANNEL_CONFIGURATION_SITE:
return mChannelConfigurationSite;
case CHANNEL_FREQUENCY_LABEL:
return mChannelFrequencyLabel;
case CHANNEL_STATE:
return mState.getDisplayValue();
case MESSAGE:
return mMessage;
case MESSAGE_TYPE:
return mMessageType;
case NETWORK_ID_1:
return mNetworkID1.getIdentifier();
case NETWORK_ID_2:
return mNetworkID2.getIdentifier();
case PRIMARY_ADDRESS_FROM:
return mPrimaryAddressFrom.getIdentifier();
case PRIMARY_ADDRESS_TO:
return mPrimaryAddressTo.getIdentifier();
case SECONDARY_ADDRESS_FROM:
return mSecondaryAddressFrom.getIdentifier();
case SECONDARY_ADDRESS_TO:
return mSecondaryAddressTo.getIdentifier();
}
return null;
}
/**
* Current channel state
*/
public State getState()
{
return mState;
}
/**
* System name from the channel configuration
*/
public String getChannelConfigurationSystem()
{
return mChannelConfigurationSystem;
}
/**
* Indicates if there is a non-null, non-empty channel configuration system value
*/
public boolean hasChannelConfigurationSystem()
{
return mChannelConfigurationSystem != null && !mChannelConfigurationSystem.isEmpty();
}
/**
* Site name from the channel configuration
*/
public String getChannelConfigurationSite()
{
return mChannelConfigurationSite;
}
/**
* Indicates if there is a non-null, non-empty channel configuration site value
*/
public boolean hasChannelConfigurationSite()
{
return mChannelConfigurationSite != null && !mChannelConfigurationSite.isEmpty();
}
/**
* Site name from the channel configuration
*/
public String getChannelConfigurationName()
{
return mChannelConfigurationName;
}
/**
* Indicates if there is a non-null, non-empty channel configuration name value
*/
public boolean hasChannelConfigurationName()
{
return mChannelConfigurationSite != null && !mChannelConfigurationSite.isEmpty();
}
/**
* Channel ID or Channel Number
*/
public String getChannelFrequencyLabel()
{
return mChannelFrequencyLabel;
}
/**
* Indicates if there is a non-null, non-empty channel identifier value
*/
public boolean hasChannelFrequencyLabel()
{
return mChannelFrequencyLabel != null && !mChannelFrequencyLabel.isEmpty();
}
/**
* Channel frequency
*/
public long getChannelFrequency()
{
return mChannelFrequency;
}
/**
* Indicates if there is a non-zero channel frequency value
*/
public boolean hasChannelFrequency()
{
return mChannelFrequency > 0;
}
/**
* Network Identifier 1
*/
public AliasedIdentifier getNetworkID1()
{
return mNetworkID1;
}
/**
* Network Identifier 2
*/
public AliasedIdentifier getNetworkID2()
{
return mNetworkID2;
}
/**
* Message
*/
public String getMessage()
{
return mMessage;
}
/**
* Indicates if there is a non-null, non-empty message value
*/
public boolean hasMessage()
{
return mMessage != null && !mMessage.isEmpty();
}
/**
* Message and Decoder type
*/
public String getMessageType()
{
return mMessageType;
}
/**
* Indicates if there is a non-null, non-empty message and decoder type value
*/
public boolean hasMessageType()
{
return mMessageType != null && !mMessageType.isEmpty();
}
/**
* Primary FROM Address
*/
public AliasedIdentifier getPrimaryAddressFrom()
{
return mPrimaryAddressFrom;
}
/**
* Primary TO Address
*/
public AliasedIdentifier getPrimaryAddressTo()
{
return mPrimaryAddressTo;
}
/**
* Secondary FROM Address
*/
public AliasedIdentifier getSecondaryAddressFrom()
{
return mSecondaryAddressFrom;
}
/**
* Secondary TO Address
*/
public AliasedIdentifier getSecondaryAddressTo()
{
return mSecondaryAddressTo;
}
/**
* Determines the audio priority from across the primary and secondary TO/FROM aliases when they exist
*/
private void determineAudioPriority()
{
int priority = Priority.DEFAULT_PRIORITY;
boolean doNotMonitorFound = false;
boolean audioPriorityFound = false;
if(getPrimaryAddressTo().hasAlias())
{
Alias primaryTo = getPrimaryAddressTo().getAlias();
if(primaryTo.hasCallPriority())
{
if(primaryTo.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(primaryTo.getCallPriority() < priority)
{
priority = primaryTo.getCallPriority();
}
}
}
}
if(getPrimaryAddressFrom().hasAlias())
{
Alias primaryFrom = getPrimaryAddressFrom().getAlias();
if(primaryFrom.hasCallPriority())
{
if(primaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(primaryFrom.getCallPriority() < priority)
{
priority = primaryFrom.getCallPriority();
}
}
}
}
if(getSecondaryAddressTo().hasAlias())
{
Alias secondaryTo = getSecondaryAddressTo().getAlias();
if(secondaryTo.hasCallPriority())
{
if(secondaryTo.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(secondaryTo.getCallPriority() < priority)
{
priority = secondaryTo.getCallPriority();
}
}
}
}
if(getSecondaryAddressFrom().hasAlias())
{
Alias secondaryFrom = getSecondaryAddressFrom().getAlias();
if(secondaryFrom.hasCallPriority())
{
if(secondaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR)
{
doNotMonitorFound = true;
}
else
{
audioPriorityFound = true;
if(secondaryFrom.getCallPriority() < priority)
{
priority = secondaryFrom.getCallPriority();
}
}
}
}
//If we found a 'Do Not Monitor' and no other audio priority, then set to do not monitor
if(doNotMonitorFound && !audioPriorityFound)
{
mAudioPriority = Priority.DO_NOT_MONITOR;
}
else
{
mAudioPriority = priority;
}
}
/**
* Determines if any of the primary or secondary to/from aliases are recordable.
*/
private void determineRecordable()
{
mRecordable = false;
if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isRecordable())
{
mRecordable = true;
return;
}
if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isRecordable())
{
mRecordable = true;
return;
}
if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isRecordable())
{
mRecordable = true;
return;
}
if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isRecordable())
{
mRecordable = true;
return;
}
}
/**
* Aggregates a de-duplicated list of broadcast channels from among the primary/secondary to/from aliases
*/
private void determineBroadcastChannels()
{
mBroadcastChannels = new ArrayList<>();
if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mPrimaryAddressTo.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mPrimaryAddressFrom.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mSecondaryAddressTo.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isStreamable())
{
for(BroadcastChannel broadcastChannel: mSecondaryAddressFrom.getAlias().getBroadcastChannels())
{
if(!mBroadcastChannels.contains(broadcastChannel))
{
mBroadcastChannels.add(broadcastChannel);
}
}
}
}
/**
* Creates a deep/full snapshot copy of this metadata set.
*
* The streamable, recordable, audio priority and broadcast channels are not copied. These are lazily determined
* when initially requested.
*/
public Metadata copyOf()
{
Metadata copy = new Metadata(mMetadataID);
copy.mState = mState;
copy.mPrimaryDecoderType = mPrimaryDecoderType;
copy.mChannelFrequency = mChannelFrequency;
copy.mChannelConfigurationSystem = hasChannelConfigurationSystem() ? new String(mChannelConfigurationSystem) : null;
copy.mChannelConfigurationSite = hasChannelConfigurationSite() ? new String(mChannelConfigurationSite) : null;
copy.mChannelConfigurationName = hasChannelConfigurationName() ? new String(mChannelConfigurationName) : null;
copy.mChannelFrequencyLabel = hasChannelFrequencyLabel() ? new String(mChannelFrequencyLabel) : null;
copy.mMessage = hasMessage() ? new String(mMessage) : null;
copy.mMessageType = hasMessageType() ? new String(mMessageType) : null;
copy.mNetworkID1 = mNetworkID1.copyOf();
copy.mNetworkID2 = mNetworkID2.copyOf();
copy.mPrimaryAddressFrom = mPrimaryAddressFrom.copyOf();
copy.mPrimaryAddressTo = mPrimaryAddressTo.copyOf();
copy.mSecondaryAddressFrom = mSecondaryAddressFrom.copyOf();
copy.mSecondaryAddressTo = mSecondaryAddressTo.copyOf();
copy.mUpdated = mUpdated;
//Reset the updated flag
mUpdated = false;
return copy;
}
}

View File

@@ -0,0 +1,287 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import channel.state.State;
import module.decode.DecoderType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
public class MutableMetadata extends Metadata implements Listener<AttributeChangeRequest>
{
private final static Logger mLog = LoggerFactory.getLogger(MutableMetadata.class);
private Broadcaster<MutableMetadataChangeEvent> mMetadataChangeEventBroadcaster = new Broadcaster<>();
/**
* Mutable channel metadata. Contains all attributes that reflect the state and current attribute values for a
* channel that is currently decoding. This metadata is intended to support any decoding channel gui components to
* graphically convey the current state of a decoding channel and to provide audio metadata
*/
public MutableMetadata()
{
super();
}
/**
* Sets the selected state for this metadata. Selected indicates that the user has selected this metadata for
* immediate monitor in the audio panel. When true, this overrides the audio priority for any audio produced
* by the channel and sets that audio to the highest priority.
*
* Any component that toggles the selected state for this metadata is also responsible for clearing the flag.
*/
public void setSelected(boolean selected)
{
mSelected = selected;
mUpdated = true;
}
/**
* Sets the recordable flag to the argument value and overrides any recordable settings in the enclosed aliases.
*/
public void setRecordable(boolean recordable)
{
mRecordable = recordable;
}
/**
* Sets the primary decoder type
*/
public void setPrimaryDecoderType(DecoderType decoderType)
{
mPrimaryDecoderType = decoderType;
mUpdated = true;
}
/**
* Request to change an attribute value for this metadata. This is the primary method for updating all attributes
* in this metadata set.
*/
@Override
public void receive(AttributeChangeRequest request)
{
switch(request.getAttribute())
{
case CHANNEL_CONFIGURATION_SYSTEM:
mChannelConfigurationSystem = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_SYSTEM);
break;
case CHANNEL_CONFIGURATION_SITE:
mChannelConfigurationSite = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_SITE);
break;
case CHANNEL_CONFIGURATION_NAME:
mChannelConfigurationName = request.getStringValue();
broadcast(Attribute.CHANNEL_CONFIGURATION_NAME);
break;
case CHANNEL_FREQUENCY:
if(request.hasValue())
{
mChannelFrequency = request.getLongValue();
}
broadcast(Attribute.CHANNEL_FREQUENCY);
break;
case CHANNEL_FREQUENCY_LABEL:
mChannelFrequencyLabel = request.getStringValue();
broadcast(Attribute.CHANNEL_FREQUENCY_LABEL);
break;
case MESSAGE:
mMessage = request.getStringValue();
broadcast(Attribute.MESSAGE);
break;
case MESSAGE_TYPE:
mMessageType = request.getStringValue();
broadcast(Attribute.MESSAGE_TYPE);
break;
case NETWORK_ID_1:
mNetworkID1.setIdentifier(request.getStringValue());
mNetworkID1.setAlias(request.getAlias());
broadcast(Attribute.NETWORK_ID_1);
break;
case NETWORK_ID_2:
mNetworkID2.setIdentifier(request.getStringValue());
mNetworkID2.setAlias(request.getAlias());
broadcast(Attribute.NETWORK_ID_2);
break;
case PRIMARY_ADDRESS_FROM:
mPrimaryAddressFrom.setIdentifier(request.getStringValue());
mPrimaryAddressFrom.setAlias(request.getAlias());
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
break;
case PRIMARY_ADDRESS_TO:
mPrimaryAddressTo.setIdentifier(request.getStringValue());
mPrimaryAddressTo.setAlias(request.getAlias());
broadcast(Attribute.PRIMARY_ADDRESS_TO);
break;
case PRIMARY_DECODER_TYPE:
mPrimaryDecoderType = request.getDecoderTypeValue();
broadcast(Attribute.PRIMARY_DECODER_TYPE);
break;
case SECONDARY_ADDRESS_FROM:
mSecondaryAddressFrom.setIdentifier(request.getStringValue());
mSecondaryAddressFrom.setAlias(request.getAlias());
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
break;
case SECONDARY_ADDRESS_TO:
mSecondaryAddressTo.setIdentifier(request.getStringValue());
mSecondaryAddressTo.setAlias(request.getAlias());
broadcast(Attribute.SECONDARY_ADDRESS_TO);
break;
case CHANNEL_STATE:
mState = (State) request.getValue();
broadcast(Attribute.CHANNEL_STATE);
break;
default:
throw new IllegalArgumentException("Unrecognized Metadata Attribute: " +
request.getAttribute().name());
}
mUpdated = true;
}
/**
* Resets temporal attributes to null value. This only resets attributes that have a non-ull value in order to
* limit the number of change events that are produced.
*/
public void resetTemporalAttributes()
{
if(hasMessage())
{
mMessage = null;
broadcast(Attribute.MESSAGE);
}
if(hasMessageType())
{
mMessageType = null;
broadcast(Attribute.MESSAGE_TYPE);
}
if(mPrimaryAddressFrom.hasIdentifier())
{
mPrimaryAddressFrom.reset();
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
}
if(mPrimaryAddressTo.hasIdentifier())
{
mPrimaryAddressTo.reset();
broadcast(Attribute.PRIMARY_ADDRESS_TO);
}
if(mSecondaryAddressFrom.hasIdentifier())
{
mSecondaryAddressFrom.reset();
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
}
if(mSecondaryAddressTo.hasIdentifier())
{
mSecondaryAddressTo.reset();
broadcast(Attribute.SECONDARY_ADDRESS_TO);
}
mUpdated = true;
}
/**
* Resets all attributes to empty values
*/
public void resetAllAttributes()
{
resetTemporalAttributes();
if(hasChannelConfigurationSystem())
{
mChannelConfigurationSystem = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_SYSTEM);
}
if(hasChannelConfigurationSite())
{
mChannelConfigurationSite = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_SITE);
}
if(hasChannelConfigurationName())
{
mChannelConfigurationSite = null;
broadcast(Attribute.CHANNEL_CONFIGURATION_NAME);
}
if(hasChannelFrequencyLabel())
{
mChannelFrequencyLabel = null;
broadcast(Attribute.CHANNEL_FREQUENCY_LABEL);
}
if(hasChannelFrequency())
{
mChannelFrequency = 0;
broadcast(Attribute.CHANNEL_FREQUENCY);
}
if(hasPrimaryDecoderType())
{
mPrimaryDecoderType = null;
broadcast(Attribute.PRIMARY_DECODER_TYPE);
}
if(mNetworkID1.hasIdentifier())
{
mNetworkID1.reset();
broadcast(Attribute.NETWORK_ID_1);
}
if(mNetworkID2.hasIdentifier())
{
mNetworkID2.reset();
broadcast(Attribute.NETWORK_ID_2);
}
mUpdated = true;
}
/**
* Broadcasts to registered listeners that an attribute has changed for this metadata
*/
private void broadcast(Attribute attribute)
{
mMetadataChangeEventBroadcaster.broadcast(new MutableMetadataChangeEvent(this, attribute));
}
/**
* Adds the listener to receive metadata change events for this channel metadata
*/
public void addListener(Listener<MutableMetadataChangeEvent> listener)
{
mMetadataChangeEventBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving metadata change events for this channel metadata
*/
public void removeListener(Listener<MutableMetadataChangeEvent> listener)
{
mMetadataChangeEventBroadcaster.removeListener(listener);
}
}

View File

@@ -0,0 +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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
public class MutableMetadataChangeEvent
{
private MutableMetadata mMetadata;
private Attribute mAttribute;
/**
* Channel metadata changed attribute event
*
* @param metadata where the change occurred
* @param attribute that changed
*/
public MutableMetadataChangeEvent(MutableMetadata metadata, Attribute attribute)
{
mMetadata = metadata;
mAttribute = attribute;
}
/**
* Channel metadata where the change occurred
*/
public MutableMetadata getMetadata()
{
return mMetadata;
}
/**
* Channel metadata attribute that has changed
*/
public Attribute getAttribute()
{
return mAttribute;
}
}

View File

@@ -0,0 +1,107 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package channel.state;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.DecoderStateEvent.Event;
import gui.SDRTrunk;
import message.Message;
import module.decode.DecoderType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ScheduledExecutorService;
/**
* Basic decoder channel state - provides the minimum channel state functionality
* to support an always un-squelched audio decoder.
*/
public class AlwaysUnsquelchedDecoderState extends DecoderState
{
private final static Logger mLog = LoggerFactory.getLogger(AlwaysUnsquelchedDecoderState.class);
private DecoderType mDecoderType;
private String mChannelName;
public AlwaysUnsquelchedDecoderState(DecoderType decoderType, String channelName)
{
super(null);
mDecoderType = decoderType;
mChannelName = channelName;
}
@Override
public void init()
{
}
@Override
public void reset()
{
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
sb.append("Activity Summary\n");
sb.append("\tDecoder:\t");
sb.append(mDecoderType);
sb.append("\n\n");
return sb.toString();
}
@Override
public void receive(Message t)
{
/* Not implemented */
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
if(event.getEvent() == Event.RESET)
{
//Each time we're reset, set the PRIMARY TO attribute back to the channel name, otherwise we won't have
//a primary ID for any audio produced by this state.
broadcast(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_TO, mChannelName));
}
}
@Override
public DecoderType getDecoderType()
{
return mDecoderType;
}
@Override
public void start(ScheduledExecutorService executor)
{
broadcast(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_TO, mChannelName));
broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE));
}
@Override
public void stop()
{
}
}

View File

@@ -1,4 +1,4 @@
package module.decode.state;
package channel.state;
import controller.channel.Channel.ChannelType;

View File

@@ -0,0 +1,627 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.state;
import audio.squelch.ISquelchStateProvider;
import audio.squelch.SquelchState;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.IAttributeChangeRequestListener;
import channel.metadata.MutableMetadata;
import channel.state.DecoderStateEvent.Event;
import channel.traffic.TrafficChannelManager;
import controller.channel.Channel.ChannelType;
import module.Module;
import module.decode.DecoderType;
import module.decode.config.DecodeConfiguration;
import module.decode.event.CallEvent;
import module.decode.event.ICallEventProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
public static final long FADE_TIMEOUT_DELAY = 1200;
public static final long RESET_TIMEOUT_DELAY = 2000;
private MutableMetadata mMutableMetadata = new MutableMetadata();
private State mState = State.IDLE;
private Listener<CallEvent> mCallEventListener;
private Listener<DecoderStateEvent> mDecoderStateListener;
private Listener<SquelchState> mSquelchStateListener;
private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver();
private StateMonitor mStateMonitor = new StateMonitor();
private ScheduledFuture<?> mStateMonitorFuture;
private ChannelType mChannelType;
private TrafficChannelManager mTrafficChannelEndListener;
private CallEvent mTrafficChannelCallEvent;
private boolean mSquelchLocked = false;
private boolean mSelected = false;
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
private long mFadeTimeout;
private long mEndTimeout;
/**
* Channel state tracks the overall state of all processing modules and decoders
* configured for the channel and provides squelch control and decoder state
* reset events.
*
* Uses a state enumeration that defines allowable channel state transitions in
* order to track a call or data decode event from start to finish. Uses a
* timer to monitor for inactivity and to provide a FADE period that indicates
* to the user that the activity has stopped while continuing to provide details
* about the call, before the state is reset to IDLE.
*
* Since this class is multi-threaded between decoder events and the internal
* timer that monitors for inactivity, this class uses the mState object for
* thread synchronization.
*
* State Descriptions:
* IDLE Normal state. No voice or data call activity
* CALL/DATA/ENCRYPTED/CONTROL
* Decoding states.
* FADE The phase after a voice or data call when either an explicit
* call end has been received, or when no new signalling updates
* have been received, and the fade timer has expired. This phase
* allows for gui updates to signal to the user that the call is
* ended, while continuing to display the call details for the user
* TEARDOWN Indicates a traffic channel that will be torn down for reuse.
*/
public ChannelState(ChannelType channelType)
{
mChannelType = channelType;
}
/**
* Metadata for this channel containing channel details, primary and secondary entities for call events and any
* decoded messages. This metadata is primarily intended to be used by graphical components to convey the
* details of the current channel state and to provide metadata backing any decoded audio packets produced by
* any audio streams for this channel.
*/
public MutableMetadata getMutableMetadata()
{
return mMutableMetadata;
}
/**
* Resets this channel state and prepares it for reuse.
*/
@Override
public void reset()
{
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
mState = State.IDLE;
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
}
@Override
public void start(ScheduledExecutorService executor)
{
if(mStateMonitorFuture == null && mStateMonitor != null)
{
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
if(mTrafficChannelEndListener != null)
{
setState(State.CALL);
}
try
{
mStateMonitorFuture = executor.scheduleAtFixedRate(mStateMonitor, 0, 20, TimeUnit.MILLISECONDS);
}
catch(RejectedExecutionException ree)
{
mLog.error("state monitor scheduled task rejected", ree);
}
}
}
@Override
public void stop()
{
mMutableMetadata.resetAllAttributes();
if(mStateMonitorFuture != null)
{
boolean success = mStateMonitorFuture.cancel(true);
if(!success)
{
mLog.error("Couldn't stop monitoring scheduled future");
}
}
mTrafficChannelEndListener = null;
if(mTrafficChannelCallEvent != null)
{
mTrafficChannelCallEvent.end();
broadcast(mTrafficChannelCallEvent);
}
mTrafficChannelCallEvent = null;
mStateMonitorFuture = null;
mSquelchLocked = false;
}
public void dispose()
{
mCallEventListener = null;
mDecoderStateListener = null;
mSquelchStateListener = null;
mStateMonitor = null;
}
private boolean isStandardChannel()
{
return mChannelType == ChannelType.STANDARD;
}
private boolean isTrafficChannel()
{
return mChannelType == ChannelType.TRAFFIC;
}
public void setStandardChannelTimeout(long milliseconds)
{
mStandardChannelFadeTimeout = milliseconds;
if(mChannelType == ChannelType.STANDARD)
{
mFadeTimeout = mStandardChannelFadeTimeout;
}
}
public void setTrafficChannelTimeout(long milliseconds)
{
mTrafficChannelFadeTimeout = milliseconds;
if(mChannelType == ChannelType.TRAFFIC)
{
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
}
}
public void setSelected(boolean selected)
{
mSelected = selected;
}
public boolean isSelected()
{
return mSelected;
}
public State getState()
{
return mState;
}
/**
* Updates the fade timeout threshold to the current time plus delay
*/
private void updateFadeTimeout()
{
if(isTrafficChannel())
{
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
}
else
{
mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
}
}
/**
* Updates the reset timeout threshold to the current time plus delay
*/
private void updateResetTimeout()
{
if(isTrafficChannel())
{
mEndTimeout = System.currentTimeMillis();
}
else
{
mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
}
}
/**
* Broadcasts the squelch state to the registered listener
*/
protected void broadcast(SquelchState state)
{
if(mSquelchStateListener != null && !mSquelchLocked)
{
mSquelchStateListener.receive(state);
}
}
/**
* Sets the squelch state listener
*/
@Override
public void setSquelchStateListener(Listener<SquelchState> listener)
{
mSquelchStateListener = listener;
}
/**
* Removes the squelch state listener
*/
@Override
public void removeSquelchStateListener()
{
mSquelchStateListener = null;
}
/**
* Sets the channel state to the specified state, or updates the timeout
* values so that the state monitor will not change state. Broadcasts a
* squelch event when the state changes and the audio squelch state should
* change. Also broadcasts changed attribute and decoder state events so
* that external processes can maintain sync with this channel state.
*/
protected void setState(State state)
{
synchronized(mState)
{
if(state == mState)
{
if(State.CALL_STATES.contains(state))
{
updateFadeTimeout();
}
}
else if(state != mState && mState.canChangeTo(state))
{
switch(state)
{
case CONTROL:
//Don't allow traffic channels to be control channels,
//otherwise they can't transition to call tear down
if(isStandardChannel())
{
broadcast(SquelchState.SQUELCH);
updateFadeTimeout();
mState = state;
}
break;
case DATA:
case ENCRYPTED:
broadcast(SquelchState.SQUELCH);
updateFadeTimeout();
mState = state;
break;
case CALL:
broadcast(SquelchState.UNSQUELCH);
updateFadeTimeout();
mState = state;
break;
case FADE:
processFadeState();
break;
case IDLE:
processIdleState();
break;
case TEARDOWN:
processTeardownState();
break;
case RESET:
mState = State.IDLE;
break;
default:
break;
}
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
}
else
{
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
}
}
}
/**
* Sets the state and processes related actions
*/
private void processFadeState()
{
updateResetTimeout();
mState = State.FADE;
broadcast(SquelchState.SQUELCH);
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
}
private void processIdleState()
{
broadcast(SquelchState.SQUELCH);
if(mState == State.FADE)
{
getMutableMetadata().resetTemporalAttributes();
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
}
mState = State.IDLE;
getMutableMetadata().receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
}
private void processTeardownState()
{
broadcast(SquelchState.SQUELCH);
getMutableMetadata().resetTemporalAttributes();
mState = State.TEARDOWN;
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
if(mTrafficChannelEndListener != null)
{
mTrafficChannelEndListener.callEnd(mTrafficChannelCallEvent.getChannel());
}
}
/**
* Broadcasts the call event to the registered listener
*/
protected void broadcast(CallEvent event)
{
if(mCallEventListener != null)
{
mCallEventListener.receive(event);
}
}
@Override
public void addCallEventListener(Listener<CallEvent> listener)
{
mCallEventListener = listener;
}
@Override
public void removeCallEventListener(Listener<CallEvent> listener)
{
mCallEventListener = null;
}
//NEW:
@Override
public Listener<AttributeChangeRequest> getAttributeChangeRequestListener()
{
return mMutableMetadata;
}
/**
* Broadcasts a channel state event to any registered listeners
*/
protected void broadcast(DecoderStateEvent event)
{
if(mDecoderStateListener != null)
{
mDecoderStateListener.receive(event);
}
}
/**
* Adds a decoder state event listener
*/
@Override
public void setDecoderStateListener(Listener<DecoderStateEvent> listener)
{
mDecoderStateListener = listener;
}
/**
* Removes the decoder state event listener
*/
@Override
public void removeDecoderStateListener()
{
mDecoderStateListener = null;
}
@Override
public Listener<DecoderStateEvent> getDecoderStateListener()
{
return mDecoderStateEventReceiver;
}
/**
* Registers a listener to be notified when a traffic channel call event is
* completed, so that the listener can perform call tear-down
*/
public void configureAsTrafficChannel(TrafficChannelManager manager, CallEvent callEvent)
{
mTrafficChannelEndListener = manager;
mTrafficChannelCallEvent = callEvent;
/* Broadcast the call event details as metadata for the audio manager */
String channel = mTrafficChannelCallEvent.getChannel();
if(channel != null)
{
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL, channel));
}
long frequency = mTrafficChannelCallEvent.getFrequency();
if(frequency > 0)
{
mMutableMetadata.receive(new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY, frequency));
}
mMutableMetadata.receive(new AttributeChangeRequest<DecoderType>(Attribute.PRIMARY_DECODER_TYPE,
mTrafficChannelCallEvent.getDecoderType()));
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_NAME, "TRAFFIC"));
String from = mTrafficChannelCallEvent.getFromID();
if(from != null)
{
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_FROM, from,
mTrafficChannelCallEvent.getFromIDAlias()));
}
String to = mTrafficChannelCallEvent.getToID();
if(to != null)
{
mMutableMetadata.receive(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_TO, to,
mTrafficChannelCallEvent.getToIDAlias()));
}
/* Rebroadcast the allocation event so that the internal decoder states
* can self-configure with the call event details */
broadcast(mTrafficChannelCallEvent);
}
/**
* DecoderStateEvent receiver wrapper
*/
public class DecoderStateEventReceiver implements Listener<DecoderStateEvent>
{
@Override
public void receive(DecoderStateEvent event)
{
if(event.getSource() != this)
{
switch(event.getEvent())
{
case ALWAYS_UNSQUELCH:
broadcast(SquelchState.UNSQUELCH);
mSquelchLocked = true;
break;
case CHANGE_CALL_TIMEOUT:
if(event instanceof ChangeChannelTimeoutEvent)
{
ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent) event;
if(timeout.getChannelType() == ChannelType.STANDARD)
{
setStandardChannelTimeout(timeout.getCallTimeout());
}
else
{
setTrafficChannelTimeout(timeout.getCallTimeout());
}
}
case CONTINUATION:
case DECODE:
case START:
if(State.CALL_STATES.contains(event.getState()))
{
setState(event.getState());
}
break;
case END:
if(isTrafficChannel())
{
setState(State.TEARDOWN);
}
else
{
setState(State.FADE);
}
break;
case RESET:
/* Channel State does not respond to reset events */
break;
default:
break;
}
}
}
}
/**
* Monitors decoder state events to automatically transition the channel
* state to IDLE (standard channel) or to TEARDOWN (traffic channel) when
* decoding stops or the monitored channel returns to a no signal state.
*
* Provides a FADE transition state to allow for momentary decoding dropouts
* and to allow the user access to call details for a fade period upon
* call end.
*/
public class StateMonitor implements Runnable
{
@Override
public void run()
{
try
{
synchronized(mState)
{
if(State.CALL_STATES.contains(mState) &&
mFadeTimeout <= System.currentTimeMillis())
{
processFadeState();
}
else if(mState == State.FADE &&
mEndTimeout <= System.currentTimeMillis())
{
if(isTrafficChannel())
{
processTeardownState();
}
else
{
processIdleState();
}
}
}
}
catch(Exception e)
{
mLog.error("Exception thrown while state monitor is running " +
"- state [" + getState() +
"] current [" + System.currentTimeMillis() +
"] mResetTimeout [" + mEndTimeout +
"] mFadeTimeout [" + mFadeTimeout +
"]", e);
}
}
}
}

View File

@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package module.decode.state;
package channel.state;
public interface ChannelStateSelectionListener

View File

@@ -0,0 +1,198 @@
package channel.state;
import alias.AliasList;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.IAttributeChangeRequestProvider;
import message.IMessageListener;
import message.Message;
import module.Module;
import module.decode.DecoderType;
import module.decode.event.ActivitySummaryProvider;
import module.decode.event.CallEvent;
import module.decode.event.ICallEventProvider;
import sample.Broadcaster;
import sample.Listener;
/**
* Channel state monitors the stream of decoded messages produced by the
* decoder and broadcasts call events as they occur within the decoded message activity.
*
* Provides access to a textual activity summary of events observed.
*/
public abstract class DecoderState extends Module implements ActivitySummaryProvider, Listener<Message>,
IAttributeChangeRequestProvider, ICallEventProvider, IDecoderStateEventListener,
IDecoderStateEventProvider, IMessageListener
{
/* This has to be a broadcaster in order for references to persist */
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
private Listener<DecoderStateEvent> mDecoderStateListener;
private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener();
protected CallEvent mCurrentCallEvent;
private AliasList mAliasList;
public DecoderState(AliasList aliasList)
{
mAliasList = aliasList;
}
public abstract DecoderType getDecoderType();
public AliasList getAliasList()
{
return mAliasList;
}
public boolean hasAliasList()
{
return mAliasList != null;
}
/**
* Reset the decoder state to prepare for processing a different sample
* source
*/
public abstract void reset();
/**
* Allow the decoder to perform any setup actions
*/
public abstract void init();
/**
* Implements the IDecoderStateEventListener interface to receive state
* reset events.
*/
public abstract void receiveDecoderStateEvent(DecoderStateEvent event);
/**
* Disposes any resources or pointers held by this instance to prepare for
* garbage collection
*/
public void dispose()
{
mCallEventBroadcaster.dispose();
mCallEventBroadcaster = null;
mDecoderStateListener = null;
mAliasList = null;
}
/**
* Activity Summary - textual summary of activity observed by the channel state.
*/
public abstract String getActivitySummary();
/**
* Broadcasts a call event to any registered listeners
*/
protected void broadcast(CallEvent event)
{
mCallEventBroadcaster.broadcast(event);
}
/**
* Adds a call event listener
*/
@Override
public void addCallEventListener(Listener<CallEvent> listener)
{
mCallEventBroadcaster.addListener(listener);
}
/**
* Removes the call event listener
*/
@Override
public void removeCallEventListener(Listener<CallEvent> listener)
{
mCallEventBroadcaster.removeListener(listener);
}
@Override
public Listener<DecoderStateEvent> getDecoderStateListener()
{
return mDecoderStateEventListener;
}
private class DecoderStateEventListener implements Listener<DecoderStateEvent>
{
@Override
public void receive(DecoderStateEvent event)
{
receiveDecoderStateEvent(event);
}
}
/**
* Broadcasts a channel state event to any registered listeners
*/
protected void broadcast(DecoderStateEvent event)
{
if(mDecoderStateListener != null)
{
mDecoderStateListener.receive(event);
}
}
/**
* Adds a decoder state event listener
*/
@Override
public void setDecoderStateListener(Listener<DecoderStateEvent> listener)
{
mDecoderStateListener = listener;
}
/**
* Removes the decoder state event listener
*/
@Override
public void removeDecoderStateListener()
{
mDecoderStateListener = null;
}
@Override
public Listener<Message> getMessageListener()
{
return this;
}
/**
* Broadcasts the attribute change request to the registered listener
*/
protected void broadcast(AttributeChangeRequest<?> request)
{
mAttributeChangeRequestBroadcaster.broadcast(request);
}
/**
* Sets the listener to receive attribute change requests from this decoder state
*/
@Override
public void setAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener)
{
mAttributeChangeRequestBroadcaster.addListener(listener);
}
/**
* Removes any listener from receiving attribute change requests
*/
@Override
public void removeAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener)
{
mAttributeChangeRequestBroadcaster.removeListener(listener);
}
/**
* Broadaster so that Attribute monitors can broadcast attribute change requests
*/
protected Listener<AttributeChangeRequest> getAttributeChangeRequestListener()
{
return mAttributeChangeRequestBroadcaster;
}
}

View File

@@ -1,4 +1,4 @@
package module.decode.state;
package channel.state;
public class DecoderStateEvent
{

View File

@@ -1,4 +1,4 @@
package module.decode.state;
package channel.state;
import sample.Listener;

View File

@@ -1,4 +1,4 @@
package module.decode.state;
package channel.state;
import sample.Listener;

View File

@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package module.decode.state;
package channel.state;
import java.util.EnumSet;

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.traffic;
import channel.state.DecoderStateEvent;
import channel.state.State;
import module.decode.event.CallEvent;
/**
* Traffic channel allocation event wraps a call event indicating the channel
* and frequency for a traffic channel allocation event, so that the traffic
* channel manager can respond and allocate a decoder channel and source if
* available, or change the wrapped call event to a call detect event and log it.
*/
public class TrafficChannelAllocationEvent extends DecoderStateEvent
{
private CallEvent mCallEvent;
public TrafficChannelAllocationEvent( Object source, CallEvent callEvent )
{
super( source, Event.TRAFFIC_CHANNEL_ALLOCATION, State.CALL );
mCallEvent = callEvent;
}
public CallEvent getCallEvent()
{
return mCallEvent;
}
}

View File

@@ -0,0 +1,447 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.traffic;
import alias.Alias;
import alias.id.priority.Priority;
import channel.state.DecoderStateEvent;
import channel.state.IDecoderStateEventListener;
import controller.channel.Channel;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEvent.Event;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.TrafficChannelEvent;
import module.Module;
import module.decode.config.DecodeConfiguration;
import module.decode.event.CallEvent;
import module.decode.event.CallEvent.CallEventType;
import module.decode.event.ICallEventProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import record.config.RecordConfiguration;
import sample.Listener;
import source.config.SourceConfigTuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener
{
private final static Logger mLog = LoggerFactory.getLogger(TrafficChannelManager.class);
public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED";
public static final String NO_TUNER_AVAILABLE = "NO TUNER AVAILABLE";
public static final String UNKNOWN_FREQUENCY = "UNKNOWN FREQUENCY";
private int mTrafficChannelPoolMaximumSize = DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT;
private List<Channel> mTrafficChannelPool = new ArrayList<Channel>();
private Map<String,Channel> mTrafficChannelsInUse = new ConcurrentHashMap<String,Channel>();
private DecoderStateEventListener mEventListener = new DecoderStateEventListener();
private Listener<CallEvent> mCallEventListener;
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private DecodeConfiguration mDecodeConfiguration;
private RecordConfiguration mRecordConfiguration;
private String mSystem;
private String mSite;
private String mAliasListName;
private CallEvent mPreviousDoNotMonitorCallEvent;
/**
* Monitors call events and allocates traffic decoder channels in response
* to traffic channel allocation call events. Manages a pool of reusable
* traffic channel allocations.
*
* @param channelModel containing channels currently in use
* @param channelProcessingManager
* @param decodeConfiguration - decoder configuration to use for each
* traffic channel allocation.
* @param recordConfiguration - recording options for each traffic channel
* @param system label to use to describe the system
* @param site label to use to describe the site
* @param aliasListName designated for the channel
* @param 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
*/
public TrafficChannelManager(ChannelModel channelModel,
ChannelProcessingManager channelProcessingManager,
DecodeConfiguration decodeConfiguration,
RecordConfiguration recordConfiguration,
String system,
String site,
String aliasListName,
long trafficChannelTimeout,
int trafficChannelPoolSize)
{
mChannelModel = channelModel;
mChannelProcessingManager = channelProcessingManager;
mDecodeConfiguration = decodeConfiguration;
mRecordConfiguration = recordConfiguration;
mSystem = system;
mSite = site;
mAliasListName = aliasListName;
mTrafficChannelPoolMaximumSize = trafficChannelPoolSize;
}
@Override
public void dispose()
{
for(Channel trafficChannel : mTrafficChannelPool)
{
mChannelModel.broadcast(new ChannelEvent(trafficChannel, Event.REQUEST_DISABLE));
}
mTrafficChannelPool.clear();
mTrafficChannelsInUse.clear();
mCallEventListener = null;
mDecodeConfiguration = null;
mPreviousDoNotMonitorCallEvent = null;
}
/**
* Provides a channel by either reusing an existing channel or constructing
* a new one, limited by the total number of constructed channels allowed.
*
* Note: you must enforce thread safety on the mTrafficChannelsInUse
* external to this method.
*
* @param channelNumber
* @param tunerChannel
* @return
*/
private Channel getChannel(String channelNumber, TunerChannel tunerChannel)
{
Channel channel = null;
if(!mTrafficChannelsInUse.containsKey(channelNumber))
{
for(Channel configuredChannel : mTrafficChannelPool)
{
if(!configuredChannel.getEnabled())
{
channel = configuredChannel;
break;
}
}
if(channel == null && mTrafficChannelPool.size() < mTrafficChannelPoolMaximumSize)
{
channel = new Channel("Traffic", ChannelType.TRAFFIC);
channel.setDecodeConfiguration(mDecodeConfiguration);
channel.setRecordConfiguration(mRecordConfiguration);
channel.setAliasListName(mAliasListName);
mChannelModel.addChannel(channel);
mTrafficChannelPool.add(channel);
}
/* If we have a configured channel, update metadata */
if(channel != null)
{
channel.setSourceConfiguration(new SourceConfigTuner(tunerChannel));
channel.setSystem(mSystem);
channel.setSite(mSite);
channel.setName(channelNumber);
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_CONFIGURATION_CHANGE));
}
}
return channel;
}
/**
* Processes the event and creates a traffic channel is resources are
* available
*/
private void process(TrafficChannelAllocationEvent event)
{
CallEvent callEvent = event.getCallEvent();
/* Check for duplicate events and suppress */
synchronized(mTrafficChannelsInUse)
{
if(mTrafficChannelsInUse.containsKey(callEvent.getChannel()))
{
return;
}
long frequency = callEvent.getFrequency();
/* Check the from/to aliases for do not monitor priority */
if(isDoNotMonitor(callEvent))
{
callEvent.setCallEventType(CallEventType.CALL_DO_NOT_MONITOR);
if(isSameCallEvent(mPreviousDoNotMonitorCallEvent, callEvent))
{
return;
}
else
{
mPreviousDoNotMonitorCallEvent = callEvent;
}
}
else if(frequency > 0)
{
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC,
frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
if(channel != null)
{
TrafficChannelEvent trafficChannelEvent =
new TrafficChannelEvent(this, channel, Event.REQUEST_ENABLE, callEvent);
//Request to enable the channel
mChannelModel.broadcast(trafficChannelEvent);
if(channel.getEnabled())
{
mTrafficChannelsInUse.put(callEvent.getChannel(), channel);
}
else
{
callEvent.setCallEventType(CallEventType.CALL_DETECT);
String details = callEvent.getDetails();
if(details == null || details.isEmpty())
{
callEvent.setDetails(CHANNEL_START_REJECTED);
}
else if(!details.contains(CHANNEL_START_REJECTED))
{
callEvent.setDetails(new StringBuilder(CHANNEL_START_REJECTED).append(" : ")
.append(callEvent.getDetails()).toString());
}
}
}
else
{
callEvent.setCallEventType(CallEventType.CALL_DETECT);
String details = callEvent.getDetails();
if(details == null || details.isEmpty())
{
callEvent.setDetails(NO_TUNER_AVAILABLE);
}
else if(!details.contains(NO_TUNER_AVAILABLE))
{
callEvent.setDetails(new StringBuilder(NO_TUNER_AVAILABLE).append(" : ")
.append(callEvent.getDetails()).toString());
}
}
}
else
{
callEvent.setCallEventType(CallEventType.CALL_DETECT);
String details = callEvent.getDetails();
if(details == null || details.isEmpty())
{
callEvent.setDetails(UNKNOWN_FREQUENCY);
}
else if(!details.contains(UNKNOWN_FREQUENCY))
{
callEvent.setDetails(new StringBuilder(UNKNOWN_FREQUENCY).append(" : ")
.append(callEvent.getDetails()).toString());
}
}
final Listener<CallEvent> listener = mCallEventListener;
if(listener != null)
{
listener.receive(callEvent);
}
}
}
/**
* Compares the call type, channel and to fields for equivalence and the
* from field for either both null, or equivalence.
*
* @param e1
* @param e2
* @return
*/
public static boolean isSameCallEvent(CallEvent e1, CallEvent e2)
{
if(e1 == null || e2 == null)
{
return false;
}
if(e1.getCallEventType() != e2.getCallEventType())
{
return false;
}
if(e1.getChannel() == null || e2.getChannel() == null || !e1.getChannel().contentEquals(e2.getChannel()))
{
return false;
}
if(e1.getToID() == null || e2.getToID() == null || !e1.getToID().contentEquals(e2.getToID()))
{
return false;
}
if(e1.getFromID() == null || e2.getFromID() == null)
{
return (e1.getFromID() == null && e2.getFromID() == null);
}
else if(e1.getFromID().contentEquals(e2.getFromID()))
{
return true;
}
return false;
}
/**
* Checks for aliases that contain a do not follow/process priority.
*
* @param event
* @return - true if there is an alias that has a do not process alias id
*/
private boolean isDoNotMonitor(CallEvent event)
{
Alias to = event.getToIDAlias();
if(to != null && to.hasCallPriority() && to.getCallPriority() == Priority.DO_NOT_MONITOR)
{
return true;
}
Alias from = event.getFromIDAlias();
if(from != null && from.hasCallPriority() && from.getCallPriority() == Priority.DO_NOT_MONITOR)
{
return true;
}
return false;
}
@Override
public Listener<DecoderStateEvent> getDecoderStateListener()
{
return mEventListener;
}
@Override
public void addCallEventListener(Listener<CallEvent> listener)
{
mCallEventListener = listener;
}
@Override
public void removeCallEventListener(Listener<CallEvent> listener)
{
mCallEventListener = null;
}
@Override
public void reset()
{
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
if(!mTrafficChannelsInUse.isEmpty())
{
List<String> channels = new ArrayList<>();
/* Copy the keyset so we don't get concurrent modification of the map */
channels.addAll(mTrafficChannelsInUse.keySet());
for(String channel : channels)
{
callEnd(channel);
}
}
}
/**
* Callback used by the TrafficChannelStatusListener class to signal the
* end of a an allocated traffic channel call event
*
* @param channelNumber - channel number from the call event that signaled
* the start of a traffic channel allocation
*/
public void callEnd(String channelNumber)
{
synchronized(mTrafficChannelsInUse)
{
if(channelNumber != null && mTrafficChannelsInUse.containsKey(channelNumber))
{
Channel channel = mTrafficChannelsInUse.get(channelNumber);
mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_DISABLE));
mTrafficChannelsInUse.remove(channelNumber);
}
}
}
/**
* Wrapper class for the decoder state event listener interface to catch
* traffic channel allocation requests
*/
public class DecoderStateEventListener implements Listener<DecoderStateEvent>
{
@Override
public void receive(DecoderStateEvent event)
{
switch(event.getEvent())
{
case TRAFFIC_CHANNEL_ALLOCATION:
process((TrafficChannelAllocationEvent) event);
break;
default:
break;
}
}
}
}

View File

@@ -1,58 +1,48 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import audio.broadcast.BroadcastModel;
import audio.broadcast.BroadcastPanel;
import icon.IconManager;
import map.MapPanel;
import map.MapService;
import module.decode.event.CallEventPanel;
import module.decode.event.MessageActivityPanel;
import module.decode.state.ChannelList;
import net.miginfocom.swing.MigLayout;
import settings.SettingsManager;
import source.SourceManager;
import source.tuner.TunerModel;
import source.tuner.TunerViewPanel;
import spectrum.ChannelSpectrumPanel;
import alias.AliasController;
import alias.AliasModel;
import audio.AudioManager;
import audio.AudioPanel;
import com.jidesoft.swing.JideSplitPane;
import audio.broadcast.BroadcastModel;
import audio.broadcast.BroadcastPanel;
import audio.broadcast.BroadcastStatusPanel;
import channel.metadata.ChannelMetadataViewer;
import com.jidesoft.swing.JideTabbedPane;
import controller.channel.ChannelController;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.map.ChannelMapModel;
import icon.IconManager;
import map.MapPanel;
import map.MapService;
import net.miginfocom.swing.MigLayout;
import properties.SystemProperties;
import settings.SettingsManager;
import source.SourceManager;
import source.tuner.TunerModel;
import source.tuner.TunerViewPanel;
import javax.swing.*;
import java.awt.*;
public class ControllerPanel extends JPanel
{
@@ -61,14 +51,12 @@ public class ControllerPanel extends JPanel
private AliasController mAliasController;
private AudioPanel mAudioPanel;
private BroadcastPanel mBroadcastPanel;
private CallEventPanel mCallEventPanel;
private ChannelController mChannelController;
private ChannelList mChannelStateList;
private ChannelMetadataViewer mChannelMetadataViewer;
private ChannelModel mChannelModel;
private ChannelSpectrumPanel mChannelSpectrumPanel;
private MapPanel mMapPanel;
private MessageActivityPanel mMessageActivityPanel;
private TunerViewPanel mTunerManagerPanel;
private BroadcastModel mBroadcastModel;
private JideTabbedPane mTabbedPane;
protected JTable mChannelActivityTable = new JTable();
@@ -85,28 +73,21 @@ public class ControllerPanel extends JPanel
SourceManager sourceManager,
TunerModel tunerModel)
{
mBroadcastModel = broadcastModel;
mChannelModel = channelModel;
mAudioPanel = new AudioPanel(iconManager, settingsManager, sourceManager, audioManager);
mMapPanel = new MapPanel(mapService, iconManager, settingsManager);
mChannelMetadataViewer = new ChannelMetadataViewer(channelProcessingManager, iconManager, settingsManager);
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
mMapPanel = new MapPanel(mapService, iconManager, settingsManager);
mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager);
mCallEventPanel = new CallEventPanel(iconManager, channelProcessingManager);
mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager,
channelProcessingManager);
mChannelStateList = new ChannelList(channelModel, channelProcessingManager, iconManager,
settingsManager, mAudioPanel);
mChannelController = new ChannelController(channelModel, channelMapModel,
sourceManager, aliasModel);
mChannelController = new ChannelController(channelModel, channelMapModel, sourceManager, aliasModel);
mAliasController = new AliasController(aliasModel, broadcastModel, iconManager);
mTunerManagerPanel = new TunerViewPanel(tunerModel);
init();
@@ -114,66 +95,24 @@ public class ControllerPanel extends JPanel
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0 ",
"[grow,fill]",
"[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[]0[grow,fill]0[]"));
add(mAudioPanel, "wrap");
//Tabbed View - configuration, calls, messages, map
mTabbedPane = new JideTabbedPane();
mTabbedPane.setFont(this.getFont());
mTabbedPane.setForeground(Color.BLACK);
mTabbedPane.addTab("Tuners", mTunerManagerPanel);
mTabbedPane.addTab("Channels", mChannelController);
mTabbedPane.addTab("Now Playing", mChannelMetadataViewer);
mTabbedPane.addTab("Aliases", mAliasController);
mTabbedPane.addTab("Channel Spectrum", mChannelSpectrumPanel);
mTabbedPane.addTab("Events", mCallEventPanel);
mTabbedPane.addTab("Channels", mChannelController);
mTabbedPane.addTab("Map", mMapPanel);
mTabbedPane.addTab("Messages", mMessageActivityPanel);
mTabbedPane.addTab("Streaming", mBroadcastPanel);
/**
* Change listener to enable/disable the channel spectrum display
* only when the tab is visible, and a channel has been selected
*/
mTabbedPane.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent event)
{
int index = mTabbedPane.getSelectedIndex();
Component component = mTabbedPane.getComponentAt(index);
if (component instanceof ChannelSpectrumPanel)
{
mChannelSpectrumPanel.setEnabled(true);
}
else
{
mChannelSpectrumPanel.setEnabled(false);
}
}
});
/* Register each of the components to receive channel events when the
* channels are selected or change */
mChannelModel.addListener(mCallEventPanel);
mChannelModel.addListener(mChannelStateList);
mChannelModel.addListener(mChannelSpectrumPanel);
mChannelModel.addListener(mMessageActivityPanel);
JScrollPane channelStateListScroll = new JScrollPane();
channelStateListScroll.getViewport().setView(mChannelStateList);
channelStateListScroll.setPreferredSize(new Dimension(400, 500));
mTabbedPane.addTab("Tuners", mTunerManagerPanel);
//Set preferred size to influence the split between these panels
mTabbedPane.setPreferredSize(new Dimension(880, 500));
JideSplitPane channelSplit = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
channelSplit.setDividerSize(5);
channelSplit.add(channelStateListScroll);
channelSplit.add(mTabbedPane);
add(channelSplit);
add(mTabbedPane,"wrap");
}
}

View File

@@ -1,343 +1,343 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package controller.channel;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent.Event;
import module.decode.DecoderType;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.table.AbstractTableModel;
import module.decode.DecoderType;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent.Event;
/**
* Channel Model
*/
public class ChannelModel extends AbstractTableModel implements IChannelEventBroadcaster
{
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
private List<Channel> mChannels = new CopyOnWriteArrayList<>();
private List<Channel> mTrafficChannels = new CopyOnWriteArrayList<>();
private List<ChannelEventListener> mListeners = new CopyOnWriteArrayList<>();
public static final int COLUMN_ENABLED = 0;
public static final int COLUMN_SYSTEM = 1;
public static final int COLUMN_SITE = 2;
public static final int COLUMN_NAME = 3;
public static final int COLUMN_ALIAS_LIST = 4;
public static final int COLUMN_SOURCE = 5;
public static final int COLUMN_DECODER = 6;
public ChannelModel()
{
}
private static final String[] COLUMN_NAMES = new String[] {"Enabled", "System", "Site", "Name", "Alias List",
"Source", "Decoder"};
/**
* Unmodifiable list of non-traffic channels currently in the model
*/
public List<Channel> getChannels()
{
return Collections.unmodifiableList( mChannels );
}
public Channel getChannelAtIndex( int row )
{
if( mChannels.size() >= row )
{
return mChannels.get( row );
}
return null;
}
/**
* Returns a list of unique system values from across the channel set
*/
public List<String> getSystems()
{
List<String> systems = new ArrayList<>();
for( Channel channel: mChannels )
{
if( channel.hasSystem() && !systems.contains( channel.getSystem() ) )
{
systems.add( channel.getSystem() );
}
}
Collections.sort( systems );
return systems;
}
/**
* Returns a list of unique site values for all channels that have a matching
* system value
*/
public List<String> getSites( String system )
{
List<String> sites = new ArrayList<>();
private List<Channel> mChannels = new CopyOnWriteArrayList<>();
private List<Channel> mTrafficChannels = new CopyOnWriteArrayList<>();
private List<ChannelEventListener> mListeners = new CopyOnWriteArrayList<>();
if( system != null )
{
for( Channel channel: mChannels )
{
if( channel.hasSystem() &&
system.equals( channel.getSystem() ) &&
channel.hasSite() &&
!sites.contains( channel.getSite() ) )
{
sites.add( channel.getSite() );
}
}
}
public ChannelModel()
{
}
Collections.sort( sites );
return sites;
}
/**
* Unmodifiable list of non-traffic channels currently in the model
*/
public List<Channel> getChannels()
{
return Collections.unmodifiableList(mChannels);
}
/**
* Broadcasts the channel event to all registered listeners
*/
@Override
public void broadcast( ChannelEvent event )
{
for( ChannelEventListener listener: mListeners )
{
listener.channelChanged( event );
}
if( event.getChannel().getChannelType() == ChannelType.STANDARD )
{
switch( event.getEvent() )
{
case NOTIFICATION_CONFIGURATION_CHANGE:
case NOTIFICATION_PROCESSING_START:
case NOTIFICATION_PROCESSING_STOP:
case NOTIFICATION_SELECTION_CHANGE:
int index = mChannels.indexOf( event.getChannel() );
fireTableRowsUpdated( index, index );
break;
default:
break;
}
}
if( event.getEvent() == Event.REQUEST_DELETE )
{
removeChannel( event.getChannel() );
}
}
public Channel getChannelAtIndex(int row)
{
if(row < mChannels.size())
{
return mChannels.get(row);
}
/**
* Bulk loading of channel list. Each channel is added and a channel add
* event is broadcast.
*/
public void addChannels( List<Channel> channels )
{
for( Channel channel: channels )
{
addChannel( channel );
}
}
return null;
}
/**
* Adds the channel to the model and broadcasts a channel add event
*/
public int addChannel( Channel channel )
{
int index = -1;
switch( channel.getChannelType() )
{
case STANDARD:
mChannels.add( channel );
index = mChannels.size() - 1;
fireTableRowsInserted( index, index );
break;
case TRAFFIC:
mTrafficChannels.add( channel );
index = mChannels.size() - 1;
break;
default:
break;
}
broadcast( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) );
if( channel.getEnabled() )
{
broadcast( new ChannelEvent( channel, Event.REQUEST_ENABLE ) );
}
return index;
}
/**
* Removes the channel from the model and broadcasts a channel remove event
*/
public void removeChannel( Channel channel )
{
if( channel != null )
{
switch( channel.getChannelType() )
{
case STANDARD:
int index = mChannels.indexOf( channel );
mChannels.remove( channel );
/**
* Returns a list of unique system values from across the channel set
*/
public List<String> getSystems()
{
List<String> systems = new ArrayList<>();
if( index >= 0 )
{
fireTableRowsDeleted( index, index );
}
break;
case TRAFFIC:
mTrafficChannels.remove( channel );
break;
default:
break;
}
broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) );
}
}
for(Channel channel : mChannels)
{
if(channel.hasSystem() && !systems.contains(channel.getSystem()))
{
systems.add(channel.getSystem());
}
}
/**
* Returns a list of channels that fall within the frequency range
*
* @param start frequency of the range
* @param stop frequency of the range
*
* @return list of channels or an empty list if none fall within the range
*/
public List<Channel> getChannelsInFrequencyRange( long start, long stop )
{
List<Channel> channels = new ArrayList<>();
for( Channel channel: mChannels )
{
if( channel.isWithin( start, stop ) )
{
channels.add( channel );
}
}
for( Channel channel: mTrafficChannels )
{
if( channel.isWithin( start, stop ) )
{
channels.add( channel );
}
}
return channels;
}
public void addListener( ChannelEventListener listener )
{
mListeners.add( listener );
}
public void removeListener( ChannelEventListener listener )
{
mListeners.remove( listener );
}
Collections.sort(systems);
//Table Model Interface Methods - standard channels only
@Override
public int getRowCount()
{
return mChannels.size();
}
return systems;
}
@Override
public int getColumnCount()
{
return 6;
}
/**
* Returns a list of unique site values for all channels that have a matching
* system value
*/
public List<String> getSites(String system)
{
List<String> sites = new ArrayList<>();
@Override
public String getColumnName( int columnIndex )
{
switch( columnIndex )
{
case 0:
return "System";
case 1:
return "Site";
case 2:
return "Name";
case 3:
return "Alias List";
case 4:
return "Source";
case 5:
return "Decoder";
}
return null;
}
if(system != null)
{
for(Channel channel : mChannels)
{
if(channel.hasSystem() &&
system.equals(channel.getSystem()) &&
channel.hasSite() &&
!sites.contains(channel.getSite()))
{
sites.add(channel.getSite());
}
}
}
@Override
public Class<?> getColumnClass( int columnIndex )
{
return String.class;
}
Collections.sort(sites);
@Override
public boolean isCellEditable( int rowIndex, int columnIndex )
{
return false;
}
return sites;
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
Channel channel = mChannels.get( rowIndex );
switch( columnIndex )
{
case 0:
return channel.getSystem();
case 1:
return channel.getSite();
case 2:
return channel.getName();
case 3:
return channel.getAliasListName();
case 4:
return channel.getSourceConfiguration().getDescription();
case 5:
return channel.getDecodeConfiguration()
.getDecoderType().getShortDisplayString();
}
return null;
}
/**
* Broadcasts the channel event to all registered listeners
*/
@Override
public void broadcast(ChannelEvent event)
{
for(ChannelEventListener listener : mListeners)
{
listener.channelChanged(event);
}
@Override
public void setValueAt( Object aValue, int rowIndex, int columnIndex )
{
throw new IllegalArgumentException( "Not yet implemented" );
}
public void createChannel( DecoderType decoderType, long frequency )
{
throw new UnsupportedOperationException( "Not yet implemented" );
}
if(event.getChannel().getChannelType() == ChannelType.STANDARD)
{
switch(event.getEvent())
{
case NOTIFICATION_CONFIGURATION_CHANGE:
case NOTIFICATION_PROCESSING_START:
case NOTIFICATION_PROCESSING_STOP:
case NOTIFICATION_SELECTION_CHANGE:
int index = mChannels.indexOf(event.getChannel());
fireTableRowsUpdated(index, index);
break;
default:
break;
}
}
if(event.getEvent() == Event.REQUEST_DELETE)
{
removeChannel(event.getChannel());
}
}
/**
* Bulk loading of channel list. Each channel is added and a channel add
* event is broadcast.
*/
public void addChannels(List<Channel> channels)
{
for(Channel channel : channels)
{
addChannel(channel);
}
}
/**
* Adds the channel to the model and broadcasts a channel add event
*/
public int addChannel(Channel channel)
{
int index = -1;
switch(channel.getChannelType())
{
case STANDARD:
mChannels.add(channel);
index = mChannels.size() - 1;
fireTableRowsInserted(index, index);
break;
case TRAFFIC:
mTrafficChannels.add(channel);
index = mChannels.size() - 1;
break;
default:
break;
}
broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD));
if(channel.getEnabled())
{
broadcast(new ChannelEvent(channel, Event.REQUEST_ENABLE));
}
return index;
}
/**
* Removes the channel from the model and broadcasts a channel remove event
*/
public void removeChannel(Channel channel)
{
if(channel != null)
{
switch(channel.getChannelType())
{
case STANDARD:
int index = mChannels.indexOf(channel);
mChannels.remove(channel);
if(index >= 0)
{
fireTableRowsDeleted(index, index);
}
break;
case TRAFFIC:
mTrafficChannels.remove(channel);
break;
default:
break;
}
broadcast(new ChannelEvent(channel, Event.NOTIFICATION_DELETE));
}
}
/**
* Returns a list of channels that fall within the frequency range
*
* @param start frequency of the range
* @param stop frequency of the range
* @return list of channels or an empty list if none fall within the range
*/
public List<Channel> getChannelsInFrequencyRange(long start, long stop)
{
List<Channel> channels = new ArrayList<>();
for(Channel channel : mChannels)
{
if(channel.isWithin(start, stop))
{
channels.add(channel);
}
}
for(Channel channel : mTrafficChannels)
{
if(channel.isWithin(start, stop))
{
channels.add(channel);
}
}
return channels;
}
public void addListener(ChannelEventListener listener)
{
mListeners.add(listener);
}
public void removeListener(ChannelEventListener listener)
{
mListeners.remove(listener);
}
//Table Model Interface Methods - standard channels only
@Override
public int getRowCount()
{
return mChannels.size();
}
@Override
public int getColumnCount()
{
return COLUMN_NAMES.length;
}
@Override
public String getColumnName(int columnIndex)
{
if(columnIndex < COLUMN_NAMES.length)
{
return COLUMN_NAMES[columnIndex];
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
Channel channel = mChannels.get(rowIndex);
switch(columnIndex)
{
case COLUMN_ENABLED:
return channel.getEnabled() ? "Enabled" : null;
case COLUMN_SYSTEM:
return channel.getSystem();
case COLUMN_SITE:
return channel.getSite();
case COLUMN_NAME:
return channel.getName();
case COLUMN_ALIAS_LIST:
return channel.getAliasListName();
case COLUMN_SOURCE:
return channel.getSourceConfiguration().getDescription();
case COLUMN_DECODER:
return channel.getDecodeConfiguration().getDecoderType().getShortDisplayString();
}
return null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
throw new IllegalArgumentException("Not yet implemented");
}
public void createChannel(DecoderType decoderType, long frequency)
{
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -14,50 +14,54 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package controller.channel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import alias.AliasModel;
import audio.AudioPacket;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.ChannelMetadataModel;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent.Event;
import controller.channel.map.ChannelMapModel;
import filter.FilterSet;
import message.Message;
import module.Module;
import module.ProcessingChain;
import module.decode.DecoderFactory;
import module.decode.DecoderType;
import module.decode.event.MessageActivityModel;
import module.log.EventLogManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import record.RecorderManager;
import record.RecorderType;
import sample.Listener;
import source.Source;
import source.SourceException;
import source.SourceManager;
import alias.AliasModel;
import audio.AudioPacket;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent.Event;
import controller.channel.map.ChannelMapModel;
import filter.FilterSet;
import source.SourceType;
import source.config.SourceConfigTuner;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
public class ChannelProcessingManager implements ChannelEventListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelProcessingManager.class);
private Map<Integer, ProcessingChain> mProcessingChains = new HashMap<>();
private Map<Integer,ProcessingChain> mProcessingChains = new HashMap<>();
private List<Listener<AudioPacket>> mAudioPacketListeners = new CopyOnWriteArrayList<>();
private List<Listener<Message>> mMessageListeners = new CopyOnWriteArrayList<>();
private ChannelModel mChannelModel;
private ChannelMapModel mChannelMapModel;
private ChannelMetadataModel mChannelMetadataModel = new ChannelMetadataModel();
private AliasModel mAliasModel;
private EventLogManager mEventLogManager;
private RecorderManager mRecorderManager;
@@ -78,6 +82,14 @@ public class ChannelProcessingManager implements ChannelEventListener
mSourceManager = sourceManager;
}
/**
* Channel metadata model containing metadata for each channel or channel time-slice that is currently processing.
*/
public ChannelMetadataModel getChannelMetadataModel()
{
return mChannelMetadataModel;
}
/**
* Indicates if a processing chain is constructed for the channel and that
* the processing chain is currently processing.
@@ -85,7 +97,7 @@ public class ChannelProcessingManager implements ChannelEventListener
private boolean isProcessing(Channel channel)
{
return mProcessingChains.containsKey(channel.getChannelID()) &&
mProcessingChains.get(channel.getChannelID()).isProcessing();
mProcessingChains.get(channel.getChannelID()).isProcessing();
}
/**
@@ -102,19 +114,19 @@ public class ChannelProcessingManager implements ChannelEventListener
{
Channel channel = event.getChannel();
switch (event.getEvent())
switch(event.getEvent())
{
case REQUEST_ENABLE:
if (!mProcessingChains.containsKey(channel.getChannelID()) ||
!mProcessingChains.get(channel.getChannelID()).isProcessing())
if(!mProcessingChains.containsKey(channel.getChannelID()) ||
!mProcessingChains.get(channel.getChannelID()).isProcessing())
{
startProcessing(event);
}
break;
case REQUEST_DISABLE:
if (channel.getEnabled())
if(channel.getEnabled())
{
switch (channel.getChannelType())
switch(channel.getChannelType())
{
case STANDARD:
stopProcessing(channel, true);
@@ -129,13 +141,13 @@ public class ChannelProcessingManager implements ChannelEventListener
}
}
case NOTIFICATION_DELETE:
if (channel.getEnabled())
if(channel.getEnabled())
{
stopProcessing(channel, true);
}
break;
case NOTIFICATION_CONFIGURATION_CHANGE:
if (isProcessing(channel))
if(isProcessing(channel))
{
stopProcessing(channel, false);
startProcessing(event);
@@ -153,7 +165,7 @@ public class ChannelProcessingManager implements ChannelEventListener
ProcessingChain processingChain = mProcessingChains.get(channel.getChannelID());
//If we're already processing, ignore the request
if (processingChain != null && processingChain.isProcessing())
if(processingChain != null && processingChain.isProcessing())
{
return;
}
@@ -164,63 +176,79 @@ public class ChannelProcessingManager implements ChannelEventListener
try
{
source = mSourceManager.getSource(channel.getSourceConfiguration(),
channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth());
channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth());
}
catch (SourceException se)
catch(SourceException se)
{
mLog.debug("Error obtaining source for channel [" + channel.getName() + "]", se);
}
if (source == null)
if(source == null)
{
channel.setEnabled(false);
mChannelModel.broadcast(
new ChannelEvent(channel, Event.NOTIFICATION_ENABLE_REJECTED));
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ENABLE_REJECTED));
return;
}
if (processingChain == null)
if(processingChain == null)
{
processingChain = new ProcessingChain(channel.getName(),
channel.getChannelType());
processingChain = new ProcessingChain(channel.getName(), channel.getChannelType());
/* Register global listeners */
for (Listener<AudioPacket> listener : mAudioPacketListeners)
for(Listener<AudioPacket> listener : mAudioPacketListeners)
{
processingChain.addAudioPacketListener(listener);
}
for (Listener<Message> listener : mMessageListeners)
for(Listener<Message> listener : mMessageListeners)
{
processingChain.addMessageListener(listener);
}
/* Processing Modules */
List<Module> modules = DecoderFactory.getModules(mChannelModel,
mChannelMapModel, this, mAliasModel, channel);
List<Module> modules = DecoderFactory.getModules(mChannelModel, mChannelMapModel, this,
mAliasModel, channel, processingChain.getChannelState().getMutableMetadata());
processingChain.addModules(modules);
/* Setup message activity model with filtering */
FilterSet<Message> messageFilter = DecoderFactory.getMessageFilters(modules);
MessageActivityModel messageModel = new MessageActivityModel(messageFilter);
processingChain.setMessageActivityModel(messageModel);
/* Inject channel metadata that will be inserted into audio packets
* for the recorder manager and streaming */
processingChain.broadcast(
new Metadata(MetadataType.SYSTEM, channel.getSystem()));
processingChain.broadcast(
new Metadata(MetadataType.SITE_ID, channel.getSite()));
processingChain.broadcast(
new Metadata(MetadataType.CHANNEL_NAME, channel.getName()));
//Override recordable flag in metadata if user has specified audio recording
if(channel.getRecordConfiguration() != null &&
channel.getRecordConfiguration().getRecorders().contains(RecorderType.AUDIO))
{
processingChain.getChannelState().getMutableMetadata().setRecordable(true);
}
//Inject channel metadata that will be inserted into audio packets for recorder manager and streaming
processingChain.getChannelState().getMutableMetadata().receive(
new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_SYSTEM, channel.getSystem()));
processingChain.getChannelState().getMutableMetadata().receive(
new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_SITE, channel.getSite()));
processingChain.getChannelState().getMutableMetadata().receive(
new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_NAME, channel.getName()));
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
{
long frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency();
processingChain.getChannelState().getMutableMetadata().receive(
new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY, frequency));
}
processingChain.getChannelState().getMutableMetadata().receive(
new AttributeChangeRequest<DecoderType>(Attribute.PRIMARY_DECODER_TYPE,
channel.getDecodeConfiguration().getDecoderType()));
}
/* Setup event logging */
List<Module> loggers = mEventLogManager.getLoggers(channel.getEventLogConfiguration(), channel.getName());
if (!loggers.isEmpty())
if(!loggers.isEmpty())
{
processingChain.addModules(loggers);
}
@@ -228,16 +256,16 @@ public class ChannelProcessingManager implements ChannelEventListener
/* Setup recorders */
List<RecorderType> recorders = channel.getRecordConfiguration().getRecorders();
if (!recorders.isEmpty())
if(!recorders.isEmpty())
{
/* Add baseband recorder */
if ((recorders.contains(RecorderType.BASEBAND) && channel.getChannelType() == ChannelType.STANDARD))
/* Add baseband recorder */
if((recorders.contains(RecorderType.BASEBAND) && channel.getChannelType() == ChannelType.STANDARD))
{
processingChain.addModule(mRecorderManager.getBasebandRecorder(channel.toString()));
}
/* Add traffic channel baseband recorder */
if (recorders.contains(RecorderType.TRAFFIC_BASEBAND) && channel.getChannelType() == ChannelType.TRAFFIC)
if(recorders.contains(RecorderType.TRAFFIC_BASEBAND) && channel.getChannelType() == ChannelType.TRAFFIC)
{
processingChain.addModule(mRecorderManager.getBasebandRecorder(channel.toString()));
}
@@ -245,33 +273,36 @@ public class ChannelProcessingManager implements ChannelEventListener
processingChain.setSource(source);
if (event instanceof TrafficChannelEvent)
if(event instanceof TrafficChannelEvent)
{
TrafficChannelEvent trafficChannelEvent = (TrafficChannelEvent) event;
processingChain.getChannelState().configureAsTrafficChannel(
trafficChannelEvent.getTrafficChannelManager(),
trafficChannelEvent.getCallEvent());
trafficChannelEvent.getTrafficChannelManager(),
trafficChannelEvent.getCallEvent());
}
processingChain.start();
getChannelMetadataModel().add(processingChain.getChannelState().getMutableMetadata(), channel);
channel.setEnabled(true);
mProcessingChains.put(channel.getChannelID(), processingChain);
mChannelModel.broadcast(
new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_START));
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_START));
}
private void stopProcessing(Channel channel, boolean remove)
{
channel.setEnabled(false);
if (mProcessingChains.containsKey(channel.getChannelID()))
if(mProcessingChains.containsKey(channel.getChannelID()))
{
ProcessingChain processingChain = mProcessingChains.get(channel.getChannelID());
getChannelMetadataModel().remove(processingChain.getChannelState().getMutableMetadata());
processingChain.stop();
processingChain.removeEventLoggingModules();
@@ -281,7 +312,7 @@ public class ChannelProcessingManager implements ChannelEventListener
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_STOP));
if (remove)
if(remove)
{
mProcessingChains.remove(channel.getChannelID());

View File

@@ -11,7 +11,7 @@ import javax.swing.JSeparator;
import module.ProcessingChain;
import module.decode.event.ActivitySummaryFrame;
import module.decode.state.DecoderState;
import channel.state.DecoderState;
import controller.channel.ChannelEvent.Event;
public class ChannelUtils

View File

@@ -1,7 +1,7 @@
package controller.channel;
import module.decode.event.CallEvent;
import module.decode.state.TrafficChannelManager;
import channel.traffic.TrafficChannelManager;
public class TrafficChannelEvent extends ChannelEvent
{

View File

@@ -1,155 +1,157 @@
package controller.channel.map;
import controller.channel.map.ChannelMapEvent.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractListModel;
import controller.channel.map.ChannelMapEvent.Event;
import sample.Broadcaster;
import sample.Listener;
public class ChannelMapModel extends AbstractListModel<ChannelMap>
{
private static final long serialVersionUID = 1L;
private List<ChannelMap> mChannelMaps = new ArrayList<>();
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(ChannelMapModel.class);
public ChannelMapModel()
{
}
private List<ChannelMap> mChannelMaps = new ArrayList<>();
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
/**
* Returns an unmodifiable list of channel maps currently in the model
*/
public List<ChannelMap> getChannelMaps()
{
return Collections.unmodifiableList( mChannelMaps );
}
public ChannelMapModel()
{
}
/**
* Returns the channel map with a matching name or null
*/
public ChannelMap getChannelMap( String name )
{
for( ChannelMap channelMap: mChannelMaps )
{
if( channelMap.getName().equalsIgnoreCase( name ) )
{
return channelMap;
}
}
return null;
}
/**
* Returns an unmodifiable list of channel maps currently in the model
*/
public List<ChannelMap> getChannelMaps()
{
return Collections.unmodifiableList(mChannelMaps);
}
/**
* Adds a listener to receive notifications when channel map updates occur
*/
public void addListener( Listener<ChannelMapEvent> listener )
{
mEventBroadcaster.addListener( listener );
}
/**
* Returns the channel map with a matching name or null
*/
public ChannelMap getChannelMap(String name)
{
for(ChannelMap channelMap : mChannelMaps)
{
if(channelMap.getName().equalsIgnoreCase(name))
{
return channelMap;
}
}
/**
* Removes the channel map update notification listener
*/
public void removeListener( Listener<ChannelMapEvent> listener )
{
mEventBroadcaster.removeListener( listener );
}
return null;
}
/**
* Broadcasts a channel map change.
*
* Note: use the add/remove methods to add or remove channel maps from this
* model. When using those methods, an add or delete event will automatically
* be generated.
*/
public void broadcast( ChannelMapEvent event )
{
if( event.getEvent() == ChannelMapEvent.Event.CHANGE ||
event.getEvent() == ChannelMapEvent.Event.RENAME )
{
int index = mChannelMaps.indexOf( event.getChannelMap() );
if( index >= 0 )
{
fireContentsChanged( this, index, index );
}
}
mEventBroadcaster.broadcast( event );
}
/**
* Adds a list of channel maps to this model
*/
public void addChannelMaps( List<ChannelMap> channelMaps )
{
for( ChannelMap channelMap: channelMaps )
{
addChannelMap( channelMap );
}
}
/**
* Adds a listener to receive notifications when channel map updates occur
*/
public void addListener(Listener<ChannelMapEvent> listener)
{
mEventBroadcaster.addListener(listener);
}
/**
* Adds the channel map to this model
*/
public void addChannelMap( ChannelMap channelMap )
{
if( !mChannelMaps.contains( channelMap ) )
{
mChannelMaps.add( channelMap );
int index = mChannelMaps.indexOf( channelMap );
fireIntervalAdded( this, index, index );
broadcast( new ChannelMapEvent( channelMap, Event.ADD ) );
}
}
/**
* Removes the channel map update notification listener
*/
public void removeListener(Listener<ChannelMapEvent> listener)
{
mEventBroadcaster.removeListener(listener);
}
/**
* Removes the channel map from this model
*/
public void removeChannelMap( ChannelMap channelMap )
{
if( mChannelMaps.contains( channelMap ) )
{
int index = mChannelMaps.indexOf( channelMap );
mChannelMaps.remove( channelMap );
fireIntervalRemoved( this, index, index );
/**
* Broadcasts a channel map change.
*
* Note: use the add/remove methods to add or remove channel maps from this
* model. When using those methods, an add or delete event will automatically
* be generated.
*/
public void broadcast(ChannelMapEvent event)
{
if(event.getEvent() == ChannelMapEvent.Event.CHANGE ||
event.getEvent() == ChannelMapEvent.Event.RENAME)
{
int index = mChannelMaps.indexOf(event.getChannelMap());
broadcast( new ChannelMapEvent( channelMap, Event.DELETE ) );
}
}
if(index >= 0)
{
fireContentsChanged(this, index, index);
}
}
/**
* Size/Number of channel maps in this model
*/
@Override
public int getSize()
{
return mChannelMaps.size();
}
mEventBroadcaster.broadcast(event);
}
/**
* Returns the channel map at the specified index
*/
@Override
public ChannelMap getElementAt( int index )
{
if( index <= mChannelMaps.size() )
{
return mChannelMaps.get( index );
}
return null;
}
/**
* Adds a list of channel maps to this model
*/
public void addChannelMaps(List<ChannelMap> channelMaps)
{
for(ChannelMap channelMap : channelMaps)
{
addChannelMap(channelMap);
}
}
/**
* Adds the channel map to this model
*/
public void addChannelMap(ChannelMap channelMap)
{
if(!mChannelMaps.contains(channelMap))
{
mChannelMaps.add(channelMap);
int index = mChannelMaps.indexOf(channelMap);
fireIntervalAdded(this, index, index);
broadcast(new ChannelMapEvent(channelMap, Event.ADD));
}
}
/**
* Removes the channel map from this model
*/
public void removeChannelMap(ChannelMap channelMap)
{
if(mChannelMaps.contains(channelMap))
{
int index = mChannelMaps.indexOf(channelMap);
mChannelMaps.remove(channelMap);
fireIntervalRemoved(this, index, index);
broadcast(new ChannelMapEvent(channelMap, Event.DELETE));
}
}
/**
* Size/Number of channel maps in this model
*/
@Override
public int getSize()
{
return mChannelMaps.size();
}
/**
* Returns the channel map at the specified index
*/
@Override
public ChannelMap getElementAt(int index)
{
if(index <= mChannelMaps.size())
{
return mChannelMaps.get(index);
}
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -51,25 +51,25 @@ public class FilterViewer extends Application
// .stopBandRipple( 0.027 )
// .build();
FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
.sampleRate( 8000 )
.stopBandCutoff( 250 )
.stopBandAmplitude( 0.0 )
.stopBandRipple( 0.01 )
.passBandStart( 300 )
.passBandAmplitude( 1.0 )
.passBandRipple( 0.01 )
.build();
// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
// .sampleRate( 8000 )
// .stopBandCutoff( 250 )
// .stopBandAmplitude( 0.0 )
// .stopBandRipple( 0.01 )
// .passBandStart( 300 )
// .passBandAmplitude( 1.0 )
// .passBandRipple( 0.01 )
// .build();
// FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
// .sampleRate( 48000 )
// .stopFrequency1( 150 )
// .passFrequencyBegin( 200 )
// .passFrequencyEnd( 3950 )
// .stopFrequency2( 4000 )
// .stopRipple( 0.0005 )
// .passRipple( 0.003)
// .build();
FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
.sampleRate( 48000 )
.stopFrequency1( 200 )
.passFrequencyBegin( 400 )
.passFrequencyEnd( 3200 )
.stopFrequency2( 3400 )
.stopRipple( 0.0003 )
.passRipple( 0.008)
.build();
float[] taps = null;

View File

@@ -1,296 +1,302 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package dsp.fsk;
import buffer.BooleanAveragingBuffer;
import dsp.symbol.SymbolEvent;
import dsp.symbol.SymbolEvent.Shift;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.TapGroup;
import instrument.tap.stream.SymbolEventTap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.real.RealBuffer;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import sample.Listener;
import sample.real.RealBuffer;
import buffer.BooleanAveragingBuffer;
import dsp.symbol.SymbolEvent;
import dsp.symbol.SymbolEvent.Shift;
/**
* Binary Frequency Shift Keying (FSK) decoder. Implements a BFSK correlation
* decoder. Provides normal or inverted decoded output. Requires the symbol
* rate to be an integer multiple of the sample rate.
*
* Implements a correlation decoder that correlates a modulated FSK signal upon
* against a one-baud delayed version of itself, converting all float samples to
* binary (0 or 1), and uses XOR to produce the correlation value. Low pass
*
* Implements a correlation decoder that correlates a modulated FSK signal upon
* against a one-baud delayed version of itself, converting all float samples to
* binary (0 or 1), and uses XOR to produce the correlation value. Low pass
* filtering smoothes the correlated output.
*
*
* Automatically aligns to symbol timing during symbol transitions (0 to 1, or
* 1 to 0) by inspecting the samples at the symbol edges and advancing or
* 1 to 0) by inspecting the samples at the symbol edges and advancing or
* retarding symbol window to maintain continuous symbol alignment.
*
*
* Use a DC-removal filter prior to this decoder to ensure samples don't have
* a DC component.
*
*
* Implements instrumentable interface, so that slice events can be received
* externally to analyze decoder performance.
*/
public class FSK2Decoder implements Instrumentable, Listener<RealBuffer>
{
public enum Output{ NORMAL, INVERTED };
private final static Logger mLog = LoggerFactory.getLogger(FSK2Decoder.class);
/* Instrumentation taps */
private static final String INSTRUMENT_DECISION =
"Tap Point: FSK2 Symbol Decision";
private List<TapGroup> mAvailableTaps;
private ArrayList<SymbolEventTap> mTaps = new ArrayList<SymbolEventTap>();
private Listener<Boolean> mListener;
private BooleanAveragingBuffer mDelayBuffer;
private BooleanAveragingBuffer mLowPassFilter;
private Slicer mSlicer;
private boolean mNormalOutput;
private int mSamplesPerSymbol;
private int mSymbolRate;
public FSK2Decoder( int sampleRate, int symbolRate, Output output )
{
/* Ensure we're using an integral symbolRate of the sampleRate */
assert( sampleRate % symbolRate == 0 );
mSamplesPerSymbol = (int)( sampleRate / symbolRate );
mNormalOutput = ( output == Output.NORMAL );
mSymbolRate = symbolRate;
mDelayBuffer = new BooleanAveragingBuffer( mSamplesPerSymbol );
mLowPassFilter = new BooleanAveragingBuffer( mSamplesPerSymbol );
mSlicer = new Slicer( mSamplesPerSymbol );
}
/**
* Disposes of all references to prepare for garbage collection
*/
public void dispose()
{
mListener = null;
}
public enum Output
{
NORMAL, INVERTED
}
/**
* Primary sample input
*/
@Override
public void receive( RealBuffer buffer )
{
for( float sample: buffer.getSamples() )
{
/* Instrumentation taps */
private static final String INSTRUMENT_DECISION =
"Tap Point: FSK2 Symbol Decision";
private List<TapGroup> mAvailableTaps;
private ArrayList<SymbolEventTap> mTaps = new ArrayList<SymbolEventTap>();
private Listener<Boolean> mListener;
private BooleanAveragingBuffer mDelayBuffer;
private BooleanAveragingBuffer mLowPassFilter;
private Slicer mSlicer;
private boolean mNormalOutput;
private int mSamplesPerSymbol;
private int mSymbolRate;
public FSK2Decoder(int sampleRate, int symbolRate, Output output)
{
/* Ensure we're using an integral symbolRate of the sampleRate */
assert (sampleRate % symbolRate == 0);
mSamplesPerSymbol = (int) (sampleRate / symbolRate);
mNormalOutput = (output == Output.NORMAL);
mSymbolRate = symbolRate;
mDelayBuffer = new BooleanAveragingBuffer(mSamplesPerSymbol);
mLowPassFilter = new BooleanAveragingBuffer(mSamplesPerSymbol);
mSlicer = new Slicer(mSamplesPerSymbol);
}
/**
* Disposes of all references to prepare for garbage collection
*/
public void dispose()
{
mListener = null;
}
/**
* Primary sample input
*/
@Override
public void receive(RealBuffer buffer)
{
for(float sample : buffer.getSamples())
{
/* Square the sample. Greater than zero is a 1 (true) and less than
* zero is a 0 (false) */
boolean bitSample = ( sample >= 0.0f );
boolean bitSample = (sample >= 0.0f);
/* Feed the delay buffer and fetch the one-baud delayed sample */
boolean delayedBitSample = mDelayBuffer.get( bitSample );
boolean delayedBitSample = mDelayBuffer.get(bitSample);
/* Correlation: xor current bit with delayed bit */
boolean softBit = bitSample ^ delayedBitSample;
boolean softBit = bitSample ^ delayedBitSample;
/* Low pass filter to smooth the correlated values */
boolean filteredSoftBit = mLowPassFilter.getAverage( softBit );
boolean filteredSoftBit = mLowPassFilter.getAverage(softBit);
/* Send the filtered correlated bit to the slicer */
mSlicer.receive( filteredSoftBit );
}
}
mSlicer.receive(filteredSoftBit);
}
}
/**
* Registers a listener to receive the decoded FSK bits
*/
public void setListener( Listener<Boolean> listener )
{
mListener = listener;
}
/**
* Registers a listener to receive the decoded FSK bits
*/
public void setListener(Listener<Boolean> listener)
{
mListener = listener;
}
/**
* Removes the listener
*/
public void removeListener( Listener<Boolean> listener )
{
mListener = null;
}
/**
* Removes the listener
*/
public void removeListener(Listener<Boolean> listener)
{
mListener = null;
}
/**
* Symbol slicer with auto-aligning baud timing
*/
public class Slicer
{
private BitSet mBitSet = new BitSet();
private int mSymbolLength;
private int mDecisionThreshold;
private int mSampleCounter;
public Slicer( int samplesPerSymbol )
{
mSymbolLength = samplesPerSymbol;
mDecisionThreshold = (int)( mSymbolLength / 2 );
/**
* Symbol slicer with auto-aligning baud timing
*/
public class Slicer
{
private BitSet mBitSet = new BitSet();
private int mSymbolLength;
private int mDecisionThreshold;
private int mSampleCounter;
public Slicer(int samplesPerSymbol)
{
mSymbolLength = samplesPerSymbol;
mDecisionThreshold = (int) (mSymbolLength / 2);
/* Adjust for an odd number of samples per baud */
if( mSymbolLength % 2 == 1 )
{
mDecisionThreshold++;
}
}
public void receive( boolean softBit )
{
if( mSampleCounter >= 0 )
{
if( softBit )
{
mBitSet.set( mSampleCounter );
}
else
{
mBitSet.clear( mSampleCounter );
}
}
mSampleCounter++;
if( mSampleCounter >= mSymbolLength )
{
boolean decision = mBitSet.cardinality() >= mDecisionThreshold;
send( decision );
if(mSymbolLength % 2 == 1)
{
mDecisionThreshold++;
}
}
public void receive(boolean softBit)
{
if(mSampleCounter >= 0)
{
if(softBit)
{
mBitSet.set(mSampleCounter);
}
else
{
mBitSet.clear(mSampleCounter);
}
}
mSampleCounter++;
if(mSampleCounter >= mSymbolLength)
{
boolean decision = mBitSet.cardinality() >= mDecisionThreshold;
send(decision);
/* Shift timing left if the left bit in the bitset is opposite
* the decision and the right bit is the same */
if( ( mBitSet.get( 0 ) ^ decision ) &&
( !( mBitSet.get( mSymbolLength - 1 ) ^ decision ) ) )
{
sendTapEvent( mBitSet, Shift.LEFT, decision );
reset();
mSampleCounter--;
}
if((mBitSet.get(0) ^ decision) &&
(!(mBitSet.get(mSymbolLength - 1) ^ decision)))
{
sendTapEvent(mBitSet, Shift.LEFT, decision);
reset();
mSampleCounter--;
}
/* Shift timing right if the left bit is the same as the
* decision and the right bit is opposite */
else if( ( !( mBitSet.get( 0 ) ^ decision ) ) &&
( mBitSet.get( mSymbolLength - 1 ) ^ decision ) )
{
sendTapEvent( mBitSet, Shift.RIGHT, decision );
else if((!(mBitSet.get(0) ^ decision)) &&
(mBitSet.get(mSymbolLength - 1) ^ decision))
{
sendTapEvent(mBitSet, Shift.RIGHT, decision);
/* Last bit from previous symbol to pre-fill next symbol */
boolean previousSoftBit = mBitSet.get( mSymbolLength - 1 );
reset();
if( previousSoftBit )
{
mBitSet.set( 0 );
}
mSampleCounter++;
}
boolean previousSoftBit = mBitSet.get(mSymbolLength - 1);
reset();
if(previousSoftBit)
{
mBitSet.set(0);
}
mSampleCounter++;
}
/* No shift */
else
{
sendTapEvent( mBitSet, Shift.NONE, decision );
reset();
}
}
}
/**
* Sends the bit decision to the listener
*/
private void send( boolean decision )
{
if( mListener != null )
{
mListener.receive( mNormalOutput ? decision : !decision );
}
}
private void reset()
{
mBitSet.clear();
mSampleCounter = 0;
}
else
{
sendTapEvent(mBitSet, Shift.NONE, decision);
/**
* Sends instrumentation tap event to all registered listeners
*/
private void sendTapEvent( BitSet bitset, Shift shift, boolean decision )
{
for( SymbolEventTap tap: mTaps )
{
SymbolEvent event =
new SymbolEvent( bitset.get( 0, mSymbolLength ),
mSymbolLength,
decision,
shift );
tap.receive( event );
}
}
}
reset();
}
}
}
/**
* Get instrumentation taps
*/
@Override
/**
* Sends the bit decision to the listener
*/
private void send(boolean decision)
{
if(mListener != null)
{
mListener.receive(mNormalOutput ? decision : !decision);
}
}
private void reset()
{
mBitSet.clear();
mSampleCounter = 0;
}
/**
* Sends instrumentation tap event to all registered listeners
*/
private void sendTapEvent(BitSet bitset, Shift shift, boolean decision)
{
for(SymbolEventTap tap : mTaps)
{
SymbolEvent event =
new SymbolEvent(bitset.get(0, mSymbolLength),
mSymbolLength,
decision,
shift);
tap.receive(event);
}
}
}
/**
* Get instrumentation taps
*/
@Override
public List<TapGroup> getTapGroups()
{
if( mAvailableTaps == null )
{
mAvailableTaps = new ArrayList<TapGroup>();
TapGroup tapGroup = new TapGroup( "FSK2 Decoder" );
tapGroup.add( new SymbolEventTap( INSTRUMENT_DECISION, 0, .025f ) );
mAvailableTaps.add( tapGroup );
}
return mAvailableTaps;
if(mAvailableTaps == null)
{
mAvailableTaps = new ArrayList<TapGroup>();
TapGroup tapGroup = new TapGroup("FSK2 Decoder");
tapGroup.add(new SymbolEventTap(INSTRUMENT_DECISION, 0, .025f));
mAvailableTaps.add(tapGroup);
}
return mAvailableTaps;
}
/**
* Add instrumentation tap
*/
@Override
public void registerTap( Tap tap )
/**
* Add instrumentation tap
*/
@Override
public void registerTap(Tap tap)
{
if( tap instanceof SymbolEventTap && !mTaps.contains( tap ) )
{
mTaps.add( (SymbolEventTap)tap );
}
if(tap instanceof SymbolEventTap && !mTaps.contains(tap))
{
mTaps.add((SymbolEventTap) tap);
}
}
/**
* Remove instrumentation tap
*/
@Override
public void unregisterTap( Tap tap )
/**
* Remove instrumentation tap
*/
@Override
public void unregisterTap(Tap tap)
{
mTaps.remove( tap );
mTaps.remove(tap);
}
}

View File

@@ -21,6 +21,7 @@ import alias.AliasModel;
import alias.action.AliasActionManager;
import audio.AudioManager;
import audio.broadcast.BroadcastModel;
import audio.broadcast.BroadcastStatusPanel;
import com.jidesoft.swing.JideSplitPane;
import controller.ControllerPanel;
import controller.ThreadPoolManager;
@@ -65,12 +66,17 @@ import java.util.concurrent.TimeUnit;
public class SDRTrunk implements Listener<TunerEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class);
private static final String PROPERTY_BROADCAST_STATUS_VISIBLE = "main.broadcast.status.visible";
private boolean mBroadcastStatusVisible;
private IconManager mIconManager;
private BroadcastStatusPanel mBroadcastStatusPanel;
private BroadcastModel mBroadcastModel;
private ControllerPanel mControllerPanel;
private SettingsManager mSettingsManager;
private SpectralDisplayPanel mSpectralPanel;
private JFrame mMainGui = new JFrame();
private JideSplitPane mSplitPane;
private String mTitle;
@@ -139,14 +145,14 @@ public class SDRTrunk implements Listener<TunerEvent>
AudioManager audioManager = new AudioManager(threadPoolManager, sourceManager.getMixerManager());
channelProcessingManager.addAudioPacketListener(audioManager);
BroadcastModel broadcastModel = new BroadcastModel(threadPoolManager, mIconManager);
mBroadcastModel = new BroadcastModel(threadPoolManager, mIconManager);
channelProcessingManager.addAudioPacketListener(broadcastModel);
channelProcessingManager.addAudioPacketListener(mBroadcastModel);
MapService mapService = new MapService(mIconManager);
channelProcessingManager.addMessageListener(mapService);
mControllerPanel = new ControllerPanel(audioManager, aliasModel, broadcastModel,
mControllerPanel = new ControllerPanel(audioManager, aliasModel, mBroadcastModel,
channelModel, channelMapModel, channelProcessingManager, mIconManager,
mapService, mSettingsManager, sourceManager, tunerModel);
@@ -160,7 +166,7 @@ public class SDRTrunk implements Listener<TunerEvent>
tunerModel.addListener(this);
PlaylistManager playlistManager = new PlaylistManager(threadPoolManager,
aliasModel, broadcastModel, channelModel, channelMapModel);
aliasModel, mBroadcastModel, channelModel, channelMapModel);
playlistManager.init();
@@ -209,9 +215,7 @@ public class SDRTrunk implements Listener<TunerEvent>
*/
private void initGUI()
{
mMainGui.setLayout(new MigLayout("insets 0 0 0 0 ",
"[grow,fill]",
"[grow,fill]"));
mMainGui.setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
/**
* Setup main JFrame window
@@ -225,12 +229,20 @@ public class SDRTrunk implements Listener<TunerEvent>
mSpectralPanel.setPreferredSize(new Dimension(1280, 300));
mControllerPanel.setPreferredSize(new Dimension(1280, 500));
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
splitPane.setDividerSize(5);
splitPane.add(mSpectralPanel);
splitPane.add(mControllerPanel);
mSplitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
mSplitPane.setDividerSize(5);
mSplitPane.add(mSpectralPanel);
mSplitPane.add(mControllerPanel);
mMainGui.add(splitPane, "cell 0 0,span,grow");
mBroadcastStatusVisible = SystemProperties.getInstance().get(PROPERTY_BROADCAST_STATUS_VISIBLE, false);
//Show broadcast status panel when user requests - disabled by default
if(mBroadcastStatusVisible)
{
mSplitPane.add(getBroadcastStatusPanel());
}
mMainGui.add(mSplitPane, "cell 0 0,span,grow");
/**
* Menu items
@@ -291,6 +303,12 @@ public class SDRTrunk implements Listener<TunerEvent>
fileMenu.add(exitMenu);
JMenu viewMenu = new JMenu("View");
viewMenu.add(new BroadcastStatusVisibleMenuItem(mControllerPanel));
menuBar.add(viewMenu);
JMenuItem screenCaptureItem = new JMenuItem("Screen Capture");
screenCaptureItem.setMnemonic(KeyEvent.VK_C);
@@ -360,6 +378,50 @@ public class SDRTrunk implements Listener<TunerEvent>
menuBar.add(screenCaptureItem);
}
/**
* Lazy constructor for broadcast status panel
*/
private BroadcastStatusPanel getBroadcastStatusPanel()
{
if(mBroadcastStatusPanel == null)
{
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
mBroadcastStatusPanel.setPreferredSize(new Dimension(880, 70));
mBroadcastStatusPanel.getTable().setEnabled(false);
}
return mBroadcastStatusPanel;
}
/**
* Toggles visibility of the broadcast channels status panel at the bottom of the controller panel
*/
private void toggleBroadcastStatusPanelVisibility()
{
mBroadcastStatusVisible = !mBroadcastStatusVisible;
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
if(mBroadcastStatusVisible)
{
mSplitPane.add(getBroadcastStatusPanel());
}
else
{
mSplitPane.remove(getBroadcastStatusPanel());
}
mMainGui.revalidate();
}
});
SystemProperties.getInstance().set(PROPERTY_BROADCAST_STATUS_VISIBLE, mBroadcastStatusVisible);
}
/**
* Loads the application properties file from the user's home directory,
* creating the properties file for the first-time, if necessary
@@ -435,4 +497,28 @@ public class SDRTrunk implements Listener<TunerEvent>
mMainGui.setTitle(mTitle + " - " + event.getTuner().getName());
}
}
public class BroadcastStatusVisibleMenuItem extends JCheckBoxMenuItem
{
private ControllerPanel mControllerPanel;
public BroadcastStatusVisibleMenuItem(ControllerPanel controllerPanel)
{
super("Show Streaming Status");
mControllerPanel = controllerPanel;
setSelected(mBroadcastStatusPanel != null);
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
toggleBroadcastStatusPanelVisibility();
setSelected(mBroadcastStatusVisible);
}
});
}
}
}

View File

@@ -1,22 +1,28 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package instrument.gui;
import alias.AliasModel;
import controller.channel.Channel;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.map.ChannelMapModel;
import instrument.Instrumentable;
import instrument.InstrumentableProcessingChain;
import instrument.tap.Tap;
@@ -38,8 +44,21 @@ import instrument.tap.stream.QPSKTap;
import instrument.tap.stream.QPSKTapPanel;
import instrument.tap.stream.SymbolEventTap;
import instrument.tap.stream.SymbolEventTapViewPanel;
import message.Message;
import module.Module;
import module.decode.DecoderFactory;
import module.decode.config.DecodeConfiguration;
import module.log.EventLogManager;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import playlist.PlaylistManager;
import sample.Listener;
import source.IControllableFileSource;
import source.Source;
import java.awt.Dimension;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
@@ -47,312 +66,290 @@ import java.awt.event.MouseListener;
import java.util.HashMap;
import java.util.List;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import message.Message;
import module.Module;
import module.decode.DecoderFactory;
import module.decode.config.DecodeConfiguration;
import module.log.EventLogManager;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import playlist.PlaylistManager;
import sample.Listener;
import source.IControllableFileSource;
import source.Source;
import alias.AliasModel;
import controller.channel.Channel;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.map.ChannelMapModel;
public class DecoderViewFrame extends JInternalFrame
implements Listener<Message>
public class DecoderViewFrame extends JInternalFrame implements Listener<Message>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger( DecoderViewFrame.class );
private final static Logger mLog = LoggerFactory.getLogger(DecoderViewFrame.class);
private DecodeConfiguration mDecodeConfig;
private DecodeConfiguration mDecodeConfig;
private InstrumentableProcessingChain mProcessingChain;
private IControllableFileSource mSource;
private HashMap<Tap,TapViewPanel> mPanelMap =
new HashMap<Tap,TapViewPanel>();
public DecoderViewFrame( PlaylistManager playlistManager,
Channel channel,
IControllableFileSource source )
{
mSource = source;
private IControllableFileSource mSource;
mDecodeConfig = channel.getDecodeConfiguration();
mProcessingChain = new InstrumentableProcessingChain();
private HashMap<Tap,TapViewPanel> mPanelMap = new HashMap<Tap,TapViewPanel>();
ChannelModel channelModel = new ChannelModel();
ChannelMapModel channelMapModel = new ChannelMapModel();
ChannelProcessingManager channelProcessingManager =
new ChannelProcessingManager( channelModel, channelMapModel,
new AliasModel(), new EventLogManager(), null, null );
List<Module> modules = DecoderFactory.getModules( channelModel,
channelMapModel, channelProcessingManager, new AliasModel(), channel );
public DecoderViewFrame(PlaylistManager playlistManager, Channel channel, IControllableFileSource source)
{
mSource = source;
mProcessingChain.addModules( modules );
mDecodeConfig = channel.getDecodeConfiguration();
mProcessingChain.addMessageListener( this );
mProcessingChain.setSource( (Source)mSource );
mProcessingChain.start();
initGui();
}
private void initGui()
{
setLayout( new MigLayout( "insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]" ) );
mProcessingChain = new InstrumentableProcessingChain();
setTitle( "Decoder [" + mDecodeConfig.getDecoderType().getDisplayString() + "]" );
setPreferredSize( new Dimension( 450, 250 ) );
setSize( 450, 250 );
ChannelModel channelModel = new ChannelModel();
ChannelMapModel channelMapModel = new ChannelMapModel();
setResizable( true );
setClosable( true );
setIconifiable( true );
setMaximizable( false );
ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager(channelModel, channelMapModel,
new AliasModel(), new EventLogManager(), null, null);
addMouseListener( new MouseListener()
{
@Override
public void mouseClicked( MouseEvent me )
List<Module> modules = DecoderFactory.getModules(channelModel, channelMapModel, channelProcessingManager,
new AliasModel(), channel, mProcessingChain.getChannelState().getMutableMetadata());
mProcessingChain.addModules(modules);
mProcessingChain.addMessageListener(this);
mProcessingChain.setSource((Source) mSource);
mProcessingChain.start();
initGui();
}
private void initGui()
{
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
setTitle("Decoder [" + mDecodeConfig.getDecoderType().getDisplayString() + "]");
setPreferredSize(new Dimension(450, 250));
setSize(450, 250);
setResizable(true);
setClosable(true);
setIconifiable(true);
setMaximizable(false);
addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent me)
{
if( me.getButton() == MouseEvent.BUTTON3 )
{
JPopupMenu popup = new JPopupMenu();
if(me.getButton() == MouseEvent.BUTTON3)
{
JPopupMenu popup = new JPopupMenu();
popup.add( getTapContextMenu() );
popup.add(getTapContextMenu());
if( !mPanelMap.values().isEmpty() )
{
popup.add( new JSeparator() );
for( TapViewPanel panel: mPanelMap.values() )
{
popup.add( panel.getContextMenu() );
}
}
popup.show( DecoderViewFrame.this, me.getX(), me.getY() );
}
if(!mPanelMap.values().isEmpty())
{
popup.add(new JSeparator());
for(TapViewPanel panel : mPanelMap.values())
{
popup.add(panel.getContextMenu());
}
}
popup.show(DecoderViewFrame.this, me.getX(), me.getY());
}
}
@Override
public void mouseEntered( MouseEvent arg0 ) {}
@Override
public void mouseExited( MouseEvent arg0 ) {}
@Override
public void mousePressed( MouseEvent arg0 ) {}
@Override
public void mouseReleased( MouseEvent arg0 ) {}
} );
}
private JMenu getTapContextMenu()
{
JMenu menu = new JMenu( "Modules" );
@Override
public void mouseEntered(MouseEvent arg0)
{
}
if( mProcessingChain != null )
{
for( Module module: mProcessingChain.getModules() )
{
if( module instanceof Instrumentable )
{
List<TapGroup> groups = ((Instrumentable)module).getTapGroups();
for( TapGroup group: groups )
{
menu.add( new TapGroupMenu( group ) );
}
}
}
}
@Override
public void mouseExited(MouseEvent arg0)
{
}
return menu;
}
public void add( Tap tap )
{
TapViewPanel panel = null;
switch( tap.getType() )
{
case EVENT_SYNC_DETECT:
break;
case STREAM_BINARY:
panel = new BinaryTapViewPanel( (BinaryTap)tap );
break;
case STREAM_COMPLEX:
panel = new ComplexTapViewPanel( (ComplexTap)tap );
break;
case STREAM_COMPLEX_SAMPLE:
panel = new ComplexSampleTapViewPanel( (ComplexSampleTap)tap );
break;
case STREAM_DIBIT:
panel = new DibitTapViewPanel( (DibitTap)tap );
break;
case STREAM_EYE_DIAGRAM:
panel = new EyeDiagramDataTapPanel( (EyeDiagramDataTap)tap ) ;
break;
case STREAM_FLOAT:
panel = new FloatTapViewPanel( (FloatTap)tap );
break;
case STREAM_QPSK:
panel = new QPSKTapPanel( (QPSKTap)tap );
break;
case STREAM_SYMBOL:
panel = new SymbolEventTapViewPanel( (SymbolEventTap)tap );
break;
}
if( panel != null )
{
add( panel, "span" );
validate();
mPanelMap.put( tap, panel );
@Override
public void mousePressed(MouseEvent arg0)
{
}
for( Module module: mProcessingChain.getModules() )
{
if( module instanceof Instrumentable )
{
((Instrumentable)module).registerTap( tap );
}
}
}
else
{
mLog.info( "Tap panel is null, couldn't add for tap " +
tap.getName() + "[" + tap.getType().toString() + "]" );
}
}
public void remove( Tap tap )
{
for( Module module: mProcessingChain.getModules() )
{
if( module instanceof Instrumentable )
{
((Instrumentable)module).unregisterTap( tap );
}
}
TapViewPanel panel = mPanelMap.get( tap );
remove( panel );
validate();
mPanelMap.remove( tap );
}
@Override
public void mouseReleased(MouseEvent arg0)
{
}
public class AddAllTapsItem extends JMenuItem
{
private static final long serialVersionUID = 1L;
private final List<Tap> mTaps;
public AddAllTapsItem( final List<Tap> taps )
{
super( "All Taps" );
mTaps = taps;
addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
for( Tap tap: mTaps )
{
DecoderViewFrame.this.add( tap );
}
}
} );
}
}
public class TapSelectionItem extends JCheckBoxMenuItem
{
private static final long serialVersionUID = 1L;
private Tap mTap;
public TapSelectionItem( Tap tap )
{
super( tap.getName() );
mTap = tap;
if( mPanelMap.keySet().contains( tap ) )
{
setSelected( true );
}
addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
if( mPanelMap.keySet().contains( mTap ) )
{
DecoderViewFrame.this.remove( mTap );
}
else
{
DecoderViewFrame.this.add( mTap );
}
}
} );
}
}
@Override
public void receive( Message message )
{
mLog.info( message.toString() );
});
}
private JMenu getTapContextMenu()
{
JMenu menu = new JMenu("Modules");
if(mProcessingChain != null)
{
for(Module module : mProcessingChain.getModules())
{
if(module instanceof Instrumentable)
{
List<TapGroup> groups = ((Instrumentable) module).getTapGroups();
for(TapGroup group : groups)
{
menu.add(new TapGroupMenu(group));
}
}
}
}
return menu;
}
public void add(Tap tap)
{
TapViewPanel panel = null;
switch(tap.getType())
{
case EVENT_SYNC_DETECT:
break;
case STREAM_BINARY:
panel = new BinaryTapViewPanel((BinaryTap) tap);
break;
case STREAM_COMPLEX:
panel = new ComplexTapViewPanel((ComplexTap) tap);
break;
case STREAM_COMPLEX_SAMPLE:
panel = new ComplexSampleTapViewPanel((ComplexSampleTap) tap);
break;
case STREAM_DIBIT:
panel = new DibitTapViewPanel((DibitTap) tap);
break;
case STREAM_EYE_DIAGRAM:
panel = new EyeDiagramDataTapPanel((EyeDiagramDataTap) tap);
break;
case STREAM_FLOAT:
panel = new FloatTapViewPanel((FloatTap) tap);
break;
case STREAM_QPSK:
panel = new QPSKTapPanel((QPSKTap) tap);
break;
case STREAM_SYMBOL:
panel = new SymbolEventTapViewPanel((SymbolEventTap) tap);
break;
}
if(panel != null)
{
add(panel, "span");
validate();
mPanelMap.put(tap, panel);
for(Module module : mProcessingChain.getModules())
{
if(module instanceof Instrumentable)
{
((Instrumentable) module).registerTap(tap);
}
}
}
else
{
mLog.info("Tap panel is null, couldn't add for tap " +
tap.getName() + "[" + tap.getType().toString() + "]");
}
}
public void remove(Tap tap)
{
for(Module module : mProcessingChain.getModules())
{
if(module instanceof Instrumentable)
{
((Instrumentable) module).unregisterTap(tap);
}
}
TapViewPanel panel = mPanelMap.get(tap);
remove(panel);
validate();
mPanelMap.remove(tap);
}
public class AddAllTapsItem extends JMenuItem
{
private static final long serialVersionUID = 1L;
private final List<Tap> mTaps;
public AddAllTapsItem(final List<Tap> taps)
{
super("All Taps");
mTaps = taps;
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
for(Tap tap : mTaps)
{
DecoderViewFrame.this.add(tap);
}
}
});
}
}
public class TapSelectionItem extends JCheckBoxMenuItem
{
private static final long serialVersionUID = 1L;
private Tap mTap;
public TapSelectionItem(Tap tap)
{
super(tap.getName());
mTap = tap;
if(mPanelMap.keySet().contains(tap))
{
setSelected(true);
}
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
if(mPanelMap.keySet().contains(mTap))
{
DecoderViewFrame.this.remove(mTap);
}
else
{
DecoderViewFrame.this.add(mTap);
}
}
});
}
}
@Override
public void receive(Message message)
{
mLog.info(message.toString());
}
public class TapGroupMenu extends JMenu
{
private static final long serialVersionUID = 1L;
private TapGroup mTapGroup;
public TapGroupMenu(TapGroup tapGroup)
{
super(tapGroup.getName());
mTapGroup = tapGroup;
add(new AddAllTapsItem(mTapGroup.getTaps()));
add(new JSeparator());
for(Tap tap : mTapGroup.getTaps())
{
add(new TapSelectionItem(tap));
}
}
}
public class TapGroupMenu extends JMenu
{
private static final long serialVersionUID = 1L;
private TapGroup mTapGroup;
public TapGroupMenu( TapGroup tapGroup )
{
super( tapGroup.getName() );
mTapGroup = tapGroup;
add( new AddAllTapsItem( mTapGroup.getTaps() ) );
add( new JSeparator() );
for( Tap tap: mTapGroup.getTaps() )
{
add( new TapSelectionItem( tap ) );
}
}
}
}

View File

@@ -23,7 +23,6 @@ import controller.NamingThreadFactory;
import icon.IconManager;
import message.Message;
import sample.Listener;
import settings.SettingsManager;
import java.awt.*;
import java.util.ArrayList;
@@ -102,7 +101,7 @@ public class MapService implements Listener<Message>
if(alias != null)
{
color = alias.getMapColor();
color = alias.getDisplayColor();
}
if(color == null)

View File

@@ -1,31 +1,39 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2015-2016 Dennis Sheirer
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package module;
import audio.AudioPacket;
import audio.IAudioPacketListener;
import audio.IAudioPacketProvider;
import audio.metadata.IMetadataListener;
import audio.metadata.IMetadataProvider;
import audio.metadata.Metadata;
import audio.squelch.ISquelchStateListener;
import audio.squelch.ISquelchStateProvider;
import audio.squelch.SquelchState;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.IAttributeChangeRequestListener;
import channel.metadata.IAttributeChangeRequestProvider;
import channel.state.ChannelState;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.IDecoderStateEventListener;
import channel.state.IDecoderStateEventProvider;
import channel.state.State;
import controller.NamingThreadFactory;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent;
@@ -39,13 +47,6 @@ import module.decode.event.CallEventModel;
import module.decode.event.ICallEventListener;
import module.decode.event.ICallEventProvider;
import module.decode.event.MessageActivityModel;
import module.decode.state.ChannelState;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.IDecoderStateEventListener;
import module.decode.state.IDecoderStateEventProvider;
import module.decode.state.State;
import module.log.EventLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -97,8 +98,8 @@ public class ProcessingChain implements IChannelEventListener
{
private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class);
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
private Broadcaster<AudioPacket> mAudioPacketBroadcaster = new Broadcaster<>();
private Broadcaster<Metadata> mMetadataBroadcaster = new Broadcaster<>();
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
@@ -186,17 +187,6 @@ public class ProcessingChain implements IChannelEventListener
}
}
/**
* Broadcasts the metadata to any registered listeners
*/
public void broadcast(Metadata metadata)
{
if(mMetadataBroadcaster != null)
{
mMetadataBroadcaster.broadcast(metadata);
}
}
/**
* Indicates if this processing chain is currently receiving samples from
* a source and sending those samples to the decoders.
@@ -227,9 +217,8 @@ public class ProcessingChain implements IChannelEventListener
{
if(isProcessing())
{
throw new IllegalStateException("Processing chain is currently "
+ "processing. Invoke stop() on the processing chain before "
+ "applying a new sample source");
throw new IllegalStateException("Processing chain is currently processing. Invoke stop() on the " +
"processing chain before applying a new sample source");
}
mSource = source;
@@ -312,70 +301,59 @@ public class ProcessingChain implements IChannelEventListener
*/
private void registerListeners(Module module)
{
if(module instanceof IAttributeChangeRequestListener)
{
mAttributeChangeRequestBroadcaster.addListener(((IAttributeChangeRequestListener)module).getAttributeChangeRequestListener());
}
if(module instanceof IAudioPacketListener)
{
mAudioPacketBroadcaster.addListener(
((IAudioPacketListener) module).getAudioPacketListener());
mAudioPacketBroadcaster.addListener(((IAudioPacketListener) module).getAudioPacketListener());
}
if(module instanceof ICallEventListener)
{
mCallEventBroadcaster.addListener(
((ICallEventListener) module).getCallEventListener());
mCallEventBroadcaster.addListener(((ICallEventListener) module).getCallEventListener());
}
if(module instanceof IChannelEventListener)
{
mChannelEventBroadcaster.addListener(
((IChannelEventListener) module).getChannelEventListener());
mChannelEventBroadcaster.addListener(((IChannelEventListener) module).getChannelEventListener());
}
if(module instanceof IComplexBufferListener)
{
mComplexBufferBroadcaster.addListener(
((IComplexBufferListener) module).getComplexBufferListener());
mComplexBufferBroadcaster.addListener(((IComplexBufferListener) module).getComplexBufferListener());
}
if(module instanceof IDecoderStateEventListener)
{
mDecoderStateEventBroadcaster.addListener(
((IDecoderStateEventListener) module).getDecoderStateListener());
mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
if(module instanceof IFrequencyChangeListener)
{
mFrequencyChangeEventBroadcaster.addListener(
((IFrequencyChangeListener) module).getFrequencyChangeListener());
mFrequencyChangeEventBroadcaster.addListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
}
if(module instanceof IMessageListener)
{
mMessageBroadcaster.addListener(
((IMessageListener) module).getMessageListener());
}
if(module instanceof IMetadataListener)
{
mMetadataBroadcaster.addListener(
((IMetadataListener) module).getMetadataListener());
mMessageBroadcaster.addListener(((IMessageListener) module).getMessageListener());
}
if(module instanceof IFilteredRealBufferListener)
{
mFilteredRealBufferBroadcaster.addListener(
((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
mFilteredRealBufferBroadcaster.addListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
}
if(module instanceof ISquelchStateListener)
{
mSquelchStateBroadcaster.addListener(
((ISquelchStateListener) module).getSquelchStateListener());
mSquelchStateBroadcaster.addListener(((ISquelchStateListener) module).getSquelchStateListener());
}
if(module instanceof IUnFilteredRealBufferListener)
{
mUnFilteredRealBufferBroadcaster.addListener(
((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener());
mUnFilteredRealBufferBroadcaster.addListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener());
}
}
@@ -385,40 +363,39 @@ public class ProcessingChain implements IChannelEventListener
*/
private void unregisterListeners(Module module)
{
if(module instanceof IAttributeChangeRequestListener)
{
mAttributeChangeRequestBroadcaster.removeListener(((IAttributeChangeRequestListener) module).getAttributeChangeRequestListener());
}
if(module instanceof IAudioPacketListener)
{
mAudioPacketBroadcaster.removeListener(
((IAudioPacketListener) module).getAudioPacketListener());
mAudioPacketBroadcaster.removeListener(((IAudioPacketListener) module).getAudioPacketListener());
}
if(module instanceof ICallEventListener)
{
mCallEventBroadcaster.removeListener(
((ICallEventListener) module).getCallEventListener());
mCallEventBroadcaster.removeListener(((ICallEventListener) module).getCallEventListener());
}
if(module instanceof IChannelEventListener)
{
mChannelEventBroadcaster.removeListener(
((IChannelEventListener) module).getChannelEventListener());
mChannelEventBroadcaster.removeListener(((IChannelEventListener) module).getChannelEventListener());
}
if(module instanceof IComplexBufferListener)
{
mComplexBufferBroadcaster.removeListener(
((IComplexBufferListener) module).getComplexBufferListener());
mComplexBufferBroadcaster.removeListener(((IComplexBufferListener) module).getComplexBufferListener());
}
if(module instanceof IDecoderStateEventListener)
{
mDecoderStateEventBroadcaster.removeListener(
((IDecoderStateEventListener) module).getDecoderStateListener());
mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener());
}
if(module instanceof IFrequencyChangeListener)
{
mFrequencyChangeEventBroadcaster.removeListener(
((IFrequencyChangeListener) module).getFrequencyChangeListener());
mFrequencyChangeEventBroadcaster.removeListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
}
if(module instanceof IMessageListener)
@@ -426,27 +403,19 @@ public class ProcessingChain implements IChannelEventListener
mMessageBroadcaster.removeListener(((IMessageListener) module).getMessageListener());
}
if(module instanceof IMetadataListener)
{
mMetadataBroadcaster.removeListener(((IMetadataListener) module).getMetadataListener());
}
if(module instanceof IFilteredRealBufferListener)
{
mFilteredRealBufferBroadcaster.removeListener(
((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
mFilteredRealBufferBroadcaster.removeListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener());
}
if(module instanceof ISquelchStateListener)
{
mSquelchStateBroadcaster.removeListener(
((ISquelchStateListener) module).getSquelchStateListener());
mSquelchStateBroadcaster.removeListener(((ISquelchStateListener) module).getSquelchStateListener());
}
if(module instanceof IUnFilteredRealBufferListener)
{
mUnFilteredRealBufferBroadcaster.removeListener(
((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener());
mUnFilteredRealBufferBroadcaster.removeListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener());
}
}
@@ -456,10 +425,14 @@ public class ProcessingChain implements IChannelEventListener
*/
private void registerProviders(Module module)
{
if(module instanceof IAttributeChangeRequestProvider)
{
((IAttributeChangeRequestProvider)module).setAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster);
}
if(module instanceof IAudioPacketProvider)
{
((IAudioPacketProvider) module).setAudioPacketListener(
mAudioPacketBroadcaster);
((IAudioPacketProvider) module).setAudioPacketListener(mAudioPacketBroadcaster);
}
if(module instanceof ICallEventProvider)
@@ -469,20 +442,17 @@ public class ProcessingChain implements IChannelEventListener
if(module instanceof IChannelEventProvider)
{
((IChannelEventProvider) module).setChannelEventListener(
mChannelEventBroadcaster);
((IChannelEventProvider) module).setChannelEventListener(mChannelEventBroadcaster);
}
if(module instanceof IDecoderStateEventProvider)
{
((IDecoderStateEventProvider) module).setDecoderStateListener(
mDecoderStateEventBroadcaster);
((IDecoderStateEventProvider) module).setDecoderStateListener(mDecoderStateEventBroadcaster);
}
if(module instanceof IFrequencyChangeProvider)
{
((IFrequencyChangeProvider) module).setFrequencyChangeListener(
mFrequencyChangeEventBroadcaster);
((IFrequencyChangeProvider) module).setFrequencyChangeListener(mFrequencyChangeEventBroadcaster);
}
if(module instanceof IMessageProvider)
@@ -490,28 +460,19 @@ public class ProcessingChain implements IChannelEventListener
((IMessageProvider) module).setMessageListener(mMessageBroadcaster);
}
if(module instanceof IMetadataProvider)
{
((IMetadataProvider) module).setMetadataListener(
mMetadataBroadcaster);
}
if(module instanceof IFilteredRealBufferProvider)
{
((IFilteredRealBufferProvider) module)
.setFilteredRealBufferListener(mFilteredRealBufferBroadcaster);
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster);
}
if(module instanceof ISquelchStateProvider)
{
((ISquelchStateProvider) module).setSquelchStateListener(
mSquelchStateBroadcaster);
((ISquelchStateProvider) module).setSquelchStateListener(mSquelchStateBroadcaster);
}
if(module instanceof IUnFilteredRealBufferProvider)
{
((IUnFilteredRealBufferProvider) module)
.setUnFilteredRealBufferListener(mUnFilteredRealBufferBroadcaster);
((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(mUnFilteredRealBufferBroadcaster);
}
}
@@ -521,6 +482,11 @@ public class ProcessingChain implements IChannelEventListener
*/
private void unregisterProviders(Module module)
{
if(module instanceof IAttributeChangeRequestProvider)
{
((IAttributeChangeRequestProvider)module).removeAttributeChangeRequestListener(mAttributeChangeRequestBroadcaster);
}
if(module instanceof IAudioPacketProvider)
{
((IAudioPacketProvider) module).setAudioPacketListener(null);
@@ -551,11 +517,6 @@ public class ProcessingChain implements IChannelEventListener
((IMessageProvider) module).setMessageListener(null);
}
if(module instanceof IMetadataProvider)
{
((IMetadataProvider) module).setMetadataListener(null);
}
if(module instanceof IFilteredRealBufferProvider)
{
((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(null);
@@ -587,7 +548,7 @@ public class ProcessingChain implements IChannelEventListener
{
module.reset();
}
/* Register with the source to receive sample data. Setup a
* timer task to process the buffer queues 50 times a second
* (every 20 ms) */
@@ -615,8 +576,7 @@ public class ProcessingChain implements IChannelEventListener
long frequency = tcs.getFrequency();
mDecoderStateEventBroadcaster.broadcast(
new DecoderStateEvent(this, Event.SOURCE_FREQUENCY,
State.IDLE, frequency));
new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, State.IDLE, frequency));
}
catch(SourceException e)
{
@@ -697,7 +657,7 @@ public class ProcessingChain implements IChannelEventListener
{
List<Module> eventLoggingModules = new ArrayList<>();
for(Module module: mModules)
for(Module module : mModules)
{
if(module instanceof EventLogger)
{
@@ -705,7 +665,7 @@ public class ProcessingChain implements IChannelEventListener
}
}
for(Module eventLoggingModule: eventLoggingModules)
for(Module eventLoggingModule : eventLoggingModules)
{
removeModule(eventLoggingModule);
}
@@ -718,7 +678,7 @@ public class ProcessingChain implements IChannelEventListener
{
List<Module> recordingModules = new ArrayList<>();
for(Module module: mModules)
for(Module module : mModules)
{
if(module instanceof RealBufferWaveRecorder || module instanceof ComplexBufferWaveRecorder)
{
@@ -726,7 +686,7 @@ public class ProcessingChain implements IChannelEventListener
}
}
for(Module recordingModule: recordingModules)
for(Module recordingModule : recordingModules)
{
removeModule(recordingModule);
}

View File

@@ -21,66 +21,61 @@ package module.decode;
import alias.AliasList;
import alias.AliasModel;
import audio.AudioModule;
import channel.metadata.Metadata;
import channel.state.AlwaysUnsquelchedDecoderState;
import channel.traffic.TrafficChannelManager;
import controller.channel.Channel;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.map.ChannelMap;
import controller.channel.map.ChannelMapModel;
import dsp.filter.FilterFactory;
import dsp.filter.fir.FIRFilterSpecification;
import filter.AllPassFilter;
import filter.FilterSet;
import filter.IFilter;
import gui.editor.EmptyValidatingEditor;
import gui.editor.ValidatingEditor;
import icon.IconManager;
import message.Message;
import message.MessageDirection;
import module.Module;
import module.decode.am.AMDecoder;
import module.decode.am.AMDecoderEditor;
import module.decode.am.AMDecoderPanel;
import module.decode.am.DecodeConfigAM;
import module.decode.config.AuxDecodeConfiguration;
import module.decode.config.DecodeConfiguration;
import module.decode.fleetsync2.Fleetsync2Decoder;
import module.decode.fleetsync2.Fleetsync2DecoderPanel;
import module.decode.fleetsync2.Fleetsync2DecoderState;
import module.decode.fleetsync2.FleetsyncMessageFilter;
import module.decode.lj1200.LJ1200Decoder;
import module.decode.lj1200.LJ1200DecoderPanel;
import module.decode.lj1200.LJ1200DecoderState;
import module.decode.lj1200.LJ1200MessageFilter;
import module.decode.ltrnet.DecodeConfigLTRNet;
import module.decode.ltrnet.LTRNetDecoder;
import module.decode.ltrnet.LTRNetDecoderEditor;
import module.decode.ltrnet.LTRNetDecoderPanel;
import module.decode.ltrnet.LTRNetDecoderState;
import module.decode.ltrnet.LTRNetMessageFilter;
import module.decode.ltrstandard.DecodeConfigLTRStandard;
import module.decode.ltrstandard.LTRStandardDecoder;
import module.decode.ltrstandard.LTRStandardDecoderEditor;
import module.decode.ltrstandard.LTRStandardDecoderPanel;
import module.decode.ltrstandard.LTRStandardDecoderState;
import module.decode.ltrstandard.LTRStandardMessageFilter;
import module.decode.mdc1200.MDCDecoder;
import module.decode.mdc1200.MDCDecoderPanel;
import module.decode.mdc1200.MDCDecoderState;
import module.decode.mdc1200.MDCMessageFilter;
import module.decode.mpt1327.DecodeConfigMPT1327;
import module.decode.mpt1327.MPT1327Decoder;
import module.decode.mpt1327.MPT1327Decoder.Sync;
import module.decode.mpt1327.MPT1327DecoderEditor;
import module.decode.mpt1327.MPT1327DecoderPanel;
import module.decode.mpt1327.MPT1327DecoderState;
import module.decode.mpt1327.MPT1327MessageFilter;
import module.decode.nbfm.DecodeConfigNBFM;
import module.decode.nbfm.NBFMDecoder;
import module.decode.nbfm.NBFMDecoderEditor;
import module.decode.nbfm.NBFMDecoderPanel;
import module.decode.p25.DecodeConfigP25Phase1;
import module.decode.p25.P25Decoder.Modulation;
import module.decode.p25.P25DecoderEditor;
import module.decode.p25.P25DecoderPanel;
import module.decode.p25.P25DecoderState;
import module.decode.p25.P25_C4FMDecoder;
import module.decode.p25.P25_LSMDecoder;
@@ -89,31 +84,41 @@ import module.decode.p25.message.filter.P25MessageFilterSet;
import module.decode.passport.DecodeConfigPassport;
import module.decode.passport.PassportDecoder;
import module.decode.passport.PassportDecoderEditor;
import module.decode.passport.PassportDecoderPanel;
import module.decode.passport.PassportDecoderState;
import module.decode.passport.PassportMessageFilter;
import module.decode.state.AlwaysUnsquelchedDecoderState;
import module.decode.state.DecoderPanel;
import module.decode.state.DecoderState;
import module.decode.state.TrafficChannelManager;
import module.decode.tait.Tait1200Decoder;
import module.decode.tait.Tait1200DecoderPanel;
import module.decode.tait.Tait1200DecoderState;
import module.demodulate.am.AMDemodulatorModule;
import module.demodulate.audio.DemodulatedAudioFilterModule;
import module.demodulate.fm.FMDemodulatorModule;
import properties.SystemProperties;
import record.RecorderType;
import record.config.RecordConfiguration;
import settings.SettingsManager;
import util.TimeStamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class DecoderFactory
{
private final static Logger mLog = LoggerFactory.getLogger(DecoderFactory.class);
private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.bandPassBuilder()
.sampleRate(48000).stopFrequency1(200).passFrequencyBegin(400).passFrequencyEnd(3200)
.stopFrequency2(3400).stopRipple(0.0003).passRipple(0.008).build();
private static float[] MPT1327_BANDPASS_FILTER;
static
{
try
{
MPT1327_BANDPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION);
}
catch(Exception e)
{
mLog.error("Couldn't design MPT1327 bandpass filter");
}
}
public static final boolean NO_REMOVE_DC = false;
public static final boolean REMOVE_DC = true;
@@ -129,14 +134,15 @@ public class DecoderFactory
ChannelMapModel channelMapModel,
ChannelProcessingManager channelProcessingManager,
AliasModel aliasModel,
Channel channel)
Channel channel,
Metadata metadata)
{
/* Get the optional alias list for the decode modules to use */
AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName());
List<Module> modules = getPrimaryModules(channelModel, channelMapModel,
channelProcessingManager, aliasList, channel);
List<Module> modules = getPrimaryModules(channelModel, channelMapModel, channelProcessingManager, aliasList,
channel, metadata);
modules.addAll(getAuxiliaryDecoders(channel.getAuxDecodeConfiguration(), aliasList));
@@ -150,7 +156,7 @@ public class DecoderFactory
ChannelMapModel channelMapModel,
ChannelProcessingManager channelProcessingManager,
AliasList aliasList,
Channel channel)
Channel channel, Metadata metadata)
{
List<Module> modules = new ArrayList<Module>();
@@ -162,11 +168,6 @@ public class DecoderFactory
int iqPass = decodeConfig.getDecoderType().getChannelBandwidth() / 2;
int iqStop = iqPass + 1250;
RecordConfiguration recordConfig = channel.getRecordConfiguration();
boolean recordAudio = recordConfig == null ? false :
recordConfig.getRecorders().contains(RecorderType.AUDIO);
switch(decodeConfig.getDecoderType())
{
case AM:
@@ -174,14 +175,14 @@ public class DecoderFactory
modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.AM, channel.getName()));
modules.add(new AMDemodulatorModule());
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
modules.add(new AudioModule(recordAudio));
modules.add(new AudioModule(metadata));
break;
case NBFM:
modules.add(new NBFMDecoder(decodeConfig));
modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.NBFM, channel.getName()));
modules.add(new FMDemodulatorModule(iqPass, iqStop));
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
modules.add(new AudioModule(recordAudio));
modules.add(new AudioModule(metadata));
break;
case LTR_STANDARD:
MessageDirection direction = ((DecodeConfigLTRStandard) decodeConfig).getMessageDirection();
@@ -189,49 +190,46 @@ public class DecoderFactory
modules.add(new LTRStandardDecoderState(aliasList));
modules.add(new FMDemodulatorModule(iqPass, iqStop));
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
modules.add(new AudioModule(recordAudio));
modules.add(new AudioModule(metadata));
break;
case LTR_NET:
modules.add(new LTRNetDecoder((DecodeConfigLTRNet) decodeConfig, aliasList));
modules.add(new LTRNetDecoderState(aliasList));
modules.add(new FMDemodulatorModule(iqPass, iqStop));
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
modules.add(new AudioModule(recordAudio));
modules.add(new AudioModule(metadata));
break;
case MPT1327:
DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327) decodeConfig;
ChannelMap channelMap = channelMapModel
.getChannelMap(mptConfig.getChannelMapName());
ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName());
Sync sync = mptConfig.getSync();
modules.add(new MPT1327Decoder(aliasList, sync));
modules.add(new MPT1327DecoderState(aliasList, channelMap,
channelType, mptConfig.getCallTimeout() * 1000));
modules.add(new MPT1327DecoderState(aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000));
if(channelType == ChannelType.STANDARD)
{
long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager,
decodeConfig, recordConfig, channel.getSystem(),
channel.getSite(), (aliasList != null ? aliasList.getName() : null), timeout,
mptConfig.getTrafficChannelPoolSize()));
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig,
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), timeout, mptConfig.getTrafficChannelPoolSize()));
}
modules.add(new FMDemodulatorModule(iqPass, iqStop));
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
modules.add(new AudioModule(recordAudio));
modules.add(new DemodulatedAudioFilterModule(MPT1327_BANDPASS_FILTER, 2.0f));
modules.add(new AudioModule(metadata));
break;
case PASSPORT:
modules.add(new PassportDecoder(decodeConfig, aliasList));
modules.add(new PassportDecoderState(aliasList));
modules.add(new FMDemodulatorModule(iqPass, iqStop));
modules.add(new DemodulatedAudioFilterModule(4000, 6000));
modules.add(new AudioModule(recordAudio));
modules.add(new AudioModule(metadata));
break;
case P25_PHASE1:
DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1) decodeConfig;
@@ -244,17 +242,15 @@ public class DecoderFactory
case C4FM:
modules.add(new FMDemodulatorModule(6750, 7500));
modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
modules.add(new P25DecoderState(aliasList, channelType,
Modulation.C4FM, p25Config.getIgnoreDataCalls()));
modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
p25Config.getIgnoreDataCalls()));
break;
case CQPSK:
modules.add(new P25_LSMDecoder(aliasList));
modules.add(new P25DecoderState(aliasList, channelType,
Modulation.CQPSK, p25Config.getIgnoreDataCalls()));
modules.add(new P25DecoderState(aliasList, channelType, Modulation.CQPSK, p25Config.getIgnoreDataCalls()));
break;
default:
throw new IllegalArgumentException("Unrecognized P25 "
+ "Phase 1 Modulation [" + modulation + "]");
throw new IllegalArgumentException("Unrecognized P25 Phase 1 Modulation [" + modulation + "]");
}
if(channelType == ChannelType.STANDARD)
@@ -262,19 +258,15 @@ public class DecoderFactory
//Set call timeout to 3 seconds for P25 traffic channels
long timeout = 3000;
modules.add(new TrafficChannelManager(channelModel,
channelProcessingManager, decodeConfig,
recordConfig, channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), timeout,
p25Config.getTrafficChannelPoolSize()));
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig,
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), timeout, p25Config.getTrafficChannelPoolSize()));
}
modules.add(new P25AudioModule(recordAudio));
modules.add(new P25AudioModule(metadata));
break;
default:
throw new IllegalArgumentException(
"Unknown decoder type [" +
decodeConfig.getDecoderType().toString() + "]");
throw new IllegalArgumentException("Unknown decoder type [" + decodeConfig.getDecoderType().toString() + "]");
}
return modules;
@@ -324,39 +316,6 @@ public class DecoderFactory
return modules;
}
public static DecoderPanel getDecoderPanel(IconManager iconManager, SettingsManager settingsManager,
DecoderState decoderState)
{
switch(decoderState.getDecoderType())
{
case AM:
return new AMDecoderPanel(iconManager, settingsManager, decoderState);
case FLEETSYNC2:
return new Fleetsync2DecoderPanel(iconManager, settingsManager, (Fleetsync2DecoderState) decoderState);
case LJ_1200:
return new LJ1200DecoderPanel(iconManager, settingsManager, (LJ1200DecoderState) decoderState);
case LTR_NET:
return new LTRNetDecoderPanel(iconManager, settingsManager, (LTRNetDecoderState) decoderState);
case LTR_STANDARD:
return new LTRStandardDecoderPanel(iconManager, settingsManager, (LTRStandardDecoderState) decoderState);
case MDC1200:
return new MDCDecoderPanel(iconManager, settingsManager, (MDCDecoderState) decoderState);
case MPT1327:
return new MPT1327DecoderPanel(iconManager, settingsManager, (MPT1327DecoderState) decoderState);
case NBFM:
return new NBFMDecoderPanel(iconManager, settingsManager, decoderState);
case P25_PHASE1:
return new P25DecoderPanel(iconManager, settingsManager, (P25DecoderState) decoderState);
case PASSPORT:
return new PassportDecoderPanel(iconManager, settingsManager, (PassportDecoderState) decoderState);
case TAIT_1200:
return new Tait1200DecoderPanel(iconManager, settingsManager, (Tait1200DecoderState) decoderState);
default:
throw new IllegalArgumentException("Unrecognized decoder: " +
decoderState.getDecoderType().getDisplayString());
}
}
/**
* Assembles a filter set containing filters for the primary channel
* decoder and each of the auxiliary decoders
@@ -451,8 +410,7 @@ public class DecoderFactory
case P25_PHASE1:
return new DecodeConfigP25Phase1();
default:
throw new IllegalArgumentException("DecodeConfigFactory - unknown decoder type ["
+ decoder.toString() + "]");
throw new IllegalArgumentException("DecodeConfigFactory - unknown decoder type [" + decoder.toString() + "]");
}
}
@@ -542,25 +500,10 @@ public class DecoderFactory
copyPass.setAFCMaximumCorrection(originalPass.getAFCMaximumCorrection());
return copyPass;
default:
throw new IllegalArgumentException("Unrecognized decoder "
+ "configuration type:" + config.getDecoderType());
throw new IllegalArgumentException("Unrecognized decoder configuration type:" + config.getDecoderType());
}
}
return null;
}
public static String getRecordingFilename(String token)
{
//Get the base recording filename
StringBuilder sb = new StringBuilder();
sb.append(SystemProperties.getInstance()
.getApplicationFolder("recordings"));
sb.append(File.separator);
sb.append(TimeStamp.getTimeStamp("_"));
sb.append("_");
sb.append(token);
return sb.toString();
}
}

View File

@@ -1,84 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.am;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import module.decode.state.DecoderState;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
public class AMDecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol = new JLabel("AM");
public AMDecoderPanel(IconManager iconManager, SettingsManager settingsManager, DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
add(mProtocol, "wrap");
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mProtocol != null)
{
mProtocol.setForeground(mColorLabelDecoder);
}
break;
default:
break;
}
}
}
}

View File

@@ -1,324 +1,309 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.event;
import alias.Alias;
import alias.AliasList;
import module.decode.DecoderType;
import java.text.SimpleDateFormat;
import java.util.Date;
import module.decode.DecoderType;
import alias.Alias;
import alias.AliasList;
import audio.metadata.MetadataType;
public abstract class CallEvent implements Comparable<CallEvent>
{
protected long mEventStart = System.currentTimeMillis();
protected long mEventEnd;
private SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd','HHmmss" );
protected long mEventStart = System.currentTimeMillis();
protected long mEventEnd;
protected DecoderType mDecoderType;
protected CallEventType mCallEventType;
protected AliasList mAliasList;
protected String mFromID;
protected String mToID;
protected String mDetails;
private boolean mValid = true;
public CallEvent( DecoderType decoder,
CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
String details )
{
mDecoderType = decoder;
mCallEventType = callEventType;
mAliasList = aliasList;
mFromID = fromID;
mToID = toID;
mDetails = details;
}
/**
* Indicates if this event is valid. Use this method to mark a previously
* dispatched event as invalid and resend it so that any downstream
* processing can remove the invalid event.
*/
public void setValid( boolean valid )
{
mValid = valid;
}
private SimpleDateFormat mSDF = new SimpleDateFormat("yyyyMMdd','HHmmss");
public boolean isValid()
{
return mValid;
}
public void setEnd( long end )
{
mEventEnd = end;
}
public void end()
{
setEnd( System.currentTimeMillis() );
}
public DecoderType getDecoderType()
{
return mDecoderType;
}
public CallEventType getCallEventType()
{
return mCallEventType;
}
public void setCallEventType( CallEventType type )
{
mCallEventType = type;
}
protected DecoderType mDecoderType;
protected CallEventType mCallEventType;
protected AliasList mAliasList;
protected String mFromID;
protected String mToID;
protected String mDetails;
public AliasList getAliasList()
{
return mAliasList;
}
public void setAliasList( AliasList aliasList )
{
mAliasList = aliasList;
}
public boolean hasAliasList()
{
return mAliasList != null;
}
public long getEventStartTime()
{
return mEventStart;
}
public long getEventEndTime()
{
return mEventEnd;
}
public String getFromID()
{
return mFromID;
}
public boolean hasFromID()
{
return mFromID != null;
}
public MetadataType getFromIDType()
{
return MetadataType.FROM;
}
public void setFromID( String id )
{
mFromID = id;
}
public String getToID()
{
return mToID;
}
public boolean hasToID()
{
return mToID != null;
}
public void setToID( String id )
{
mToID = id;
}
private boolean mValid = true;
public MetadataType getToIDType()
{
return MetadataType.TO;
}
public String getDetails()
{
return mDetails;
}
public void setDetails( String details )
{
mDetails = details;
}
public abstract Alias getFromIDAlias();
public abstract Alias getToIDAlias();
public abstract String getChannel();
public CallEvent(DecoderType decoder, CallEventType callEventType, AliasList aliasList, String fromID,
String toID, String details)
{
mDecoderType = decoder;
mCallEventType = callEventType;
mAliasList = aliasList;
mFromID = fromID;
mToID = toID;
mDetails = details;
}
public abstract long getFrequency();
public static String getCSVHeader()
{
return "START_DATE,START_TIME,END_DATE,END_TIME,"
+ "DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,"
+ "CHANNEL,FREQUENCY,DETAILS";
}
public String toCSV()
{
StringBuilder sb = new StringBuilder();
/**
* Indicates if this event is valid. Use this method to mark a previously
* dispatched event as invalid and resend it so that any downstream
* processing can remove the invalid event.
*/
public void setValid(boolean valid)
{
mValid = valid;
}
sb.append( "'" );
sb.append( mSDF.format( new Date( getEventStartTime() ) ) );
sb.append( "','" );
if( mEventEnd != 0 )
{
sb.append( mSDF.format( new Date( getEventEndTime() ) ) );
}
else
{
sb.append( "','" );
}
sb.append( "','" );
sb.append( getDecoderType().toString() );
sb.append( "','" );
sb.append( getCallEventType().toString() );
sb.append( "'," );
if( getFromID() != null )
{
sb.append( "'" );
sb.append( getFromID() );
sb.append( "'" );
}
sb.append( "," );
Alias fromAlias = getFromIDAlias();
if( fromAlias != null )
{
sb.append( "'" );
sb.append( fromAlias.getName() );
sb.append( "'" );
}
sb.append( "," );
public boolean isValid()
{
return mValid;
}
if( getToID() != null )
{
sb.append( "'" );
sb.append( getToID() );
sb.append( "'" );
}
sb.append( "," );
Alias toAlias = getToIDAlias();
if( toAlias != null )
{
sb.append( "'" );
sb.append( toAlias.getName() );
sb.append( "'" );
}
sb.append( ",'" );
sb.append( getChannel() );
sb.append( "','" );
sb.append( getFrequency() );
sb.append( "'," );
if( getDetails() != null )
{
sb.append( "'" );
sb.append( getDetails() );
sb.append( "'" );
}
return sb.toString();
}
public enum CallEventType
{
ANNOUNCEMENT( "Announcement" ),
CALL( "Call" ),
CALL_ALERT( "Call Alert" ),
CALL_DETECT( "Call Detect" ),
CALL_DO_NOT_MONITOR( "Call-Do Not Monitor" ),
CALL_END( "Call End" ),
CALL_UNIQUE_ID( "UID Call" ),
CALL_NO_TUNER( "Call - No Tuner" ),
CALL_TIMEOUT( "Call Timeout" ),
COMMAND( "Command" ),
DATA_CALL( "Data Call" ),
DEREGISTER( "Deregister" ),
EMERGENCY( "EMERGENCY" ),
ENCRYPTED_CALL( "Encrypted Call" ),
FUNCTION( "Function" ),
GPS( "GPS" ),
GROUP_CALL( "Group Call" ),
ID_ANI( "ANI" ),
ID_UNIQUE( "Unique ID" ),
NOTIFICATION( "Notification" ),
PAGE( "Page" ),
PATCH_GROUP_ADD( "Patch Group Add"),
PATCH_GROUP_CALL( "Patch Group Call" ),
PATCH_GROUP_DELETE( "Patch Group Delete"),
QUERY( "Query" ),
REGISTER( "Register" ),
REGISTER_ESN( "ESN" ),
RESPONSE( "Response" ),
SDM( "Short Data Message" ),
STATION_ID( "Station ID" ),
STATUS( "Status" ),
TELEPHONE_INTERCONNECT( "Telephone Interconnect" ),
UNIT_TO_UNIT_CALL( "Unit To Unit Call" ),
UNKNOWN( "Unknown" ),
INVALID( "Invalid" );
private String mLabel;
private CallEventType( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return mLabel;
}
}
public void setEnd(long end)
{
mEventEnd = end;
}
@Override
public int compareTo( CallEvent other )
{
if( other != null && other == this )
{
return 0;
}
return Long.compare( mEventStart, other.getEventStartTime() );
}
public void end()
{
setEnd(System.currentTimeMillis());
}
public DecoderType getDecoderType()
{
return mDecoderType;
}
public CallEventType getCallEventType()
{
return mCallEventType;
}
public void setCallEventType(CallEventType type)
{
mCallEventType = type;
}
public AliasList getAliasList()
{
return mAliasList;
}
public void setAliasList(AliasList aliasList)
{
mAliasList = aliasList;
}
public boolean hasAliasList()
{
return mAliasList != null;
}
public long getEventStartTime()
{
return mEventStart;
}
public long getEventEndTime()
{
return mEventEnd;
}
public String getFromID()
{
return mFromID;
}
public boolean hasFromID()
{
return mFromID != null;
}
public void setFromID(String id)
{
mFromID = id;
}
public String getToID()
{
return mToID;
}
public boolean hasToID()
{
return mToID != null;
}
public void setToID(String id)
{
mToID = id;
}
public String getDetails()
{
return mDetails;
}
public void setDetails(String details)
{
mDetails = details;
}
public abstract Alias getFromIDAlias();
public abstract Alias getToIDAlias();
public abstract String getChannel();
public abstract long getFrequency();
public static String getCSVHeader()
{
return "START_DATE,START_TIME,END_DATE,END_TIME,DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,CHANNEL," +
"FREQUENCY,DETAILS";
}
public String toCSV()
{
StringBuilder sb = new StringBuilder();
sb.append("'");
sb.append(mSDF.format(new Date(getEventStartTime())));
sb.append("','");
if(mEventEnd != 0)
{
sb.append(mSDF.format(new Date(getEventEndTime())));
}
else
{
sb.append("','");
}
sb.append("','");
sb.append(getDecoderType().toString());
sb.append("','");
sb.append(getCallEventType().toString());
sb.append("',");
if(getFromID() != null)
{
sb.append("'");
sb.append(getFromID());
sb.append("'");
}
sb.append(",");
Alias fromAlias = getFromIDAlias();
if(fromAlias != null)
{
sb.append("'");
sb.append(fromAlias.getName());
sb.append("'");
}
sb.append(",");
if(getToID() != null)
{
sb.append("'");
sb.append(getToID());
sb.append("'");
}
sb.append(",");
Alias toAlias = getToIDAlias();
if(toAlias != null)
{
sb.append("'");
sb.append(toAlias.getName());
sb.append("'");
}
sb.append(",'");
sb.append(getChannel());
sb.append("','");
sb.append(getFrequency());
sb.append("',");
if(getDetails() != null)
{
sb.append("'");
sb.append(getDetails());
sb.append("'");
}
return sb.toString();
}
public enum CallEventType
{
ANNOUNCEMENT("Announcement"),
CALL("Call"),
CALL_ALERT("Call Alert"),
CALL_DETECT("Call Detect"),
CALL_DO_NOT_MONITOR("Call-Do Not Monitor"),
CALL_END("Call End"),
CALL_UNIQUE_ID("UID Call"),
CALL_NO_TUNER("Call - No Tuner"),
CALL_TIMEOUT("Call Timeout"),
COMMAND("Command"),
DATA_CALL("Data Call"),
DEREGISTER("Deregister"),
EMERGENCY("EMERGENCY"),
ENCRYPTED_CALL("Encrypted Call"),
FUNCTION("Function"),
GPS("GPS"),
GROUP_CALL("Group Call"),
ID_ANI("ANI"),
ID_UNIQUE("Unique ID"),
NOTIFICATION("Notification"),
PAGE("Page"),
PATCH_GROUP_ADD("Patch Group Add"),
PATCH_GROUP_CALL("Patch Group Call"),
PATCH_GROUP_DELETE("Patch Group Delete"),
QUERY("Query"),
REGISTER("Register"),
REGISTER_ESN("ESN"),
RESPONSE("Response"),
SDM("Short Data Message"),
STATION_ID("Station ID"),
STATUS("Status"),
TELEPHONE_INTERCONNECT("Telephone Interconnect"),
UNIT_TO_UNIT_CALL("Unit To Unit Call"),
UNKNOWN("Unknown"),
INVALID("Invalid");
private String mLabel;
private CallEventType(String label)
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return mLabel;
}
}
@Override
public int compareTo(CallEvent other)
{
if(other != null && other == this)
{
return 0;
}
return Long.compare(mEventStart, other.getEventStartTime());
}
}

View File

@@ -19,10 +19,14 @@
package module.decode.event;
import alias.Alias;
import gui.SDRTrunk;
import icon.IconManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
/**
* Custom renderer for JTable cells that contain Alias objects. Renders the
@@ -32,6 +36,7 @@ import javax.swing.table.DefaultTableCellRenderer;
public class CallEventAliasCellRenderer extends DefaultTableCellRenderer
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(CallEventAliasCellRenderer.class);
private IconManager mIconManager;
@@ -41,35 +46,26 @@ public class CallEventAliasCellRenderer extends DefaultTableCellRenderer
mIconManager = iconManager;
}
public void setValue(Object obj)
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if(obj != null && obj instanceof Alias)
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(value instanceof Alias)
{
Alias alias = (Alias) obj;
Alias alias = (Alias)value;
setText(alias.getName());
ImageIcon icon = getIcon(alias, IconManager.DEFAULT_ICON_SIZE);
if(icon != null)
{
setIcon(icon);
}
label.setText(alias.getName());
label.setForeground(alias.getDisplayColor());
label.setIcon(mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE));
}
else
{
setText(" ");
setIcon(null);
}
}
private ImageIcon getIcon(Alias alias, int height)
{
if(mIconManager != null && alias != null)
{
return mIconManager.getIcon(alias.getIconName(), height);
label.setText("");
label.setForeground(table.getForeground());
label.setIcon(null);
}
return null;
return label;
}
}

View File

@@ -1,38 +1,38 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package module.decode.event;
import alias.Alias;
import module.decode.event.CallEvent.CallEventType;
import sample.Listener;
import javax.swing.table.AbstractTableModel;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import module.decode.event.CallEvent.CallEventType;
import sample.Listener;
public class CallEventModel extends AbstractTableModel implements Listener<CallEvent>
{
private static final long serialVersionUID = 1L;
private static DecimalFormat mFrequencyFormatter = new DecimalFormat( "0.000000" );
private static DecimalFormat mFrequencyFormatter = new DecimalFormat("0.000000");
public static final int TIME = 0;
public static final int EVENT = 1;
public static final int FROM_ID = 2;
@@ -43,168 +43,179 @@ public class CallEventModel extends AbstractTableModel implements Listener<CallE
public static final int FREQUENCY = 7;
public static final int DETAILS = 8;
protected int mMaxMessages = 500;
protected int mMaxMessages = 500;
protected List<CallEvent> mEvents = new ArrayList<CallEvent>();
protected List<CallEvent> mEvents = new ArrayList<CallEvent>();
protected String[] mHeaders = new String[] { "Time",
"Event",
"From",
"Alias",
"To",
"Alias",
"Channel",
"Frequency",
"Details" };
protected String[] mHeaders = new String[]{"Time",
"Event",
"From",
"Alias",
"To",
"Alias",
"Channel",
"Frequency",
"Details"};
private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" );
public CallEventModel()
{
}
public void dispose()
{
mEvents.clear();
}
public void reset()
{
mEvents.clear();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
private SimpleDateFormat mSDFTime = new SimpleDateFormat("HH:mm:ss");
public void setMaxMessageCount( int count )
{
mMaxMessages = count;
}
public CallEventModel()
{
}
/**
* Adds, updates or deletes the event from the model. Producers can send
* the same call event multiple times to indicate that information in the
* event is updated. Producers can also mark the event as invalid and the
* event will be removed from the model.
*/
public void receive( final CallEvent event )
{
if( event.isValid() )
{
if( !mEvents.contains( event ) )
{
mEvents.add( 0, event );
fireTableRowsInserted( 0, 0 );
public void dispose()
{
mEvents.clear();
}
prune();
}
else
{
int row = mEvents.indexOf( event );
fireTableRowsUpdated( row, row );
}
}
else
{
if( mEvents.contains( event ) )
{
int row = mEvents.indexOf( event );
mEvents.remove( event );
fireTableRowsDeleted( row, row );
}
}
}
private void prune()
{
while( mEvents.size() > mMaxMessages )
{
int index = mEvents.size() - 1;
mEvents.remove( index );
fireTableRowsDeleted( index, index );
}
}
public void reset()
{
mEvents.clear();
@Override
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
* Adds, updates or deletes the event from the model. Producers can send
* the same call event multiple times to indicate that information in the
* event is updated. Producers can also mark the event as invalid and the
* event will be removed from the model.
*/
public void receive(final CallEvent event)
{
if(event.isValid())
{
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
else
{
if(mEvents.contains(event))
{
int row = mEvents.indexOf(event);
mEvents.remove(event);
fireTableRowsDeleted(row, row);
}
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public int getRowCount()
{
return mEvents.size();
return mEvents.size();
}
@Override
@Override
public int getColumnCount()
{
return mHeaders.length;
return mHeaders.length;
}
public String getColumnName( int column )
{
return mHeaders[ column ];
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
public String getColumnName(int column)
{
synchronized( mEvents )
{
switch( columnIndex )
{
case TIME:
StringBuilder sb = new StringBuilder();
sb.append( mSDFTime.format(
mEvents.get( rowIndex ).getEventStartTime() ) );
if( mEvents.get( rowIndex ).getEventEndTime() != 0 )
{
sb.append( " - " );
sb.append( mSDFTime.format(
mEvents.get( rowIndex ).getEventEndTime() ) );
}
else if( mEvents.get( rowIndex )
.getCallEventType() == CallEventType.CALL )
{
sb.append( " - In Progress" );
}
return sb.toString();
case EVENT:
return mEvents.get( rowIndex ).getCallEventType();
case FROM_ID:
return mEvents.get( rowIndex ).getFromID();
case FROM_ALIAS:
return mEvents.get( rowIndex ).getFromIDAlias();
case TO_ID:
return mEvents.get( rowIndex ).getToID();
case TO_ALIAS:
return mEvents.get( rowIndex ).getToIDAlias();
case CHANNEL:
return mEvents.get( rowIndex ).getChannel();
case FREQUENCY:
long frequency = mEvents.get( rowIndex ).getFrequency();
if( frequency != 0 )
{
return mFrequencyFormatter.format( (double)frequency / 1E6d );
}
else
{
return null;
}
case DETAILS:
return mEvents.get( rowIndex ).getDetails();
}
}
return null;
return mHeaders[column];
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
{
switch(columnIndex)
{
case TIME:
StringBuilder sb = new StringBuilder();
sb.append(mSDFTime.format(
mEvents.get(rowIndex).getEventStartTime()));
if(mEvents.get(rowIndex).getEventEndTime() != 0)
{
sb.append(" - ");
sb.append(mSDFTime.format(
mEvents.get(rowIndex).getEventEndTime()));
}
else if(mEvents.get(rowIndex)
.getCallEventType() == CallEventType.CALL)
{
sb.append(" - In Progress");
}
return sb.toString();
case EVENT:
return mEvents.get(rowIndex).getCallEventType();
case FROM_ID:
return mEvents.get(rowIndex).getFromID();
case FROM_ALIAS:
return mEvents.get(rowIndex).getFromIDAlias();
case TO_ID:
return mEvents.get(rowIndex).getToID();
case TO_ALIAS:
return mEvents.get(rowIndex).getToIDAlias();
case CHANNEL:
return mEvents.get(rowIndex).getChannel();
case FREQUENCY:
long frequency = mEvents.get(rowIndex).getFrequency();
if(frequency != 0)
{
return mFrequencyFormatter.format((double) frequency / 1E6d);
}
else
{
return null;
}
case DETAILS:
return mEvents.get(rowIndex).getDetails();
}
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
if(columnIndex == FROM_ALIAS || columnIndex == TO_ALIAS)
{
return Alias.class;
}
return super.getColumnClass(columnIndex);
}
}

View File

@@ -18,112 +18,58 @@
******************************************************************************/
package module.decode.event;
import controller.channel.Channel;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEvent.Event;
import controller.channel.ChannelEventListener;
import controller.channel.ChannelProcessingManager;
import icon.IconManager;
import module.ProcessingChain;
import net.miginfocom.swing.MigLayout;
import sample.Listener;
import javax.swing.*;
import java.awt.*;
public class CallEventPanel extends JPanel implements ChannelEventListener
public class CallEventPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private JTable mTable;
private CallEventModel mEmptyCallEventModel = new CallEventModel();
private JScrollPane mEmptyScroller;
private Channel mDisplayedChannel;
private CallEventAliasCellRenderer mRenderer;
private ChannelProcessingManager mChannelProcessingManager;
public CallEventPanel(IconManager iconManager, ChannelProcessingManager channelProcessingManager)
/**
* View for call event table
* @param iconManager to display alias icons in table rows
*/
public CallEventPanel(IconManager iconManager)
{
mChannelProcessingManager = channelProcessingManager;
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
JTable table = new JTable(new CallEventModel());
table.setAutoCreateRowSorter(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mTable = new JTable(mEmptyCallEventModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mRenderer = new CallEventAliasCellRenderer(iconManager);
table.getColumnModel().getColumn(CallEventModel.FROM_ALIAS).setCellRenderer(mRenderer);
table.getColumnModel().getColumn(CallEventModel.TO_ALIAS).setCellRenderer(mRenderer);
mEmptyScroller = new JScrollPane(table);
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
}
@Override
public void channelChanged(final ChannelEvent event)
public void receive(ProcessingChain processingChain)
{
if(event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && event.getChannel().isSelected())
EventQueue.invokeLater(new Runnable()
{
if(mDisplayedChannel == null || (mDisplayedChannel != null && mDisplayedChannel != event.getChannel()))
@Override
public void run()
{
ProcessingChain chain = mChannelProcessingManager.getProcessingChain(event.getChannel());
mTable.setModel(processingChain != null ? processingChain.getCallEventModel() : mEmptyCallEventModel);
if(chain != null)
if(processingChain != null)
{
final CallEventModel model = chain.getCallEventModel();
if(model != null)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
removeAll();
JTable table = new JTable(model);
table.setAutoCreateRowSorter(true);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
table.getColumnModel().getColumn(CallEventModel.FROM_ALIAS)
.setCellRenderer(mRenderer);
table.getColumnModel().getColumn(CallEventModel.TO_ALIAS)
.setCellRenderer(mRenderer);
add(new JScrollPane(table));
mDisplayedChannel = event.getChannel();
revalidate();
repaint();
}
});
}
mTable.getColumnModel().getColumn(CallEventModel.FROM_ALIAS).setCellRenderer(mRenderer);
mTable.getColumnModel().getColumn(CallEventModel.TO_ALIAS).setCellRenderer(mRenderer);
}
}
}
else if(event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || event.getEvent() == Event.REQUEST_DISABLE)
{
if(mDisplayedChannel != null && mDisplayedChannel == event.getChannel())
{
mDisplayedChannel = null;
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
removeAll();
add(mEmptyScroller);
revalidate();
repaint();
}
});
}
}
});
}
}

View File

@@ -1,50 +1,25 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.event;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import message.Message;
import module.ProcessingChain;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSplitButton;
import controller.channel.Channel;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEvent.Event;
@@ -52,278 +27,285 @@ import controller.channel.ChannelEventListener;
import controller.channel.ChannelProcessingManager;
import filter.FilterEditorPanel;
import filter.FilterSet;
import message.Message;
import module.ProcessingChain;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
public class MessageActivityPanel extends JPanel implements ChannelEventListener
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class MessageActivityPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog =
LoggerFactory.getLogger( MessageActivityPanel.class );
private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel();
private JTable mTable = new JTable(EMPTY_MODEL);
private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel();
private JTable mMessageTable;
private Channel mDisplayedChannel;
private MessageActivityModel mDisplayedModel = EMPTY_MODEL;
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
private ChannelProcessingManager mChannelProcessingManager;
public MessageActivityPanel( ChannelProcessingManager channelProcessingManager )
{
mChannelProcessingManager = channelProcessingManager;
setLayout( new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]") );
add( mManagementPanel, "span,growx" );
mMessageTable = new JTable( mDisplayedModel );
add( new JScrollPane( mMessageTable ), "span,grow" );
}
@Override
public void channelChanged( ChannelEvent event )
public MessageActivityPanel(ChannelProcessingManager channelProcessingManager)
{
boolean changed = false;
if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE &&
event.getChannel().isSelected() )
{
if( mDisplayedChannel == null ||
( mDisplayedChannel != null &&
mDisplayedChannel != event.getChannel() ) )
{
mDisplayedChannel = event.getChannel();
mChannelProcessingManager = channelProcessingManager;
ProcessingChain chain = mChannelProcessingManager
.getProcessingChain( mDisplayedChannel );
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
if( chain != null )
{
mDisplayedModel = chain.getMessageActivityModel();
}
else
{
mDisplayedModel = EMPTY_MODEL;
}
add(mManagementPanel, "span,growx");
mMessageTable.setModel( mDisplayedModel );
if( mDisplayedChannel != null )
{
mManagementPanel.enable();
}
else
{
mManagementPanel.disable();
}
changed = true;
}
}
else if( event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP ||
event.getEvent() == Event.REQUEST_DISABLE )
{
if( mDisplayedChannel != null &&
mDisplayedChannel == event.getChannel() )
{
mDisplayedChannel = null;
mMessageTable.setModel( EMPTY_MODEL );
mManagementPanel.disable();
changed = true;
}
}
if( changed )
{
revalidate();
repaint();
}
add(new JScrollPane(mTable), "span,grow");
}
public class MessageManagementPanel extends JPanel
{
@Override
public void receive(ProcessingChain processingChain)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
mTable.setModel(processingChain != null ? processingChain.getMessageActivityModel() : EMPTY_MODEL);
if(processingChain != null)
{
mManagementPanel.enableButtons();
}
else
{
mManagementPanel.disableButtons();
}
}
});
}
// @Override
// public void channelChanged(ChannelEvent event)
// {
// boolean changed = false;
//
// if(event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE &&
// event.getChannel().isSelected())
// {
// if(mDisplayedChannel == null || (mDisplayedChannel != null && mDisplayedChannel != event.getChannel()))
// {
// mDisplayedChannel = event.getChannel();
//
// ProcessingChain chain = mChannelProcessingManager.getProcessingChain(mDisplayedChannel);
//
// if(chain != null)
// {
// mDisplayedModel = chain.getMessageActivityModel();
// }
// else
// {
// mDisplayedModel = EMPTY_MODEL;
// }
//
// mMessageTable.setModel(mDisplayedModel);
//
// if(mDisplayedChannel != null)
// {
// mManagementPanel.enableButtons();
// }
// else
// {
// mManagementPanel.disableButtons();
// }
//
// changed = true;
// }
// }
// else if(event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || event.getEvent() == Event.REQUEST_DISABLE)
// {
// if(mDisplayedChannel != null && mDisplayedChannel == event.getChannel())
// {
// mDisplayedChannel = null;
//
// mMessageTable.setModel(EMPTY_MODEL);
//
// mManagementPanel.disableButtons();
//
// changed = true;
// }
// }
//
// if(changed)
// {
// revalidate();
// repaint();
// }
// }
public class MessageManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
private MessageFilterButton mFilterButton = new MessageFilterButton();
public MessageManagementPanel()
{
setLayout( new MigLayout("insets 2 2 5 5", "[]5[left,grow]", "") );
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
disable();
add( mFilterButton );
add( mHistoryButton );
disableButtons();
add(mFilterButton);
add(mHistoryButton);
}
public void enable()
public void enableButtons()
{
mHistoryButton.setEnabled( true );
mFilterButton.setEnabled( true );
mHistoryButton.setEnabled(true);
mFilterButton.setEnabled(true);
}
public void disable()
public void disableButtons()
{
mHistoryButton.setEnabled( false );
mFilterButton.setEnabled( false );
mHistoryButton.setEnabled(false);
mFilterButton.setEnabled(false);
}
}
public class MessageHistoryButton extends JideSplitButton
{
}
public class MessageHistoryButton extends JideSplitButton
{
private static final long serialVersionUID = 1L;
public MessageHistoryButton()
{
super( "Clear" );
JPanel historyPanel = new JPanel();
historyPanel.add( new JLabel( "Message History:" ) );
final JSlider slider = new JSlider();
slider.setMinimum( 0 );
slider.setMaximum( 2000 );
slider.setMajorTickSpacing( 500 );
slider.setPaintTicks( true );
slider.setPaintLabels( true );
slider.addMouseListener( new MouseListener()
{
@Override
public void mouseClicked( MouseEvent arg0 )
super("Clear");
JPanel historyPanel = new JPanel();
historyPanel.add(new JLabel("Message History:"));
final JSlider slider = new JSlider();
slider.setMinimum(0);
slider.setMaximum(2000);
slider.setMajorTickSpacing(500);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent arg0)
{
if( SwingUtilities.isLeftMouseButton( arg0 ) &&
arg0.getClickCount() == 2 )
{
slider.setValue( 500 ); //default
}
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
{
slider.setValue(500); //default
}
}
public void mouseEntered( MouseEvent arg0 ) {}
public void mouseExited( MouseEvent arg0 ) {}
public void mousePressed( MouseEvent arg0 ) {}
public void mouseReleased( MouseEvent arg0 ) {}
} );
if( mDisplayedModel != null )
{
slider.setValue( mDisplayedModel.getMaxMessageCount() );
}
else
{
slider.setValue( 500 );
}
slider.addChangeListener( new ChangeListener()
{
@Override
public void stateChanged( ChangeEvent arg0 )
public void mouseEntered(MouseEvent arg0)
{
if( mDisplayedModel != null )
{
mDisplayedModel.setMaxMessageCount( slider.getValue() );
}
}
});
historyPanel.add( slider );
add( historyPanel );
public void mouseExited(MouseEvent arg0)
{
}
public void mousePressed(MouseEvent arg0)
{
}
public void mouseReleased(MouseEvent arg0)
{
}
});
slider.setValue(((MessageActivityModel)mTable.getModel()).getMaxMessageCount());
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
{
((MessageActivityModel)mTable.getModel()).setMaxMessageCount(slider.getValue());
}
});
historyPanel.add(slider);
add(historyPanel);
/* Clear messages */
addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
if( mDisplayedModel != null )
{
mDisplayedModel.clear();
}
}
} );
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
((MessageActivityModel)mTable.getModel()).clear();
}
});
}
}
}
public class MessageFilterButton extends JideButton
{
public class MessageFilterButton extends JideButton
{
private static final long serialVersionUID = 1L;
public MessageFilterButton()
{
super( "Filter" );
addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
if( mDisplayedChannel == null )
{
JOptionPane.showMessageDialog( MessageFilterButton.this,
"Please enable and select a decoding channel to "
+ "adjust the channel's message display filter" );
}
else
{
if( mDisplayedModel != null )
{
final JFrame editor = new JFrame();
editor.setTitle( "Message Filter Editor" );
editor.setSize( 600, 400 );
editor.setLocationRelativeTo( MessageFilterButton.this );
editor.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
editor.setLayout( new MigLayout( "", "[grow,fill]",
"[grow,fill][][]" ) );
super("Filter");
@SuppressWarnings( "unchecked" )
FilterSet<Message> filter = (FilterSet<Message>)
mDisplayedModel.getMessageFilter();
FilterEditorPanel<Message> panel =
new FilterEditorPanel<Message>( filter );
JScrollPane scroller = new JScrollPane( panel );
scroller.setViewportView( panel );
editor.add( scroller, "wrap" );
editor.add( new JLabel( "Right-click to select/deselect "
+ "all nodes" ), "wrap" );
JButton close = new JButton( "Close" );
close.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
editor.dispose();
}
} );
editor.add( close );
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
final JFrame editor = new JFrame();
EventQueue.invokeLater( new Runnable()
{
public void run()
{
editor.setVisible( true );
}
} );
}
}
}
} );
editor.setTitle("Message Filter Editor");
editor.setSize(600, 400);
editor.setLocationRelativeTo(MessageFilterButton.this);
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
editor.setLayout(new MigLayout("", "[grow,fill]",
"[grow,fill][][]"));
@SuppressWarnings("unchecked")
FilterSet<Message> filter = (FilterSet<Message>) ((MessageActivityModel)mTable.getModel()).getMessageFilter();
FilterEditorPanel<Message> panel = new FilterEditorPanel<Message>(filter);
JScrollPane scroller = new JScrollPane(panel);
scroller.setViewportView(panel);
editor.add(scroller, "wrap");
editor.add(new JLabel("Right-click to select/deselect all nodes"), "wrap");
JButton close = new JButton("Close");
close.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
editor.dispose();
}
});
editor.add(close);
EventQueue.invokeLater(new Runnable()
{
public void run()
{
editor.setVisible(true);
}
});
}
});
}
}
}
}

View File

@@ -1,201 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.fleetsync2;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class Fleetsync2DecoderPanel extends DecoderPanel
{
private final static Logger mLog = LoggerFactory.getLogger(Fleetsync2DecoderPanel.class);
private static final long serialVersionUID = 1L;
private static final String PROTOCOL = "FSync II";
private JLabel mToLabel = new JLabel(" ");
private JLabel mTo = new JLabel(" ");
private JLabel mToAlias = new JLabel(" ");
private JLabel mFromLabel = new JLabel(" ");
private JLabel mFrom = new JLabel(" ");
private JLabel mFromAlias = new JLabel(" ");
private JLabel mProtocol = new JLabel(PROTOCOL);
private JLabel mMessage = new JLabel(" ");
private JLabel mMessageType = new JLabel(" ");
public Fleetsync2DecoderPanel(IconManager iconManager, SettingsManager settingsManager,
Fleetsync2DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
@Override
public Fleetsync2DecoderState getDecoderState()
{
return (Fleetsync2DecoderState) super.getDecoderState();
}
public void dispose()
{
super.dispose();
}
protected void init()
{
/* Calling super init will get and broadcast color settings to properly
* setup the jlabel colors */
super.init();
setLayout(new MigLayout("insets 1 0 0 0", "[grow,fill]", "[]0[]0[]"));
mToLabel.setFont(mFontAuxDecoder);
mToLabel.setForeground(mColorLabelAuxDecoder);
add(mToLabel);
mTo.setFont(mFontAuxDecoder);
mTo.setForeground(mColorLabelAuxDecoder);
add(mTo);
mToAlias.setFont(mFontAuxDecoder);
mToAlias.setForeground(mColorLabelAuxDecoder);
add(mToAlias, "wrap");
mFromLabel.setFont(mFontAuxDecoder);
mFromLabel.setForeground(mColorLabelAuxDecoder);
add(mFromLabel);
mFrom.setFont(mFontAuxDecoder);
mFrom.setForeground(mColorLabelAuxDecoder);
add(mFrom);
mFromAlias.setFont(mFontAuxDecoder);
mFromAlias.setForeground(mColorLabelAuxDecoder);
add(mFromAlias, "wrap");
mProtocol.setFont(mFontAuxDecoder);
mProtocol.setForeground(mColorLabelAuxDecoder);
add(mProtocol);
mMessageType.setFont(mFontAuxDecoder);
mMessageType.setForeground(mColorLabelAuxDecoder);
add(mMessageType);
mMessage.setFont(mFontAuxDecoder);
mMessage.setForeground(mColorLabelAuxDecoder);
add(mMessage, "wrap");
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
switch(changedAttribute)
{
case FROM_TALKGROUP:
String from = getDecoderState().getFromID();
if(from != null)
{
mFromLabel.setText("FROM:");
mFrom.setText(from);
}
else
{
mFromLabel.setText(" ");
mFrom.setText(" ");
}
break;
case FROM_TALKGROUP_ALIAS:
setAliasLabel(mFromAlias, getDecoderState().getFromIDAlias());
break;
case MESSAGE:
mMessage.setText(getDecoderState().getMessage());
break;
case MESSAGE_TYPE:
mMessageType.setText(getDecoderState().getMessageType());
break;
case TO_TALKGROUP:
String to = getDecoderState().getToID();
if(to != null)
{
mToLabel.setText("TO:");
mTo.setText(to);
}
else
{
mToLabel.setText(" ");
mTo.setText(" ");
}
break;
case TO_TALKGROUP_ALIAS:
setAliasLabel(mToAlias, getDecoderState().getToIDAlias());
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting color = (ColorSetting) setting;
if(color.getColorSettingName() ==
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER)
{
mFromLabel.setForeground(color.getColor());
mFrom.setForeground(color.getColor());
mFromAlias.setForeground(color.getColor());
mToLabel.setForeground(color.getColor());
mTo.setForeground(color.getColor());
mToAlias.setForeground(color.getColor());
mProtocol.setForeground(color.getColor());
mMessageType.setForeground(color.getColor());
mMessage.setForeground(color.getColor());
}
}
}
}

View File

@@ -1,353 +1,316 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.fleetsync2;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.State;
import message.Message;
import module.decode.DecoderType;
import util.StringUtils;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.ScheduledExecutorService;
import message.Message;
import module.decode.DecoderType;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
import alias.Alias;
import alias.AliasList;
public class Fleetsync2DecoderState extends DecoderState
{
private TreeSet<String> mIdents = new TreeSet<String>();
private TreeSet<String> mEmergencyIdents = new TreeSet<String>();
private TreeSet<String> mIdents = new TreeSet<String>();
private TreeSet<String> mEmergencyIdents = new TreeSet<String>();
private String mFrom;
private Alias mFromAlias;
private AliasedStringAttributeMonitor mFromAttribute;
private AliasedStringAttributeMonitor mToAttribute;
private String mMessage;
private String mMessageType;
private long mFrequency;
private String mTo;
private Alias mToAlias;
private String mMessage;
private String mMessageType;
private long mFrequency;
public Fleetsync2DecoderState( AliasList aliasList )
{
super( aliasList );
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.FLEETSYNC2;
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
@Override
public void dispose()
{
super.dispose();
}
@Override
public void init()
{
}
/**
* Resets the overall decoder state and clears any accumulated event details
*/
@Override
public void reset()
{
mIdents.clear();
mEmergencyIdents.clear();
resetState();
}
/**
* Resets this decoder state
*/
public void resetState()
{
setFromID( null );
setFromIDAlias( null );
setToID( null );
setToIDAlias( null );
setMessage( null );
setMessageType( null );
}
@Override
public void receive( Message message )
public Fleetsync2DecoderState(AliasList aliasList)
{
if( message instanceof FleetsyncMessage )
{
FleetsyncMessage fleetsync = (FleetsyncMessage)message;
if( fleetsync.isValid() )
{
State state = State.CALL;
setFromID( fleetsync.getFromID() );
setFromIDAlias( fleetsync.getFromIDAlias() );
mIdents.add( fleetsync.getFromID() );
FleetsyncMessageType type = fleetsync.getMessageType();
super(aliasList);
if( type != FleetsyncMessageType.ANI )
{
setToID( fleetsync.getToID() );
setToIDAlias( fleetsync.getToIDAlias() );
mIdents.add( fleetsync.getToID() );
}
setMessageType( type.getLabel() );
mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.FLEETSYNC);
mToAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.FLEETSYNC);
}
switch( type )
{
case GPS:
setMessage( fleetsync.getGPSLocation() );
state = State.DATA;
break;
case STATUS:
StringBuilder sb = new StringBuilder();
sb.append( fleetsync.getStatus() );
Alias status = fleetsync.getStatusAlias();
if( status != null )
{
sb.append( "/" );
sb.append( status.getName() );
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.FLEETSYNC2;
}
setMessage( sb.toString() );
state = State.DATA;
break;
case EMERGENCY:
case LONE_WORKER_EMERGENCY:
mEmergencyIdents.add( fleetsync.getFromID() );
state = State.DATA;
break;
default:
break;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
FleetsyncCallEvent fsCallEvent =
FleetsyncCallEvent.getFleetsync2Event( fleetsync, mFrequency );
@Override
public void stop()
{
}
fsCallEvent.setAliasList( getAliasList() );
@Override
public void dispose()
{
super.dispose();
}
broadcast( fsCallEvent );
@Override
public void init()
{
}
/**
* Resets the overall decoder state and clears any accumulated event details
*/
@Override
public void reset()
{
mIdents.clear();
mEmergencyIdents.clear();
resetState();
}
/**
* Resets this decoder state
*/
public void resetState()
{
mFromAttribute.reset();
mToAttribute.reset();
mMessage = null;
mMessageType = null;
}
@Override
public void receive(Message message)
{
if(message instanceof FleetsyncMessage)
{
FleetsyncMessage fleetsync = (FleetsyncMessage) message;
if(fleetsync.isValid())
{
State state = State.CALL;
mFromAttribute.process(fleetsync.getFromID());
mIdents.add(fleetsync.getFromID());
FleetsyncMessageType type = fleetsync.getMessageType();
if(type != FleetsyncMessageType.ANI)
{
mToAttribute.process(fleetsync.getToID());
mIdents.add(fleetsync.getToID());
}
setMessageType(type.getLabel());
switch(type)
{
case GPS:
setMessage(fleetsync.getGPSLocation());
state = State.DATA;
break;
case STATUS:
StringBuilder sb = new StringBuilder();
sb.append(fleetsync.getStatus());
Alias status = fleetsync.getStatusAlias();
if(status != null)
{
sb.append("/");
sb.append(status.getName());
}
setMessage(sb.toString());
state = State.DATA;
break;
case EMERGENCY:
case LONE_WORKER_EMERGENCY:
mEmergencyIdents.add(fleetsync.getFromID());
state = State.DATA;
break;
default:
break;
}
FleetsyncCallEvent fsCallEvent =
FleetsyncCallEvent.getFleetsync2Event(fleetsync, mFrequency);
fsCallEvent.setAliasList(getAliasList());
broadcast(fsCallEvent);
/* Broadcast decode event so that the channel state will
* kick in and reset everything after a short delay */
broadcast( new DecoderStateEvent( this, Event.DECODE, state ) );
}
}
* kick in and reset everything after a short delay */
broadcast(new DecoderStateEvent(this, Event.DECODE, state));
}
}
}
public String getFromID()
{
return mFrom;
}
public void setFromID( String from )
{
mFrom = from;
broadcast( ChangedAttribute.FROM_TALKGROUP );
}
public Alias getFromIDAlias()
{
return mFromAlias;
}
public void setFromIDAlias( Alias alias )
{
mFromAlias = alias;
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
}
public String getToID()
{
return mTo;
}
public void setToID( String to )
{
mTo = to;
broadcast( ChangedAttribute.TO_TALKGROUP );
}
public Alias getToIDAlias()
{
return mToAlias;
}
public void setToIDAlias( Alias alias )
{
mToAlias = alias;
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
}
public String getMessage()
{
return mMessage;
}
public String getMessage()
{
return mMessage;
}
public void setMessage( String message )
{
mMessage = message;
broadcast( ChangedAttribute.MESSAGE );
}
public String getMessageType()
{
return mMessageType;
}
public void setMessage(String message)
{
if(!StringUtils.isEqual(mMessage, message))
{
mMessage = message;
broadcast(new AttributeChangeRequest<String>(Attribute.MESSAGE, mMessage));
}
}
public void setMessageType( String type )
{
mMessageType = type;
broadcast( ChangedAttribute.MESSAGE_TYPE );
}
/**
* Responds to reset events issued by the channel state
*/
@Override
public void receiveDecoderStateEvent( DecoderStateEvent event )
{
switch( event.getEvent() )
{
case RESET:
resetState();
break;
case SOURCE_FREQUENCY:
mFrequency = event.getFrequency();
break;
default:
break;
}
}
@Override
public String getMessageType()
{
return "Fleetsync " + mMessageType;
}
public void setMessageType(String type)
{
if(!StringUtils.isEqual(mMessageType, type))
{
mMessageType = type;
broadcast(new AttributeChangeRequest<String>(Attribute.MESSAGE_TYPE, getMessageType()));
}
}
/**
* Responds to reset events issued by the channel state
*/
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
switch(event.getEvent())
{
case RESET:
resetState();
break;
case SOURCE_FREQUENCY:
mFrequency = event.getFrequency();
break;
default:
break;
}
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
sb.append( "=============================\n" );
sb.append( "Decoder:\tFleetsync II\n\n" );
sb.append( "Fleetsync Idents\n" );
sb.append("=============================\n");
sb.append("Decoder:\tFleetsync II\n\n");
sb.append("Fleetsync Idents\n");
if( mIdents.isEmpty() )
{
sb.append( " None\n" );
}
else
{
Iterator<String> it = mIdents.iterator();
while( it.hasNext() )
{
String ident = it.next();
sb.append( " " );
sb.append( formatIdent( ident ) );
if(mIdents.isEmpty())
{
sb.append(" None\n");
}
else
{
Iterator<String> it = mIdents.iterator();
if( hasAliasList() )
{
Alias alias = getAliasList().getFleetsyncAlias( ident );
if( alias != null )
{
sb.append( " " );
sb.append( alias.getName() );
}
}
sb.append( "\n" );
}
}
sb.append( "\nFleetsync Emergency Activations\n" );
while(it.hasNext())
{
String ident = it.next();
if( mEmergencyIdents.isEmpty() )
{
sb.append( " None\n" );
}
else
{
Iterator<String> it = mEmergencyIdents.iterator();
while( it.hasNext() )
{
String ident = it.next();
sb.append( " " );
sb.append( formatIdent( ident ) );
sb.append(" ");
sb.append(formatIdent(ident));
if( hasAliasList() )
{
Alias alias = getAliasList().getFleetsyncAlias( ident );
if( alias != null )
{
sb.append( " " );
sb.append( alias.getName() );
}
sb.append( "\n" );
}
}
}
if(hasAliasList())
{
Alias alias = getAliasList().getFleetsyncAlias(ident);
return sb.toString();
if(alias != null)
{
sb.append(" ");
sb.append(alias.getName());
}
}
sb.append("\n");
}
}
sb.append("\nFleetsync Emergency Activations\n");
if(mEmergencyIdents.isEmpty())
{
sb.append(" None\n");
}
else
{
Iterator<String> it = mEmergencyIdents.iterator();
while(it.hasNext())
{
String ident = it.next();
sb.append(" ");
sb.append(formatIdent(ident));
if(hasAliasList())
{
Alias alias = getAliasList().getFleetsyncAlias(ident);
if(alias != null)
{
sb.append(" ");
sb.append(alias.getName());
}
sb.append("\n");
}
}
}
return sb.toString();
}
public static String formatIdent( String ident )
{
StringBuilder sb = new StringBuilder();
if( ident.length() == 7 )
{
sb.append( ident.substring( 0, 3 ) );
sb.append( "-" );
sb.append( ident.substring( 3, 7 ) );
public static String formatIdent(String ident)
{
StringBuilder sb = new StringBuilder();
return sb.toString();
}
else
{
return ident;
}
}
if(ident.length() == 7)
{
sb.append(ident.substring(0, 3));
sb.append("-");
sb.append(ident.substring(3, 7));
return sb.toString();
}
else
{
return ident;
}
}
}

View File

@@ -1,127 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.lj1200;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
public class LJ1200DecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private static final String PROTOCOL = "LJ-1200";
private JLabel mProtocol = new JLabel(PROTOCOL);
private JLabel mTo = new JLabel();
private JLabel mToAlias = new JLabel();
public LJ1200DecoderPanel(IconManager iconManager, SettingsManager settingsManager, LJ1200DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public void dispose()
{
super.dispose();
}
protected void init()
{
/* Calling super init will get and broadcast color settings to properly
* setup the jlabel colors */
super.init();
setLayout(new MigLayout("insets 1 0 0 0", "[grow,fill]", ""));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
mTo.setFont(mFontDecoder);
mTo.setForeground(mColorLabelDecoder);
mToAlias.setFont(mFontDecoder);
mToAlias.setForeground(mColorLabelDecoder);
add(mProtocol);
add(mTo);
add(mToAlias, "wrap");
}
public LJ1200DecoderState getDecoderState()
{
return (LJ1200DecoderState) super.getDecoderState();
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
switch(changedAttribute)
{
case TO_TALKGROUP:
mTo.setText(getDecoderState().getAddress());
repaint();
break;
case TO_TALKGROUP_ALIAS:
Alias alias = getDecoderState().getAddressAlias();
if(alias != null)
{
mToAlias.setText(alias.getName());
}
else
{
mToAlias.setText("");
}
repaint();
break;
default:
break;
}
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting color = (ColorSetting) setting;
if(color.getColorSettingName() ==
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER)
{
mProtocol.setForeground(color.getColor());
mTo.setForeground(color.getColor());
mToAlias.setForeground(color.getColor());
}
}
}
}

View File

@@ -1,22 +1,39 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.lj1200;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.State;
import message.Message;
import module.decode.DecoderType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -24,151 +41,114 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ScheduledExecutorService;
import message.Message;
import module.decode.DecoderType;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.AliasList;
public class LJ1200DecoderState extends DecoderState
{
private final static Logger mLog = LoggerFactory.getLogger( LJ1200DecoderState.class );
private final static Logger mLog = LoggerFactory.getLogger(LJ1200DecoderState.class);
private Set<String> mAddresses = new TreeSet<String>();
private String mAddress;
private Alias mAddressAlias;
public LJ1200DecoderState( AliasList aliasList )
{
super( aliasList );
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.LJ_1200;
}
private Set<String> mAddresses = new TreeSet<String>();
@Override
public void start( ScheduledExecutorService executor )
{
}
private AliasedStringAttributeMonitor mAddressAttribute;
@Override
public void stop()
{
}
public String getAddress()
{
return mAddress;
}
public Alias getAddressAlias()
{
return mAddressAlias;
}
@Override
public void receive( Message message )
public LJ1200DecoderState(AliasList aliasList)
{
if( message instanceof LJ1200Message )
{
LJ1200Message lj = (LJ1200Message)message;
if( lj.isValid() )
{
setAddress( lj.getAddress() );
setAddressAlias( lj.getTransponderAlias() );
mAddresses.add( mAddress );
super(aliasList);
broadcast( LJ1200CallEvent.getLJ1200Event( lj ) );
broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) );
}
}
else if( message instanceof LJ1200TransponderMessage )
{
}
mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.LOJACK);
}
private void setAddress( String address )
{
mAddress = address;
broadcast( ChangedAttribute.TO_TALKGROUP );
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.LJ_1200;
}
private void setAddressAlias( Alias alias )
{
mAddressAlias = alias;
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
}
@Override
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void receive(Message message)
{
if(message instanceof LJ1200Message)
{
LJ1200Message lj = (LJ1200Message) message;
if(lj.isValid())
{
String address = lj.getAddress();
mAddressAttribute.process(address);
mAddresses.add(address);
broadcast(LJ1200CallEvent.getLJ1200Event(lj));
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
}
}
else if(message instanceof LJ1200TransponderMessage)
{
}
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
sb.append( "=============================\n" );
sb.append( "Decoder:\tLJ-1200I\n\n" );
sb.append("=============================\n");
sb.append("Decoder:\tLJ-1200I\n\n");
if( !mAddresses.isEmpty() )
{
sb.append( "Transponder Addresses:\n" );
List<String> addresses = new ArrayList<String>( mAddresses );
Collections.sort( addresses );
for( String address: addresses )
{
sb.append( "\t" );
sb.append( address );
sb.append( "\n" );
}
}
if(!mAddresses.isEmpty())
{
sb.append("Transponder Addresses:\n");
return sb.toString();
List<String> addresses = new ArrayList<String>(mAddresses);
Collections.sort(addresses);
for(String address : addresses)
{
sb.append("\t");
sb.append(address);
sb.append("\n");
}
}
return sb.toString();
}
@Override
public void init()
{
/* No initialization steps */
}
@Override
public void init()
{
/* No initialization steps */
}
@Override
public void receiveDecoderStateEvent( DecoderStateEvent event )
{
if( event.getEvent() == Event.RESET )
{
resetState();
}
}
private void resetState()
{
setAddress( null );
broadcast( ChangedAttribute.TO_TALKGROUP );
setAddressAlias( null );
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
if(event.getEvent() == Event.RESET)
{
resetState();
}
}
@Override
public void reset()
{
mAddresses.clear();
resetState();
}
private void resetState()
{
mAddressAttribute.reset();
}
@Override
public void reset()
{
mAddresses.clear();
resetState();
}
}

View File

@@ -1,157 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.ltrnet;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class LTRNetDecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol = new JLabel("LTR-Net");
private JLabel mTalkgroup = new JLabel();
private JLabel mTalkgroupAlias = new JLabel();
private JLabel mLCN = new JLabel("LCN:");
private JLabel mDescription = new JLabel();
public LTRNetDecoderPanel(IconManager iconManager, SettingsManager settingsManager, LTRNetDecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
mTalkgroup.setFont(mFontDecoder);
mTalkgroup.setForeground(mColorLabelDecoder);
mTalkgroupAlias.setFont(mFontDecoder);
mTalkgroupAlias.setForeground(mColorLabelDecoder);
mLCN.setFont(mFontDecoder);
mLCN.setForeground(mColorLabelDecoder);
mDescription.setFont(mFontDecoder);
mDescription.setForeground(mColorLabelDecoder);
add(mProtocol);
add(mTalkgroup);
add(mTalkgroupAlias, "wrap");
add(mLCN);
add(mDescription, "span");
}
public LTRNetDecoderState getDecoderState()
{
return (LTRNetDecoderState) super.getDecoderState();
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
LTRNetDecoderState state = getDecoderState();
switch(changedAttribute)
{
case TO_TALKGROUP:
mTalkgroup.setText(state.getToTalkgroup());
break;
case TO_TALKGROUP_ALIAS:
Alias tgAlias = state.getToTalkgroupAlias();
setAliasLabel(mTalkgroupAlias, tgAlias);
break;
case DESCRIPTION:
mDescription.setText(state.getDescription());
break;
case CHANNEL_NUMBER:
mLCN.setText("LCN: " + state.getChannelNumber());
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mProtocol != null)
{
mProtocol.setForeground(mColorLabelDecoder);
}
if(mTalkgroup != null)
{
mTalkgroup.setForeground(mColorLabelDecoder);
}
if(mTalkgroupAlias != null)
{
mTalkgroupAlias.setForeground(mColorLabelDecoder);
}
if(mLCN != null)
{
mLCN.setForeground(mColorLabelDecoder);
}
if(mDescription != null)
{
mDescription.setForeground(mColorLabelDecoder);
}
break;
default:
break;
}
}
}
}

View File

@@ -17,30 +17,31 @@
******************************************************************************/
package module.decode.ltrnet;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import channel.metadata.AliasedIntegerAttributeMonitor;
import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.State;
import message.Message;
import module.decode.DecoderType;
import module.decode.event.CallEvent;
import module.decode.event.CallEvent.CallEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.StringUtils;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.ScheduledExecutorService;
import message.Message;
import module.decode.DecoderType;
import module.decode.event.CallEvent;
import module.decode.event.CallEvent.CallEventType;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.AliasList;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
public class LTRNetDecoderState extends DecoderState
{
private final static Logger mLog = LoggerFactory.getLogger(LTRNetDecoderState.class);
@@ -53,21 +54,33 @@ public class LTRNetDecoderState extends DecoderState
private TreeSet<Integer> mUniqueIDs = new TreeSet<Integer>();
private TreeSet<String> mNeighborIDs = new TreeSet<String>();
private TreeSet<String> mSiteIDs = new TreeSet<String>();
private HashMap<Integer, Long> mReceiveFrequencies =
new HashMap<Integer, Long>();
private HashMap<Integer, Long> mTransmitFrequencies =
new HashMap<Integer, Long>();
private HashMap<Integer, String> mActiveCalls = new HashMap<Integer, String>();
private HashMap<Integer,Long> mReceiveFrequencies =
new HashMap<Integer,Long>();
private HashMap<Integer,Long> mTransmitFrequencies =
new HashMap<Integer,Long>();
private HashMap<Integer,String> mActiveCalls = new HashMap<Integer,String>();
private String mTalkgroup;
private Alias mTalkgroupAlias;
private String mDescription;
private AliasedStringAttributeMonitor mToAttribute;
private AliasedIntegerAttributeMonitor mFromUIDAttribute;
private AliasedStringAttributeMonitor mESNAttribute;
private String mMessage;
private String mMessageType;
private int mChannelNumber;
private long mFrequency = 0;
public LTRNetDecoderState(AliasList aliasList)
{
super(aliasList);
mToAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP);
mToAttribute.addIllegalValue("0-00-000");
mFromUIDAttribute = new AliasedIntegerAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.LTR_NET_UID);
mESNAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.ESN);
}
@Override
@@ -79,47 +92,44 @@ public class LTRNetDecoderState extends DecoderState
@Override
public void receive(Message message)
{
if (message.isValid())
if(message.isValid())
{
State state = State.IDLE;
if (message instanceof LTRNetOSWMessage)
if(message instanceof LTRNetOSWMessage)
{
LTRNetOSWMessage ltr = (LTRNetOSWMessage) message;
switch (ltr.getMessageType())
switch(ltr.getMessageType())
{
case CA_ENDD:
if (mChannelNumber == 0)
if(mChannelNumber == 0)
{
mChannelNumber = ltr.getChannel();
broadcast(ChangedAttribute.CHANNEL_NUMBER);
setChannelNumber(ltr.getChannel());
}
/* Process FCC Station ID Events */
if (ltr.getGroup() == 254)
if(ltr.getGroup() == 254)
{
if (mCurrentCallEvent == null ||
mCurrentCallEvent.getCallEventType() !=
CallEventType.STATION_ID)
if(mCurrentCallEvent == null ||
mCurrentCallEvent.getCallEventType() !=
CallEventType.STATION_ID)
{
mCurrentCallEvent = new LTRCallEvent.Builder(
DecoderType.LTR_NET, CallEventType.STATION_ID)
.aliasList(getAliasList())
.channel(String.valueOf(mChannelNumber))
.frequency(mFrequency)
.to(ltr.getTalkgroupID())
.build();
DecoderType.LTR_NET, CallEventType.STATION_ID)
.aliasList(getAliasList())
.channel(String.valueOf(mChannelNumber))
.frequency(mFrequency)
.to(ltr.getTalkgroupID())
.build();
broadcast(mCurrentCallEvent);
broadcast(new DecoderStateEvent(this,
Event.START, State.DATA));
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else
{
broadcast(new DecoderStateEvent(this,
Event.CONTINUATION, State.DATA));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
}
else
@@ -128,15 +138,14 @@ public class LTRNetDecoderState extends DecoderState
}
break;
case CA_STRT:
if (mChannelNumber == 0)
if(mChannelNumber == 0)
{
mChannelNumber = ltr.getChannel();
broadcast(ChangedAttribute.CHANNEL_NUMBER);
setChannelNumber(ltr.getChannel());
}
/* If the call event channel matches our current channel
* then it's a call, otherwise it's a call detect. */
if (ltr.getChannel() == mChannelNumber)
if(ltr.getChannel() == mChannelNumber)
{
processCallMessage(ltr);
}
@@ -147,10 +156,9 @@ public class LTRNetDecoderState extends DecoderState
break;
case SY_IDLE:
if (mChannelNumber != ltr.getChannel())
if(mChannelNumber != ltr.getChannel())
{
mChannelNumber = ltr.getChannel();
broadcast(ChangedAttribute.CHANNEL_NUMBER);
setChannelNumber(ltr.getChannel());
}
break;
case MA_CHNH:
@@ -159,24 +167,22 @@ public class LTRNetDecoderState extends DecoderState
break;
case FQ_RXHI:
case FQ_RXLO:
if (ltr.getFrequency() > 0)
if(ltr.getFrequency() > 0)
{
mReceiveFrequencies.put(ltr.getHomeRepeater(),
ltr.getFrequency());
mReceiveFrequencies.put(ltr.getHomeRepeater(), ltr.getFrequency());
}
break;
case FQ_TXHI:
case FQ_TXLO:
if (ltr.getFrequency() > 0)
if(ltr.getFrequency() > 0)
{
mTransmitFrequencies.put(ltr.getHomeRepeater(),
ltr.getFrequency());
mTransmitFrequencies.put(ltr.getHomeRepeater(), ltr.getFrequency());
}
break;
case ID_NBOR:
String neighborID = ltr.getNeighborID();
if (neighborID != null)
if(neighborID != null)
{
mNeighborIDs.add(neighborID);
}
@@ -186,40 +192,36 @@ public class LTRNetDecoderState extends DecoderState
int uniqueID = ltr.getRadioUniqueID();
if (uniqueID != LTRNetOSWMessage.INT_NULL_VALUE)
if(uniqueID != LTRNetOSWMessage.INT_NULL_VALUE)
{
mUniqueIDs.add(uniqueID);
}
if (getCurrentLTRCallEvent() == null)
if(getCurrentLTRCallEvent() == null)
{
mCurrentCallEvent = new LTRCallEvent.Builder(
DecoderType.LTR_NET, CallEventType.REGISTER)
.aliasList(getAliasList())
.channel(String.valueOf(mChannelNumber))
.frequency(mFrequency)
.from(String.valueOf(uniqueID))
.build();
DecoderType.LTR_NET, CallEventType.REGISTER)
.aliasList(getAliasList())
.channel(String.valueOf(mChannelNumber))
.frequency(mFrequency)
.from(String.valueOf(uniqueID))
.build();
}
else
{
mCurrentCallEvent.setFromID(
String.valueOf(ltr.getRadioUniqueID()));
String.valueOf(ltr.getRadioUniqueID()));
mCurrentCallEvent.setDetails("Unique ID");
broadcast(mCurrentCallEvent);
}
mTalkgroup = String.valueOf(uniqueID);
broadcast(ChangedAttribute.TO_TALKGROUP);
mTalkgroupAlias = ltr.getRadioUniqueIDAlias();
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
mFromUIDAttribute.process(uniqueID);
break;
case ID_SITE:
String siteID = ltr.getSiteID();
if (siteID != null)
if(siteID != null)
{
mSiteIDs.add(siteID);
}
@@ -228,11 +230,11 @@ public class LTRNetDecoderState extends DecoderState
break;
}
}
else if (message instanceof LTRNetISWMessage)
else if(message instanceof LTRNetISWMessage)
{
LTRNetISWMessage ltr = ((LTRNetISWMessage) message);
switch (ltr.getMessageType())
switch(ltr.getMessageType())
{
case CA_STRT:
processCallMessage(ltr);
@@ -246,31 +248,25 @@ public class LTRNetDecoderState extends DecoderState
String esn = ltr.getESN();
if (!esn.contains("xxxx"))
if(!esn.contains("xxxx"))
{
mESNs.add(ltr.getESN());
}
mDescription = "ESN";
broadcast(ChangedAttribute.DESCRIPTION);
mTalkgroup = ltr.getESN();
broadcast(ChangedAttribute.TO_TALKGROUP);
mTalkgroupAlias = ltr.getESNAlias();
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
setMessageType("ESN");
mESNAttribute.process(ltr.getESN());
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
if (mCurrentCallEvent == null)
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = new LTRCallEvent.Builder(
DecoderType.LTR_NET, CallEventType.REGISTER_ESN)
.aliasList(getAliasList())
.details("ESN:" + ltr.getESN())
.frequency(mFrequency)
.from(ltr.getESN())
.build();
DecoderType.LTR_NET, CallEventType.REGISTER_ESN)
.aliasList(getAliasList())
.details("ESN:" + ltr.getESN())
.frequency(mFrequency)
.from(ltr.getESN())
.build();
broadcast(mCurrentCallEvent);
}
@@ -281,33 +277,27 @@ public class LTRNetDecoderState extends DecoderState
int uniqueid = ltr.getRadioUniqueID();
if (uniqueid != LTRNetISWMessage.INT_NULL_VALUE)
if(uniqueid != LTRNetISWMessage.INT_NULL_VALUE)
{
mUniqueIDs.add(uniqueid);
mDescription = "REGISTER UID";
broadcast(ChangedAttribute.DESCRIPTION);
setMessageType("REGISTER UID");
mFromUIDAttribute.process(uniqueid);
mTalkgroup = String.valueOf(uniqueid);
broadcast(ChangedAttribute.TO_TALKGROUP);
mTalkgroupAlias = ltr.getRadioUniqueIDAlias();
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
if (getCurrentLTRCallEvent() == null)
if(getCurrentLTRCallEvent() == null)
{
mCurrentCallEvent = new LTRCallEvent.Builder(
DecoderType.LTR_NET, CallEventType.REGISTER)
.aliasList(getAliasList())
.channel(String.valueOf(mChannelNumber))
.frequency(mFrequency)
.from(String.valueOf(uniqueid))
.build();
DecoderType.LTR_NET, CallEventType.REGISTER)
.aliasList(getAliasList())
.channel(String.valueOf(mChannelNumber))
.frequency(mFrequency)
.from(String.valueOf(uniqueid))
.build();
}
else
{
mCurrentCallEvent.setFromID(
String.valueOf(ltr.getRadioUniqueID()));
String.valueOf(ltr.getRadioUniqueID()));
mCurrentCallEvent.setDetails("Unique ID");
broadcast(mCurrentCallEvent);
@@ -323,7 +313,7 @@ public class LTRNetDecoderState extends DecoderState
public LTRCallEvent getCurrentLTRCallEvent()
{
if (mCurrentCallEvent != null)
if(mCurrentCallEvent != null)
{
return (LTRCallEvent) mCurrentCallEvent;
}
@@ -339,7 +329,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append("Activity Summary\n");
sb.append("Decoder:\tLTR-Net\n\n");
if (mSiteIDs.isEmpty())
if(mSiteIDs.isEmpty())
{
sb.append("Site:\tUnknown\n");
}
@@ -347,7 +337,7 @@ public class LTRNetDecoderState extends DecoderState
{
Iterator<String> it = mSiteIDs.iterator();
while (it.hasNext())
while(it.hasNext())
{
sb.append("Site:\t");
@@ -355,11 +345,11 @@ public class LTRNetDecoderState extends DecoderState
sb.append(siteID);
if (hasAliasList())
if(hasAliasList())
{
Alias siteAlias = getAliasList().getSiteID(String.valueOf(siteID));
if (siteAlias != null)
if(siteAlias != null)
{
sb.append(" ");
sb.append(siteAlias.getName());
@@ -372,31 +362,31 @@ public class LTRNetDecoderState extends DecoderState
sb.append("\nLCNs (transmit | receive)\n");
if (mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty())
if(mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty())
{
sb.append(" None\n");
}
else
{
for (int x = 1; x < 21; x++)
for(int x = 1; x < 21; x++)
{
long rcv = 0;
if (mReceiveFrequencies.containsKey(x))
if(mReceiveFrequencies.containsKey(x))
{
rcv = mReceiveFrequencies.get(x);
}
long xmt = 0;
if (mTransmitFrequencies.containsKey(x))
if(mTransmitFrequencies.containsKey(x))
{
xmt = mTransmitFrequencies.get(x);
}
if (rcv > 0 || xmt > 0)
if(rcv > 0 || xmt > 0)
{
if (x < 10)
if(x < 10)
{
sb.append(" ");
}
@@ -404,7 +394,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append(x);
sb.append(": ");
if (xmt == 0)
if(xmt == 0)
{
sb.append("---.-----");
}
@@ -414,7 +404,7 @@ public class LTRNetDecoderState extends DecoderState
}
sb.append(" | ");
if (rcv == 0)
if(rcv == 0)
{
sb.append("---.-----");
}
@@ -423,7 +413,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append(mDecimalFormatter.format((double) rcv / 1E6d));
}
if (x == mChannelNumber)
if(x == mChannelNumber)
{
sb.append(" **");
}
@@ -435,7 +425,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append("\nTalkgroups\n");
if (mTalkgroups.isEmpty())
if(mTalkgroups.isEmpty())
{
sb.append(" None\n");
}
@@ -443,7 +433,7 @@ public class LTRNetDecoderState extends DecoderState
{
Iterator<String> it = mTalkgroups.iterator();
while (it.hasNext())
while(it.hasNext())
{
String tgid = it.next();
@@ -451,11 +441,11 @@ public class LTRNetDecoderState extends DecoderState
sb.append(tgid);
sb.append(" ");
if (hasAliasList())
if(hasAliasList())
{
Alias alias = getAliasList().getTalkgroupAlias(tgid);
if (alias != null)
if(alias != null)
{
sb.append(alias.getName());
}
@@ -467,7 +457,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append("\nRadio Unique IDs\n");
if (mUniqueIDs.isEmpty())
if(mUniqueIDs.isEmpty())
{
sb.append(" None\n");
}
@@ -475,7 +465,7 @@ public class LTRNetDecoderState extends DecoderState
{
Iterator<Integer> it = mUniqueIDs.iterator();
while (it.hasNext())
while(it.hasNext())
{
int uid = it.next();
@@ -483,11 +473,11 @@ public class LTRNetDecoderState extends DecoderState
sb.append(uid);
sb.append(" ");
if (hasAliasList())
if(hasAliasList())
{
Alias alias = getAliasList().getUniqueID(uid);
if (alias != null)
if(alias != null)
{
sb.append(alias.getName());
}
@@ -499,7 +489,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append("\nESNs\n");
if (mESNs.isEmpty())
if(mESNs.isEmpty())
{
sb.append(" None\n");
}
@@ -507,7 +497,7 @@ public class LTRNetDecoderState extends DecoderState
{
Iterator<String> it = mESNs.iterator();
while (it.hasNext())
while(it.hasNext())
{
String esn = it.next();
@@ -515,11 +505,11 @@ public class LTRNetDecoderState extends DecoderState
sb.append(esn);
sb.append(" ");
if (hasAliasList())
if(hasAliasList())
{
Alias alias = getAliasList().getESNAlias(esn);
if (alias != null)
if(alias != null)
{
sb.append(alias.getName());
}
@@ -531,7 +521,7 @@ public class LTRNetDecoderState extends DecoderState
sb.append("\nNeighbor Sites\n");
if (mNeighborIDs.isEmpty())
if(mNeighborIDs.isEmpty())
{
sb.append(" None\n");
}
@@ -539,7 +529,7 @@ public class LTRNetDecoderState extends DecoderState
{
Iterator<String> it = mNeighborIDs.iterator();
while (it.hasNext())
while(it.hasNext())
{
String neighbor = it.next();
@@ -547,11 +537,11 @@ public class LTRNetDecoderState extends DecoderState
sb.append(neighbor);
sb.append(" ");
if (hasAliasList())
if(hasAliasList())
{
Alias alias = getAliasList().getSiteID(String.valueOf(neighbor));
if (alias != null)
if(alias != null)
{
sb.append(alias.getName());
}
@@ -569,8 +559,8 @@ public class LTRNetDecoderState extends DecoderState
*/
private void processCallDetectMessage(LTRNetMessage message)
{
if (!mActiveCalls.containsKey(message.getChannel()) ||
!mActiveCalls.get(message.getChannel()).contentEquals(message.getTalkgroupID()))
if(!mActiveCalls.containsKey(message.getChannel()) ||
!mActiveCalls.get(message.getChannel()).contentEquals(message.getTalkgroupID()))
{
mActiveCalls.put(message.getChannel(), message.getTalkgroupID());
@@ -578,17 +568,17 @@ public class LTRNetDecoderState extends DecoderState
long frequency = 0;
if (mTransmitFrequencies.containsKey(channel))
if(mTransmitFrequencies.containsKey(channel))
{
frequency = mTransmitFrequencies.get(channel);
}
broadcast(new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEventType.CALL_DETECT)
.aliasList(getAliasList())
.channel(String.valueOf(message.getChannel()))
.frequency(frequency)
.to(message.getTalkgroupID())
.build());
.aliasList(getAliasList())
.channel(String.valueOf(message.getChannel()))
.frequency(frequency)
.to(message.getTalkgroupID())
.build());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
@@ -601,9 +591,9 @@ public class LTRNetDecoderState extends DecoderState
private boolean isDifferentTalkgroup(String talkgroup)
{
return talkgroup != null &&
mCurrentCallEvent != null &&
mCurrentCallEvent.getToID() != null &&
!mCurrentCallEvent.getToID().contentEquals(talkgroup);
mCurrentCallEvent != null &&
mCurrentCallEvent.getToID() != null &&
!mCurrentCallEvent.getToID().contentEquals(talkgroup);
}
private boolean isValidTalkgroup(String talkgroup)
@@ -616,63 +606,55 @@ public class LTRNetDecoderState extends DecoderState
int group = message.getGroup();
/* Process call registration */
if (group == 253)
if(group == 253)
{
setMessageType("REGISTER");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
mDescription = "REGISTER";
broadcast(ChangedAttribute.DESCRIPTION);
}
/* Process call */
else
{
String talkgroup = message.getTalkgroupID();
if (isValidTalkgroup(talkgroup))
if(isValidTalkgroup(talkgroup))
{
mToAttribute.process(talkgroup);
final LTRCallEvent current = getCurrentLTRCallEvent();
/* If this is a new call or the talkgroup is different from the current
* call, create a new call event */
if (current == null || isDifferentTalkgroup(talkgroup))
//If this is a new call or the talkgroup is different from the current call, create a new call event
if(current == null || isDifferentTalkgroup(talkgroup))
{
/* Invalidate the current call */
if (current != null)
/* Invalidate the current call */
if(current != null)
{
current.setValid(false);
broadcast(current);
}
mTalkgroup = message.getTalkgroupID();
broadcast(ChangedAttribute.TO_TALKGROUP);
/* A talkgroup must be seen at least once before it will be added
* to the mTalkgroups list that is used in the activity summary,
* so that we don't pollute the summary with one-off error talkgroups */
if (mTalkgroupsFirstHeard.contains(mTalkgroup))
/* A talkgroup must be seen at least once before it will be added
* to the mTalkgroups list that is used in the activity summary,
* so that we don't pollute the summary with one-off error talkgroups */
if(mTalkgroupsFirstHeard.contains(talkgroup))
{
mTalkgroups.add(mTalkgroup);
mTalkgroups.add(talkgroup);
}
else
{
mTalkgroupsFirstHeard.add(mTalkgroup);
mTalkgroupsFirstHeard.add(talkgroup);
}
mTalkgroupAlias = message.getTalkgroupIDAlias();
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
CallEvent callEvent = new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEventType.CALL)
.aliasList(getAliasList())
.channel(String.valueOf(message.getChannel()))
.frequency(mFrequency)
.to(message.getTalkgroupID())
.build();
.aliasList(getAliasList())
.channel(String.valueOf(message.getChannel()))
.frequency(mFrequency)
.to(message.getTalkgroupID())
.build();
broadcast(callEvent);
mCurrentCallEvent = callEvent;
broadcast(new Metadata(MetadataType.TO, mTalkgroup, mTalkgroupAlias, true));
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
@@ -683,8 +665,8 @@ public class LTRNetDecoderState extends DecoderState
private void processCallEndMessage(LTRNetMessage message)
{
if (mCurrentCallEvent != null &&
mCurrentCallEvent.getCallEventType() == CallEventType.CALL)
if(mCurrentCallEvent != null &&
mCurrentCallEvent.getCallEventType() == CallEventType.CALL)
{
mCurrentCallEvent.setEnd(System.currentTimeMillis());
broadcast(mCurrentCallEvent);
@@ -719,8 +701,8 @@ public class LTRNetDecoderState extends DecoderState
*/
private void resetState()
{
if (mCurrentCallEvent != null && mCurrentCallEvent
.getCallEventType() == CallEventType.CALL)
if(mCurrentCallEvent != null && mCurrentCallEvent
.getCallEventType() == CallEventType.CALL)
{
mCurrentCallEvent.end();
broadcast(mCurrentCallEvent);
@@ -728,29 +710,39 @@ public class LTRNetDecoderState extends DecoderState
mCurrentCallEvent = null;
mTalkgroup = null;
broadcast(ChangedAttribute.TO_TALKGROUP);
mTalkgroupAlias = null;
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
mDescription = null;
broadcast(ChangedAttribute.DESCRIPTION);
mToAttribute.reset();
mESNAttribute.reset();
mFromUIDAttribute.reset();
mMessage = null;
mMessageType = null;
}
public String getToTalkgroup()
public String getMessage()
{
return mTalkgroup;
return mMessage;
}
public Alias getToTalkgroupAlias()
public void setMessage(String message)
{
return mTalkgroupAlias;
if(!StringUtils.isEqual(mMessage, message))
{
mMessage = message;
broadcast(new AttributeChangeRequest<String>(Attribute.MESSAGE, mMessageType));
}
}
public String getDescription()
public String getMessageType()
{
return mDescription;
return mMessageType;
}
private void setMessageType(String messageType)
{
if(!StringUtils.isEqual(mMessageType, messageType))
{
mMessageType = messageType;
broadcast(new AttributeChangeRequest<String>(Attribute.MESSAGE_TYPE, mMessageType));
}
}
public boolean hasChannelNumber()
@@ -763,6 +755,16 @@ public class LTRNetDecoderState extends DecoderState
return mChannelNumber;
}
public void setChannelNumber(int channelNumber)
{
if(mChannelNumber != channelNumber)
{
mChannelNumber = channelNumber;
broadcast(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL,
"LCN:" + getChannelNumber()));
}
}
@Override
public void init()
{
@@ -772,7 +774,7 @@ public class LTRNetDecoderState extends DecoderState
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
switch (event.getEvent())
switch(event.getEvent())
{
case RESET:
resetState();

View File

@@ -1,164 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.ltrstandard;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class LTRStandardDecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol = new JLabel("LTR Standard");
private JLabel mTalkgroup = new JLabel();
private JLabel mTalkgroupAlias = new JLabel();
private JLabel mLCN = new JLabel("LCN:");
public LTRStandardDecoderPanel(IconManager iconManager, SettingsManager settingsManager,
LTRStandardDecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public void dispose()
{
super.dispose();
}
public void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
mTalkgroup.setFont(mFontDecoder);
mTalkgroup.setForeground(mColorLabelDecoder);
mTalkgroupAlias.setFont(mFontDecoder);
mTalkgroupAlias.setForeground(mColorLabelDecoder);
mLCN.setFont(mFontDecoder);
mLCN.setForeground(mColorLabelDecoder);
add(mProtocol);
add(mTalkgroup);
add(mTalkgroupAlias, "wrap");
add(mLCN, "wrap");
}
public LTRStandardDecoderState getDecoderState()
{
return (LTRStandardDecoderState) super.getDecoderState();
}
@Override
public void receive(final ChangedAttribute attribute)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
LTRStandardDecoderState state = getDecoderState();
switch(attribute)
{
case CHANNEL_NUMBER:
if(state.getChannelNumber() != 0)
{
mLCN.setText("LCN: " + state.getChannelNumber());
}
break;
case TO_TALKGROUP:
mTalkgroup.setText(state.getTalkgroup());
/* fall-through */
case TO_TALKGROUP_ALIAS:
Alias alias = state.getTalkgroupAlias();
if(alias != null)
{
mTalkgroupAlias.setText(alias.getName());
mTalkgroupAlias.setIcon(mIconManager.getIcon(alias.getIconName(),
IconManager.DEFAULT_ICON_SIZE));
}
else
{
mTalkgroupAlias.setText(null);
mTalkgroupAlias.setIcon(null);
}
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mProtocol != null)
{
mProtocol.setForeground(mColorLabelDecoder);
}
if(mTalkgroup != null)
{
mTalkgroup.setForeground(mColorLabelDecoder);
}
if(mTalkgroupAlias != null)
{
mTalkgroupAlias.setForeground(mColorLabelDecoder);
}
if(mLCN != null)
{
mLCN.setForeground(mColorLabelDecoder);
}
break;
default:
break;
}
}
}
}

View File

@@ -1,22 +1,45 @@
/*******************************************************************************
* 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 <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.ltrstandard;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.State;
import message.Message;
import module.decode.DecoderType;
import module.decode.event.CallEvent.CallEventType;
import module.decode.ltrnet.LTRCallEvent;
import module.decode.ltrstandard.message.CallEndMessage;
import module.decode.ltrstandard.message.CallMessage;
import module.decode.ltrstandard.message.IdleMessage;
import module.decode.ltrstandard.message.LTRStandardMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -26,471 +49,435 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ScheduledExecutorService;
import message.Message;
import module.decode.DecoderType;
import module.decode.event.CallEvent.CallEventType;
import module.decode.ltrnet.LTRCallEvent;
import module.decode.ltrstandard.message.CallEndMessage;
import module.decode.ltrstandard.message.CallMessage;
import module.decode.ltrstandard.message.IdleMessage;
import module.decode.ltrstandard.message.LTRStandardMessage;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.AliasList;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
public class LTRStandardDecoderState extends DecoderState
{
private final static Logger mLog =
LoggerFactory.getLogger( LTRStandardDecoderState.class );
private final static Logger mLog =
LoggerFactory.getLogger(LTRStandardDecoderState.class);
private Map<Integer,LTRCallEvent> mActiveCalls = new HashMap<>();
private Set<String> mTalkgroupsFirstHeard = new HashSet<>();
private Set<String> mTalkgroups = new TreeSet<>();
private String mTalkgroup;
private Alias mTalkgroupAlias;
private Map<Integer,LTRCallEvent> mActiveCalls = new HashMap<>();
private Set<String> mTalkgroupsFirstHeard = new HashSet<>();
private Set<String> mTalkgroups = new TreeSet<>();
private AliasedStringAttributeMonitor mTalkgroupAttribute;
private long mFrequency;
private LCNTracker mLCNTracker = new LCNTracker();
public LTRStandardDecoderState( AliasList aliasList )
{
super( aliasList );
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.LTR_STANDARD;
}
@Override
public void start( ScheduledExecutorService executor )
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
}
@Override
public void receive( Message message )
public LTRStandardDecoderState(AliasList aliasList)
{
if( message.isValid() && message instanceof LTRStandardMessage )
{
switch( ((LTRStandardMessage)message).getMessageType() )
{
case CA_STRT:
CallMessage start = (CallMessage)message;
int channel = start.getChannel();
mLCNTracker.processCallChannel( channel );
mLCNTracker.processFreeChannel( start.getFree() );
super(aliasList);
//Only process calls on this LCN, or call detects for
//talkgroups that are homed on this LCN
if( mLCNTracker.isValidChannel( channel ) &&
( mLCNTracker.isCurrentChannel( channel ) ||
mLCNTracker.isCurrentChannel( start.getHomeRepeater() ) ) )
{
LTRCallEvent event = mActiveCalls.get( channel );
if( event == null ||
!event.isMatchingTalkgroup( start.getToID() ) )
{
//Check for different talkgroup
if( event != null )
{
event.end();
mActiveCalls.remove( channel );
}
boolean current = mLCNTracker.isCurrentChannel( channel );
event = new LTRCallEvent.Builder(
DecoderType.LTR_STANDARD,
current ? CallEventType.CALL : CallEventType.CALL_DETECT )
.to( start.getToID() )
.aliasList( getAliasList() )
.channel( start.getChannelFormatted() )
.frequency( current ? mFrequency : 0 )
.build();
mActiveCalls.put( channel, event );
broadcast( event );
if( mLCNTracker.isCurrentChannel( channel ) )
{
mTalkgroup = start.getToID();
broadcast( ChangedAttribute.TO_TALKGROUP );
processTalkgroup( mTalkgroup );
mTalkgroupAlias = start.getToIDAlias();
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
broadcast( new Metadata( MetadataType.TO,
mTalkgroup, mTalkgroupAlias, true ) );
}
}
broadcast( new DecoderStateEvent( this,
Event.CONTINUATION, State.CALL ) );
}
break;
case CA_ENDD:
CallEndMessage end = (CallEndMessage)message;
//Home channel is 31 for call end -- use the free channel
//as the call end channel
int repeater = end.getFree();
mLCNTracker.processCallChannel( repeater );
if( mLCNTracker.isCurrentChannel( repeater ) )
{
mTalkgroup = end.getToID();
broadcast( ChangedAttribute.TO_TALKGROUP );
processTalkgroup( mTalkgroup );
mTalkgroupAlias = end.getToIDAlias();
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
LTRCallEvent event = mActiveCalls.remove( repeater );
if( event != null )
{
event.end();
broadcast( event );
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
}
}
break;
case SY_IDLE:
IdleMessage idle = (IdleMessage)message;
int lcn = idle.getChannel();
mLCNTracker.processCallChannel( lcn );
break;
case UN_KNWN:
default:
break;
}
}
mTalkgroupAttribute = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP);
}
private void processTalkgroup( String talkgroup )
{
if( mTalkgroupsFirstHeard.contains( talkgroup ) )
@Override
public DecoderType getDecoderType()
{
return DecoderType.LTR_STANDARD;
}
@Override
public void start(ScheduledExecutorService executor)
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
}
@Override
public void receive(Message message)
{
if(message.isValid() && message instanceof LTRStandardMessage)
{
mTalkgroups.add( talkgroup );
switch(((LTRStandardMessage) message).getMessageType())
{
case CA_STRT:
CallMessage start = (CallMessage) message;
int channel = start.getChannel();
setChannelNumber(channel);
mLCNTracker.processFreeChannel(start.getFree());
//Only process calls on this LCN, or call detects for
//talkgroups that are homed on this LCN
if(mLCNTracker.isValidChannel(channel) &&
(mLCNTracker.isCurrentChannel(channel) ||
mLCNTracker.isCurrentChannel(start.getHomeRepeater())))
{
LTRCallEvent event = mActiveCalls.get(channel);
if(event == null || !event.isMatchingTalkgroup(start.getToID()))
{
//Check for different talkgroup
if(event != null)
{
event.end();
mActiveCalls.remove(channel);
}
boolean current = mLCNTracker.isCurrentChannel(channel);
event = new LTRCallEvent.Builder(
DecoderType.LTR_STANDARD,
current ? CallEventType.CALL : CallEventType.CALL_DETECT)
.to(start.getToID())
.aliasList(getAliasList())
.channel(start.getChannelFormatted())
.frequency(current ? mFrequency : 0)
.build();
mActiveCalls.put(channel, event);
broadcast(event);
if(mLCNTracker.isCurrentChannel(channel))
{
String talkgroup = start.getToID();
mTalkgroupAttribute.process(talkgroup);
processTalkgroup(talkgroup);
}
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL));
}
break;
case CA_ENDD:
CallEndMessage end = (CallEndMessage) message;
//Home channel is 31 for call end -- use the free channel
//as the call end channel
int repeater = end.getFree();
setChannelNumber(repeater);
if(mLCNTracker.isCurrentChannel(repeater))
{
String talkgroup = end.getToID();
mTalkgroupAttribute.process(talkgroup);
processTalkgroup(talkgroup);
LTRCallEvent event = mActiveCalls.remove(repeater);
if(event != null)
{
event.end();
broadcast(event);
broadcast(new DecoderStateEvent(this, Event.END, State.FADE));
}
}
break;
case SY_IDLE:
IdleMessage idle = (IdleMessage) message;
int lcn = idle.getChannel();
mLCNTracker.processCallChannel(lcn);
break;
case UN_KNWN:
default:
break;
}
}
}
private void processTalkgroup(String talkgroup)
{
if(mTalkgroupsFirstHeard.contains(talkgroup))
{
mTalkgroups.add(talkgroup);
}
else
{
mTalkgroupsFirstHeard.add( talkgroup );
mTalkgroupsFirstHeard.add(talkgroup);
}
}
/**
* Performs a full reset
*/
public void reset()
{
mActiveCalls.clear();
mTalkgroupsFirstHeard.clear();
mTalkgroups.clear();
mLCNTracker.reset();
}
resetState();
}
/**
* Performs a temporal reset following a call or other decode event
*/
private void resetState()
{
for( Integer key: mActiveCalls.keySet() )
{
LTRCallEvent event = mActiveCalls.get( key );
if( event != null )
{
event.end();
broadcast( event );
}
}
mActiveCalls.clear();
mTalkgroup = null;
broadcast( ChangedAttribute.TO_TALKGROUP );
/**
* Performs a full reset
*/
public void reset()
{
mActiveCalls.clear();
mTalkgroupsFirstHeard.clear();
mTalkgroups.clear();
mLCNTracker.reset();
resetState();
}
/**
* Performs a temporal reset following a call or other decode event
*/
private void resetState()
{
for(Integer key : mActiveCalls.keySet())
{
LTRCallEvent event = mActiveCalls.get(key);
if(event != null)
{
event.end();
broadcast(event);
}
}
mActiveCalls.clear();
mTalkgroupAttribute.reset();
}
mTalkgroupAlias = null;
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
}
public String getTalkgroup()
{
return mTalkgroup;
}
public Alias getTalkgroupAlias()
{
return mTalkgroupAlias;
}
public boolean hasChannelNumber()
{
return mLCNTracker.getCurrentChannel() != 0;
}
public int getChannelNumber()
{
return mLCNTracker.getCurrentChannel();
}
@Override
public void init()
{
}
private void setChannelNumber(int channel)
{
int original = mLCNTracker.getCurrentChannel();
@Override
public void receiveDecoderStateEvent( DecoderStateEvent event )
{
switch( event.getEvent() )
{
case RESET:
resetState();
break;
case SOURCE_FREQUENCY:
mFrequency = event.getFrequency();
break;
default:
break;
}
}
@Override
mLCNTracker.processCallChannel(channel);
if(mLCNTracker.getCurrentChannel() != original)
{
broadcast(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL,
"LCN:" + mLCNTracker.getCurrentChannel()));
}
}
@Override
public void init()
{
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
switch(event.getEvent())
{
case RESET:
resetState();
break;
case SOURCE_FREQUENCY:
mFrequency = event.getFrequency();
break;
default:
break;
}
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
sb.append( "Activity Summary\n\n" );
sb.append( "Decoder:\tLTR-Standard\n" );
sb.append( "Monitored LCN: " );
if( hasChannelNumber() )
{
sb.append( getChannelNumber() );
}
else
{
sb.append( "*Insufficient Data*" );
}
sb.append( "\n" );
sb.append( "Active LCNs:\t" );
sb.append("Activity Summary\n\n");
List<Integer> lcns = mLCNTracker.getActiveLCNs();
if( lcns.size() > 0 )
{
sb.append( mLCNTracker.getActiveLCNs() );
}
else
{
sb.append( "*Insufficient Data*" );
}
sb.append( "\n\n" );
sb.append("Decoder:\tLTR-Standard\n");
sb.append( "Talkgroups\n" );
if( mTalkgroups.isEmpty() )
{
sb.append( " None\n" );
}
else
{
for( String talkgroup: mTalkgroups )
{
sb.append( " " );
sb.append( talkgroup );
sb.append( " " );
if( hasAliasList() )
{
Alias alias = getAliasList().getTalkgroupAlias( talkgroup );
if( alias != null )
{
sb.append( alias.getName() );
}
}
sb.append( "\n" );
}
}
return sb.toString();
sb.append("Monitored LCN: ");
if(hasChannelNumber())
{
sb.append(getChannelNumber());
}
else
{
sb.append("*Insufficient Data*");
}
sb.append("\n");
sb.append("Active LCNs:\t");
List<Integer> lcns = mLCNTracker.getActiveLCNs();
if(lcns.size() > 0)
{
sb.append(mLCNTracker.getActiveLCNs());
}
else
{
sb.append("*Insufficient Data*");
}
sb.append("\n\n");
sb.append("Talkgroups\n");
if(mTalkgroups.isEmpty())
{
sb.append(" None\n");
}
else
{
for(String talkgroup : mTalkgroups)
{
sb.append(" ");
sb.append(talkgroup);
sb.append(" ");
if(hasAliasList())
{
Alias alias = getAliasList().getTalkgroupAlias(talkgroup);
if(alias != null)
{
sb.append(alias.getName());
}
}
sb.append("\n");
}
}
return sb.toString();
}
/**
* Tracks the set of call and free channels for a system in order to
* dynamically determine the current LCN and minimize false triggers from
* bogus decoded LTR messages. Tracks the number of occurances of each
* LCN and uses a dynamic threshold to determine LCN validity.
*/
public class LCNTracker
{
private static final int DEFAULT_COUNT = 10;
private int[] mCallLCNCounts;
private int[] mFreeLCNCounts;
private int mCallHighestCount;
private int mFreeHighestCount;
private int mCurrentLCN;
public LCNTracker()
{
reset();
}
public void logStatistics()
{
for( int x = 1; x <= 20; x++ )
{
mLog.debug( "Call " + x + ": " + mCallLCNCounts[ x ] );
}
mLog.debug( "Call Highest Count: " + mCallHighestCount );
for( int x = 1; x <= 20; x++ )
{
mLog.debug( "Free " + x + ": " + mFreeLCNCounts[ x ] );
}
mLog.debug( "Free Highest Count: " + mFreeHighestCount );
mLog.debug( "Current LCN: " + mCurrentLCN );
}
public void reset()
{
//Dim channel arrays to 21 -- ignore the 0 index
mCallLCNCounts = new int[ 21 ];
mFreeLCNCounts = new int[ 21 ];
mCallHighestCount = 3;
mFreeHighestCount = DEFAULT_COUNT;
mCurrentLCN = 0;
}
public boolean isCurrentChannel( int channel )
{
if( mCurrentLCN == 0 )
{
return true;
}
else
{
return channel == mCurrentLCN;
}
}
public int getCurrentChannel()
{
return mCurrentLCN;
}
/**
* Indicates if the channel is valid based on the observed current and
* free channels reported by the system. Tracks the count of each
* reported free channel the highest free channel count is used as a
* threshold where a channel is valid when its count exceeds 20% of the
* highest free channel count. A channel is also valid when it is
* the currently monitored channel.
*/
public boolean isValidChannel( int channel )
{
if( isCurrentChannel( channel ) )
{
return true;
}
/**
* Tracks the set of call and free channels for a system in order to
* dynamically determine the current LCN and minimize false triggers from
* bogus decoded LTR messages. Tracks the number of occurances of each
* LCN and uses a dynamic threshold to determine LCN validity.
*/
public class LCNTracker
{
private static final int DEFAULT_COUNT = 10;
private int[] mCallLCNCounts;
private int[] mFreeLCNCounts;
private int mCallHighestCount;
private int mFreeHighestCount;
private int mCurrentLCN;
int count = mFreeLCNCounts[ channel ];
int threshold = (int)( (double)mFreeHighestCount * 0.2 );
public LCNTracker()
{
reset();
}
return count >= threshold;
}
public void logStatistics()
{
for(int x = 1; x <= 20; x++)
{
mLog.debug("Call " + x + ": " + mCallLCNCounts[x]);
}
mLog.debug("Call Highest Count: " + mCallHighestCount);
for(int x = 1; x <= 20; x++)
{
mLog.debug("Free " + x + ": " + mFreeLCNCounts[x]);
}
mLog.debug("Free Highest Count: " + mFreeHighestCount);
public void processCallChannel( int channel )
{
if( 1 <= channel && channel <= 20 )
{
mCallLCNCounts[ channel ]++;
if( mCallLCNCounts[ channel ] > mCallHighestCount )
{
mCallHighestCount = mCallLCNCounts[ channel ];
mCurrentLCN = channel;
broadcast( ChangedAttribute.CHANNEL_NUMBER );
}
}
}
public void processFreeChannel( int channel )
{
if( 1 <= channel && channel <= 20 )
{
mFreeLCNCounts[ channel ]++;
if( mFreeLCNCounts[ channel ] > mFreeHighestCount )
{
mFreeHighestCount = mFreeLCNCounts[ channel ];
}
}
}
public List<Integer> getActiveLCNs()
{
List<Integer> active = new ArrayList<>();
if( mFreeHighestCount > DEFAULT_COUNT )
{
for( int x = 1; x <= 20; x++ )
{
if( mCurrentLCN != 0 && isCurrentChannel( x ) )
{
active.add( x );
}
else if( isValidChannel( x ) )
{
active.add( x );
}
}
}
return active;
}
}
mLog.debug("Current LCN: " + mCurrentLCN);
}
public void reset()
{
//Dim channel arrays to 21 -- ignore the 0 index
mCallLCNCounts = new int[21];
mFreeLCNCounts = new int[21];
mCallHighestCount = 3;
mFreeHighestCount = DEFAULT_COUNT;
mCurrentLCN = 0;
}
public boolean isCurrentChannel(int channel)
{
if(mCurrentLCN == 0)
{
return true;
}
else
{
return channel == mCurrentLCN;
}
}
public int getCurrentChannel()
{
return mCurrentLCN;
}
/**
* Indicates if the channel is valid based on the observed current and
* free channels reported by the system. Tracks the count of each
* reported free channel the highest free channel count is used as a
* threshold where a channel is valid when its count exceeds 20% of the
* highest free channel count. A channel is also valid when it is
* the currently monitored channel.
*/
public boolean isValidChannel(int channel)
{
if(isCurrentChannel(channel))
{
return true;
}
int count = mFreeLCNCounts[channel];
int threshold = (int) ((double) mFreeHighestCount * 0.2);
return count >= threshold;
}
public void processCallChannel(int channel)
{
if(1 <= channel && channel <= 20)
{
mCallLCNCounts[channel]++;
if(mCallLCNCounts[channel] > mCallHighestCount)
{
mCallHighestCount = mCallLCNCounts[channel];
mCurrentLCN = channel;
}
}
}
public void processFreeChannel(int channel)
{
if(1 <= channel && channel <= 20)
{
mFreeLCNCounts[channel]++;
if(mFreeLCNCounts[channel] > mFreeHighestCount)
{
mFreeHighestCount = mFreeLCNCounts[channel];
}
}
}
public List<Integer> getActiveLCNs()
{
List<Integer> active = new ArrayList<>();
if(mFreeHighestCount > DEFAULT_COUNT)
{
for(int x = 1; x <= 20; x++)
{
if(mCurrentLCN != 0 && isCurrentChannel(x))
{
active.add(x);
}
else if(isValidChannel(x))
{
active.add(x);
}
}
}
return active;
}
}
}

View File

@@ -1,183 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.mdc1200;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class MDCDecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private static final String sPROTOCOL = "MDC-1200";
private JLabel mFromLabel = new JLabel(" ");
private JLabel mFrom = new JLabel();
private JLabel mFromAlias = new JLabel();
private JLabel mToLabel = new JLabel(" ");
private JLabel mTo = new JLabel();
private JLabel mToAlias = new JLabel();
private JLabel mProtocol = new JLabel(sPROTOCOL);
private JLabel mMessage = new JLabel();
private JLabel mMessageType = new JLabel();
public MDCDecoderPanel(IconManager iconManager, SettingsManager settingsManager, MDCDecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public MDCDecoderState getDecoderState()
{
return (MDCDecoderState) super.getDecoderState();
}
protected void init()
{
/* Calling super init will get and broadcast color settings to properly
* setup the jlabel colors */
super.init();
setLayout(new MigLayout("insets 1 0 0 0", "[grow,fill]", "[]0[]0[]"));
add(mFromLabel);
add(mFrom);
add(mFromAlias, "wrap");
add(mToLabel);
add(mTo);
add(mToAlias, "wrap");
add(mProtocol);
add(mMessageType);
add(mMessage, "wrap");
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
switch(changedAttribute)
{
case FROM_TALKGROUP:
String from = getDecoderState().getFrom();
if(from != null)
{
mFromLabel.setText("FM:");
}
else
{
mFromLabel.setText(" ");
}
mFrom.setText(from);
break;
case FROM_TALKGROUP_ALIAS:
Alias fromAlias = getDecoderState().getFromAlias();
if(fromAlias != null)
{
mFromAlias.setText(fromAlias.getName());
}
else
{
mFromAlias.setText(null);
}
break;
case TO_TALKGROUP:
String to = getDecoderState().getTo();
if(to != null)
{
mToLabel.setText("TO:");
}
else
{
mToLabel.setText(" ");
}
mTo.setText(to);
break;
case TO_TALKGROUP_ALIAS:
Alias toAlias = getDecoderState().getToAlias();
if(toAlias != null)
{
mToAlias.setText(toAlias.getName());
}
else
{
mToAlias.setText(null);
}
break;
case MESSAGE:
mMessage.setText(getDecoderState().getMessage());
break;
case MESSAGE_TYPE:
mMessageType.setText(getDecoderState().getMessageType());
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting color = (ColorSetting) setting;
if(color.getColorSettingName() ==
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER)
{
mFromLabel.setForeground(color.getColor());
mFrom.setForeground(color.getColor());
mFromAlias.setForeground(color.getColor());
mToLabel.setForeground(color.getColor());
mTo.setForeground(color.getColor());
mToAlias.setForeground(color.getColor());
mProtocol.setForeground(color.getColor());
mMessageType.setForeground(color.getColor());
mMessage.setForeground(color.getColor());
}
}
}
}

View File

@@ -1,306 +1,269 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.mdc1200;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.State;
import message.Message;
import module.decode.DecoderType;
import util.StringUtils;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.ScheduledExecutorService;
import message.Message;
import module.decode.DecoderType;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.State;
import module.decode.state.DecoderStateEvent.Event;
import alias.Alias;
import alias.AliasList;
public class MDCDecoderState extends DecoderState
{
private TreeSet<String> mIdents = new TreeSet<String>();
private TreeSet<String> mEmergencyIdents = new TreeSet<String>();
private TreeSet<String> mIdents = new TreeSet<String>();
private TreeSet<String> mEmergencyIdents = new TreeSet<String>();
private String mFrom;
private Alias mFromAlias;
private String mTo;
private Alias mToAlias;
private String mMessage;
private String mMessageType;
private AliasedStringAttributeMonitor mFromAttribute;
private String mMessage;
private String mMessageType;
public MDCDecoderState( AliasList aliasList )
{
super( aliasList );
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.MDC1200;
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
@Override
public void reset()
{
mIdents.clear();
mEmergencyIdents.clear();
resetState();
}
private void resetState()
{
setFrom( null );
setFromAlias( null );
setTo( null);
setToAlias( null );
setMessage( null );
setMessageType( null );
}
@Override
public void receive( Message message )
public MDCDecoderState(AliasList aliasList)
{
if( message instanceof MDCMessage )
{
MDCMessage mdc = (MDCMessage)message;
super(aliasList);
mIdents.add( mdc.getUnitID() );
if( mdc.isEmergency() )
{
mEmergencyIdents.add( mdc.getUnitID() );
}
setFrom( mdc.getFromID() );
setFromAlias( mdc.getFromIDAlias() );
MDCMessageType type = mdc.getMessageType();
setMessageType( type.getLabel() );
StringBuilder sb = new StringBuilder();
sb.append( "OPCODE " );
sb.append( String.valueOf( mdc.getOpcode() ) );
if( mdc.isBOT() )
{
sb.append( " TYPE:BOT" );
}
if( mdc.isEOT() )
{
sb.append( " TYPE:EOT" );
}
setMessage( sb.toString() );
MDCCallEvent event = MDCCallEvent.getMDCCallEvent( mdc );
event.setAliasList( getAliasList() );
broadcast( event );
switch( type )
{
case ANI:
broadcast( new DecoderStateEvent( this, Event.DECODE, State.CALL ) );
break;
case ACKNOWLEDGE:
case EMERGENCY:
case PAGING:
case STATUS:
default:
broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) );
break;
}
}
mFromAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_FROM,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.MDC1200);
}
public String getFrom()
{
return mFrom;
}
public void setFrom( String from )
{
mFrom = from;
broadcast( ChangedAttribute.FROM_TALKGROUP );
}
public Alias getFromAlias()
{
return mFromAlias;
}
public void setFromAlias( Alias alias )
{
mFromAlias = alias;
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
}
public String getTo()
{
return mTo;
}
public void setTo( String to )
{
mTo = to;
broadcast( ChangedAttribute.TO_TALKGROUP );
}
public Alias getToAlias()
{
return mToAlias;
}
public void setToAlias( Alias alias )
{
mToAlias = alias;
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
}
public String getMessage()
{
return mMessage;
}
public void setMessage( String message )
{
mMessage = message;
broadcast( ChangedAttribute.MESSAGE );
}
public String getMessageType()
{
return mMessageType;
}
public void setMessageType( String type )
{
mMessageType = type;
broadcast( ChangedAttribute.MESSAGE_TYPE );
}
@Override
@Override
public DecoderType getDecoderType()
{
return DecoderType.MDC1200;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void reset()
{
mIdents.clear();
mEmergencyIdents.clear();
resetState();
}
private void resetState()
{
mFromAttribute.reset();
mMessage = null;
mMessageType = null;
}
@Override
public void receive(Message message)
{
if(message instanceof MDCMessage)
{
MDCMessage mdc = (MDCMessage) message;
mIdents.add(mdc.getUnitID());
if(mdc.isEmergency())
{
mEmergencyIdents.add(mdc.getUnitID());
}
mFromAttribute.process(mdc.getFromID());
MDCMessageType type = mdc.getMessageType();
setMessageType(type.getLabel());
StringBuilder sb = new StringBuilder();
sb.append("OPCODE ");
sb.append(String.valueOf(mdc.getOpcode()));
if(mdc.isBOT())
{
sb.append(" TYPE:BOT");
}
if(mdc.isEOT())
{
sb.append(" TYPE:EOT");
}
setMessage(sb.toString());
MDCCallEvent event = MDCCallEvent.getMDCCallEvent(mdc);
event.setAliasList(getAliasList());
broadcast(event);
switch(type)
{
case ANI:
broadcast(new DecoderStateEvent(this, Event.DECODE, State.CALL));
break;
case ACKNOWLEDGE:
case EMERGENCY:
case PAGING:
case STATUS:
default:
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
break;
}
}
}
public String getMessage()
{
return mMessage;
}
public void setMessage(String message)
{
if(!StringUtils.isEqual(mMessage, message))
{
mMessage = message;
broadcast(new AttributeChangeRequest<String>(Attribute.MESSAGE, mMessage));
}
}
public String getMessageType()
{
return mMessageType;
}
public void setMessageType(String type)
{
if(!StringUtils.isEqual(mMessageType, type))
{
mMessageType = type;
broadcast(new AttributeChangeRequest<String>(Attribute.MESSAGE_TYPE, "MDC:" + mMessageType));
}
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
sb.append( "=============================\n" );
sb.append( "Decoder:\tMDC-1200\n\n" );
sb.append( "MDC-1200 Idents\n" );
sb.append("=============================\n");
sb.append("Decoder:\tMDC-1200\n\n");
sb.append("MDC-1200 Idents\n");
if( mIdents.isEmpty() )
{
sb.append( " None\n" );
}
else
{
Iterator<String> it = mIdents.iterator();
while( it.hasNext() )
{
String ident = it.next();
sb.append( " " );
sb.append( ident );
sb.append( " " );
if( hasAliasList() )
{
Alias alias = getAliasList().getMDC1200Alias( ident );
if( alias != null )
{
sb.append( alias.getName() );
}
}
sb.append( "\n" );
}
}
if(mIdents.isEmpty())
{
sb.append(" None\n");
}
else
{
Iterator<String> it = mIdents.iterator();
sb.append( "MDC-1200 Emergency Idents\n" );
while(it.hasNext())
{
String ident = it.next();
if( mEmergencyIdents.isEmpty() )
{
sb.append( " None\n" );
}
else
{
Iterator<String> it = mEmergencyIdents.iterator();
while( it.hasNext() )
{
String ident = it.next();
sb.append( " " );
sb.append( ident );
sb.append( " " );
sb.append(" ");
sb.append(ident);
sb.append(" ");
if( hasAliasList() )
{
Alias alias = getAliasList().getMDC1200Alias( ident );
if( alias != null )
{
sb.append( alias.getName() );
}
}
sb.append( "\n" );
}
}
if(hasAliasList())
{
Alias alias = getAliasList().getMDC1200Alias(ident);
return sb.toString();
if(alias != null)
{
sb.append(alias.getName());
}
}
sb.append("\n");
}
}
sb.append("MDC-1200 Emergency Idents\n");
if(mEmergencyIdents.isEmpty())
{
sb.append(" None\n");
}
else
{
Iterator<String> it = mEmergencyIdents.iterator();
while(it.hasNext())
{
String ident = it.next();
sb.append(" ");
sb.append(ident);
sb.append(" ");
if(hasAliasList())
{
Alias alias = getAliasList().getMDC1200Alias(ident);
if(alias != null)
{
sb.append(alias.getName());
}
}
sb.append("\n");
}
}
return sb.toString();
}
@Override
public void init()
{
/* No initialization required */
}
@Override
public void init()
{
/* No initialization required */
}
/**
* Responds to reset events issued by the channel state
*/
@Override
public void receiveDecoderStateEvent( DecoderStateEvent event )
{
switch( event.getEvent() )
{
case RESET:
resetState();
break;
default:
break;
}
}
/**
* Responds to reset events issued by the channel state
*/
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
switch(event.getEvent())
{
case RESET:
resetState();
break;
default:
break;
}
}
}

View File

@@ -1,322 +1,290 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.mpt1327;
import alias.AliasList;
import bits.MessageFramer;
import bits.SyncPattern;
import dsp.filter.Filters;
import dsp.filter.dc.DCRemovalFilterLyons;
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
import dsp.filter.halfband.real.HalfBandFilter_RB_RB;
import dsp.fsk.FSK2Decoder;
import dsp.fsk.FSK2Decoder.Output;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.TapGroup;
import instrument.tap.stream.BinaryTap;
import instrument.tap.stream.FloatBufferTap;
import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import module.decode.Decoder;
import module.decode.DecoderType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.real.IFilteredRealBufferListener;
import sample.real.RealBuffer;
import alias.AliasList;
import bits.MessageFramer;
import bits.SyncPattern;
import dsp.filter.FilterFactory;
import dsp.filter.Filters;
import dsp.filter.Window.WindowType;
import dsp.filter.dc.DCRemovalFilterLyons;
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
import dsp.filter.halfband.real.HalfBandFilter_RB_RB;
import dsp.fsk.FSK2Decoder;
import dsp.fsk.FSK2Decoder.Output;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
/**
* MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
* complex or floating point samples and output fully framed MPT1327 control and
* traffic messages.
*/
public class MPT1327Decoder extends Decoder
implements IFilteredRealBufferListener, Instrumentable
public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable
{
private final static Logger mLog = LoggerFactory.getLogger( MPT1327Decoder.class );
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
private static final int sDECIMATED_SAMPLE_RATE = 24000;
/* Baud or Symbol Rate */
private static final int sSYMBOL_RATE = 1200;
private final static Logger mLog = LoggerFactory.getLogger(MPT1327Decoder.class);
/* Message length -- longest possible message is:
* 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
private static final int sDECIMATED_SAMPLE_RATE = 24000;
/* Baud or Symbol Rate */
private static final int sSYMBOL_RATE = 1200;
/* Message length -- longest possible message is:
* 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */
private static final int sMESSAGE_LENGTH = 350;
/* Instrumentation Taps */
private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS =
"Tap Point: Half-band Decimation Filter > < Low Pass Filter";
private static final String INSTRUMENT_LOW_PASS_TO_DECODER =
"Tap Point: Low Pass Filter > < Decoder";
private static final String INSTRUMENT_DECODER_TO_FRAMER =
"Tap Point: Decoder > < Sync Detect/Message Framer";
private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS =
"Tap Point: Half-band Decimation Filter > < Low Pass Filter";
private static final String INSTRUMENT_LOW_PASS_TO_DECODER =
"Tap Point: Low Pass Filter > < Decoder";
private static final String INSTRUMENT_DECODER_TO_FRAMER =
"Tap Point: Decoder > < Sync Detect/Message Framer";
private List<TapGroup> mAvailableTaps;
private HalfBandFilter_RB_RB mDecimationFilter;
private RealFIRFilter_RB_RB mLowPassFilter;
private DCRemovalFilterLyons mDCRemovalFilter;
private FSK2Decoder mFSKDecoder;
private Broadcaster<Boolean> mSymbolBroadcaster;
private HalfBandFilter_RB_RB mDecimationFilter;
private RealFIRFilter_RB_RB mLowPassFilter;
private DCRemovalFilterLyons mDCRemovalFilter;
private FSK2Decoder mFSKDecoder;
private Broadcaster<Boolean> mSymbolBroadcaster;
private MessageFramer mControlMessageFramer;
private MessageFramer mTrafficMessageFramer;
private MPT1327MessageProcessor mMessageProcessor;
public MPT1327Decoder( AliasList aliasList, Sync sync )
{
public MPT1327Decoder(AliasList aliasList, Sync sync)
{
/* Decimation filter - 48000 / 2 = 24000 output */
mDecimationFilter = new HalfBandFilter_RB_RB(Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true);
/* Decimation filter - 48000 / 2 = 24000 output */
mDecimationFilter = new HalfBandFilter_RB_RB(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true );
/* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */
mLowPassFilter = new RealFIRFilter_RB_RB(
FilterFactory.getLowPass( 24000, //sampleRate,
2000, //passFrequency,
4000, //stopFrequency,
60, //attenuation,
WindowType.HANNING, //windowType,
true ) //forceOddLength
, 2.0f ); //Gain
mDecimationFilter.setListener( mLowPassFilter );
/**
* Normal: 2FSK Decoder with inverted output
* French: 2FSK Decoder with normal output
*/
if(sync == Sync.NORMAL)
{
mFSKDecoder = new FSK2Decoder(24000, 1200, Output.INVERTED);
}
else if(sync == Sync.FRENCH)
{
mFSKDecoder = new FSK2Decoder(24000, 1200, Output.NORMAL);
}
else
{
throw new IllegalArgumentException("MPT1327 Decoder - unrecognized Sync type");
}
/**
* Normal: 2FSK Decoder with inverted output
* French: 2FSK Decoder with normal output
*/
if( sync == Sync.NORMAL )
{
mFSKDecoder = new FSK2Decoder( 24000, 1200, Output.INVERTED );
}
else if( sync == Sync.FRENCH )
{
mFSKDecoder = new FSK2Decoder( 24000, 1200, Output.NORMAL );
}
else
{
throw new IllegalArgumentException( "MPT1327 Decoder - unrecognized Sync type" );
}
mLowPassFilter.setListener( mFSKDecoder );
mSymbolBroadcaster = new Broadcaster<Boolean>();
mFSKDecoder.setListener( mSymbolBroadcaster );
mDecimationFilter.setListener(mFSKDecoder);
mSymbolBroadcaster = new Broadcaster<Boolean>();
mFSKDecoder.setListener(mSymbolBroadcaster);
/* Message framer for control channel messages */
mControlMessageFramer =
new MessageFramer( sync.getControlSyncPattern().getPattern(),
sMESSAGE_LENGTH );
mSymbolBroadcaster.addListener( mControlMessageFramer );
mControlMessageFramer = new MessageFramer(sync.getControlSyncPattern().getPattern(), sMESSAGE_LENGTH);
mSymbolBroadcaster.addListener(mControlMessageFramer);
/* Message framer for traffic channel massages */
mTrafficMessageFramer =
new MessageFramer( sync.getTrafficSyncPattern().getPattern(),
sMESSAGE_LENGTH );
mSymbolBroadcaster.addListener( mTrafficMessageFramer );
mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), sMESSAGE_LENGTH);
mSymbolBroadcaster.addListener(mTrafficMessageFramer);
/* Fully decoded and framed messages processor */
mMessageProcessor = new MPT1327MessageProcessor( aliasList );
mMessageProcessor.setMessageListener( this );
mControlMessageFramer.addMessageListener( mMessageProcessor );
mTrafficMessageFramer.addMessageListener( mMessageProcessor );
}
@Override
public Listener<RealBuffer> getFilteredRealBufferListener()
{
return mDecimationFilter;
}
mMessageProcessor = new MPT1327MessageProcessor(aliasList);
mMessageProcessor.setMessageListener(this);
mControlMessageFramer.addMessageListener(mMessageProcessor);
mTrafficMessageFramer.addMessageListener(mMessageProcessor);
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.MPT1327;
}
/**
* Cleanup method
*/
public void dispose()
{
super.dispose();
@Override
public Listener<RealBuffer> getFilteredRealBufferListener()
{
return mDecimationFilter;
}
mSymbolBroadcaster.dispose();
mControlMessageFramer.dispose();
@Override
public DecoderType getDecoderType()
{
return DecoderType.MPT1327;
}
if( mDCRemovalFilter != null )
{
mDCRemovalFilter.dispose();
}
/**
* Cleanup method
*/
public void dispose()
{
super.dispose();
mFSKDecoder.dispose();
mSymbolBroadcaster.dispose();
mDecimationFilter.dispose();
mControlMessageFramer.dispose();
mLowPassFilter.dispose();
mMessageProcessor.dispose();
if(mDCRemovalFilter != null)
{
mDCRemovalFilter.dispose();
}
mTrafficMessageFramer.dispose();
}
mFSKDecoder.dispose();
/* Instrumentation Taps */
@Override
mDecimationFilter.dispose();
mLowPassFilter.dispose();
mMessageProcessor.dispose();
mTrafficMessageFramer.dispose();
}
/* Instrumentation Taps */
@Override
public List<TapGroup> getTapGroups()
{
if( mAvailableTaps == null )
{
mAvailableTaps = new ArrayList<TapGroup>();
if(mAvailableTaps == null)
{
mAvailableTaps = new ArrayList<TapGroup>();
TapGroup group = new TapGroup( "MPT-1327 Decoder" );
group.add(
new FloatTap( INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f ) );
group.add(
new FloatTap( INSTRUMENT_LOW_PASS_TO_DECODER, 0, 0.5f ) );
group.add(
new BinaryTap( INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f ) );
TapGroup group = new TapGroup("MPT-1327 Decoder");
mAvailableTaps.add( group );
group.add(new FloatTap(INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f));
group.add(new FloatTap(INSTRUMENT_LOW_PASS_TO_DECODER, 0, 0.5f));
group.add(new BinaryTap(INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f));
mAvailableTaps.add(group);
/* Add the taps from the FSK decoder */
mAvailableTaps.addAll( mFSKDecoder.getTapGroups() );
}
return mAvailableTaps;
mAvailableTaps.addAll(mFSKDecoder.getTapGroups());
}
return mAvailableTaps;
}
@Override
public void registerTap( Tap tap )
@Override
public void registerTap(Tap tap)
{
/* Send request to decoder */
mFSKDecoder.registerTap( tap );
switch( tap.getName() )
{
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
FloatBufferTap hb1Tap = (FloatBufferTap)tap;
mDecimationFilter.setListener( hb1Tap );
hb1Tap.setListener( mLowPassFilter );
break;
case INSTRUMENT_LOW_PASS_TO_DECODER:
FloatBufferTap lowTap = (FloatBufferTap)tap;
mLowPassFilter.setListener( lowTap );
lowTap.setListener( mFSKDecoder );
break;
case INSTRUMENT_DECODER_TO_FRAMER:
BinaryTap decoderTap = (BinaryTap)tap;
mFSKDecoder.setListener( decoderTap );
decoderTap.setListener( mSymbolBroadcaster );
break;
}
/* Send request to decoder */
mFSKDecoder.registerTap(tap);
switch(tap.getName())
{
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
FloatBufferTap hb1Tap = (FloatBufferTap) tap;
mDecimationFilter.setListener(hb1Tap);
hb1Tap.setListener(mLowPassFilter);
break;
case INSTRUMENT_LOW_PASS_TO_DECODER:
FloatBufferTap lowTap = (FloatBufferTap) tap;
mLowPassFilter.setListener(lowTap);
lowTap.setListener(mFSKDecoder);
break;
case INSTRUMENT_DECODER_TO_FRAMER:
BinaryTap decoderTap = (BinaryTap) tap;
mFSKDecoder.setListener(decoderTap);
decoderTap.setListener(mSymbolBroadcaster);
break;
}
}
@Override
public void unregisterTap( Tap tap )
@Override
public void unregisterTap(Tap tap)
{
mFSKDecoder.unregisterTap( tap );
switch( tap.getName() )
{
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
mDecimationFilter.setListener( mLowPassFilter );
break;
case INSTRUMENT_LOW_PASS_TO_DECODER:
mLowPassFilter.setListener( mFSKDecoder );
break;
case INSTRUMENT_DECODER_TO_FRAMER:
mFSKDecoder.setListener( mSymbolBroadcaster );
break;
}
mFSKDecoder.unregisterTap(tap);
switch(tap.getName())
{
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
mDecimationFilter.setListener(mLowPassFilter);
break;
case INSTRUMENT_LOW_PASS_TO_DECODER:
mLowPassFilter.setListener(mFSKDecoder);
break;
case INSTRUMENT_DECODER_TO_FRAMER:
mFSKDecoder.setListener(mSymbolBroadcaster);
break;
}
}
public enum Sync
{
NORMAL( "Normal",
SyncPattern.MPT1327_CONTROL,
SyncPattern.MPT1327_TRAFFIC ),
FRENCH( "French",
SyncPattern.MPT1327_CONTROL_FRENCH,
SyncPattern.MPT1327_TRAFFIC_FRENCH );
private String mLabel;
private SyncPattern mControlSyncPattern;
private SyncPattern mTrafficSyncPattern;
public enum Sync
{
NORMAL("Normal", SyncPattern.MPT1327_CONTROL, SyncPattern.MPT1327_TRAFFIC),
FRENCH("French", SyncPattern.MPT1327_CONTROL_FRENCH, SyncPattern.MPT1327_TRAFFIC_FRENCH);
private Sync( String label,
SyncPattern controlPattern,
SyncPattern trafficPattern )
{
mLabel = label;
mControlSyncPattern = controlPattern;
mTrafficSyncPattern = trafficPattern;
}
public String getLabel()
{
return mLabel;
}
public SyncPattern getControlSyncPattern()
{
return mControlSyncPattern;
}
public SyncPattern getTrafficSyncPattern()
{
return mTrafficSyncPattern;
}
public String toString()
{
return getLabel();
}
}
private String mLabel;
private SyncPattern mControlSyncPattern;
private SyncPattern mTrafficSyncPattern;
@Override
public void reset()
{
mControlMessageFramer.reset();
mTrafficMessageFramer.reset();
}
private Sync(String label, SyncPattern controlPattern, SyncPattern trafficPattern)
{
mLabel = label;
mControlSyncPattern = controlPattern;
mTrafficSyncPattern = trafficPattern;
}
@Override
public void start( ScheduledExecutorService executor )
{
// TODO Auto-generated method stub
}
public String getLabel()
{
return mLabel;
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
public SyncPattern getControlSyncPattern()
{
return mControlSyncPattern;
}
public SyncPattern getTrafficSyncPattern()
{
return mTrafficSyncPattern;
}
public String toString()
{
return getLabel();
}
}
@Override
public void reset()
{
mControlMessageFramer.reset();
mTrafficMessageFramer.reset();
}
@Override
public void start(ScheduledExecutorService executor)
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
}

View File

@@ -1,200 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.mpt1327;
import controller.channel.Channel.ChannelType;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class MPT1327DecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol = new JLabel("");
private JLabel mSiteOrToTalkgroup = new JLabel("");
private JLabel mSiteOrToTalkgroupAlias = new JLabel("");
private JLabel mFromTalkgroupLabel = new JLabel("");
private JLabel mFromTalkgroup = new JLabel("");
private JLabel mFromTalkgroupAliasLabel = new JLabel("");
public MPT1327DecoderPanel(IconManager iconManager, SettingsManager settingsManager, MPT1327DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public MPT1327DecoderState getDecoderState()
{
return (MPT1327DecoderState) super.getDecoderState();
}
public void dispose()
{
super.dispose();
}
public void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setText("MPT1327");
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
MPT1327DecoderState mptState = getDecoderState();
mSiteOrToTalkgroup.setFont(mFontDecoder);
mSiteOrToTalkgroup.setForeground(mColorLabelDecoder);
mSiteOrToTalkgroupAlias.setFont(mFontDecoder);
mSiteOrToTalkgroupAlias.setForeground(mColorLabelDecoder);
mFromTalkgroupLabel.setFont(mFontDetails);
mFromTalkgroupLabel.setForeground(mColorLabelDetails);
mFromTalkgroup.setFont(mFontDecoder);
mFromTalkgroup.setForeground(mColorLabelDecoder);
mFromTalkgroupAliasLabel.setFont(mFontDecoder);
mFromTalkgroupAliasLabel.setForeground(mColorLabelDecoder);
switch(mptState.getChannelType())
{
case STANDARD:
mSiteOrToTalkgroup.setText(mptState.getSite());
setAliasLabel(mSiteOrToTalkgroupAlias, mptState.getSiteAlias());
break;
case TRAFFIC:
mSiteOrToTalkgroup.setText(mptState.getToTalkgroup());
setAliasLabel(mSiteOrToTalkgroupAlias, mptState.getToTalkgroupAlias());
mFromTalkgroupLabel.setText("From:");
mFromTalkgroup.setText(getDecoderState().getFromTalkgroup());
setAliasLabel(mFromTalkgroupAliasLabel, mptState.getFromTalkgroupAlias());
break;
default:
throw new IllegalArgumentException("Unrecognized channel "
+ "type in MPT1327 Decoder [" + getDecoderState()
.getChannelType().name() + "]");
}
add(mProtocol);
add(mSiteOrToTalkgroup);
add(mSiteOrToTalkgroupAlias, "wrap");
if(mptState.getChannelType() == ChannelType.TRAFFIC)
{
add(mFromTalkgroupLabel);
add(mFromTalkgroup);
add(mFromTalkgroupAliasLabel, "wrap");
}
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
final MPT1327DecoderState state = getDecoderState();
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
switch(changedAttribute)
{
case CHANNEL_SITE_NUMBER:
mSiteOrToTalkgroup.setText(String.valueOf(state.getSite()));
setAliasLabel(mSiteOrToTalkgroupAlias, state.getSiteAlias(), false);
repaint();
break;
case TO_TALKGROUP:
mSiteOrToTalkgroup.setText(state.getToTalkgroup());
setAliasLabel(mSiteOrToTalkgroupAlias, state.getToTalkgroupAlias());
repaint();
break;
case FROM_TALKGROUP:
mFromTalkgroup.setText(state.getFromTalkgroup());
setAliasLabel(mFromTalkgroupAliasLabel, state.getFromTalkgroupAlias());
repaint();
break;
default:
break;
}
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mProtocol != null)
{
mProtocol.setForeground(mColorLabelDecoder);
}
if(mSiteOrToTalkgroup != null)
{
mSiteOrToTalkgroup.setForeground(mColorLabelDecoder);
}
if(mSiteOrToTalkgroupAlias != null)
{
mSiteOrToTalkgroupAlias.setForeground(mColorLabelDecoder);
}
if(mFromTalkgroup != null)
{
mFromTalkgroup.setForeground(mColorLabelDecoder);
}
if(mFromTalkgroupAliasLabel != null)
{
mFromTalkgroupAliasLabel.setForeground(mColorLabelDecoder);
}
break;
case CHANNEL_STATE_LABEL_DETAILS:
if(mFromTalkgroupLabel != null)
{
mFromTalkgroupLabel.setForeground(mColorLabelDetails);
}
break;
default:
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,179 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.mpt1327;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class MPT1327TrafficPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocolLabel;
private JLabel mFromTalkgroupLabel;
private JLabel mFromTalkgroupAliasLabel;
private JLabel mToLabel;
private JLabel mToTalkgroupLabel;
private JLabel mToTalkgroupAliasLabel;
public MPT1327TrafficPanel(IconManager iconManager, SettingsManager settingsManager,
MPT1327DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public MPT1327DecoderState getDecoderState()
{
return (MPT1327DecoderState) super.getDecoderState();
}
public void dispose()
{
super.dispose();
}
public void init()
{
mProtocolLabel = new JLabel("MPT1327");
mProtocolLabel.setFont(mFontDecoder);
mProtocolLabel.setForeground(mColorLabelDecoder);
mFromTalkgroupLabel = new JLabel();
mFromTalkgroupLabel.setFont(mFontDecoder);
mFromTalkgroupLabel.setForeground(mColorLabelDecoder);
mFromTalkgroupAliasLabel = new JLabel();
mFromTalkgroupAliasLabel.setFont(mFontDecoder);
mFromTalkgroupAliasLabel.setForeground(mColorLabelDecoder);
mToLabel = new JLabel("TO:");
mToLabel.setFont(mFontDetails);
mToLabel.setForeground(mColorLabelDetails);
mToTalkgroupLabel = new JLabel(getDecoderState().getFromTalkgroup());
mToTalkgroupLabel.setFont(mFontDecoder);
mToTalkgroupLabel.setForeground(mColorLabelDecoder);
mToTalkgroupAliasLabel = new JLabel();
mToTalkgroupAliasLabel.setFont(mFontDecoder);
mToTalkgroupAliasLabel.setForeground(mColorLabelDecoder);
add(mProtocolLabel);
add(mFromTalkgroupLabel);
add(mFromTalkgroupAliasLabel, "wrap");
add(mToLabel);
add(mToTalkgroupLabel);
add(mToTalkgroupAliasLabel, "wrap");
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
final MPT1327DecoderState state = getDecoderState();
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
switch(changedAttribute)
{
case FROM_TALKGROUP:
String talkgroup = state.getFromTalkgroup();
mFromTalkgroupLabel.setText(talkgroup);
Alias alias = state.getFromTalkgroupAlias();
if(alias != null)
{
mFromTalkgroupAliasLabel.setText(alias.getName());
String icon = alias.getIconName();
mFromTalkgroupAliasLabel.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE));
}
break;
case TO_TALKGROUP:
String toTalkgroup = state.getToTalkgroup();
mToTalkgroupLabel.setText(toTalkgroup);
Alias toAlias = state.getToTalkgroupAlias();
if(toAlias != null)
{
mToTalkgroupAliasLabel.setText(toAlias.getName());
String icon = toAlias.getIconName();
if(icon != null)
{
mToTalkgroupAliasLabel.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE));
}
}
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
mProtocolLabel.setForeground(mColorLabelDecoder);
mFromTalkgroupLabel.setForeground(mColorLabelDecoder);
mFromTalkgroupAliasLabel.setForeground(mColorLabelDecoder);
mToTalkgroupLabel.setForeground(mColorLabelDecoder);
mToTalkgroupAliasLabel.setForeground(mColorLabelDecoder);
break;
case CHANNEL_STATE_LABEL_DETAILS:
mToLabel.setForeground(mColorLabelDetails);
break;
default:
break;
}
}
}
}

View File

@@ -1,80 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.nbfm;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import module.decode.state.DecoderState;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
public class NBFMDecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol = new JLabel("NBFM");
public NBFMDecoderPanel(IconManager iconManager, SettingsManager settingsManager, DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
protected void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
add(mProtocol, "wrap");
}
@Override
public void receive(ChangedAttribute t)
{
/* Not implemented */
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting color = (ColorSetting) setting;
if(color.getColorSettingName() ==
ColorSettingName.CHANNEL_STATE_LABEL_DECODER)
{
mProtocol.setForeground(color.getColor());
}
}
}
}

View File

@@ -1,226 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.p25;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class P25DecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol;
private JLabel mTo = new JLabel(" ");
private JLabel mToAlias = new JLabel(" ");
private JLabel mNAC = new JLabel("NAC:");
private JLabel mFrom = new JLabel(" ");
private JLabel mFromAlias = new JLabel(" ");
private JLabel mSystem = new JLabel("SYS:");
private JLabel mSite = new JLabel("Site:");
private JLabel mSiteAlias = new JLabel("");
public P25DecoderPanel(IconManager iconManager, SettingsManager settingsManager, P25DecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
mProtocol = new JLabel("P25-1 " + decoderState.getModulation().getShortLabel());
init();
}
public void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
mTo.setFont(mFontDecoder);
mTo.setForeground(mColorLabelDecoder);
mToAlias.setFont(mFontDecoder);
mToAlias.setForeground(mColorLabelDecoder);
mNAC.setFont(mFontDetails);
mNAC.setForeground(mColorLabelDecoder);
mFrom.setFont(mFontDecoder);
mFrom.setForeground(mColorLabelDecoder);
mFromAlias.setFont(mFontDecoder);
mFromAlias.setForeground(mColorLabelDecoder);
mSystem.setFont(mFontDetails);
mSystem.setForeground(mColorLabelDecoder);
mSiteAlias.setFont(mFontDecoder);
mSiteAlias.setForeground(mColorLabelDecoder);
mSite.setFont(mFontDetails);
mSite.setForeground(mColorLabelDecoder);
add(mProtocol);
add(mTo);
add(mToAlias, "wrap");
add(mNAC);
add(mFrom);
add(mFromAlias, "wrap");
add(mSystem);
add(mSite);
add(mSiteAlias, "wrap");
}
public P25DecoderState getDecoderState()
{
return (P25DecoderState) super.getDecoderState();
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
final P25DecoderState state = getDecoderState();
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
switch(changedAttribute)
{
case NAC:
mNAC.setText("NAC:" + state.getNAC());
break;
case SYSTEM:
mSystem.setText("SYS:" + state.getSystem());
break;
case SITE:
mSite.setText("SITE:" + state.getSite());
break;
case SITE_ALIAS:
mSiteAlias.setText(state.getSiteAlias());
break;
case FROM_TALKGROUP:
if(state.getFromTalkgroup() == null)
{
mFrom.setText(" ");
}
else
{
mFrom.setText(state.getFromTalkgroup());
}
break;
case FROM_TALKGROUP_ALIAS:
Alias from = state.getFromAlias();
if(from != null)
{
mFromAlias.setText(from.getName());
final String icon = from.getIconName();
if(icon != null && mIconManager != null)
{
mFromAlias.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE));
}
}
else
{
mFromAlias.setText(" ");
mFromAlias.setIcon(null);
}
break;
case TO_TALKGROUP:
if(state.getToTalkgroup() == null)
{
mTo.setText(" ");
}
else
{
mTo.setText(state.getToTalkgroup());
}
break;
case TO_TALKGROUP_ALIAS:
final Alias to = state.getToAlias();
if(to != null)
{
mToAlias.setText(to.getName());
final String icon = to.getIconName();
if(icon != null && mIconManager != null)
{
mToAlias.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE));
}
}
else
{
mToAlias.setText(" ");
mToAlias.setIcon(null);
}
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mProtocol != null)
{
mProtocol.setForeground(mColorLabelDecoder);
}
break;
default:
break;
}
}
}
}

View File

@@ -20,9 +20,17 @@ package module.decode.p25;
import alias.Alias;
import alias.AliasList;
import alias.PatchGroupAlias;
import alias.id.AliasIDType;
import alias.id.talkgroup.TalkgroupID;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.state.ChangeChannelTimeoutEvent;
import channel.state.DecoderState;
import channel.state.DecoderStateEvent;
import channel.state.DecoderStateEvent.Event;
import channel.state.State;
import channel.traffic.TrafficChannelAllocationEvent;
import controller.channel.Channel.ChannelType;
import message.Message;
import module.decode.DecoderType;
@@ -123,13 +131,6 @@ import module.decode.p25.reference.IPProtocol;
import module.decode.p25.reference.LinkControlOpcode;
import module.decode.p25.reference.Response;
import module.decode.p25.reference.Vendor;
import module.decode.state.ChangeChannelTimeoutEvent;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
import module.decode.state.TrafficChannelAllocationEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -159,10 +160,10 @@ public class P25DecoderState extends DecoderState
private Set<module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast> mSecondaryControlChannels =
new TreeSet<>();
private Map<Integer, IdentifierUpdate> mBands = new HashMap<>();
private Map<String, Long> mRegistrations = new HashMap<>();
private Map<String, IAdjacentSite> mNeighborMap = new HashMap<>();
private Map<String, List<String>> mPatchGroupMap = new HashMap<>();
private Map<Integer,IdentifierUpdate> mBands = new HashMap<>();
private Map<String,Long> mRegistrations = new HashMap<>();
private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>();
private Map<String,List<String>> mPatchGroupMap = new HashMap<>();
private String mLastCommandEventID;
private String mLastPageEventID;
@@ -170,14 +171,12 @@ public class P25DecoderState extends DecoderState
private String mLastRegistrationEventID;
private String mLastResponseEventID;
//TODO: create multi-attribute monitor for NAC and System
private String mNAC;
private String mSystem;
private String mSite;
private String mSiteAlias;
private String mFromTalkgroup;
private Alias mFromAlias;
private String mToTalkgroup;
private Alias mToAlias;
private AliasedStringAttributeMonitor mSiteAttributeMonitor;
private AliasedStringAttributeMonitor mFromTalkgroupMonitor;
private AliasedStringAttributeMonitor mToTalkgroupMonitor;
private String mCurrentChannel = "CURRENT";
private long mCurrentChannelFrequency = 0;
@@ -188,7 +187,7 @@ public class P25DecoderState extends DecoderState
private P25CallEvent mCurrentCallEvent;
private List<String> mCallDetectTalkgroups = new ArrayList<>();
private Map<String, P25CallEvent> mChannelCallMap = new HashMap<>();
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
public P25DecoderState(AliasList aliasList,
ChannelType channelType,
@@ -199,6 +198,16 @@ public class P25DecoderState extends DecoderState
mChannelType = channelType;
mModulation = modulation;
mIgnoreDataCalls = ignoreDataCalls;
mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP);
mFromTalkgroupMonitor.addIllegalValue("000000");
mToTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.TALKGROUP);
mToTalkgroupMonitor.addIllegalValue("0000");
mToTalkgroupMonitor.addIllegalValue("000000");
}
public Modulation getModulation()
@@ -225,9 +234,9 @@ public class P25DecoderState extends DecoderState
{
resetState();
updateNAC(null);
updateSite(null);
updateSystem(null);
mNAC = null;
mSiteAttributeMonitor.reset();
mSystem = null;
}
/**
@@ -235,17 +244,8 @@ public class P25DecoderState extends DecoderState
*/
private void resetState()
{
mFromTalkgroup = null;
broadcast(ChangedAttribute.FROM_TALKGROUP);
mFromAlias = null;
broadcast(ChangedAttribute.FROM_TALKGROUP_ALIAS);
mToTalkgroup = null;
broadcast(ChangedAttribute.TO_TALKGROUP);
mToAlias = null;
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
mFromTalkgroupMonitor.reset();
mToTalkgroupMonitor.reset();
mCallDetectTalkgroups.clear();
@@ -412,7 +412,7 @@ public class P25DecoderState extends DecoderState
String to = hdu.getToID();
updateTo(to);
mToTalkgroupMonitor.process(to);
if(mCurrentCallEvent == null)
{
@@ -496,13 +496,13 @@ public class P25DecoderState extends DecoderState
}
break;
case CALL_TERMINATION_OR_CANCELLATION:
/* This opcode as handled at the beginning of the method */
/* This opcode as handled at the beginning of the method */
break;
case CHANNEL_IDENTIFIER_UPDATE:
//TODO: does the activity summary need this message?
/* This message is handled by the P25MessageProcessor and
* inserted into any channels needing frequency band info */
* inserted into any channels needing frequency band info */
break;
case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT:
//TODO: does the activity summary need this message?
@@ -621,8 +621,8 @@ public class P25DecoderState extends DecoderState
String from = gvcuser.getSourceAddress();
String to = gvcuser.getGroupAddress();
updateFrom(from);
updateTo(to);
mFromTalkgroupMonitor.process(from);
mToTalkgroupMonitor.process(to);
if(mCurrentCallEvent == null)
{
@@ -709,7 +709,7 @@ public class P25DecoderState extends DecoderState
String site = rfsssb.getRFSubsystemID() + "-" +
rfsssb.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
if(mCurrentChannel == null ||
!mCurrentChannel.contentEquals(rfsssb.getChannel()))
@@ -732,7 +732,7 @@ public class P25DecoderState extends DecoderState
String site = rfsssbe.getRFSubsystemID() + "-" +
rfsssbe.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
if(mCurrentChannel == null ||
!mCurrentChannel.contentEquals(rfsssbe.getTransmitChannel()))
@@ -755,7 +755,7 @@ public class P25DecoderState extends DecoderState
String site = sccb.getRFSubsystemID() + "-" +
sccb.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
}
else
{
@@ -771,7 +771,7 @@ public class P25DecoderState extends DecoderState
String site = sccb.getRFSubsystemID() + "-" +
sccb.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
}
else
{
@@ -845,7 +845,7 @@ public class P25DecoderState extends DecoderState
String to = tivcu.getAddress();
updateTo(to);
mToTalkgroupMonitor.process(to);
if(mCurrentCallEvent == null)
{
@@ -922,10 +922,10 @@ public class P25DecoderState extends DecoderState
(module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser) tdulc;
String from = uuvcu.getSourceAddress();
updateFrom(from);
mFromTalkgroupMonitor.process(from);
String to = uuvcu.getTargetAddress();
updateTo(to);
mToTalkgroupMonitor.process(to);
if(mCurrentCallEvent != null)
{
@@ -1170,9 +1170,9 @@ public class P25DecoderState extends DecoderState
module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUser) ldu;
updateFrom(gvcuser.getSourceAddress());
mFromTalkgroupMonitor.process(gvcuser.getSourceAddress());
updateTo(gvcuser.getGroupAddress());
mToTalkgroupMonitor.process(gvcuser.getGroupAddress());
if(mChannelType == ChannelType.STANDARD)
{
@@ -1265,7 +1265,7 @@ public class P25DecoderState extends DecoderState
String site = rfsssb.getRFSubsystemID() + "-" +
rfsssb.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
}
else
{
@@ -1281,7 +1281,7 @@ public class P25DecoderState extends DecoderState
String site = rfsssbe.getRFSubsystemID() + "-" +
rfsssbe.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
}
else
{
@@ -1297,7 +1297,7 @@ public class P25DecoderState extends DecoderState
String site = sccb.getRFSubsystemID() + "-" +
sccb.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
}
else
{
@@ -1313,7 +1313,7 @@ public class P25DecoderState extends DecoderState
String site = sccb.getRFSubsystemID() + "-" +
sccb.getSiteID();
updateSite(site);
mSiteAttributeMonitor.process(site);
}
else
{
@@ -1386,7 +1386,7 @@ public class P25DecoderState extends DecoderState
TelephoneInterconnectVoiceChannelUser tivcu =
(TelephoneInterconnectVoiceChannelUser) ldu;
updateTo(tivcu.getAddress());
mToTalkgroupMonitor.process(tivcu.getAddress());
if(mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT)
{
@@ -1467,9 +1467,9 @@ public class P25DecoderState extends DecoderState
UnitToUnitVoiceChannelUser uuvcu =
(UnitToUnitVoiceChannelUser) ldu;
updateFrom(uuvcu.getSourceAddress());
mFromTalkgroupMonitor.process(uuvcu.getSourceAddress());
updateTo(uuvcu.getTargetAddress());
mToTalkgroupMonitor.process(uuvcu.getTargetAddress());
if(mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL)
{
@@ -1884,7 +1884,7 @@ public class P25DecoderState extends DecoderState
updateNAC(mRFSSStatusMessageExtended.getNAC());
updateSystem(mRFSSStatusMessageExtended.getSystemID());
updateSite(mRFSSStatusMessageExtended.getRFSubsystemID() +
mSiteAttributeMonitor.process(mRFSSStatusMessageExtended.getRFSubsystemID() +
"-" + mRFSSStatusMessageExtended.getSiteID());
}
else
@@ -2658,7 +2658,7 @@ public class P25DecoderState extends DecoderState
updateSystem(message.getSystemID());
updateSite(message.getRFSubsystemID() + "-" + message.getSiteID());
mSiteAttributeMonitor.process(message.getRFSubsystemID() + "-" + message.getSiteID());
}
private void processTSBKDataChannelAnnouncement(TSBKMessage message)
@@ -2843,7 +2843,7 @@ public class P25DecoderState extends DecoderState
patchedTalkgroups = new ArrayList<>();
}
for(String talkgroupToAdd: talkgroupsToAdd)
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
@@ -2887,7 +2887,7 @@ public class P25DecoderState extends DecoderState
if(patchedTalkgroups != null)
{
for(String talkgroupToRemove: talkgroupsToRemove)
for(String talkgroupToRemove : talkgroupsToRemove)
{
if(patchedTalkgroups.contains(talkgroupToRemove))
{
@@ -2929,7 +2929,7 @@ public class P25DecoderState extends DecoderState
if(existingAlias instanceof PatchGroupAlias)
{
patchGroupAlias = (PatchGroupAlias)existingAlias;
patchGroupAlias = (PatchGroupAlias) existingAlias;
}
else
{
@@ -2953,7 +2953,7 @@ public class P25DecoderState extends DecoderState
patchGroupAlias.clearPatchedAliases();
for(String patchedTalkgroup: patchedTalkgroups)
for(String patchedTalkgroup : patchedTalkgroups)
{
Alias patchedAlias = getAliasList().getTalkgroupAlias(patchedTalkgroup);
@@ -2974,7 +2974,7 @@ public class P25DecoderState extends DecoderState
if(alias instanceof PatchGroupAlias)
{
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
PatchGroupAlias patchGroupAlias = (PatchGroupAlias) alias;
getAliasList().removeAlias(patchGroupAlias);
@@ -3361,79 +3361,12 @@ public class P25DecoderState extends DecoderState
}
}
/**
* Broadcasts an update for the TO group or unit address
*/
private void updateTo(final String to)
private void updateSystem(String system)
{
if(to != null && !to.contentEquals("0000") && !to.contentEquals("000000"))
if(mSystem == null || (system != null && !mSystem.contentEquals(system)))
{
if(mToTalkgroup == null || !mToTalkgroup.contentEquals(to))
{
mToTalkgroup = to;
broadcast(ChangedAttribute.TO_TALKGROUP);
if(hasAliasList())
{
mToAlias = getAliasList().getTalkgroupAlias(mToTalkgroup);
}
else
{
mToAlias = null;
}
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
/* Send audio metadata update */
broadcast(new Metadata(MetadataType.TO, mToTalkgroup, mToAlias, true));
if(mCurrentCallEvent != null &&
(mCurrentCallEvent.getToID() == null ||
!mCurrentCallEvent.getToID().contentEquals(to)))
{
mCurrentCallEvent.setToID(to);
broadcast(mCurrentCallEvent);
}
}
}
}
/**
* Broadcasts an update for the FROM group or unit address
*/
private void updateFrom(final String from)
{
if(from != null && !from.contentEquals("0000") && !from.contentEquals("000000"))
{
if(mFromTalkgroup == null || !mFromTalkgroup.contentEquals(from))
{
mFromTalkgroup = from;
broadcast(ChangedAttribute.FROM_TALKGROUP);
if(hasAliasList())
{
mFromAlias = getAliasList().getTalkgroupAlias(mFromTalkgroup);
}
else
{
mFromAlias = null;
}
broadcast(ChangedAttribute.FROM_TALKGROUP_ALIAS);
/* Send audio metadata update */
broadcast(new Metadata(MetadataType.FROM, mFromTalkgroup, mFromAlias, true));
if(mCurrentCallEvent != null &&
(mCurrentCallEvent.getFromID() == null ||
!mCurrentCallEvent.getFromID().contentEquals(from)))
{
mCurrentCallEvent.setFromID(from);
broadcast(mCurrentCallEvent);
}
}
mSystem = system;
broadcastSystemAndNACUpdate();
}
}
@@ -3445,48 +3378,14 @@ public class P25DecoderState extends DecoderState
if(mNAC == null || (nac != null && !mNAC.contentEquals(nac)))
{
mNAC = nac;
broadcast(ChangedAttribute.NAC);
broadcastSystemAndNACUpdate();
}
}
/**
* Updates the site information
*/
private void updateSite(String site)
private void broadcastSystemAndNACUpdate()
{
if(mSite == null || (site != null && !mSite.contentEquals(site)))
{
mSite = site;
broadcast(ChangedAttribute.SITE);
if(hasAliasList())
{
Alias alias = getAliasList().getSiteID(mSite);
if(alias != null)
{
mSiteAlias = alias.getName();
}
else
{
mSiteAlias = null;
}
}
broadcast(ChangedAttribute.SITE_ALIAS);
}
}
private void updateSystem(String system)
{
if(mSystem == null || (system != null && !mSystem.contentEquals(system)))
{
mSystem = system;
broadcast(ChangedAttribute.SYSTEM);
}
String label = String.format("SYS:%s NAC:%s", mSystem, mNAC);
broadcast(new AttributeChangeRequest<String>(Attribute.NETWORK_ID_1, label));
}
/**
@@ -3754,46 +3653,6 @@ public class P25DecoderState extends DecoderState
return sb.toString();
}
public String getNAC()
{
return mNAC;
}
public String getSystem()
{
return mSystem;
}
public String getSiteAlias()
{
return mSiteAlias;
}
public String getSite()
{
return mSite;
}
public String getFromTalkgroup()
{
return mFromTalkgroup;
}
public Alias getFromAlias()
{
return mFromAlias;
}
public String getToTalkgroup()
{
return mToTalkgroup;
}
public Alias getToAlias()
{
return mToAlias;
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
@@ -3810,33 +3669,21 @@ public class P25DecoderState extends DecoderState
{
if(event instanceof TrafficChannelAllocationEvent)
{
TrafficChannelAllocationEvent allocationEvent =
(TrafficChannelAllocationEvent) event;
TrafficChannelAllocationEvent allocationEvent = (TrafficChannelAllocationEvent) event;
mCurrentCallEvent = (P25CallEvent) allocationEvent.getCallEvent();
mCurrentChannel = allocationEvent.getCallEvent().getChannel();
broadcast(ChangedAttribute.CHANNEL_NUMBER);
broadcast(new AttributeChangeRequest<String>(Attribute.CHANNEL_FREQUENCY_LABEL, mCurrentChannel));
mCurrentChannelFrequency = allocationEvent.getCallEvent().getFrequency();
broadcast(ChangedAttribute.SOURCE);
broadcast(new AttributeChangeRequest<Long>(Attribute.CHANNEL_FREQUENCY, mCurrentChannelFrequency));
mFromTalkgroup = allocationEvent.getCallEvent().getFromID();
broadcast(ChangedAttribute.FROM_TALKGROUP);
mFromTalkgroupMonitor.reset();
mFromTalkgroupMonitor.process(allocationEvent.getCallEvent().getFromID());
mToTalkgroup = allocationEvent.getCallEvent().getToID();
broadcast(ChangedAttribute.TO_TALKGROUP);
if(allocationEvent.getCallEvent().hasAliasList())
{
AliasList aliasList = allocationEvent.getCallEvent().getAliasList();
mFromAlias = aliasList.getTalkgroupAlias(mFromTalkgroup);
broadcast(ChangedAttribute.FROM_TALKGROUP_ALIAS);
mToAlias = aliasList.getTalkgroupAlias(mToTalkgroup);
broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS);
}
mToTalkgroupMonitor.reset();
mToTalkgroupMonitor.process(allocationEvent.getCallEvent().getToID());
}
}
break;

View File

@@ -1,56 +1,42 @@
package module.decode.p25.audio;
import java.util.concurrent.ScheduledExecutorService;
import audio.AudioFormats;
import audio.AudioPacket;
import audio.IAudioPacketProvider;
import audio.squelch.ISquelchStateListener;
import audio.squelch.SquelchState;
import channel.metadata.Metadata;
import dsp.gain.NonClippingGain;
import jmbe.iface.AudioConversionLibrary;
import jmbe.iface.AudioConverter;
import message.IMessageListener;
import message.Message;
import module.Module;
import module.decode.p25.message.ldu.LDUMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import audio.AudioFormats;
import audio.AudioPacket;
import audio.IAudioPacketProvider;
import audio.metadata.AudioMetadata;
import audio.metadata.IMetadataListener;
import audio.metadata.Metadata;
import audio.squelch.ISquelchStateListener;
import audio.squelch.SquelchState;
import controller.channel.ChannelEvent;
import controller.channel.IChannelEventListener;
import dsp.gain.NonClippingGain;
public class P25AudioModule extends Module implements Listener<Message>,
IAudioPacketProvider, IMessageListener, IChannelEventListener,
IMetadataListener, ISquelchStateListener
import java.util.concurrent.ScheduledExecutorService;
public class P25AudioModule extends Module implements Listener<Message>, IAudioPacketProvider, IMessageListener,
ISquelchStateListener
{
private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class);
private static final String IMBE_CODEC = "IMBE";
/* Provides a unique identifier for this audio module instance to use as a
* source identifier for all audio packets */
private static int UNIQUE_ID = 0;
private int mSourceID;
private static boolean mLibraryLoadStatusLogged = false;
private boolean mCanConvertAudio = false;
private AudioConverter mAudioConverter;
private Listener<AudioPacket> mAudioPacketListener;
private AudioMetadata mAudioMetadata;
private ChannelEventListener mChannelEventListener = new ChannelEventListener();
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
private Metadata mMetadata;
public P25AudioModule(boolean record)
public P25AudioModule(Metadata metadata)
{
mSourceID = ++UNIQUE_ID;
mAudioMetadata = new AudioMetadata(mSourceID, record);
mMetadata = metadata;
loadConverter();
}
@@ -75,7 +61,6 @@ public class P25AudioModule extends Module implements Listener<Message>,
@Override
public void reset()
{
mAudioMetadata.reset();
}
@Override
@@ -113,8 +98,7 @@ public class P25AudioModule extends Module implements Listener<Message>,
audio = mGain.apply(audio);
mAudioPacketListener.receive(
new AudioPacket(audio, mAudioMetadata.copyOf()));
mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf()));
}
}
}
@@ -211,41 +195,6 @@ public class P25AudioModule extends Module implements Listener<Message>,
mAudioPacketListener = null;
}
@Override
public Listener<Metadata> getMetadataListener()
{
return mAudioMetadata;
}
@Override
public Listener<ChannelEvent> getChannelEventListener()
{
return mChannelEventListener;
}
/**
* Wrapper for channel event listener. Responds to channel state reset
* events to remove/cleanup current audio metadata
*/
public class ChannelEventListener implements Listener<ChannelEvent>
{
@Override
public void receive(ChannelEvent event)
{
switch (event.getEvent())
{
case NOTIFICATION_STATE_RESET:
mAudioMetadata.reset();
break;
case NOTIFICATION_SELECTION_CHANGE:
mAudioMetadata.setSelected(event.getChannel().isSelected());
break;
default:
break;
}
}
}
/**
* Wrapper for squelch state listener. Internally, the P25 audio module
* doesn't have a squelch state. If there are IMBE audio frames, we have
@@ -259,10 +208,7 @@ public class P25AudioModule extends Module implements Listener<Message>,
{
if (state == SquelchState.SQUELCH && mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END,
mAudioMetadata.copyOf()));
mAudioMetadata.reset();
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
}
}
}

View File

@@ -1,225 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.passport;
import alias.Alias;
import icon.IconManager;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderPanel;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public class PassportDecoderPanel extends DecoderPanel
{
private static final long serialVersionUID = 1L;
private JLabel mProtocol = new JLabel("Passport");
private JLabel mToTalkgroup = new JLabel();
private JLabel mToTalkgroupAlias = new JLabel();
private JLabel mSiteAndChannel = new JLabel("Site:");
private JLabel mFromTalkgroup = new JLabel();
private JLabel mFromTalkgroupAlias = new JLabel();
public PassportDecoderPanel(IconManager iconManager, SettingsManager settingsManager, PassportDecoderState decoderState)
{
super(iconManager, settingsManager, decoderState);
init();
}
public void dispose()
{
super.dispose();
}
public void init()
{
super.init();
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[]0[]0[]"));
mProtocol.setFont(mFontDecoder);
mProtocol.setForeground(mColorLabelDecoder);
mToTalkgroup.setFont(mFontDecoder);
mToTalkgroup.setForeground(mColorLabelDecoder);
mToTalkgroupAlias.setFont(mFontDecoder);
mToTalkgroupAlias.setForeground(mColorLabelDecoder);
mSiteAndChannel.setFont(mFontDecoder);
mSiteAndChannel.setForeground(mColorLabelDecoder);
mFromTalkgroup.setFont(mFontDecoder);
mFromTalkgroup.setForeground(mColorLabelDecoder);
mFromTalkgroupAlias.setFont(mFontDecoder);
mFromTalkgroupAlias.setForeground(mColorLabelDecoder);
add(mProtocol);
add(mToTalkgroup);
add(mToTalkgroupAlias, "wrap");
add(mSiteAndChannel);
add(mFromTalkgroup);
add(mFromTalkgroupAlias, "wrap");
}
public PassportDecoderState getDecoderState()
{
return (PassportDecoderState) super.getDecoderState();
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
PassportDecoderState state = getDecoderState();
switch(changedAttribute)
{
case TO_TALKGROUP:
String tg = state.getTalkgroup();
if(tg != null)
{
mToTalkgroup.setText("TG:" + tg);
}
else
{
mToTalkgroup.setText(null);
}
break;
case TO_TALKGROUP_ALIAS:
Alias toAlias = state.getTalkgroupAlias();
if(toAlias != null)
{
mToTalkgroupAlias.setText(toAlias.getName());
mToTalkgroupAlias.setIcon(mIconManager.getIcon(toAlias.getIconName(), IconManager.DEFAULT_ICON_SIZE));
}
else
{
mToTalkgroupAlias.setText(null);
mToTalkgroupAlias.setIcon(null);
}
break;
case FROM_TALKGROUP:
String mid = state.getMobileID();
if(mid != null)
{
mFromTalkgroup.setText("MIN:" + mid);
}
else
{
mFromTalkgroup.setText(null);
}
break;
case FROM_TALKGROUP_ALIAS:
Alias fromAlias = state.getMobileIDAlias();
if(fromAlias != null)
{
mFromTalkgroupAlias.setText(fromAlias.getName());
mFromTalkgroupAlias.setIcon(mIconManager
.getIcon(fromAlias.getIconName(), IconManager.DEFAULT_ICON_SIZE));
}
else
{
mFromTalkgroupAlias.setText(null);
mFromTalkgroupAlias.setIcon(null);
}
break;
case CHANNEL_NUMBER:
StringBuilder sb = new StringBuilder();
sb.append("Site: ");
sb.append(state.getSiteNumber());
sb.append("/");
sb.append(state.getChannelNumber());
mSiteAndChannel.setText(sb.toString());
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged(Setting setting)
{
super.settingChanged(setting);
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_DECODER:
if(mColorLabelDecoder != null)
{
if(mProtocol != null)
{
mProtocol.setForeground(mColorLabelDecoder);
}
if(mToTalkgroup != null)
{
mToTalkgroup.setForeground(mColorLabelDecoder);
}
if(mToTalkgroupAlias != null)
{
mToTalkgroupAlias.setForeground(mColorLabelDecoder);
}
if(mSiteAndChannel != null)
{
mSiteAndChannel.setForeground(mColorLabelDecoder);
}
if(mFromTalkgroup != null)
{
mFromTalkgroup.setForeground(mColorLabelDecoder);
}
if(mFromTalkgroupAlias != null)
{
mFromTalkgroupAlias.setForeground(mColorLabelDecoder);
}
}
break;
default:
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,98 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package module.decode.state;
import java.util.concurrent.ScheduledExecutorService;
import message.Message;
import module.decode.DecoderType;
import module.decode.state.DecoderStateEvent.Event;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
/**
* Basic decoder channel state - provides the minimum channel state functionality
* to support an always un-squelched audio decoder.
*/
public class AlwaysUnsquelchedDecoderState extends DecoderState
{
private DecoderType mDecoderType;
private String mChannelName;
public AlwaysUnsquelchedDecoderState( DecoderType decoderType, String channelName )
{
super( null );
mDecoderType = decoderType;
mChannelName = channelName;
}
@Override
public void init()
{
}
@Override
public void reset()
{
/* No reset actions needed */
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
sb.append( "Activity Summary\n" );
sb.append( "\tDecoder:\t" );
sb.append( mDecoderType );
sb.append( "\n\n" );
return sb.toString();
}
@Override
public void receive( Message t )
{
/* Not implemented */
}
@Override
public void receiveDecoderStateEvent( DecoderStateEvent event )
{
/* Not implemented */
}
@Override
public DecoderType getDecoderType()
{
return mDecoderType;
}
@Override
public void start( ScheduledExecutorService executor )
{
broadcast( new Metadata( MetadataType.TO, mChannelName, false ) );
broadcast( new DecoderStateEvent( this, Event.ALWAYS_UNSQUELCH, State.IDLE ) );
}
@Override
public void stop()
{
}
}

View File

@@ -1,29 +0,0 @@
package module.decode.state;
public enum ChangedAttribute
{
AUDIO_TYPE,
CHANNEL_NUMBER,
CHANNEL_NAME,
CHANNEL_SITE_NUMBER,
CHANNEL_SITE_NUMBER_ALIAS,
CHANNEL_STATE,
DESCRIPTION,
FROM_TALKGROUP,
FROM_TALKGROUP_ALIAS,
FROM_TALKGROUP_TYPE,
MESSAGE,
MESSAGE_TYPE,
NAC,
SITE,
SITE_ALIAS,
SITE_NAME,
SOURCE,
SYSTEM,
SYSTEM_NAME,
SQUELCH,
TO_TALKGROUP,
TO_TALKGROUP_ALIAS,
TO_TALKGROUP_TYPE,
WACN;
}

View File

@@ -1,317 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.state;
import audio.AudioPanel;
import controller.channel.Channel;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEvent.Event;
import controller.channel.ChannelEventListener;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import icon.IconManager;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting.ColorSettingName;
import settings.ColorSettingMenuItem;
import settings.ColorSettingResetMenuItem;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Gui wrapper for the list of currently processing primary channels
*/
public class ChannelList extends JPanel implements ChannelEventListener
{
private static final long serialVersionUID = 1L;
private Map<Channel, ChannelPanel> mDisplayedPanels = new ConcurrentHashMap<>();
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private IconManager mIconManager;
private SettingsManager mSettingsManager;
private AudioPanel mAudioPanel;
public ChannelList(ChannelModel channelModel,
ChannelProcessingManager channelProcessingManager,
IconManager iconManager,
SettingsManager settingsManager,
AudioPanel audioPanel)
{
mChannelModel = channelModel;
mChannelProcessingManager = channelProcessingManager;
mIconManager = iconManager;
mSettingsManager = settingsManager;
mAudioPanel = audioPanel;
init();
}
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0,wrap 1", "[grow,fill]", "[]0[]"));
setBackground(Color.BLACK);
addMouseListener(new ListSelectionListener());
add(mAudioPanel);
JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
add(separator);
}
@Override
public void channelChanged(ChannelEvent event)
{
switch(event.getEvent())
{
case NOTIFICATION_PROCESSING_START:
addChannelPanel(event.getChannel());
break;
case NOTIFICATION_PROCESSING_STOP:
removeChannelPanel(event.getChannel());
break;
default:
break;
}
}
private void addChannelPanel(Channel channel)
{
if(!mDisplayedPanels.containsKey(channel))
{
ChannelPanel channelPanel = new ChannelPanel(mChannelModel,
mChannelProcessingManager, mIconManager, mSettingsManager, channel);
add(channelPanel);
mDisplayedPanels.put(channel, channelPanel);
revalidate();
repaint();
}
}
public void removeChannelPanel(Channel channel)
{
if(mDisplayedPanels.containsKey(channel))
{
ChannelPanel panel = mDisplayedPanels.remove(channel);
if(panel != null)
{
remove(panel);
panel.dispose();
}
revalidate();
repaint();
}
}
public class ListSelectionListener implements MouseListener
{
@Override
public void mouseClicked(MouseEvent e)
{
Component component = getComponentAt(e.getPoint());
if(component instanceof ChannelPanel)
{
ChannelPanel panel = (ChannelPanel) component;
Channel channel = panel.getChannel();
if(e.getButton() == MouseEvent.BUTTON1)
{
if(channel.isSelected())
{
mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_DESELECT));
}
else
{
mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_SELECT));
}
}
else if(e.getButton() == MouseEvent.BUTTON3)
{
JPopupMenu popupMenu = new JPopupMenu();
JMenu menu = panel.getContextMenu();
if(menu != null)
{
popupMenu.add(menu);
}
JMenu colorMenu = new JMenu("Color");
popupMenu.add(colorMenu);
JMenu backgroundMenu = new JMenu("Background");
colorMenu.add(backgroundMenu);
backgroundMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_BACKGROUND));
backgroundMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_BACKGROUND));
JMenu channelsMenu = new JMenu("Channel State");
colorMenu.add(channelsMenu);
JMenu callMenu = new JMenu("Call");
channelsMenu.add(callMenu);
callMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL));
callMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL));
callMenu.add(new JSeparator());
callMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL));
callMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL));
JMenu controlMenu = new JMenu("Control");
channelsMenu.add(controlMenu);
controlMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL));
controlMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL));
controlMenu.add(new JSeparator());
controlMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL));
controlMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL));
JMenu dataMenu = new JMenu("Data");
channelsMenu.add(dataMenu);
dataMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA));
dataMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA));
dataMenu.add(new JSeparator());
dataMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA));
dataMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA));
JMenu fadeMenu = new JMenu("Fade");
channelsMenu.add(fadeMenu);
fadeMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE));
fadeMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE));
fadeMenu.add(new JSeparator());
fadeMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE));
fadeMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE));
JMenu idleMenu = new JMenu("Idle");
channelsMenu.add(idleMenu);
idleMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE));
idleMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE));
idleMenu.add(new JSeparator());
idleMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE));
idleMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE));
JMenu noTunerMenu = new JMenu("No Tuner");
channelsMenu.add(noTunerMenu);
noTunerMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER));
noTunerMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER));
noTunerMenu.add(new JSeparator());
noTunerMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER));
noTunerMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER));
JMenu labelMenu = new JMenu("Labels");
colorMenu.add(labelMenu);
labelMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER));
labelMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER));
labelMenu.add(new JSeparator());
labelMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DECODER));
labelMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DECODER));
labelMenu.add(new JSeparator());
labelMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS));
labelMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS));
JMenu selectedMenu = new JMenu("Selected");
colorMenu.add(selectedMenu);
selectedMenu.add(new ColorSettingMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL));
selectedMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL));
popupMenu.show(ChannelList.this, e.getX(), e.getY());
}
}
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
}
}

View File

@@ -1,465 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.state;
import controller.channel.Channel;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEvent.Event;
import controller.channel.ChannelEventListener;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.ChannelUtils;
import icon.IconManager;
import module.ProcessingChain;
import module.decode.DecoderFactory;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import record.RecorderType;
import record.config.RecordConfiguration;
import sample.Listener;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class ChannelPanel extends JPanel
implements Listener<ChangedAttribute>,
SettingChangeListener,
ChannelEventListener
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(ChannelPanel.class);
private Font mFontDetails = new Font(Font.MONOSPACED, Font.PLAIN, 12);
private Font mFontDecoder = new Font(Font.MONOSPACED, Font.PLAIN, 12);
protected Color mColorChannelBackground;
protected Color mColorChannelSelected;
protected Color mColorTopCall;
protected Color mColorMiddleCall;
protected Color mColorTopControl;
protected Color mColorMiddleControl;
protected Color mColorTopData;
protected Color mColorMiddleData;
protected Color mColorTopFade;
protected Color mColorMiddleFade;
protected Color mColorTopIdle;
protected Color mColorMiddleIdle;
protected Color mColorTopNoTuner;
protected Color mColorMiddleNoTuner;
protected Color mColorLabelDetails;
protected Color mColorLabelDecoder;
protected Color mColorLabelAuxDecoder;
private JLabel mStateLabel;
private JLabel mSourceLabel;
private JLabel mOptionsLabel;
private JLabel mSystemLabel;
private JLabel mSiteLabel;
private JLabel mChannelLabel;
protected SettingsManager mSettingsManager;
protected IconManager mIconManager;
private Channel mChannel;
private ChannelState mChannelState;
private List<DecoderPanel> mDecoderPanels = new ArrayList<>();
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
/**
* Gui component to a channel state. Provides System, Site and channel
* name, source and channel state visualization.
*
* Updates color background according to current channel state.
*/
public ChannelPanel(ChannelModel channelModel,
ChannelProcessingManager channelProcessingManager,
IconManager iconManager,
SettingsManager settingsManager,
Channel channel)
{
mChannelModel = channelModel;
mChannelProcessingManager = channelProcessingManager;
mIconManager = iconManager;
mSettingsManager = settingsManager;
mChannel = channel;
ProcessingChain processingChain =
channelProcessingManager.getProcessingChain(mChannel);
if(processingChain != null)
{
mChannelState = processingChain.getChannelState();
init();
for(DecoderState decoderState : processingChain.getDecoderStates())
{
DecoderPanel panel = DecoderFactory.getDecoderPanel(mIconManager, mSettingsManager, decoderState);
mDecoderPanels.add(panel);
add(panel, "grow,span");
}
mChannelState.setChangedAttributeListener(this);
mChannelModel.addListener(this);
mSettingsManager.addListener(this);
}
else
{
mLog.error("Processing Chain was null!");
}
}
private void init()
{
getColors();
setLayout(new MigLayout("insets 1 1 1 1", "[grow,fill]", "[]0[]0[]"));
mSystemLabel = new JLabel(mChannel.getSystem());
mSystemLabel.setFont(mFontDetails);
mSystemLabel.setForeground(mColorLabelDetails);
add(mSystemLabel);
mSiteLabel = new JLabel(mChannel.getSite());
mSiteLabel.setFont(mFontDetails);
mSiteLabel.setForeground(mColorLabelDetails);
add(mSiteLabel);
mChannelLabel = new JLabel(mChannel.getName());
mChannelLabel.setFont(mFontDetails);
mChannelLabel.setForeground(mColorLabelDetails);
add(mChannelLabel, "wrap");
mStateLabel = new JLabel(mChannelState.getState().getDisplayValue());
mStateLabel.setFont(mFontDecoder);
mStateLabel.setForeground(mColorLabelDecoder);
add(mStateLabel);
mSourceLabel = new JLabel(mChannel.getSourceConfiguration().getDescription());
mSourceLabel.setFont(mFontDetails);
mSourceLabel.setForeground(mColorLabelDetails);
add(mSourceLabel);
StringBuilder sb = new StringBuilder();
if(!mChannel.getEventLogConfiguration().getLoggers().isEmpty())
{
sb.append("LOG ");
}
RecordConfiguration recordConfig = mChannel.getRecordConfiguration();
if(recordConfig.getRecorders().contains(RecorderType.AUDIO))
{
sb.append("AUDIO ");
}
if(recordConfig.getRecorders().contains(RecorderType.BASEBAND) ||
recordConfig.getRecorders().contains(RecorderType.TRAFFIC_BASEBAND))
{
sb.append("BASEBAND");
}
mOptionsLabel = new JLabel(sb.toString());
mOptionsLabel.setFont(mFontDetails);
mOptionsLabel.setForeground(mColorLabelAuxDecoder);
add(mOptionsLabel, "wrap");
}
public Channel getChannel()
{
return mChannel;
}
public void dispose()
{
mChannelModel.removeListener(this);
mChannelModel = null;
mChannelProcessingManager = null;
mSettingsManager.removeListener(this);
mSettingsManager = null;
mChannelState.removeChangedAttributeListener();
mChannelState = null;
mChannel = null;
for(DecoderPanel panel : mDecoderPanels)
{
panel.dispose();
remove(panel);
}
mDecoderPanels.clear();
}
@Override
public void receive(final ChangedAttribute changedAttribute)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
switch(changedAttribute)
{
case CHANNEL_STATE:
if(mChannelState != null && mChannelState.getState() != null)
{
mStateLabel.setText(mChannelState.getState().getDisplayValue());
}
break;
case SOURCE:
mSourceLabel.setText(mChannel.getSourceConfiguration()
.getDescription());
break;
case CHANNEL_NAME:
case SITE_NAME:
case SYSTEM_NAME:
mChannelLabel.setText(mChannel.toString());
break;
default:
break;
}
repaint();
}
});
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
setBackground(mColorChannelBackground);
Graphics2D g2 = (Graphics2D) g.create();
Paint p = null;
final Channel channel = mChannel;
if(mChannelState != null)
{
switch(mChannelState.getState())
{
case CALL:
p = getGradient(mColorTopCall, mColorMiddleCall);
break;
case CONTROL:
p = getGradient(mColorTopControl, mColorMiddleControl);
break;
case DATA:
p = getGradient(mColorTopData, mColorMiddleData);
break;
case FADE:
p = getGradient(mColorTopFade, mColorMiddleFade);
break;
case IDLE:
default:
p = getGradient(mColorTopIdle, mColorMiddleIdle);
break;
}
}
if(p == null)
{
p = getGradient(mColorTopIdle, mColorMiddleIdle);
}
g2.setPaint(p);
g2.fillRect(0, 0, getWidth(), getHeight());
/* Draw bottom separator line */
g2.setColor(Color.LIGHT_GRAY);
g2.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1);
/* Draw channel selected box */
if(getChannel() != null && getChannel().isSelected())
{
g2.setColor(mColorChannelSelected);
g2.drawRect(1, 1, getWidth() - 2, getHeight() - 2);
}
g2.dispose();
}
private void getColors()
{
mColorChannelBackground = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_BACKGROUND).getColor();
mColorChannelSelected = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL).getColor();
mColorTopCall = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL).getColor();
mColorMiddleCall = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL).getColor();
mColorTopControl = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL).getColor();
mColorMiddleControl = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL).getColor();
mColorTopData = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA).getColor();
mColorMiddleData = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA).getColor();
mColorTopFade = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE).getColor();
mColorMiddleFade = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE).getColor();
mColorTopIdle = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE).getColor();
mColorMiddleIdle = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE).getColor();
mColorTopNoTuner = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER).getColor();
mColorMiddleNoTuner = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER).getColor();
mColorLabelDetails = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS).getColor();
mColorLabelDecoder = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DECODER).getColor();
mColorLabelAuxDecoder = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER).getColor();
}
private GradientPaint getGradient(Color top, Color middle)
{
return new GradientPaint(0f, -50.0f, top,
0f, (float) getHeight() / 2.2f, middle, true);
}
@Override
public void settingChanged(Setting setting)
{
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_BACKGROUND:
mColorChannelBackground = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_CALL:
mColorMiddleCall = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_CALL:
mColorTopCall = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL:
mColorMiddleControl = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_CONTROL:
mColorTopControl = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_DATA:
mColorMiddleData = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_DATA:
mColorTopData = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_FADE:
mColorMiddleFade = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_FADE:
mColorTopFade = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE:
mColorMiddleIdle = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_IDLE:
mColorTopIdle = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER:
mColorMiddleNoTuner = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER:
mColorTopNoTuner = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_AUX_DECODER:
mColorLabelAuxDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DECODER:
mColorLabelDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DETAILS:
mColorLabelDetails = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_SELECTED_CHANNEL:
mColorChannelSelected = colorSetting.getColor();
repaint();
break;
default:
break;
}
}
}
@Override
public void settingDeleted(Setting setting)
{
}
public JMenu getContextMenu()
{
return ChannelUtils.getContextMenu(mChannelModel, mChannelProcessingManager,
mChannel, ChannelPanel.this);
}
@Override
public void channelChanged(ChannelEvent event)
{
if(event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE &&
event.getChannel() == mChannel)
{
repaint();
}
}
}

View File

@@ -1,676 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package module.decode.state;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import module.Module;
import module.decode.config.DecodeConfiguration;
import module.decode.event.CallEvent;
import module.decode.event.ICallEventProvider;
import module.decode.state.DecoderStateEvent.Event;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import audio.metadata.IMetadataProvider;
import audio.metadata.Metadata;
import audio.metadata.MetadataReset;
import audio.metadata.MetadataType;
import audio.squelch.ISquelchStateProvider;
import audio.squelch.SquelchState;
import controller.channel.Channel.ChannelType;
public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider,
IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider
{
private final static Logger mLog =
LoggerFactory.getLogger( ChannelState.class );
public static final long FADE_TIMEOUT_DELAY = 1200;
public static final long RESET_TIMEOUT_DELAY = 2000;
private State mState = State.IDLE;
private Listener<CallEvent> mCallEventListener;
private Listener<ChangedAttribute> mChangedAttributeListener;
private Listener<DecoderStateEvent> mDecoderStateListener;
private Listener<Metadata> mMetadataListener;
private Listener<SquelchState> mSquelchStateListener;
private DecoderStateEventReceiver mDecoderStateEventReceiver =
new DecoderStateEventReceiver();
private boolean mSquelchLocked = false;
private boolean mSelected = false;
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
private long mFadeTimeout;
private long mEndTimeout;
private StateMonitor mStateMonitor = new StateMonitor();
private ScheduledFuture<?> mStateMonitorFuture;
private ChannelType mChannelType;
private TrafficChannelManager mTrafficChannelEndListener;
private CallEvent mTrafficChannelCallEvent;
/**
* Channel state tracks the overall state of all processing modules and decoders
* configured for the channel and provides squelch control and decoder state
* reset events.
*
* Uses a state enumeration that defines allowable channel state transitions in
* order to track a call or data decode event from start to finish. Uses a
* timer to monitor for inactivity and to provide a FADE period that indicates
* to the user that the activity has stopped while continuing to provide details
* about the call, before the state is reset to IDLE.
*
* Since this class is multi-threaded between decoder events and the internal
* timer that monitors for inactivity, this class uses the mState object for
* thread synchronization.
*
* State Descriptions:
* IDLE Normal state. No voice or data call activity
* CALL/DATA/ENCRYPTED/CONTROL
* Decoding states.
* FADE The phase after a voice or data call when either an explicit
* call end has been received, or when no new signalling updates
* have been received, and the fade timer has expired. This phase
* allows for gui updates to signal to the user that the call is
* ended, while continuing to display the call details for the user
* TEARDOWN Indicates a traffic channel that will be torn down for reuse.
*/
public ChannelState( ChannelType channelType )
{
mChannelType = channelType;
}
/**
* Resets this channel state and prepares it for reuse.
*/
@Override
public void reset()
{
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
broadcast( new MetadataReset() );
mState = State.IDLE;
broadcast( ChangedAttribute.CHANNEL_STATE );
}
@Override
public void start( ScheduledExecutorService executor )
{
if( mStateMonitorFuture == null && mStateMonitor != null )
{
if( mTrafficChannelEndListener != null )
{
setState( State.CALL );
}
try
{
mStateMonitorFuture = executor.scheduleAtFixedRate( mStateMonitor, 0, 20, TimeUnit.MILLISECONDS );
}
catch( RejectedExecutionException ree )
{
mLog.error( "state monitor scheduled task rejected", ree );
}
}
}
@Override
public void stop()
{
if( mStateMonitorFuture != null )
{
boolean success = mStateMonitorFuture.cancel( true );
if( !success )
{
mLog.error("Couldn't stop monitoring scheduled future" );
}
}
mTrafficChannelEndListener = null;
if( mTrafficChannelCallEvent != null )
{
mTrafficChannelCallEvent.end();
broadcast( mTrafficChannelCallEvent );
}
mTrafficChannelCallEvent = null;
mStateMonitorFuture = null;
mSquelchLocked = false;
}
public void dispose()
{
mCallEventListener = null;
mChangedAttributeListener = null;
mDecoderStateListener = null;
mSquelchStateListener = null;
mStateMonitor = null;
}
private boolean isStandardChannel()
{
return mChannelType == ChannelType.STANDARD;
}
private boolean isTrafficChannel()
{
return mChannelType == ChannelType.TRAFFIC;
}
public void setStandardChannelTimeout( long milliseconds )
{
mStandardChannelFadeTimeout = milliseconds;
if( mChannelType == ChannelType.STANDARD )
{
mFadeTimeout = mStandardChannelFadeTimeout;
}
}
public void setTrafficChannelTimeout( long milliseconds )
{
mTrafficChannelFadeTimeout = milliseconds;
if( mChannelType == ChannelType.TRAFFIC )
{
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
}
}
public void setSelected( boolean selected )
{
mSelected = selected;
}
public boolean isSelected()
{
return mSelected;
}
public State getState()
{
return mState;
}
/**
* Updates the fade timeout threshold to the current time plus delay
*/
private void updateFadeTimeout()
{
if( isTrafficChannel() )
{
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
}
else
{
mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
}
}
/**
* Updates the reset timeout threshold to the current time plus delay
*/
private void updateResetTimeout()
{
if( isTrafficChannel() )
{
mEndTimeout = System.currentTimeMillis();
}
else
{
mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
}
}
/**
* Broadcasts the squelch state to the registered listener
*/
protected void broadcast( SquelchState state )
{
if( mSquelchStateListener != null && !mSquelchLocked )
{
mSquelchStateListener.receive( state );
}
}
/**
* Sets the squelch state listener
*/
@Override
public void setSquelchStateListener( Listener<SquelchState> listener )
{
mSquelchStateListener = listener;
}
/**
* Removes the squelch state listener
*/
@Override
public void removeSquelchStateListener()
{
mSquelchStateListener = null;
}
/**
* Sets the channel state to the specified state, or updates the timeout
* values so that the state monitor will not change state. Broadcasts a
* squelch event when the state changes and the audio squelch state should
* change. Also broadcasts changed attribute and decoder state events so
* that external processes can maintain sync with this channel state.
*/
protected void setState( State state )
{
synchronized( mState )
{
if( state == mState )
{
if( State.CALL_STATES.contains( state ) )
{
updateFadeTimeout();
}
}
else if( state != mState && mState.canChangeTo( state ) )
{
switch( state )
{
case CONTROL:
//Don't allow traffic channels to be control channels,
//otherwise they can't transition to call tear down
if( isStandardChannel() )
{
broadcast( SquelchState.SQUELCH );
updateFadeTimeout();
mState = state;
broadcast( ChangedAttribute.CHANNEL_STATE );
}
break;
case DATA:
case ENCRYPTED:
broadcast( SquelchState.SQUELCH );
updateFadeTimeout();
mState = state;
broadcast( ChangedAttribute.CHANNEL_STATE );
break;
case CALL:
broadcast( SquelchState.UNSQUELCH );
updateFadeTimeout();
mState = state;
broadcast( ChangedAttribute.CHANNEL_STATE );
break;
case FADE:
processFadeState();
break;
case IDLE:
processIdleState();
break;
case TEARDOWN:
processTeardownState();
break;
case RESET:
mState = State.IDLE;
break;
default:
break;
}
}
else
{
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
}
}
}
/**
* Sets the state and processes related actions
*/
private void processFadeState()
{
updateResetTimeout();
mState = State.FADE;
broadcast( SquelchState.SQUELCH );
broadcast( ChangedAttribute.CHANNEL_STATE );
}
private void processIdleState()
{
broadcast( SquelchState.SQUELCH );
if( mState == State.FADE )
{
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
broadcast( new MetadataReset() );
}
mState = State.IDLE;
broadcast( ChangedAttribute.CHANNEL_STATE );
}
private void processTeardownState()
{
broadcast( SquelchState.SQUELCH );
broadcast( new MetadataReset() );
mState = State.TEARDOWN;
broadcast( ChangedAttribute.CHANNEL_STATE );
if( mTrafficChannelEndListener != null )
{
mTrafficChannelEndListener.callEnd( mTrafficChannelCallEvent.getChannel() );
}
}
/**
* Broadcasts the call event to the registered listener
*/
protected void broadcast( CallEvent event )
{
if( mCallEventListener != null )
{
mCallEventListener.receive( event );
}
}
@Override
public void addCallEventListener( Listener<CallEvent> listener )
{
mCallEventListener = listener;
}
@Override
public void removeCallEventListener( Listener<CallEvent> listener )
{
mCallEventListener = null;
}
/**
* Broadcasts the channel state attribute change event to all registered
* listeners
*/
protected void broadcast( ChangedAttribute attribute )
{
if( mChangedAttributeListener != null )
{
mChangedAttributeListener.receive( attribute );
}
}
/**
* Adds the listener to receive channel state attribute change events
*/
@Override
public void setChangedAttributeListener( Listener<ChangedAttribute> listener )
{
mChangedAttributeListener = listener;
}
/**
* Removes the listener to receive channel state attribute change events
*/
@Override
public void removeChangedAttributeListener()
{
mChangedAttributeListener = null;
}
/**
* Broadcasts a channel state event to any registered listeners
*/
protected void broadcast( DecoderStateEvent event )
{
if( mDecoderStateListener != null )
{
mDecoderStateListener.receive( event );
}
}
/**
* Adds a decoder state event listener
*/
@Override
public void setDecoderStateListener( Listener<DecoderStateEvent> listener )
{
mDecoderStateListener = listener;
}
/**
* Removes the decoder state event listener
*/
@Override
public void removeDecoderStateListener()
{
mDecoderStateListener = null;
}
@Override
public Listener<DecoderStateEvent> getDecoderStateListener()
{
return mDecoderStateEventReceiver;
}
/**
* Broadcasts metadata to the registered listener
*/
protected void broadcast( Metadata metadata )
{
if( mMetadataListener != null )
{
mMetadataListener.receive( metadata );
}
}
/**
* Adds a metadata listener
*/
@Override
public void setMetadataListener( Listener<Metadata> listener )
{
mMetadataListener = listener;
}
/**
* Removes the decoder state event listener
*/
@Override
public void removeMetadataListener()
{
mMetadataListener = null;
}
/**
* Registers a listener to be notified when a traffic channel call event is
* completed, so that the listener can perform call tear-down
*/
public void configureAsTrafficChannel( TrafficChannelManager manager, CallEvent callEvent )
{
mTrafficChannelEndListener = manager;
mTrafficChannelCallEvent = callEvent;
/* Broadcast the call event details as metadata for the audio manager */
String channel = mTrafficChannelCallEvent.getChannel();
if( channel != null )
{
broadcast( new Metadata( MetadataType.CHANNEL_NUMBER, channel, true ) );
}
broadcast( new Metadata( MetadataType.PROTOCOL,
mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) );
String details = mTrafficChannelCallEvent.getDetails();
if( details != null )
{
broadcast( new Metadata( MetadataType.DETAILS, details, true ) );
}
String from = mTrafficChannelCallEvent.getFromID();
if( from != null )
{
broadcast( new Metadata( mTrafficChannelCallEvent.getFromIDType(),
from, mTrafficChannelCallEvent.getFromIDAlias(), true ) );
}
String to = mTrafficChannelCallEvent.getToID();
if( to != null )
{
broadcast( new Metadata( mTrafficChannelCallEvent.getToIDType(), to,
mTrafficChannelCallEvent.getToIDAlias(), true ) );
}
long frequency = mTrafficChannelCallEvent.getFrequency();
if( frequency > 0 )
{
broadcast( new Metadata( MetadataType.FREQUENCY, String.valueOf( frequency ), true ) );
}
/* Rebroadcast the allocation event so that the internal decoder states
* can self-configure with the call event details */
broadcast( mTrafficChannelCallEvent );
}
/**
* DecoderStateEvent receiver wrapper
*/
public class DecoderStateEventReceiver implements Listener<DecoderStateEvent>
{
@Override
public void receive( DecoderStateEvent event )
{
if( event.getSource() != this )
{
switch( event.getEvent() )
{
case ALWAYS_UNSQUELCH:
broadcast( SquelchState.UNSQUELCH );
mSquelchLocked = true;
break;
case CHANGE_CALL_TIMEOUT:
if( event instanceof ChangeChannelTimeoutEvent )
{
ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event;
if( timeout.getChannelType() == ChannelType.STANDARD )
{
setStandardChannelTimeout( timeout.getCallTimeout() );
}
else
{
setTrafficChannelTimeout( timeout.getCallTimeout() );
}
}
case CONTINUATION:
case DECODE:
case START:
if( State.CALL_STATES.contains( event.getState() ) )
{
setState( event.getState() );
}
break;
case END:
if( isTrafficChannel() )
{
setState( State.TEARDOWN );
}
else
{
setState( State.FADE );
}
break;
case RESET:
/* Channel State does not respond to reset events */
break;
default:
break;
}
}
}
}
/**
* Monitors decoder state events to automatically transition the channel
* state to IDLE (standard channel) or to TEARDOWN (traffic channel) when
* decoding stops or the monitored channel returns to a no signal state.
*
* Provides a FADE transition state to allow for momentary decoding dropouts
* and to allow the user access to call details for a fade period upon
* call end.
*/
public class StateMonitor implements Runnable
{
@Override
public void run()
{
try
{
synchronized( mState )
{
if( State.CALL_STATES.contains( mState ) &&
mFadeTimeout <= System.currentTimeMillis() )
{
processFadeState();
}
else if( mState == State.FADE &&
mEndTimeout <= System.currentTimeMillis() )
{
if( isTrafficChannel() )
{
processTeardownState();
}
else
{
processIdleState();
}
}
}
}
catch( Exception e )
{
mLog.error( "Exception thrown while state monitor is running " +
"- state [" + getState() +
"] current [" + System.currentTimeMillis() +
"] mResetTimeout [" + mEndTimeout +
"] mFadeTimeout [" + mFadeTimeout +
"]", e );
}
}
}
}

View File

@@ -1,162 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.state;
import alias.Alias;
import icon.IconManager;
import sample.Listener;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import javax.swing.*;
import java.awt.*;
public abstract class DecoderPanel extends JPanel
implements Listener<ChangedAttribute>, SettingChangeListener
{
private static final long serialVersionUID = 1L;
protected Font mFontDetails = new Font(Font.MONOSPACED, Font.PLAIN, 12);
protected Font mFontDecoder = new Font(Font.MONOSPACED, Font.PLAIN, 12);
protected Font mFontAuxDecoder = new Font(Font.MONOSPACED, Font.PLAIN, 10);
public static final int ICON_SIZE = 12;
protected Color mColorLabelDetails;
protected Color mColorLabelDecoder;
protected Color mColorLabelAuxDecoder;
protected IconManager mIconManager;
protected SettingsManager mSettingsManager;
protected DecoderState mDecoderState;
public DecoderPanel(IconManager iconManager, SettingsManager settingsManager, DecoderState decoderState)
{
mIconManager = iconManager;
mSettingsManager = settingsManager;
mSettingsManager.addListener(this);
mDecoderState = decoderState;
mDecoderState.setChangedAttributeListener(this);
}
public void dispose()
{
mSettingsManager.removeListener(this);
mSettingsManager = null;
mDecoderState.removeChangedAttributeListener();
mDecoderState = null;
}
public DecoderState getDecoderState()
{
return mDecoderState;
}
protected void init()
{
getColors();
setOpaque(false); //Use the parent panel's background color
}
private void getColors()
{
mColorLabelDetails = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS).getColor();
mColorLabelDecoder = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DECODER).getColor();
mColorLabelAuxDecoder = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER).getColor();
}
@Override
public void settingChanged(Setting setting)
{
if(setting instanceof ColorSetting)
{
ColorSetting colorSetting = (ColorSetting) setting;
switch(colorSetting.getColorSettingName())
{
case CHANNEL_STATE_LABEL_AUX_DECODER:
mColorLabelAuxDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DECODER:
mColorLabelDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DETAILS:
mColorLabelDetails = colorSetting.getColor();
repaint();
break;
default:
break;
}
}
}
@Override
public void settingDeleted(Setting setting)
{
}
protected void setAliasLabel(final JLabel label, final Alias alias)
{
setAliasLabel(label, alias, true);
}
protected void setAliasLabel(final JLabel label, final Alias alias, boolean includeIcon)
{
if(alias != null)
{
label.setText(alias.getName());
if(includeIcon)
{
String iconName = alias.getIconName();
if(iconName != null)
{
label.setIcon(mIconManager.getIcon(iconName, IconManager.DEFAULT_ICON_SIZE));
}
else
{
label.setIcon(null);
}
}
else
{
label.setIcon(null);
}
}
else
{
label.setText("");
label.setIcon(null);
}
}
}

View File

@@ -1,227 +0,0 @@
package module.decode.state;
import message.IMessageListener;
import message.Message;
import module.Module;
import module.decode.DecoderType;
import module.decode.event.ActivitySummaryProvider;
import module.decode.event.CallEvent;
import module.decode.event.ICallEventProvider;
import sample.Broadcaster;
import sample.Listener;
import alias.AliasList;
import audio.metadata.IMetadataProvider;
import audio.metadata.Metadata;
/**
* Channel state monitors the stream of decoded messages produced by the
* decoder and broadcasts call events as they occur within the decoded message activity.
*
* Provides access to a textual activity summary of events observed.
*/
public abstract class DecoderState extends Module
implements ActivitySummaryProvider,
Listener<Message>,
ICallEventProvider,
IChangedAttributeProvider,
IDecoderStateEventListener,
IDecoderStateEventProvider,
IMessageListener,
IMetadataProvider
{
/* This has to be a broadcaster in order for references to persist */
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
private Listener<ChangedAttribute> mChangedAttributeListener;
private Listener<DecoderStateEvent> mDecoderStateListener;
private Listener<Metadata> mMetadataListener;
private DecoderStateEventListener mDecoderStateEventListener =
new DecoderStateEventListener();
protected CallEvent mCurrentCallEvent;
private AliasList mAliasList;
public DecoderState( AliasList aliasList )
{
mAliasList = aliasList;
}
public abstract DecoderType getDecoderType();
public AliasList getAliasList()
{
return mAliasList;
}
public boolean hasAliasList()
{
return mAliasList != null;
}
/**
* Reset the decoder state to prepare for processing a different sample
* source
*/
public abstract void reset();
/**
* Allow the decoder to perform any setup actions
*/
public abstract void init();
/**
* Implements the IDecoderStateEventListener interface to receive state
* reset events.
*/
public abstract void receiveDecoderStateEvent( DecoderStateEvent event );
/**
* Disposes any resources or pointers held by this instance to prepare for
* garbage collection
*/
public void dispose()
{
mCallEventBroadcaster.dispose();
mCallEventBroadcaster = null;
mChangedAttributeListener = null;
mDecoderStateListener = null;
mMetadataListener = null;
mAliasList = null;
}
/**
* Activity Summary - textual summary of activity observed by the channel state.
*/
public abstract String getActivitySummary();
/**
* Broadcasts a call event to any registered listeners
*/
protected void broadcast( CallEvent event )
{
mCallEventBroadcaster.broadcast( event );
}
/**
* Adds a call event listener
*/
@Override
public void addCallEventListener( Listener<CallEvent> listener )
{
mCallEventBroadcaster.addListener( listener );
}
/**
* Removes the call event listener
*/
@Override
public void removeCallEventListener( Listener<CallEvent> listener )
{
mCallEventBroadcaster.removeListener( listener );
}
@Override
public Listener<DecoderStateEvent> getDecoderStateListener()
{
return mDecoderStateEventListener;
}
private class DecoderStateEventListener implements Listener<DecoderStateEvent>
{
@Override
public void receive( DecoderStateEvent event )
{
receiveDecoderStateEvent( event );
}
}
/**
* Broadcasts the channel state attribute change event to all registered
* listeners
*/
protected void broadcast( ChangedAttribute attribute )
{
if( mChangedAttributeListener != null )
{
mChangedAttributeListener.receive( attribute );
}
}
/**
* Adds the listener to receive channel state attribute change events
*/
@Override
public void setChangedAttributeListener( Listener<ChangedAttribute> listener )
{
mChangedAttributeListener = listener;
}
/**
* Removes the listener to receive channel state attribute change events
*/
@Override
public void removeChangedAttributeListener()
{
mChangedAttributeListener = null;
}
/**
* Broadcasts a channel state event to any registered listeners
*/
protected void broadcast( DecoderStateEvent event )
{
if( mDecoderStateListener != null )
{
mDecoderStateListener.receive( event );
}
}
/**
* Adds a decoder state event listener
*/
@Override
public void setDecoderStateListener( Listener<DecoderStateEvent> listener )
{
mDecoderStateListener = listener;
}
/**
* Removes the decoder state event listener
*/
@Override
public void removeDecoderStateListener()
{
mDecoderStateListener = null;
}
/**
* Broadcasts metadata to a registered listener
*/
protected void broadcast( Metadata metadata )
{
if( mMetadataListener != null )
{
mMetadataListener.receive( metadata );
}
}
@Override
public void setMetadataListener( Listener<Metadata> listener )
{
mMetadataListener = listener;
}
@Override
public void removeMetadataListener()
{
mMetadataListener = null;
}
@Override
public Listener<Message> getMessageListener()
{
return this;
}
}

Some files were not shown because too many files have changed in this diff Show More