#90 [WIP] adds AttributeMonitor and AliasedStringAttributeMonitor for managing the metadata attributes and issuing attribute change requests, so that we can remove attribute management code out of the decoder states. Adds heuristic tracking with the monitor so that we can better track the talkgroups and other attributes and smooth out the rapidly changing values when we're decoding a weak signal. This will eventually close out issue #101

This commit is contained in:
Denny
2017-01-18 20:34:37 -05:00
parent acabab35ff
commit 5f179c4863
6 changed files with 423 additions and 257 deletions

View File

@@ -0,0 +1,114 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.metadata;
import alias.Alias;
import alias.AliasList;
import alias.id.AliasIDType;
import sample.Listener;
import static alias.id.AliasIDType.*;
public class AliasedStringAttributeMonitor extends AttributeMonitor<String>
{
private static final AliasIDType[] VALID_ID_TYPES = {ESN, FLEETSYNC, MDC1200, MIN, MPT1327, SITE, TALKGROUP};
private AliasList mAliasList;
private AliasIDType mAliasIDType;
/**
* Provides monitoring of a String attribute's values with occurrence heuristics enabled and alias lookup with each
* change in the attribute's value.
*
* @param attribute to monitor
* @param listener to receive change requests
* @param aliasList to lookup alias from
* @param aliasIDType to determine correct alias lookup method in alias list
*/
public AliasedStringAttributeMonitor(Attribute attribute, Listener<AttributeChangeRequest> listener, AliasList aliasList,
AliasIDType aliasIDType)
{
super(attribute, listener);
if(!isValidIDType(aliasIDType))
{
throw new IllegalArgumentException("Alias ID Type [" + aliasIDType.name() + "] is not supported");
}
mAliasList = aliasList;
mAliasIDType = aliasIDType;
}
/**
* Indicates if the alias ID type is a supported/valid type for this monitor.
*/
public static boolean isValidIDType(AliasIDType type)
{
for(AliasIDType validType: VALID_ID_TYPES)
{
if(type == validType)
{
return true;
}
}
return false;
}
/**
* Lookup of the alias that corresponds to the value's alias ID type
*/
@Override
protected Alias getAlias()
{
Alias alias = null;
if(mAliasList != null)
{
if(hasValue())
{
switch(mAliasIDType)
{
case ESN:
alias = mAliasList.getESNAlias(getValue());
break;
case FLEETSYNC:
alias = mAliasList.getFleetsyncAlias(getValue());
break;
case MDC1200:
alias = mAliasList.getMDC1200Alias(getValue());
break;
case MIN:
alias = mAliasList.getMobileIDNumberAlias(getValue());
break;
case MPT1327:
alias = mAliasList.getMPT1327Alias(getValue());
break;
case SITE:
alias = mAliasList.getSiteID(getValue());
break;
case TALKGROUP:
alias = mAliasList.getTalkgroupAlias(getValue());
break;
}
}
}
return alias;
}
}

View File

@@ -0,0 +1,185 @@
/*******************************************************************************
* 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
*/
public void process(T t)
{
if(t != null && !mIllegalValues.contains(t))
{
boolean changed = false;
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()));
}
}
}
/**
* Alias for the current value. This class does not return an alias, but subclasses can return an alias that
* is appropriate for the monitored attribute value.
*
* @return an optional alias for the current value
*/
protected Alias getAlias()
{
return null;
}
/**
* Resets the attribute value to null and resets occurrence heuristics.
*/
public void reset()
{
mCurrentValue = null;
mOccurrenceCounts.clear();
}
/**
* Adds a value to the internal list of illegal values. The monitor will not allow the attribute to be changed to
* any values contained in this list.
*
* @param illegalValue to prevent for the monitored alias
*/
public void addIllegalValue(T illegalValue)
{
mIllegalValues.add(illegalValue);
}
/**
* Returns the current value or null if the monitor is newly started or reset.
*/
public T getValue()
{
return mCurrentValue;
}
/**
* Indicates if the current value is non-null
*/
public boolean hasValue()
{
return mCurrentValue != null;
}
public static void main(String[] args)
{
AttributeMonitor<String> monitor = new AttributeMonitor<>(Attribute.PRIMARY_ADDRESS_TO, new Listener<AttributeChangeRequest>()
{
@Override
public void receive(AttributeChangeRequest attributeChangeRequest)
{
mLog.debug("Attribute Changed - value:" + attributeChangeRequest.getValue());
}
});
monitor.addIllegalValue("0000");
monitor.addIllegalValue("000000");
monitor.process("ABCD");
monitor.process("EFGH");
monitor.process("0000");
monitor.process("0000");
monitor.process("ABCD");
monitor.reset();
monitor.process("EFGH");
monitor.process("ABCD");
monitor.process("ABCD");
monitor.process("EFGH");
monitor.process("EFGH");
monitor.process("000000");
monitor.process("000000");
monitor.process("000000");
monitor.process("000000");
}
}

View File

@@ -24,5 +24,5 @@ public interface IAttributeChangeRequestProvider
{
public void setAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener);
public void removeAttributeChangeRequestListener();
public void removeAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener);
}

View File

@@ -25,7 +25,7 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
{
/* This has to be a broadcaster in order for references to persist */
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
private Listener<AttributeChangeRequest> mAttributeChangeRequestListener;
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
private Listener<DecoderStateEvent> mDecoderStateListener;
private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener();
@@ -167,10 +167,7 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
*/
protected void broadcast(AttributeChangeRequest<?> request)
{
if(mAttributeChangeRequestListener != null)
{
mAttributeChangeRequestListener.receive(request);
}
mAttributeChangeRequestBroadcaster.broadcast(request);
}
/**
@@ -179,16 +176,23 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
@Override
public void setAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener)
{
mAttributeChangeRequestListener = listener;
mAttributeChangeRequestBroadcaster.addListener(listener);
}
/**
* Removes any listener from receiving attribute change requests
*/
@Override
public void removeAttributeChangeRequestListener()
public void removeAttributeChangeRequestListener(Listener<AttributeChangeRequest> listener)
{
mAttributeChangeRequestListener = null;
mAttributeChangeRequestBroadcaster.removeListener(listener);
}
/**
* Broadaster so that Attribute monitors can broadcast attribute change requests
*/
protected Listener<AttributeChangeRequest> getAttributeChangeRequestListener()
{
return mAttributeChangeRequestBroadcaster;
}
}