mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
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:
1
.idea/dictionaries/denny.xml
generated
1
.idea/dictionaries/denny.xml
generated
@@ -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>
|
||||
|
||||
@@ -117,9 +117,9 @@ public class Alias
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
public Color getMapColor()
|
||||
public Color getDisplayColor()
|
||||
{
|
||||
return new Color( mColor );
|
||||
return new Color( getColor() );
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
125
src/audio/broadcast/BroadcastStatusPanel.java
Normal file
125
src/audio/broadcast/BroadcastStatusPanel.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package audio.metadata;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public interface IMetadataListener
|
||||
{
|
||||
public Listener<Metadata> getMetadataListener();
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package audio.metadata;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public interface IMetadataProvider
|
||||
{
|
||||
public void setMetadataListener( Listener<Metadata> listener );
|
||||
public void removeMetadataListener();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
src/channel/metadata/AliasedIdentifier.java
Normal file
111
src/channel/metadata/AliasedIdentifier.java
Normal 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;
|
||||
}
|
||||
}
|
||||
99
src/channel/metadata/AliasedIntegerAttributeMonitor.java
Normal file
99
src/channel/metadata/AliasedIntegerAttributeMonitor.java
Normal 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;
|
||||
}
|
||||
}
|
||||
114
src/channel/metadata/AliasedStringAttributeMonitor.java
Normal file
114
src/channel/metadata/AliasedStringAttributeMonitor.java
Normal 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;
|
||||
}
|
||||
}
|
||||
38
src/channel/metadata/Attribute.java
Normal file
38
src/channel/metadata/Attribute.java
Normal 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,
|
||||
}
|
||||
131
src/channel/metadata/AttributeChangeRequest.java
Normal file
131
src/channel/metadata/AttributeChangeRequest.java
Normal 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;
|
||||
}
|
||||
}
|
||||
189
src/channel/metadata/AttributeMonitor.java
Normal file
189
src/channel/metadata/AttributeMonitor.java
Normal 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");
|
||||
}
|
||||
}
|
||||
275
src/channel/metadata/ChannelMetadataModel.java
Normal file
275
src/channel/metadata/ChannelMetadataModel.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
227
src/channel/metadata/ChannelMetadataPanel.java
Normal file
227
src/channel/metadata/ChannelMetadataPanel.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/channel/metadata/ChannelMetadataViewer.java
Normal file
77
src/channel/metadata/ChannelMetadataViewer.java
Normal 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);
|
||||
}
|
||||
}
|
||||
26
src/channel/metadata/IAttributeChangeRequestListener.java
Normal file
26
src/channel/metadata/IAttributeChangeRequestListener.java
Normal 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();
|
||||
}
|
||||
28
src/channel/metadata/IAttributeChangeRequestProvider.java
Normal file
28
src/channel/metadata/IAttributeChangeRequestProvider.java
Normal 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);
|
||||
}
|
||||
662
src/channel/metadata/Metadata.java
Normal file
662
src/channel/metadata/Metadata.java
Normal 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;
|
||||
}
|
||||
}
|
||||
287
src/channel/metadata/MutableMetadata.java
Normal file
287
src/channel/metadata/MutableMetadata.java
Normal 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);
|
||||
}
|
||||
}
|
||||
53
src/channel/metadata/MutableMetadataChangeEvent.java
Normal file
53
src/channel/metadata/MutableMetadataChangeEvent.java
Normal 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;
|
||||
}
|
||||
}
|
||||
107
src/channel/state/AlwaysUnsquelchedDecoderState.java
Normal file
107
src/channel/state/AlwaysUnsquelchedDecoderState.java
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package module.decode.state;
|
||||
package channel.state;
|
||||
|
||||
import controller.channel.Channel.ChannelType;
|
||||
|
||||
627
src/channel/state/ChannelState.java
Normal file
627
src/channel/state/ChannelState.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
198
src/channel/state/DecoderState.java
Normal file
198
src/channel/state/DecoderState.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package module.decode.state;
|
||||
package channel.state;
|
||||
|
||||
public class DecoderStateEvent
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
package module.decode.state;
|
||||
package channel.state;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package module.decode.state;
|
||||
package channel.state;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
@@ -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;
|
||||
|
||||
45
src/channel/traffic/TrafficChannelAllocationEvent.java
Normal file
45
src/channel/traffic/TrafficChannelAllocationEvent.java
Normal 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;
|
||||
}
|
||||
}
|
||||
447
src/channel/traffic/TrafficChannelManager.java
Normal file
447
src/channel/traffic/TrafficChannelManager.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user