Merge pull request #1601 from DSheirer/1368-decode-event-filtering-2

1368 Enhance Message & Event Filtering Options
This commit is contained in:
Denny Sheirer
2023-07-14 06:51:57 -04:00
committed by GitHub
110 changed files with 5353 additions and 3200 deletions

View File

@@ -35,11 +35,11 @@ import javax.swing.JPanel;
public class NowPlayingPanel extends JPanel
{
private ChannelMetadataPanel mChannelMetadataPanel;
private ChannelDetailPanel mChannelDetailPanel;
private DecodeEventPanel mDecodeEventPanel;
private MessageActivityPanel mMessageActivityPanel;
private ChannelPowerPanel mChannelPowerPanel;
private final ChannelMetadataPanel mChannelMetadataPanel;
private final ChannelDetailPanel mChannelDetailPanel;
private final DecodeEventPanel mDecodeEventPanel;
private final MessageActivityPanel mMessageActivityPanel;
private final ChannelPowerPanel mChannelPowerPanel;
/**
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,

View File

@@ -1,35 +1,61 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
/**
* Implements a message filter that passes (true) all messages
* Implements an all-pass filter that automatically passes/allows any presented object.
*
* A key type of String is used, but that makes no difference in how this filter functions.
*/
@SuppressWarnings( "rawtypes" )
public class AllPassFilter<T> extends Filter<T>
public class AllPassFilter<T> extends Filter<T, String>
{
public AllPassFilter()
private static final String KEY = "Other/Unlisted";
private final AllPassKeyExtractor mKeyExtractor = new AllPassKeyExtractor();
/**
* Constructor
* @param name to display for this filter
*/
public AllPassFilter(String name)
{
super(name);
add(new FilterElement<>(KEY));
}
@Override
public Function getKeyExtractor()
{
super( "Allow All Messages" );
return mKeyExtractor;
}
@Override
public boolean passes( T t )
{
return true;
}
@Override
public boolean canProcess( T t )
{
return true;
}
@Override
@SuppressWarnings( "unchecked" )
public List getFilterElements()
{
return Collections.EMPTY_LIST;
}
/**
* Key extractor that always returns the same (String)key.
*/
public class AllPassKeyExtractor implements Function<T,String>
{
@Override
public String apply(T t)
{
return KEY;
}
}
}

View File

@@ -1,55 +1,206 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class Filter<T> implements IFilter<T>
/**
* Base filter class that uses an extractor function to extract a filter key (K) from an object of type T and then
* lookup the filter element (rule) indicating if objects of this type are enabled.
*
* @param <T> object type that this filter can process.
* @param <K> key type used to lookup filter elements
*/
public abstract class Filter<T,K> implements IFilter<T>
{
protected String mName;
protected boolean mEnabled = true;
public Filter( String name )
{
mName = name;
}
private static final Logger LOG = LoggerFactory.getLogger(Filter.class);
private String mName;
private Map<K, FilterElement<K>> mFilterElementMap = new HashMap<>();
private IFilterChangeListener mFilterChangeListener;
/**
* Indicates if this filter is enabled or disabled
*/
public boolean isEnabled()
{
return mEnabled;
}
/**
* Constructs an instance
* @param name of this filter
*/
public Filter(String name)
{
mName = name;
}
/**
* Enables (true) or disables (false) this filter. An enabled filter will
* evaluate messages and a disabled filter will return false on all message
* test methods.
*/
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
}
/**
* List of filter elements managed by this filter
*/
public abstract List<FilterElement<?>> getFilterElements();
/**
* Indicates if this filter contains a filter element matching the key value.
* @param key to check
* @return true if this filter has a matching filter element.
*/
protected boolean hasKey(K key)
{
return mFilterElementMap.containsKey(key);
}
/**
* Name of this filter
*/
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public String toString()
{
return mName;
}
/**
* Key extractor function provided by the subclass.
* @return key extractor function
*/
public abstract Function<T,K> getKeyExtractor();
@Override
public boolean passes(T t)
{
K key = getKeyExtractor().apply(t);
if(mFilterElementMap.containsKey(key))
{
return mFilterElementMap.get(key).isEnabled();
}
return false;
}
/**
* Indicates if this filter can process the item T, meaning that it has a FilterElement that matches the key
* value for items of type T.
* @param t to test
* @return true if this filter can process item t
*/
@Override
public boolean canProcess(T t)
{
return hasKey(getKeyExtractor().apply(t));
}
@Override
public boolean isEnabled()
{
for(FilterElement filterElement: mFilterElementMap.values())
{
if(filterElement.isEnabled())
{
return true;
}
}
return false;
}
/**
* Adds the child filter element to this filter
* @param filterElement to add
*/
public void add(FilterElement<K> filterElement)
{
if(mFilterElementMap.containsKey(filterElement.getElement()))
{
LOG.warn("Filter element for key [" + filterElement.getElement() + "] named [" + filterElement.getName() +
"] already exists - overwriting existing value");
}
mFilterElementMap.put(filterElement.getElement(), filterElement);
filterElement.register(mFilterChangeListener);
}
/**
* List of filter elements managed by this filter
*/
public final List<FilterElement<K>> getFilterElements()
{
return new ArrayList<>(mFilterElementMap.values());
}
/**
* Name of this filter
*/
public String getName()
{
return mName;
}
/**
* Sets the name of this filter
*
* @param name to set
*/
public void setName(String name)
{
mName = name;
}
/**
* Pretty version or name of this filter.
*
* @return name
*/
public String toString()
{
return mName;
}
/**
* Count of enabled child filter elements.
*
* @return count
*/
@Override
public int getEnabledCount()
{
int count = 0;
for(FilterElement filterElement : mFilterElementMap.values())
{
if(filterElement.isEnabled())
{
count++;
}
}
return count;
}
/**
* Count of child filter elements
*
* @return count
*/
@Override
public int getElementCount()
{
return mFilterElementMap.size();
}
/**
* Registers a filter change listener on this filter and all filter elements.
* @param listener to be notified of any filter changes.
*/
@Override
public void register(IFilterChangeListener listener)
{
mFilterChangeListener = listener;
for(FilterElement child: mFilterElementMap.values())
{
child.register(listener);
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
import java.awt.Component;
import net.miginfocom.swing.MigLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
/**
* Filter editor
* @param <T> item type for editing
*/
public class FilterEditor<T> extends JFrame
{
private FilterEditorPanel<T> mEditorPanel;
/**
* Constructor
* @param title for the editor window frame
* @param owner to register the popup location
* @param filterSet to use initially
*/
public FilterEditor(String title, Component owner, FilterSet<T> filterSet)
{
if(filterSet == null)
{
throw new IllegalArgumentException("Unable to construct FilterEditor - FilterSet cannot be null");
}
setTitle(title);
setSize(600, 400);
setLocationRelativeTo(owner);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][]"));
mEditorPanel = new FilterEditorPanel<>(filterSet);
JScrollPane scroller = new JScrollPane(mEditorPanel);
scroller.setViewportView(mEditorPanel);
add(scroller, "wrap");
JButton close = new JButton("Close");
close.addActionListener(e -> dispose());
add(close);
}
/**
* Updates this editor with the filterset.
* @param filterSet to use in this editor.
*/
public void updateFilterSet(FilterSet<T> filterSet)
{
mEditorPanel.updateFilterSet(filterSet);
}
}

View File

@@ -1,100 +1,128 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
import java.awt.Component;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreeSelectionModel;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.List;
/**
* Editor panel for managing the state of a filter set.
*
* @param <T> element type for the filter set.
*/
public class FilterEditorPanel<T> extends JPanel
{
private static final long serialVersionUID = 1L;
private JTree mTree;
private DefaultTreeModel mModel;
private FilterSet<T> mFilterSet;
/**
* Constructs an instance
* @param filterSet to manage
*/
public FilterEditorPanel(FilterSet<T> filterSet)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mFilterSet = filterSet;
init();
}
/**
* Initializes the panel and the tree model.
*/
private void init()
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode(mFilterSet);
mModel = new DefaultTreeModel(root);
addFilterSet(mFilterSet, root);
mTree = new JTree(mModel);
mTree.setShowsRootHandles(true);
mTree.addMouseListener(new MouseHandler());
mTree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
mTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
mTree.setCellRenderer(new EditorTreeCellRenderer());
mTree.setCellEditor(new FilterTreeNodeEditor());
mTree.setCellEditor(new FilterTreeCellEditor());
mTree.setEditable(true);
add(mTree);
}
/**
* Updates the filter set for this editor panel
* @param filterSet to use
*/
public void updateFilterSet(FilterSet<T> filterSet)
{
remove(mTree);
mFilterSet = filterSet;
init();
revalidate();
}
/**
* Adds the filter set as a child tree node to the parent.
* @param filterSet to add to the tree
* @param parent node for the filter set child tree node.
*/
private void addFilterSet(FilterSet<T> filterSet, DefaultMutableTreeNode parent)
{
List<IFilter<T>> filters = filterSet.getFilters();
/* sort the filters in alphabetical order by name */
filters.sort(Comparator.comparing(IFilter::getName));
for(IFilter<T> filter : filters)
{
DefaultMutableTreeNode child = new DefaultMutableTreeNode(filter);
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(filter);
mModel.insertNodeInto(childNode, parent, parent.getChildCount());
mModel.insertNodeInto(child, parent, parent.getChildCount());
if(filter instanceof FilterSet)
if(filter instanceof FilterSet childFilterSet)
{
addFilterSet((FilterSet<T>) filter, child);
addFilterSet(childFilterSet, childNode);
}
else if(filter instanceof Filter)
else if(filter instanceof Filter childFilter)
{
addFilter((Filter<T>) filter, child);
addFilter(childFilter, childNode);
}
}
}
private void addFilter(Filter<T> filter, DefaultMutableTreeNode parent)
/**
* Adds the filter as a child tree node to the parent.
*
* @param filter to add
* @param parent tree node
*/
private void addFilter(Filter filter, DefaultMutableTreeNode parent)
{
List<FilterElement<?>> elements = filter.getFilterElements();
@@ -103,123 +131,39 @@ public class FilterEditorPanel<T> extends JPanel
for(FilterElement<?> element : elements)
{
DefaultMutableTreeNode child = new DefaultMutableTreeNode(element);
mModel.insertNodeInto(child, parent, parent.getChildCount());
}
}
public class MouseHandler implements MouseListener
/**
* Accesses the tree's root tree node.
* @return root node.
*/
private DefaultMutableTreeNode getRoot()
{
public MouseHandler()
if(mTree.getModel().getRoot() instanceof DefaultMutableTreeNode root)
{
return root;
}
@Override
public void mouseClicked(MouseEvent event)
{
if(SwingUtilities.isRightMouseButton(event))
{
int row = mTree.getRowForLocation(event.getX(),
event.getY());
if(row != -1)
{
mTree.setSelectionRow(row);
Object selectedNode =
mTree.getLastSelectedPathComponent();
if(selectedNode instanceof DefaultMutableTreeNode)
{
final DefaultMutableTreeNode node =
(DefaultMutableTreeNode) selectedNode;
if(node.getChildCount() > 0)
{
JPopupMenu selectionMenu = new JPopupMenu();
JMenuItem selectAll = new JMenuItem("Select All");
selectAll.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
setFilterEnabled(mModel, node, true);
}
});
selectionMenu.add(selectAll);
JMenuItem deselectAll = new JMenuItem("Deselect All");
deselectAll.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
setFilterEnabled(mModel, node, false);
}
});
selectionMenu.add(deselectAll);
selectionMenu.show(mTree,
event.getX(),
event.getY());
}
}
}
}
}
@Override
public void mousePressed(MouseEvent e)
{
}
@Override
public void mouseReleased(MouseEvent e)
{
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
return null;
}
/**
* Recursively sets the enabled state on all filter node children below the
* specified parent node.
* Recursively sets the enabled state on all filter node children below the specified parent node.
*
* @param model - model containing the tree nodes
* @param node - parent node
* @param enabled - true or false to enable or disable the filters
*/
private void setFilterEnabled(DefaultTreeModel model,
DefaultMutableTreeNode node,
boolean enabled)
private void setFilterEnabled(DefaultTreeModel model, DefaultMutableTreeNode node, boolean enabled)
{
Object obj = node.getUserObject();
if(obj instanceof FilterSet<?>)
if(obj instanceof FilterElement filterElement)
{
((FilterSet<?>) obj).setEnabled(enabled);
filterElement.setEnabled(enabled);
}
else if(obj instanceof Filter)
{
((Filter<?>) obj).setEnabled(enabled);
}
else if(obj instanceof FilterElement)
{
((FilterElement<?>) obj).setEnabled(enabled);
}
model.nodeChanged(node);
/* Recursively set the children of this node */
Enumeration<?> children = node.children();
@@ -228,43 +172,123 @@ public class FilterEditorPanel<T> extends JPanel
{
Object child = children.nextElement();
if(child instanceof DefaultMutableTreeNode)
if(child instanceof DefaultMutableTreeNode childNode)
{
setFilterEnabled(model, (DefaultMutableTreeNode) child, enabled);
setFilterEnabled(model, childNode, enabled);
}
}
model.nodeChanged(node);
}
/**
* Recursively searches the tree to find the tree node containing the specified filter element.
*
* @param node to start search
* @param element to find
*/
private DefaultMutableTreeNode findFilterElementNode(DefaultMutableTreeNode node, FilterElement<?> element)
{
if(node != null)
{
if(node.getUserObject() instanceof FilterElement<?> selfFilterElement && selfFilterElement.equals(element))
{
return node;
}
for(int x = 0; x < node.getChildCount(); x++)
{
if(node.getChildAt(x) instanceof DefaultMutableTreeNode child)
{
DefaultMutableTreeNode found = findFilterElementNode(child, element);
if(found != null)
{
return found;
}
}
}
}
return null;
}
/**
* Updates the display value for the specified node and all parents in the nodes parentage.
* @param node to update
*/
private void updateParentage(DefaultMutableTreeNode node)
{
mModel.nodeChanged(node);
TreeNode parent = node.getParent();
while(parent != null)
{
mModel.nodeChanged(parent);
parent = parent.getParent();
}
}
/**
* Recursively searches the tree to find the tree node containing the specified filter.
*
* @param node to start search
* @param filter to find
*/
private DefaultMutableTreeNode findFilterNode(DefaultMutableTreeNode node, IFilter<?> filter)
{
if(node != null)
{
if(node.getUserObject() instanceof IFilter<?> selfFilter && selfFilter.equals(filter))
{
return node;
}
for(int x = 0; x < node.getChildCount(); x++)
{
if(node.getChildAt(x) instanceof DefaultMutableTreeNode child)
{
DefaultMutableTreeNode found = findFilterNode(child, filter);
if(found != null)
{
return found;
}
}
}
}
else
{
System.out.println("Can't evaluate - node is null");
}
return null;
}
/**
* Custom cell renderer
*/
public class EditorTreeCellRenderer extends DefaultTreeCellRenderer
{
private static final long serialVersionUID = 1L;
public Component getTreeCellRendererComponent(JTree tree,
Object treeNode,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus)
public Component getTreeCellRendererComponent(JTree tree, Object treeNode, boolean selected, boolean expanded,
boolean leaf, int row, boolean hasFocus)
{
if(treeNode instanceof DefaultMutableTreeNode)
{
Object userObject = ((DefaultMutableTreeNode) treeNode).getUserObject();
JCheckBox checkBox = null;
if(userObject instanceof IFilter)
if(userObject instanceof IFilter filter)
{
IFilter<?> filter = (IFilter<?>) userObject;
checkBox = new JCheckBox(filter.getName());
checkBox = new FilterCheckBox(filter);
checkBox.setSelected(filter.isEnabled());
}
else if(userObject instanceof FilterElement)
else if(userObject instanceof FilterElement element)
{
FilterElement<?> element = (FilterElement<?>) userObject;
checkBox = new JCheckBox(element.getName());
checkBox = new FilterElementCheckBox(element);
checkBox.setSelected(element.isEnabled());
}
@@ -285,95 +309,110 @@ public class FilterEditorPanel<T> extends JPanel
}
}
return super.getTreeCellRendererComponent(tree, treeNode, selected,
expanded, leaf, row, hasFocus);
return super.getTreeCellRendererComponent(tree, treeNode, selected, expanded, leaf, row, hasFocus);
}
}
/**
* Check box used in filter tree nodes
*/
public class FilterCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
private IFilter<?> mFilter;
private IFilter<T> mFilter;
public FilterCheckBox(IFilter<T> filter)
/**
* Constructs an instance
*
* @param filter
*/
public FilterCheckBox(IFilter<?> filter)
{
super(filter.getName());
mFilter = filter;
setSelected(mFilter.isEnabled());
addItemListener(e -> {
DefaultMutableTreeNode selfNode = findFilterNode(getRoot(), mFilter);
addItemListener(new ItemListener()
{
@Override
public void itemStateChanged(ItemEvent e)
if(selfNode != null)
{
mFilter.setEnabled(FilterCheckBox.this.isSelected());
setFilterEnabled(mModel, selfNode, FilterCheckBox.this.isSelected());
updateLabel();
updateParentage(selfNode);
}
});
}
@Override
public void setSelected(boolean b)
{
super.setSelected(b);
updateLabel();
}
public void updateLabel()
{
setText(mFilter.getName() + " (" + mFilter.getEnabledCount() + "/" + mFilter.getElementCount() + ")");
}
}
/**
* Check box used in filter element tree nodes
*/
public class FilterElementCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
private FilterElement<?> mFilter;
/**
* Constructs an instance
*
* @param filter element
*/
public FilterElementCheckBox(FilterElement<?> filter)
{
super(filter.getName());
mFilter = filter;
setSelected(mFilter.isEnabled());
addItemListener(new ItemListener()
{
@Override
public void itemStateChanged(ItemEvent arg0)
addItemListener(arg0 -> {
mFilter.setEnabled(FilterElementCheckBox.this.isSelected());
DefaultMutableTreeNode self = findFilterElementNode(getRoot(), mFilter);
if(self != null)
{
mFilter.setEnabled(FilterElementCheckBox.this.isSelected());
updateParentage(self);
}
});
}
}
public class FilterTreeNodeEditor implements TreeCellEditor
/**
* Editor for filter tree nodes
*/
public class FilterTreeCellEditor implements TreeCellEditor
{
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Component getTreeCellEditorComponent(JTree tree, Object node,
boolean isSelected, boolean expanded, boolean leaf, int row)
public Component getTreeCellEditorComponent(JTree tree, Object node, boolean isSelected, boolean expanded, boolean leaf, int row)
{
if(node instanceof DefaultMutableTreeNode)
if(node instanceof DefaultMutableTreeNode mutableTreeNode)
{
Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
Object userObject = mutableTreeNode.getUserObject();
if(userObject instanceof IFilter)
if(userObject instanceof IFilter filter)
{
return new FilterCheckBox((IFilter) userObject);
return new FilterCheckBox(filter);
}
else if(userObject instanceof FilterElement)
else if(userObject instanceof FilterElement filterElement)
{
return new FilterElementCheckBox((FilterElement<?>) userObject);
return new FilterElementCheckBox(filterElement);
}
}
return new JLabel(node.toString());
}
@Override
public void addCellEditorListener(CellEditorListener l)
{
}
public void addCellEditorListener(CellEditorListener l) {}
@Override
public void cancelCellEditing()
{
}
public void cancelCellEditing() {}
@Override
public Object getCellEditorValue()
@@ -388,9 +427,7 @@ public class FilterEditorPanel<T> extends JPanel
}
@Override
public void removeCellEditorListener(CellEditorListener l)
{
}
public void removeCellEditorListener(CellEditorListener l) {}
@Override
public boolean shouldSelectCell(EventObject anEvent)

View File

@@ -1,55 +1,148 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
/**
* Filter element provides filtering and enabled state tracking for an individual filterable element.
*
* @param <T> element to track and filter.
*/
public class FilterElement<T> implements Comparable<FilterElement<T>>
{
private boolean mEnabled;
private T mElement;
public FilterElement( T element, boolean enabled )
{
mElement = element;
mEnabled = enabled;
}
public FilterElement( T t )
{
this( t, true );
}
public T getElement()
{
return mElement;
}
public String toString()
{
return getName();
}
public boolean isEnabled()
{
return mEnabled;
}
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
}
public String getName()
{
return mElement.toString();
}
private boolean mEnabled;
private final T mElement;
private IFilterChangeListener mFilterChangeListener;
@Override
public int compareTo( FilterElement<T> other )
/**
* Constructs an instance
*
* @param element to track
* @param enabled enabled state of this filter element
*/
public FilterElement(T element, boolean enabled)
{
return toString().compareTo( other.toString() );
mElement = element;
mEnabled = enabled;
}
/**
* Constructs an instance with a default state of enabled (true).
*
* @param t element to track
*/
public FilterElement(T t)
{
this(t, true);
}
/**
* Registers the listener with each child filter element.
*
* @param listener to register
*/
public void register(IFilterChangeListener listener)
{
mFilterChangeListener = listener;
}
/**
* Element being filtered.
*
* @return element
*/
public T getElement()
{
return mElement;
}
/**
* Name or display string for the element.
*
* @return string version of the element.
*/
public String toString()
{
return getName();
}
/**
* Indicates if this filter is enabled indicating that elements of this type will pass this filter.
*
* @return enabled state.
*/
public boolean isEnabled()
{
return mEnabled;
}
/**
* Sets the enabled state of this filter.
*
* @param enabled true to allow this filter's element type to pass the filter.
*/
public void setEnabled(boolean enabled)
{
mEnabled = enabled;
if(mFilterChangeListener != null)
{
mFilterChangeListener.filterChanged();
}
}
/**
* Name of the tracked element.
*
* @return name
*/
public String getName()
{
return mElement.toString();
}
/**
* Implements compareto interface for ordering of filter elements.
*
* @param other the object to be compared.
* @return comparison value
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof FilterElement)) return false;
return compareTo((FilterElement) o) == 0;
public int compareTo(FilterElement<T> other)
{
return toString().compareTo(other.toString());
}
/**
* Implement equals interface for comparison of filter elements.
*
* @param o to compare
* @return comparison
*/
@Override
public boolean equals(Object o)
{
if(o instanceof FilterElement fe)
{
return compareTo(fe) == 0;
}
return false;
}
}

View File

@@ -1,108 +1,239 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
import io.github.dsheirer.log.LoggingSuppressor;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Filter set is a parent container for filters.
*
* @param <T> type of element tracked/filtered by this filter set.
*/
public class FilterSet<T> implements IFilter<T>
{
protected List<IFilter<T>> mFilters = new ArrayList<IFilter<T>>();
protected boolean mEnabled = true;
protected String mName;
private static final Logger LOGGER = LoggerFactory.getLogger(FilterSet.class);
private static LoggingSuppressor sLoggingSuppressor = new LoggingSuppressor(LOGGER);
private List<IFilter<T>> mFilters = new ArrayList<>();
private String mName;
private IFilterChangeListener mFilterChangeListener;
public FilterSet( String name )
{
mName = name;
}
public FilterSet()
{
mName = "Message Filter";
}
public FilterSet(IFilter<T> filter)
/**
* Constructs an instance of a filter set.
*
* @param name of this filter set.
*/
public FilterSet(String name)
{
mName = name;
}
/**
* Constructs an instance with a single filter using the default 'Filters' name.
*
* @param filter for this filter set.
*/
public FilterSet(IFilter<T> filter)
{
this("Filters");
addFilter(filter);
}
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public String toString()
{
return getName();
}
@Override
public boolean passes( T t )
{
if( mEnabled )
{
for( IFilter<T> filter: mFilters )
{
// Make sure to loop through all filters.
// Only return if true or all filters have been evaluated.
if( filter.canProcess( t ) && filter.passes( t ))
{
return true;
}
}
}
return false;
}
@Override
public boolean canProcess( T t )
{
if( mEnabled )
/**
* Registers the listener with each child filter element.
* @param listener to register
*/
public void register(IFilterChangeListener listener)
{
mFilterChangeListener = listener;
for(IFilter child: mFilters)
{
for( IFilter<T> filter: mFilters )
child.register(listener);
}
}
/**
* Name of this filter set.
*
* @return name
*/
public String getName()
{
return mName;
}
/**
* Sets the name of this filter set.
*
* @param name to set
*/
public void setName(String name)
{
mName = name;
}
/**
* Name for this filter set.
*
* @return name
*/
public String toString()
{
return getName();
}
/**
* Indicates if the element passes at least one of the child filters in this filter set.
*
* @param t to evaluate
* @return true if the argument passes a child filter.
*/
@Override
public boolean passes(T t)
{
for(IFilter<T> filter : mFilters)
{
//Stop at the first filter that says that it can process the message type.
if(filter.canProcess(t))
{
if( filter.canProcess( t ) )
{
return true;
}
return filter.passes(t);
}
}
return false;
}
}
/**
* Indicates if this filter set contains at least one filter that can evaluate the type of argument that is presented.
*
* @param t element to evaluate
* @return true if this filter set can evaluate objects of the argument's type.
*/
@Override
public boolean canProcess(T t)
{
for(IFilter<T> filter : mFilters)
{
if(filter.canProcess(t))
{
return true;
}
}
sLoggingSuppressor.info(t.getClass().getSimpleName(), 1, "FilterSet [" +
this.getClass().getSimpleName() + "] has no filter element for items of type [" +
t.getClass().getSimpleName() + "]");
return false;
}
/**
* Child filters
*
* @return filters
*/
public List<IFilter<T>> getFilters()
{
return mFilters;
}
public void addFilters( List<IFilter<T>> filters )
{
mFilters.addAll( filters );
}
public void addFilter( IFilter<T> filter )
{
mFilters.add( filter );
}
public void removeFilter( IFilter<T> filter )
{
mFilters.remove( filter );
return new ArrayList<>(mFilters);
}
@Override
public boolean isEnabled()
{
return mEnabled;
}
/**
* Adds a list of child filters to this filter set.
* @param filters to add
*/
public void addFilters(List<IFilter<T>> filters)
{
for(IFilter<T> filter: filters)
{
addFilter(filter);
}
}
/**
* Adds the child filter to this filter set.
* @param filter to add
*/
public void addFilter(IFilter<T> filter)
{
if(filter instanceof Filter<?,?> filterInstance && filterInstance.getKeyExtractor() == null)
{
LOGGER.warn("Filter [" + filter.getClass() + "] has a null key extractor");
}
mFilters.add(filter);
filter.register(mFilterChangeListener);
}
/**
* Indicates if any of the child filters contain at least one filter element that is enabled.
* @return enabled state
*/
@Override
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
}
public boolean isEnabled()
{
for(IFilter filter: mFilters)
{
if(filter.isEnabled())
{
return true;
}
}
return false;
}
/**
* Recursive count of enabled child filter elements.
* @return count
*/
@Override
public int getEnabledCount()
{
int count = 0;
for(IFilter filter: mFilters)
{
count += filter.getEnabledCount();
}
return count;
}
/**
* Recursive count of child filter elements.
* @return count
*/
@Override
public int getElementCount()
{
int count = 0;
for(IFilter filter: mFilters)
{
count += filter.getElementCount();
}
return count;
}
}

View File

@@ -1,36 +1,75 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
/**
* Filter interface
*
* @param <T> type of filter
*/
public interface IFilter<T>
{
/**
* Generic filter method.
*
* @param message - message to filter
* @return - true if the message passes the filter
*/
public boolean passes( T t );
/**
* Generic filter method.
*
* @param t - message to filter
* @return - true if the message passes the filter
*/
boolean passes(T t);
/**
* Indicates if the filter can process (filter) the object
*
* @param message - candidate message for filtering
*
* @return - true if the filter is capable of filtering the message
*/
public boolean canProcess( T t );
/**
* Indicates if the filter can process (filter) the object
*
* @param t - candidate message for filtering
* @return - true if the filter is capable of filtering the message
*/
boolean canProcess(T t);
/**
* Indicates if this filter is enabled to evaluate messages
*/
public boolean isEnabled();
/**
* Indicates if this filter is enabled to evaluate messages
*/
boolean isEnabled();
/**
* Sets the enabled state of the filter
*/
public void setEnabled( boolean enabled );
/**
* Display name for the filter
*/
public String getName();
/**
* Display name for the filter
*/
String getName();
/**
* (Recursive) count of enabled child filter elements.
*
* @return total enable count.
*/
int getEnabledCount();
/**
* (Recursive) count of total child filter elements.
*
* @return total count of child filter elements.
*/
int getElementCount();
/**
* Registers the listener to be notified when the enabled state of any child filter elements is updated.
*
* @param listener to be notified
*/
void register(IFilterChangeListener listener);
}

View File

@@ -0,0 +1,25 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.filter;
public interface IFilterChangeListener
{
void filterChanged();
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,30 +14,36 @@
*
* 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
package io.github.dsheirer.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.SyncLossMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class SyncLossMessageFilter extends Filter<IMessage>
/**
* Filter for sync-loss messages
*/
public class SyncLossMessageFilter extends Filter<IMessage,String>
{
private static final String SYNC_LOSS_KEY = "Sync-Loss";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public SyncLossMessageFilter()
{
super("Sync Loss");
super("Sync Loss Messages");
add(new FilterElement<>(SYNC_LOSS_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
return mEnabled && canProcess(message);
return mKeyExtractor;
}
@Override
@@ -47,9 +52,20 @@ public class SyncLossMessageFilter extends Filter<IMessage>
return message instanceof SyncLossMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return Collections.EMPTY_LIST;
@Override
public String apply(IMessage message)
{
if(message instanceof SyncLossMessage)
{
return SYNC_LOSS_KEY;
}
return null;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -21,7 +21,6 @@ package io.github.dsheirer.module;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.List;
@@ -50,7 +49,7 @@ public abstract class HistoryModule<T> extends Module implements Listener<T>
*/
public List<T> getItems()
{
return new ArrayList<>(mItems);
return new ArrayList<>(mItems);
}
@Override

View File

@@ -117,8 +117,8 @@ public class ProcessingChain implements Listener<ChannelEvent>
private Broadcaster<IMessage> mMessageBroadcaster = new Broadcaster<>();
private Broadcaster<SquelchStateEvent> mSquelchStateEventBroadcaster = new Broadcaster<>();
private AtomicBoolean mRunning = new AtomicBoolean();
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(500);
private MessageHistory mMessageHistory = new MessageHistory(500);
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(200);
private MessageHistory mMessageHistory = new MessageHistory(200);
private AbstractChannelState mChannelState;
private EventBus mEventBus;
protected Source mSource;

View File

@@ -47,6 +47,7 @@ import io.github.dsheirer.module.decode.dmr.DMRDecoderState;
import io.github.dsheirer.module.decode.dmr.DMRTrafficChannelManager;
import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
import io.github.dsheirer.module.decode.dmr.audio.DMRAudioModule;
import io.github.dsheirer.module.decode.dmr.message.filter.DmrMessageFilterSet;
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2Decoder;
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2DecoderState;
import io.github.dsheirer.module.decode.fleetsync2.FleetsyncMessageFilter;
@@ -80,16 +81,18 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderC4FM;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderLSM;
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderState;
import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25MessageFilterSet;
import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25P1MessageFilterSet;
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderHDQPSK;
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState;
import io.github.dsheirer.module.decode.p25.phase2.message.filter.P25P2MessageFilterSet;
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
import io.github.dsheirer.module.decode.passport.PassportDecoder;
import io.github.dsheirer.module.decode.passport.PassportDecoderState;
import io.github.dsheirer.module.decode.passport.PassportMessageFilter;
import io.github.dsheirer.module.decode.tait.Tait1200Decoder;
import io.github.dsheirer.module.decode.tait.Tait1200DecoderState;
import io.github.dsheirer.module.decode.tait.Tait1200MessageFilter;
import io.github.dsheirer.module.decode.traffic.TrafficChannelManager;
import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule;
import io.github.dsheirer.preference.UserPreferences;
@@ -494,7 +497,7 @@ public class DecoderFactory
*/
public static FilterSet<IMessage> getMessageFilters(List<Module> modules)
{
FilterSet<IMessage> filterSet = new FilterSet<>();
FilterSet<IMessage> filterSet = new FilterSet<>("Message Filters");
for(Module module : modules)
{
@@ -504,28 +507,28 @@ public class DecoderFactory
}
}
/* If we don't have any filters, add an ALL-PASS filter */
if(filterSet.getFilters().isEmpty())
{
filterSet.addFilter(new AllPassFilter<>());
}
//Add an all-others filter as a catch-all for anything that isn't handled by the decoder filters.
filterSet.addFilter(new AllPassFilter<>("All Other Messages Filter"));
return filterSet;
}
/**
* Returns a set of IMessageFilter objects (FilterSets or Filters) that
* can process all of the messages produced by the specified decoder type.
* can process all messages produced by the specified decoder type.
*/
public static List<IFilter<IMessage>> getMessageFilter(DecoderType decoder)
{
ArrayList<IFilter<IMessage>> filters = new ArrayList<>();
List<IFilter<IMessage>> filters = new ArrayList<>();
switch(decoder)
{
case DCS:
filters.add(new DCSMessageFilter());
break;
case DMR:
filters.add(new DmrMessageFilterSet());
break;
case FLEETSYNC2:
filters.add(new FleetsyncMessageFilter());
break;
@@ -545,13 +548,16 @@ public class DecoderFactory
filters.add(new MPT1327MessageFilter());
break;
case P25_PHASE1:
filters.add(new P25MessageFilterSet());
filters.add(new P25P1MessageFilterSet());
break;
case P25_PHASE2:
filters.add(new P25P2MessageFilterSet());
break;
case PASSPORT:
filters.add(new PassportMessageFilter());
break;
case DMR:
//filters.add(new DMR) //todo: not finished
case TAIT_1200:
filters.add(new Tait1200MessageFilter());
break;
default:
break;

View File

@@ -26,6 +26,7 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.p25.identifier.channel.StandardChannel;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.ISourceEventListener;
@@ -100,9 +101,8 @@ public abstract class AnalogDecoderState extends DecoderState implements ISource
if(mDecodeEvent == null)
{
mDecodeEvent = DecodeEvent.builder(System.currentTimeMillis())
mDecodeEvent = DecodeEvent.builder(DecodeEventType.CALL, System.currentTimeMillis())
.channel(mChannelDescriptor)
.eventDescription("CALL")
.details(getDecoderType().name())
.identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers()))
.build();

View File

@@ -22,45 +22,55 @@ package io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* Message filter for Digital Coded Squelch (DCS)
*/
public class DCSMessageFilter extends Filter<IMessage>
public class DCSMessageFilter extends Filter<IMessage,String>
{
private Map<String,FilterElement<String>> mElements = new HashMap<>();
private static final String DCS_MESSAGE_FILTER_TAG = "DCS";
private static final String DCS_KEY = "DCS";
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public DCSMessageFilter()
{
super("DCS Message Filter");
mElements.put(DCS_MESSAGE_FILTER_TAG, new FilterElement<>("DCS Code", true));
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled();
}
return false;
super("DCS Messages");
add(new FilterElement(DCS_KEY));
}
/**
* Indicates that this filter can process DCS messages.
* @param message to test
* @return true if the message can be processed
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DCSMessage;
return message instanceof DCSMessage && super.canProcess(message);
}
/**
* Key extractor that always returns the same key constant.
* @return extractor function.
*/
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, String> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
return DCS_KEY;
}
}
}

View File

@@ -286,9 +286,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
DecodeEvent smsEvent = DMRDecodeEvent.builder(sms.getTimestamp())
.eventType(DecodeEventType.SMS)
.eventDescription(DecodeEventType.SMS.toString())
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp())
.details("MESSAGE: " + sms.getSMS())
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
.timeslot(sms.getTimeslot())
@@ -312,8 +310,7 @@ public class DMRDecoderState extends TimeslotDecoderState
mic.update(hyteraSmsPacket.getSource());
mic.update(hyteraSmsPacket.getDestination());
DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.SMS.name())
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("SMS:" + hyteraSmsPacket.getSMS())
@@ -325,8 +322,7 @@ public class DMRDecoderState extends TimeslotDecoderState
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.UNKNOWN_PACKET.name())
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
@@ -335,8 +331,7 @@ public class DMRDecoderState extends TimeslotDecoderState
}
else
{
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
.eventDescription(DecodeEventType.DATA_PACKET.name())
DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.timeslot(packet.getTimeslot())
.details(packet.toString())
@@ -347,9 +342,8 @@ public class DMRDecoderState extends TimeslotDecoderState
GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket());
if (geoPosition != null) {
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp())
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, packet.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.protocol(Protocol.LRRP)
.location(geoPosition)
@@ -505,14 +499,8 @@ public class DMRDecoderState extends TimeslotDecoderState
case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD:
if(csbk instanceof Acknowledge)
{
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.RESPONSE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(((Acknowledge)csbk).getReason().toString())
.build();
broadcast(ackEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE,
((Acknowledge) csbk).getReason().toString()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE, getTimeslot()));
break;
@@ -520,14 +508,8 @@ public class DMRDecoderState extends TimeslotDecoderState
case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC:
if(csbk instanceof Acknowledge)
{
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.RESPONSE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(((Acknowledge)csbk).getReason().toString())
.build();
broadcast(ackEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE,
((Acknowledge) csbk).getReason().toString()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
@@ -537,33 +519,16 @@ public class DMRDecoderState extends TimeslotDecoderState
switch(((Ahoy)csbk).getServiceKind())
{
case AUTHENTICATE_REGISTER_RADIO_CHECK_SERVICE:
DecodeEvent registerEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(DecodeEventType.REGISTER.toString())
.build();
broadcast(registerEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND, DecodeEventType.REGISTER.toString()));
break;
case CANCEL_CALL_SERVICE:
DecodeEvent cancelEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("CANCEL CALL")
.build();
broadcast(cancelEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND, "CANCEL CALL"));
break;
case SUPPLEMENTARY_SERVICE:
if(csbk instanceof StunReviveKill)
{
DecodeEvent stunEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(((StunReviveKill)csbk).getCommand() + " RADIO")
.build();
broadcast(stunEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
((StunReviveKill)csbk).getCommand() + " RADIO"));
}
break;
case FULL_DUPLEX_MS_TO_MS_PACKET_CALL_SERVICE:
@@ -577,15 +542,9 @@ public class DMRDecoderState extends TimeslotDecoderState
if(csbk instanceof ServiceRadioCheck)
{
ServiceRadioCheck src = (ServiceRadioCheck)csbk;
DecodeEvent checkEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.RADIO_CHECK.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(src.getServiceDescription() + " SERVICE FOR " +
(src.isTalkgroupTarget() ? "TALKGROUP" : "RADIO"))
.build();
broadcast(checkEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.RADIO_CHECK,
src.getServiceDescription() + " SERVICE FOR " +
(src.isTalkgroupTarget() ? "TALKGROUP" : "RADIO")));
}
break;
}
@@ -599,14 +558,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(aloha.hasRadioIdentifier())
{
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.RESPONSE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("Aloha Acknowledge")
.build();
broadcast(ackEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE, "Aloha Acknowledge"));
resetState();
}
}
@@ -618,24 +570,13 @@ public class DMRDecoderState extends TimeslotDecoderState
switch(((Announcement)csbk).getAnnouncementType())
{
case MASS_REGISTRATION:
DecodeEvent massEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("MASS REGISTRATION")
.build();
broadcast(massEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.REGISTER, "MASS REGISTRATION"));
break;
case VOTE_NOW_ADVICE:
if(csbk instanceof VoteNowAdvice)
{
DecodeEvent voteEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode())
.build();
broadcast(voteEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
"VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode()));
}
break;
}
@@ -653,13 +594,8 @@ public class DMRDecoderState extends TimeslotDecoderState
case STANDARD_PROTECT:
if(csbk instanceof Protect)
{
DecodeEvent protectEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.COMMAND.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("PROTECT: " + ((Protect)csbk).getProtectKind())
.build();
broadcast(protectEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
"PROTECT: " + ((Protect)csbk).getProtectKind()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot()));
break;
@@ -714,13 +650,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = DMRDecodeEvent.builder(csbk.getTimestamp())
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.DATA_CALL.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -753,13 +683,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = DMRDecodeEvent.builder(csbk.getTimestamp())
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.CALL_GROUP.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
event = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -791,13 +715,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = DMRDecodeEvent.builder(csbk.getTimestamp())
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.CALL_UNIT_TO_UNIT.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
event = getDecodeEvent(csbk, DecodeEventType.CALL_UNIT_TO_UNIT, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -817,15 +735,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(cmAloha.hasRadioIdentifier())
{
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.eventDescription(DecodeEventType.RESPONSE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details("Aloha Acknowledge")
.build();
broadcast(ackEvent);
broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE, "Aloha Acknowledge"));
resetState();
}
}
@@ -850,13 +760,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = DMRDecodeEvent.builder(csbk.getTimestamp())
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.DATA_CALL.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -871,9 +775,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_REGISTRATION_REQUEST:
DecodeEvent event = DMRDecodeEvent.builder(csbk.getTimestamp())
DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp())
.details("Registration Request")
.eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -881,9 +784,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_REGISTRATION_RESPONSE:
DecodeEvent regRespEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp())
.details("Registration Response")
.eventDescription(DecodeEventType.REGISTER.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -909,13 +811,7 @@ public class DMRDecoderState extends TimeslotDecoderState
if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers()))
{
detectedEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
.channel(channel)
.details(csbk.getOpcode().getLabel())
.eventDescription(DecodeEventType.CALL_GROUP.toString())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
detectedEvent = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent);
}
else
@@ -930,9 +826,8 @@ public class DMRDecoderState extends TimeslotDecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
case MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION:
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp())
.details("TALKGROUP AFFILIATION")
.eventDescription(DecodeEventType.AFFILIATE.toString())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.build();
@@ -945,6 +840,23 @@ public class DMRDecoderState extends TimeslotDecoderState
}
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
.channel(channel)
.details(csbk.getOpcode().getLabel())
.identifiers(mergedIdentifiers)
.timeslot(channel.getTimeslot())
.build();
}
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
.timeslot(csbk.getTimeslot())
.details(details)
.build();
}
/**
* Creates a copy of the current identifier collection, removes any USER class identifiers and loads the identifiers
* argument values into the collection.
@@ -1191,8 +1103,7 @@ public class DMRDecoderState extends TimeslotDecoderState
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.update(gpsInformation.getGPSLocation());
DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp())
.eventDescription(DecodeEventType.GPS.toString())
DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp())
.identifiers(ic)
.timeslot(message.getTimeslot())
.details("LOCATION:" + gpsInformation.getGPSLocation())
@@ -1223,9 +1134,8 @@ public class DMRDecoderState extends TimeslotDecoderState
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = DMRDecodeEvent.builder(timestamp)
mCurrentCallEvent = DMRDecodeEvent.builder(type, timestamp)
.channel(getCurrentChannel())
.eventDescription(type.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -1236,7 +1146,6 @@ public class DMRDecoderState extends TimeslotDecoderState
{
if(type != DecodeEventType.CALL)
{
mCurrentCallEvent.setEventDescription(type.toString());
mCurrentCallEvent.setDetails(details);
}

View File

@@ -325,12 +325,12 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
int lsn = channel.getLogicalSlotNumber();
DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn);
DecodeEventType decodeEventType = getEventType(opcode, identifierCollection, encrypted);
if(isStale(event, timestamp, identifierCollection)) //Create new event
{
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
.eventDescription(getEventType(opcode, identifierCollection, encrypted).toString())
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -349,10 +349,9 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
.eventDescription(getEventType(opcode, identifierCollection, encrypted).toString() + " - Continue")
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.details("CONTINUE - CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -389,15 +388,6 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
{
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
{
if(event.getEventDescription() == null)
{
event.setEventDescription(getEventType(opcode, identifierCollection, encrypted) + IGNORED);
}
else if(!event.getEventDescription().endsWith(IGNORED))
{
event.setEventDescription(event.getEventDescription() + IGNORED);
}
if(event.getDetails() == null)
{
event.setDetails(DATA_CALL_IGNORED);

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,12 +14,13 @@
*
* 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 io.github.dsheirer.module.decode.dmr.event;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
import io.github.dsheirer.module.decode.event.DecodeEventType;
/**
* DMR Channel Grant Event.
@@ -33,11 +33,12 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
/**
* Constructs an instance
* @param decodeEventType for the event
* @param timestamp for the event
*/
public DMRChannelGrantEvent(long timestamp)
public DMRChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
super(timestamp);
super(decodeEventType, timestamp);
}
/**
@@ -62,9 +63,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(long timeStart)
public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(DecodeEventType decodeEventType, long timeStart)
{
return new DMRChannelGrantDecodeEventBuilder(timeStart);
return new DMRChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -74,7 +75,7 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected DMRChannel mChannel;
protected String mDetails;
@@ -85,8 +86,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
*
* @param timeStart
*/
public DMRChannelGrantDecodeEventBuilder(long timeStart)
public DMRChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -121,7 +123,7 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
/**
* Sets the channel descriptor for this event
* @param channelDescriptor
* @param channel
*/
public DMRChannelGrantDecodeEventBuilder channel(DMRChannel channel)
{
@@ -129,16 +131,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public DMRChannelGrantDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -165,12 +157,11 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
*/
public DMRChannelGrantEvent build()
{
DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mTimeStart);
DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannel);
decodeEvent.setTimeslot(mChannel.getTimeslot());
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,12 +14,13 @@
*
* 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 io.github.dsheirer.module.decode.dmr.event;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
@@ -32,9 +32,9 @@ public class DMRDecodeEvent extends DecodeEvent
* Constucts a decode event
* @param start
*/
public DMRDecodeEvent(long start)
public DMRDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.DMR);
}
@@ -43,9 +43,9 @@ public class DMRDecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.DMR);
return decodeEventBuilder;
}

View File

@@ -39,7 +39,7 @@ public enum Opcode
STANDARD_CHANNEL_TIMING(Vendor.STANDARD, 7, "CHANNEL TIMING"),
STANDARD_ALOHA(Vendor.STANDARD, 25, "ALOHA"),
STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER(Vendor.STANDARD, 26, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT INBOUND HEADER"),
STANDARD_AHOY(Vendor.STANDARD, 28, "AHOY"),
STANDARD_ACTIVATION(Vendor.STANDARD, 30, "ACTIVATION"),
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST(Vendor.STANDARD, 31, "RANDOM ACCESS SERVICE REQUEST"),
@@ -102,13 +102,18 @@ public enum Opcode
HYTERA_68_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA 68 XPT PREAMBLE"),
HYTERA_68_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA 68 CSBKO 62"),
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");
private Vendor mVendor;
private int mValue;
private String mLabel;
/**
* Constructor
* @param vendor for the opcode
* @param value for the opcode
* @param label to display for the opcode
*/
Opcode(Vendor vendor, int value, String label)
{
mVendor = vendor;
@@ -151,19 +156,86 @@ public enum Opcode
*/
public boolean isDataChannelGrantOpcode()
{
return DATA_CHANNEL_GRANT_OPCODES.contains(this);
return DATA_CHANNEL_GRANTS.contains(this);
}
/**
* Data Channel Grant opcodes
* Data channel grant opcodes
*/
public static final EnumSet<Opcode> DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM,
STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM,
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
public static final EnumSet<Opcode> DATA_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM, STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM, STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
/**
* Data opcodes
*/
public static final EnumSet<Opcode> DATA_OPCODES = EnumSet.of(STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER,
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER, STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_OUTBOUND_HEADER,
STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_INBOUND_HEADER, STANDARD_PREAMBLE);
/**
* Mobile request and response opcodes
*/
public static final EnumSet<Opcode> MOBILE_REQUEST_RESPONSE = EnumSet.of(STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST,
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST);
/**
* Network request and response and announcement opcodes
*/
public static final EnumSet<Opcode> NETWORK_REQUEST_RESPONSE = EnumSet.of(STANDARD_FEATURE_NOT_SUPPORTED,
STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_RESPONSE, STANDARD_AHOY, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC,
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_TSCC, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD,
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_PAYLOAD, STANDARD_NEGATIVE_ACKNOWLEDGE_RESPONSE, STANDARD_CLEAR,
STANDARD_MOVE_TSCC, STANDARD_CHANNEL_TIMING,
STANDARD_ALOHA, STANDARD_ACTIVATION, STANDARD_ANNOUNCEMENT, STANDARD_MAINTENANCE, STANDARD_PROTECT);
/**
* Voice channel grant opcodes
*/
public static final EnumSet<Opcode> VOICE_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_VOICE_CHANNEL_GRANT,
STANDARD_TALKGROUP_VOICE_CHANNEL_GRANT, STANDARD_BROADCAST_TALKGROUP_VOICE_CHANNEL_GRANT,
STANDARD_DUPLEX_PRIVATE_VOICE_CHANNEL_GRANT);
/**
* Hytera opcodes
*/
public static final EnumSet<Opcode> HYTERA = EnumSet.of(HYTERA_08_ACKNOWLEDGE, HYTERA_08_ANNOUNCEMENT,
HYTERA_08_CSBKO_44, HYTERA_08_CSBKO_47, HYTERA_68_XPT_SITE_STATE, HYTERA_68_ALOHA,
HYTERA_68_ACKNOWLEDGE, HYTERA_68_ANNOUNCEMENT, HYTERA_68_XPT_PREAMBLE, HYTERA_68_CSBKO_62);
/**
* Motorola Capacity Max opcodes
*/
public static final EnumSet<Opcode> MOTOROLA_CAPACITY_MAX = EnumSet.of(MOTOROLA_CAPMAX_ALOHA);
/**
* Motorola Capacity Plus opcodes
*/
public static final EnumSet<Opcode> MOTOROLA_CAPACITY_PLUS = EnumSet.of(MOTOROLA_CAPPLUS_CALL_ALERT,
MOTOROLA_CAPPLUS_CALL_ALERT_ACK, MOTOROLA_CAPPLUS_DATA_WINDOW_ANNOUNCEMENT,
MOTOROLA_CAPPLUS_DATA_WINDOW_GRANT, MOTOROLA_CAPPLUS_NEIGHBOR_REPORT, MOTOROLA_CAPPLUS_CSBKO_60,
MOTOROLA_CAPPLUS_PREAMBLE, MOTOROLA_CAPPLUS_SITE_STATUS);
/**
* Motorola Connect Plus opcodes
*/
public static final EnumSet<Opcode> MOTOROLA_CONNECT_PLUS = EnumSet.of(MOTOROLA_CONPLUS_NEIGHBOR_REPORT,
MOTOROLA_CONPLUS_VOICE_CHANNEL_USER, MOTOROLA_CONPLUS_DATA_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_10,
MOTOROLA_CONPLUS_TERMINATE_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_16, MOTOROLA_CONPLUS_REGISTRATION_REQUEST,
MOTOROLA_CONPLUS_REGISTRATION_RESPONSE, MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION,
MOTOROLA_CONPLUS_DATA_WINDOW_ANNOUNCEMENT, MOTOROLA_CONPLUS_DATA_WINDOW_GRANT);
/**
* Indicates if the opcode is included in one of the enumset groupings above
* @return
*/
public boolean isGrouped()
{
return DATA_CHANNEL_GRANTS.contains(this) || DATA_OPCODES.contains(this) ||
MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_REQUEST_RESPONSE.contains(this) ||
VOICE_CHANNEL_GRANTS.contains(this) || HYTERA.contains(this) || MOTOROLA_CAPACITY_MAX.contains(this) ||
MOTOROLA_CAPACITY_PLUS.contains(this) || MOTOROLA_CONNECT_PLUS.contains(this);
}
/**
* Lookup map of vendors and opcode value to opcodes

View File

@@ -67,14 +67,14 @@ public enum LCOpcode
//CAP+ SLCO 15 = Rest Channel https://forums.radioreference.com/threads/understanding-capacity-plus-trunking.209318/page-7
SHORT_CAPACITY_PLUS_REST_CHANNEL_NOTIFICATION(Vendor.MOTOROLA_CAPACITY_PLUS, false,15, "REST CHANNEL NOTIFICATION"),
SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "HYTERA XPT CHANNEL"),
SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "STANDARD XPT CHANNEL"),
SHORT_HYTERA_XPT_CHANNEL(Vendor.HYTERA_68, false, 8, "HYTERA XPT CHANNEL"),
SHORT_CONNECT_PLUS_TRAFFIC_CHANNEL(Vendor.STANDARD, false, 9, "TRAFFIC CHANNEL INFO"),
SHORT_CONNECT_PLUS_CONTROL_CHANNEL(Vendor.STANDARD, false, 10, "CONTROL CHANNEL INFO"),
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "SHORT UNKNOWN");
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "UNKNOWN");
private static final EnumSet<LCOpcode> TALKER_ALIAS_OPCODES = EnumSet.of(FULL_STANDARD_TALKER_ALIAS_HEADER,
FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3);

View File

@@ -63,7 +63,6 @@ public class PacketSequenceMessageFactory
if(packetSequence.hasPacketSequenceHeader())
{
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
boolean confirmed = primaryHeader.isConfirmedData();
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
if(packet != null)

View File

@@ -0,0 +1,74 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
import java.util.Collection;
import java.util.function.Function;
/**
* Filter for opcode based control (CSBK) messages.
*/
public class ControlMessageFilter extends Filter<IMessage, Opcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
* @param name of this control message filter
* @param opcodes to use with this filter
*/
public ControlMessageFilter(String name, Collection<Opcode> opcodes)
{
super(name);
for(Opcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
@Override
public Function<IMessage, Opcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,Opcode>
{
@Override
public Opcode apply(IMessage message)
{
if(message instanceof CSBKMessage csbk)
{
return csbk.getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for DMR control (CSBK) messages using Opcodes for filter elements.
*/
public class ControlMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public ControlMessageFilterSet()
{
super("Control (CSBK) Messages");
addFilter(new ControlMessageFilter("Data Channel Grants", Opcode.DATA_CHANNEL_GRANTS));
addFilter(new ControlMessageFilter("Data-Related", Opcode.DATA_OPCODES));
addFilter(new ControlMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE));
addFilter(new ControlMessageFilter("Network Request/Response", Opcode.NETWORK_REQUEST_RESPONSE));
addFilter(new ControlMessageFilter("Voice Channel Grants", Opcode.VOICE_CHANNEL_GRANTS));
addFilter(new ControlMessageFilter("Hytera", Opcode.HYTERA));
addFilter(new ControlMessageFilter("Motorola Capacity Max", Opcode.MOTOROLA_CAPACITY_MAX));
addFilter(new ControlMessageFilter("Motorola Capacity Plus", Opcode.MOTOROLA_CAPACITY_PLUS));
addFilter(new ControlMessageFilter("Motorola Connect Plus", Opcode.MOTOROLA_CONNECT_PLUS));
//Add all remaining opcodes that are not grouped into one of the above enumsets.
List<Opcode> otherOpcodes = new ArrayList<>();
for(Opcode opcode: Opcode.values())
{
if(!opcode.isGrouped())
{
otherOpcodes.add(opcode);
}
}
addFilter(new ControlMessageFilter("Other/Unknown", otherOpcodes));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof CSBKMessage && super.canProcess(message);
}
}

View File

@@ -0,0 +1,80 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.type.DataType;
import java.util.function.Function;
/**
* Filter for data messages, excluding CSBK messages.
*/
public class DataMessageFilter extends Filter<IMessage, DataType>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public DataMessageFilter()
{
super("Data Messages");
for(DataType dataType: DataType.values())
{
add(new FilterElement<>(dataType));
}
}
@Override
public Function<IMessage, DataType> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DataMessage && !(message instanceof CSBKMessage) && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,DataType>
{
@Override
public DataType apply(IMessage message)
{
if(message instanceof DataMessage dataMessage)
{
return dataMessage.getSlotType().getDataType();
}
return null;
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.filter.SyncLossMessageFilter;
import io.github.dsheirer.message.IMessage;
/**
* Filter set for DMR messages
*/
public class DmrMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public DmrMessageFilterSet()
{
super("DMR Messages");
addFilter(new ControlMessageFilterSet());
addFilter(new VoiceMessageFilter());
addFilter(new DataMessageFilter());
addFilter(new DmrPacketSequenceFilter());
addFilter(new LinkControlMessageFilterSet());
addFilter(new SyncLossMessageFilter());
addFilter(new DmrOtherMessageFilter());
}
}

View File

@@ -0,0 +1,73 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
import java.util.function.Function;
/**
* Filter for unknown messages, meaning messages not handled by any other filter.
*/
public class DmrOtherMessageFilter extends Filter<IMessage,String>
{
private static final String OTHER_KEY = "Other/Unknown Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public DmrOtherMessageFilter()
{
super("Other DMR messages");
add(new FilterElement<>(OTHER_KEY));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DMRMessage && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof DMRMessage)
{
return OTHER_KEY;
}
return null;
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraUnknownPacket;
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
import java.util.function.Function;
/**
* Filter for DMR packet sequence messages
*/
public class DmrPacketSequenceFilter extends Filter<IMessage,String>
{
private static final String KEY_ARS = "Automatic Registration Service (ARS)";
private static final String KEY_HYTERA_SMS = "Hytera Short Message Service (SMS)";
private static final String KEY_HYTERA_UNKNOWN = "Hytera Unknown";
private static final String KEY_LRRP = "Location Request/Response Protocol (LRRP)";
private static final String KEY_UDT_SMS = "Unified Data Transport - Short Message Service";
private static final String KEY_XCMP = "Extensible Command Message Protocol (XCMP)";
private static final String KEY_UNKNOWN = "Other/Unknown";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*/
public DmrPacketSequenceFilter()
{
super("Packet Sequence Messages");
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DMRPacketMessage && super.canProcess(message);
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof UDTShortMessageService)
{
return KEY_UDT_SMS;
}
else if(message instanceof DMRPacketMessage packetMessage)
{
if(packetMessage.getPacket() instanceof ARSPacket)
{
return KEY_ARS;
}
else if(packetMessage.getPacket() instanceof LRRPPacket)
{
return KEY_LRRP;
}
else if(packetMessage.getPacket() instanceof XCMPPacket)
{
return KEY_XCMP;
}
else if(packetMessage.getPacket() instanceof HyteraSmsPacket)
{
return KEY_HYTERA_SMS;
}
else if(packetMessage.getPacket() instanceof HyteraUnknownPacket)
{
return KEY_HYTERA_UNKNOWN;
}
//TODO: finish this
}
return KEY_UNKNOWN;
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
import java.util.Collection;
import java.util.function.Function;
/**
* Message filter for link control opcodes.
*
* Note: this does not include link control messages carried by data messages with embedded link control.
*/
public class LinkControlMessageFilter extends Filter<IMessage, LCOpcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public LinkControlMessageFilter(String name, Collection<LCOpcode> opcodes)
{
super(name);
for(LCOpcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
@Override
public Function<IMessage, LCOpcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LCOpcode>
{
@Override
public LCOpcode apply(IMessage message)
{
if(message instanceof LCMessage lcMessage)
{
return lcMessage.getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for short & voice-fragment link control messages
*
* Note: this filter set does NOT work for link control messages in Data Message with Link Control.
*/
public class LinkControlMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public LinkControlMessageFilterSet()
{
super("Short/Voice-Fragment Link Control Messages");
List<LCOpcode> fullLinkControl = new ArrayList<>();
List<LCOpcode> shortLinkControl = new ArrayList<>();
for(LCOpcode opcode: LCOpcode.values())
{
if(opcode.isFull())
{
fullLinkControl.add(opcode);
}
else
{
shortLinkControl.add(opcode);
}
}
addFilter(new LinkControlMessageFilter("Full Link Control", fullLinkControl));
addFilter(new LinkControlMessageFilter("Short Link Control", shortLinkControl));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LCMessage && super.canProcess(message);
}
}

View File

@@ -0,0 +1,86 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dmr.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
import java.util.function.Function;
/**
* Filter for voice messages
*/
public class VoiceMessageFilter extends Filter<IMessage, DMRSyncPattern>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*/
public VoiceMessageFilter()
{
super("Voice Messages");
add(new FilterElement<>(DMRSyncPattern.BASE_STATION_VOICE));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_B));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_C));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_D));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_E));
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_F));
add(new FilterElement<>(DMRSyncPattern.MOBILE_STATION_VOICE));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_B));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_C));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_D));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_E));
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_F));
add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_1));
add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_2));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof VoiceMessage && super.canProcess(message);
}
@Override
public Function<IMessage,DMRSyncPattern> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,DMRSyncPattern>
{
@Override
public DMRSyncPattern apply(IMessage message)
{
if(message instanceof VoiceMessage voice)
{
return voice.getSyncPattern();
}
return null;
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -27,18 +27,18 @@ public enum DataType
VOICE_HEADER(1, 288, "VOICE HEADER"),
TLC(2, 288, "TERMINATOR"),
CSBK(3, 288, "CSBK"),
MBC_HEADER(4, 288, "MBC HEADER"),
MBC_BLOCK(5, 276, "MBC"),
MBC_HEADER(4, 288, "MULTI-BLOCK CONTROL HEADER"),
MBC_BLOCK(5, 276, "MULTI-BLOCK CONTROL BLOCK"),
DATA_HEADER(6, 288, "DATA HEADER"),
RATE_1_OF_2_DATA(7, 276, "RATE 1/2 PACKET"),
RATE_3_OF_4_DATA(8, 276, "RATE 3/4 PACKET"),
SLOT_IDLE(9, 276, "IDLE"),
RATE_1_DATA(10, 276, "RATE 1/1 PACKET"),
USB_DATA(11, 276, "UNIFIED SINGLE BLOCK DATA"),
MBC_ENC_HEADER(12, 276, "MBC ENCRYPTED HEADER"),
MBC_ENC_HEADER(12, 276, "MULTI-BLOCK CONTROL ENCRYPTED HEADER"),
DATA_ENC_HEADER(13, 276, "DATA ENCRYPTED HEADER"),
CHANNEL_CONTROL_ENC_HEADER(14, 276, "CONTROL CHANNEL ENCRYPTED HEADER"),
RESERVED_15(15, -1, "RESERVED"),
RESERVED_15(15, -1, "RESERVED 15"),
UNKNOWN(-1, -1, "UNKNOWN");
private int mValue;

View File

@@ -0,0 +1,125 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event;
import java.awt.EventQueue;
import java.util.LinkedList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
/**
* AbstractTableModel implementation supporting clearable method options.
*/
public abstract class ClearableHistoryModel<T> extends AbstractTableModel
{
public static final int DEFAULT_HISTORY_SIZE = 200;
private LinkedList<T> mItems = new LinkedList<>();
private int mHistorySize = DEFAULT_HISTORY_SIZE;
/**
* Access an item/row by the model index value.
* @param index to retrieve
* @return item or null
*/
public T getItem(int index)
{
if(index < mItems.size())
{
return mItems.get(index);
}
return null;
}
/**
* Adds the item to the top of the item list and removes any tail items while the item list size exceeds the
* maximum history size for this model.
* @param item to add
*/
public void add(T item)
{
mItems.addFirst(item);
ClearableHistoryModel.this.fireTableRowsInserted(0, 0);
while(mItems.size() > mHistorySize)
{
mItems.removeLast();
super.fireTableRowsDeleted(mItems.size() - 1, mItems.size() - 1);
}
}
/**
* Clears all messages from history
*/
public void clear()
{
EventQueue.invokeLater(() -> {
mItems.clear();
fireTableDataChanged();
});
}
/**
* Clears the current messages and loads the messages argument
*/
public void clearAndSet(List<T> items)
{
EventQueue.invokeLater(() -> {
mItems.clear();
fireTableDataChanged();
for(T item: items)
{
add(item);
}
});
}
/**
* Current history size
* @return history size
*/
public int getHistorySize()
{
return mHistorySize;
}
/**
* Resets the history size to the default value.
*/
public void resetHistorySize()
{
setHistorySize(DEFAULT_HISTORY_SIZE);
}
/**
* Sets the history size
* @param historySize
*/
public void setHistorySize(int historySize)
{
mHistorySize = historySize;
}
@Override
public int getRowCount()
{
return mItems.size();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -32,27 +32,33 @@ public class DecodeEvent implements IDecodeEvent
{
private long mTimeStart;
private long mTimeEnd;
private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
private String mDetails;
private Protocol mProtocol;
private Integer mTimeslot;
private int mTimeslot = -1;
public DecodeEvent(long start)
/**
* Constructs an instance
* @param decodeEventType is mandatory to support event filtering by event type.
* @param start time of the event.
*/
public DecodeEvent(DecodeEventType decodeEventType, long start)
{
mDecodeEventType = decodeEventType;
mTimeStart = start;
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param decodeEventType for the event
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
return new DecodeEventBuilder(timeStart);
return new DecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -115,23 +121,6 @@ public class DecodeEvent implements IDecodeEvent
mTimeEnd = mTimeStart + duration;
}
/**
* Event description
*/
@Override
public String getEventDescription()
{
return mEventDescription;
}
/**
* Sets the event description text
*/
public void setEventDescription(String description)
{
mEventDescription = description;
}
/**
* {@link DecodeEventType}
*/
@@ -140,13 +129,6 @@ public class DecodeEvent implements IDecodeEvent
return mDecodeEventType;
}
/**
* Sets the {@link DecodeEventType}
*/
public void setEventType(DecodeEventType eventType) {
this.mDecodeEventType = eventType;
}
/**
* Identifier collection for this event.
*/
@@ -222,24 +204,24 @@ public class DecodeEvent implements IDecodeEvent
* @return timeslot or default value of 0
*/
@Override
public Integer getTimeslot()
public int getTimeslot()
{
return mTimeslot;
}
/**
* Indicates if this event specifies a timeslot
* Indicates if this event specifies a timeslot that is not negative
*/
public boolean hasTimeslot()
{
return mTimeslot != null;
return mTimeslot >= 0;
}
/**
* Sets the timeslot for this event
* @param timeslot of the event
*/
public void setTimeslot(Integer timeslot)
public void setTimeslot(int timeslot)
{
mTimeslot = timeslot;
}
@@ -249,7 +231,7 @@ public class DecodeEvent implements IDecodeEvent
{
StringBuilder sb = new StringBuilder();
sb.append(mProtocol);
sb.append(" DECODE EVENT: ").append(getEventDescription());
sb.append(" DECODE EVENT: ").append(getEventType());
sb.append(" DETAILS:").append(getDetails());
if(mIdentifierCollection != null)
{
@@ -267,19 +249,19 @@ public class DecodeEvent implements IDecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
protected String mDetails;
protected Protocol mProtocol = Protocol.UNKNOWN;
protected Integer mTimeslot;
protected int mTimeslot = -1;
/**
* Constructs a builder instance with the specified start time in milliseconds
*/
public DecodeEventBuilder(long timeStart)
public DecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -313,21 +295,6 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
public DecodeEventBuilder eventType(DecodeEventType eventType) {
mDecodeEventType = eventType;
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public DecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -359,7 +326,12 @@ public class DecodeEvent implements IDecodeEvent
return this;
}
public DecodeEventBuilder timeslot(Integer timeslot)
/**
* Sets the timeslot for this event
* @param timeslot
* @return
*/
public DecodeEventBuilder timeslot(int timeslot)
{
mTimeslot = timeslot;
return this;
@@ -370,11 +342,10 @@ public class DecodeEvent implements IDecodeEvent
*/
public DecodeEvent build()
{
DecodeEvent decodeEvent = new DecodeEvent(mTimeStart);
DecodeEvent decodeEvent = new DecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setTimeslot(mTimeslot);

View File

@@ -21,25 +21,20 @@ package io.github.dsheirer.module.decode.event;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.awt.EventQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
public class DecodeEventModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
/**
* Decode event table model
*/
public class DecodeEventModel extends ClearableHistoryModel<IDecodeEvent> implements Listener<IDecodeEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventModel.class);
public static final int COLUMN_TIME = 0;
public static final int COLUMN_DURATION = 1;
public static final int COLUMN_EVENT = 2;
@@ -50,12 +45,6 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
public static final int COLUMN_CHANNEL = 7;
public static final int COLUMN_FREQUENCY = 8;
public static final int COLUMN_DETAILS = 9;
protected int mMaxMessages = 200;
protected List<IDecodeEvent> mEvents = new ArrayList<>();
protected FilterSet<IDecodeEvent> mEventFilterSet = new DecodeEventFilterSet();
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public DecodeEventModel()
@@ -70,72 +59,10 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DECODE_EVENT)
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_TIME);
}
fireTableDataChanged();
}
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
for(int row = 0; row < mEvents.size(); row++)
{
fireTableCellUpdated(row, COLUMN_FROM_ID);
fireTableCellUpdated(row, COLUMN_TO_ID);
}
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
{
it.remove();
}
}
public void clear()
{
mEvents.clear();
fireTableDataChanged();
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();
fireTableDataChanged();
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
/**
@@ -146,49 +73,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
*/
public void receive(final IDecodeEvent event)
{
//Disabled until #1368 decode event is fully implemented
// if (!mEventFilterSet.passes(event))
// {
// return;
// }
if(!mEvents.contains(event))
{
mEvents.add(0, event);
fireTableRowsInserted(0, 0);
prune();
}
else
{
int row = mEvents.indexOf(event);
fireTableRowsUpdated(row, row);
}
}
private void prune()
{
while(mEvents.size() > mMaxMessages)
{
int index = mEvents.size() - 1;
mEvents.remove(index);
fireTableRowsDeleted(index, index);
}
}
@Override
public FilterSet<IDecodeEvent> getFilterSet() {
return mEventFilterSet;
}
@Override
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
this.mEventFilterSet = filterSet;
}
@Override
public int getRowCount()
{
return mEvents.size();
EventQueue.invokeLater(() -> add(event));
}
@Override
@@ -205,58 +90,55 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
synchronized(mEvents)
IDecodeEvent event = getItem(rowIndex);
if(event != null)
{
IDecodeEvent event = mEvents.get(rowIndex);
if(event != null)
switch(columnIndex)
{
switch(columnIndex)
{
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventDescription();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
case COLUMN_TIME:
return event.getTimeStart();
case COLUMN_DURATION:
return event.getDuration();
case COLUMN_EVENT:
return event.getEventType().getLabel();
case COLUMN_FROM_ID:
return event.getIdentifierCollection();
case COLUMN_FROM_ALIAS:
return event.getIdentifierCollection();
case COLUMN_TO_ID:
return event.getIdentifierCollection();
case COLUMN_TO_ALIAS:
return event.getIdentifierCollection();
case COLUMN_CHANNEL:
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
if(channelDescriptor != null)
if(channelDescriptor != null)
{
if(event.hasTimeslot())
{
if(event.hasTimeslot())
{
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
}
else
{
return channelDescriptor.toString();
}
return channelDescriptor + " TS:" + event.getTimeslot();
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
return channelDescriptor.toString();
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}
else
{
if(event.hasTimeslot())
{
return "TS:" + event.getTimeslot();
}
else
{
return null;
}
}
case COLUMN_FREQUENCY:
return event.getChannelDescriptor();
case COLUMN_DETAILS:
return event.getDetails();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -25,7 +25,6 @@ import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.identifier.Form;
@@ -33,10 +32,7 @@ import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
@@ -58,7 +54,10 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain>
{
@@ -75,7 +74,10 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private TimestampCellRenderer mTimestampCellRenderer;
private EventManagementPanel mEventManagementPanel;
private FilterSet<IDecodeEvent> mFilterSet = new DecodeEventFilterSet();
private TableRowSorter<TableModel> mTableRowSorter;
private HistoryManagementPanel<IDecodeEvent> mHistoryManagementPanel;
/**
* View for call event table
@@ -91,17 +93,20 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mTable = new JTable(mEventModel);
mTable.setAutoCreateRowSorter(true);
mTableRowSorter = new TableRowSorter<>(mEventModel);
mTableRowSorter.setRowFilter(new EventRowFilter());
mTable.setRowSorter(mTableRowSorter);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mEventManagementPanel = new EventManagementPanel();
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
//Disabled until #1368 decode event is fully implemented
// add(mEventManagementPanel, "span,growx");
mHistoryManagementPanel = new HistoryManagementPanel<>(mEventModel, "Event Filter Editor");
mHistoryManagementPanel.updateFilterSet(mFilterSet);
add(mHistoryManagementPanel, "span,growx");
mEmptyScroller = new JScrollPane(mTable);
add(mEmptyScroller);
//Register filter change listener to refresh the table any time the event filters are changed.
mFilterSet.register(() -> mEventModel.fireTableDataChanged());
}
public void dispose()
@@ -124,22 +129,14 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
private void updateCellRenderers()
{
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
.setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
.setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
.setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME).setCellRenderer(mTimestampCellRenderer);
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION).setCellRenderer(new DurationCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID).setCellRenderer(new IdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS).setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID).setCellRenderer(new IdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS).setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL).setCellRenderer(new ChannelDescriptorCellRenderer());
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY).setCellRenderer(new FrequencyCellRenderer());
}
@Override
@@ -156,76 +153,17 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mEventModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mEventModel);
mEventManagementPanel.enableButtons();
mHistoryManagementPanel.setEnabled(true);
}
else
{
mCurrentEventHistory = null;
mEventModel.clearAndSet(Collections.emptyList());
mEventManagementPanel.disableButtons();
mHistoryManagementPanel.setEnabled(false);
}
});
}
public class EventManagementPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private EventFilterButton<IDecodeEvent> mFilterButton;
private EventClearButton mEventClearButton;
public EventManagementPanel()
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
initializeFilterButton();
initializeClearButton();
disableButtons();
add(mFilterButton);
add(mEventClearButton);
}
public void enableButtons()
{
mFilterButton.setEnabled(true);
mEventClearButton.setEnabled(true);
}
public void disableButtons()
{
mFilterButton.setEnabled(false);
mEventClearButton.setEnabled(false);
}
private void initializeFilterButton()
{
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
if (filterProvider != null) {
filterSet = filterProvider.getFilterSet();
}
mFilterButton = new EventFilterButton<>("Message Filter Editor", filterSet);
}
private void initializeClearButton() {
mEventClearButton = new EventClearButton(
((DecodeEventModel) mTable.getModel()).getMaxMessageCount()
);
mEventClearButton.setEventClearHandler(new EventClearHandler() {
@Override
public void onHistoryLimitChanged(int newHistoryLimit) {
((DecodeEventModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
}
@Override
public void onClearHistoryClicked() {
((DecodeEventModel) mTable.getModel()).clear();
}
});
}
}
/**
* Custom cell renderer for displaying identifiers from an identifier collection
*/
@@ -479,4 +417,26 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
setHorizontalAlignment(JLabel.CENTER);
}
}
/**
* Row filter for decode events
*/
public class EventRowFilter extends RowFilter<TableModel, Integer>
{
@Override
public boolean include(Entry<? extends TableModel, ? extends Integer> entry)
{
if(entry.getModel() instanceof DecodeEventModel model)
{
IDecodeEvent event = model.getItem(entry.getIdentifier());
if(event != null)
{
return mFilterSet.canProcess(event) && mFilterSet.passes(event);
}
}
return false;
}
}
}

View File

@@ -19,10 +19,17 @@
package io.github.dsheirer.module.decode.event;
import java.util.Arrays;
import java.util.EnumSet;
/**
* Enumeration of event types for decoded events.
*/
public enum DecodeEventType
{
AFFILIATE("Affiliate"),
ANNOUNCEMENT("Announcement"),
ACKNOWLEDGE("Acknowledge"),
AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"),
CALL("Call"),
CALL_ENCRYPTED("Encrypted Call"),
@@ -32,6 +39,7 @@ public enum DecodeEventType
CALL_PATCH_GROUP_ENCRYPTED("Encrypted Patch Call"),
CALL_ALERT("Call Alert"),
CALL_DETECT("Call Detect"),
CALL_IN_PROGRESS("Call In Progress"),
CALL_DO_NOT_MONITOR("Call-Do Not Monitor"),
CALL_END("Call End"),
CALL_INTERCONNECT("Telephone Call"),
@@ -41,6 +49,7 @@ public enum DecodeEventType
CALL_UNIT_TO_UNIT_ENCRYPTED("Encrypted Unit To Unit Call"),
CALL_NO_TUNER("Call - No Tuner"),
CALL_TIMEOUT("Call Timeout"),
CELLOCATOR("Cellocator"),
COMMAND("Command"),
DATA_CALL("Data Call"),
DATA_CALL_ENCRYPTED("Encrypted Data Call"),
@@ -53,6 +62,7 @@ public enum DecodeEventType
ID_ANI("ANI"),
ID_UNIQUE("Unique ID"),
IP_PACKET("IP Packet"),
LRRP("Motorola LRRP"),
NOTIFICATION("Notification"),
PAGE("Page"),
QUERY("Query"),
@@ -72,16 +82,82 @@ public enum DecodeEventType
private String mLabel;
/**
* Encrypted voice call event types for filtering
*/
public static final EnumSet<DecodeEventType> VOICE_CALLS_ENCRYPTED = EnumSet.of(DecodeEventType.CALL_ENCRYPTED,
DecodeEventType.CALL_GROUP_ENCRYPTED, DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED);
/**
* Voice call event types for filtering
*/
public static final EnumSet<DecodeEventType> VOICE_CALLS = EnumSet.of(DecodeEventType.CALL_GROUP,
DecodeEventType.CALL_PATCH_GROUP, DecodeEventType.CALL_ALERT, DecodeEventType.CALL_DETECT,
DecodeEventType.CALL_DO_NOT_MONITOR, DecodeEventType.CALL_END, DecodeEventType.CALL_INTERCONNECT,
DecodeEventType.CALL_UNIQUE_ID, DecodeEventType.CALL_UNIT_TO_UNIT, DecodeEventType.CALL_NO_TUNER,
DecodeEventType.CALL_TIMEOUT);
/**
* Command event types for filtering
*/
public static final EnumSet<DecodeEventType> COMMANDS = EnumSet.of(DecodeEventType.ANNOUNCEMENT,
DecodeEventType.STATION_ID, DecodeEventType.ACKNOWLEDGE, DecodeEventType.PAGE, DecodeEventType.QUERY,
DecodeEventType.RADIO_CHECK, DecodeEventType.STATUS, DecodeEventType.COMMAND, DecodeEventType.EMERGENCY,
DecodeEventType.NOTIFICATION, DecodeEventType.FUNCTION);
/**
* Data call event types for filtering
*/
public static final EnumSet<DecodeEventType> DATA_CALLS = EnumSet.of(DecodeEventType.DATA_CALL,
DecodeEventType.DATA_CALL_ENCRYPTED, DecodeEventType.DATA_PACKET, DecodeEventType.GPS,
DecodeEventType.IP_PACKET, DecodeEventType.UDP_PACKET, DecodeEventType.SDM, DecodeEventType.ID_ANI,
DecodeEventType.ID_UNIQUE);
/**
* Registration event types for filtering
*/
public static final EnumSet<DecodeEventType> REGISTRATION = EnumSet.of(DecodeEventType.AFFILIATE,
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, DecodeEventType.REGISTER, DecodeEventType.REGISTER_ESN,
DecodeEventType.DEREGISTER, DecodeEventType.REQUEST, DecodeEventType.RESPONSE, DecodeEventType.RESPONSE_PACKET);
/**
* All other event types of this enumeration that are not included in the groupings above.
*/
public static final EnumSet<DecodeEventType> OTHERS = EnumSet.copyOf(Arrays.stream(DecodeEventType.values())
.filter(decodeEventType -> !decodeEventType.isGrouped()).toList());
/**
* Constructor
* @param label for the element
*/
DecodeEventType(String label)
{
mLabel = label;
}
/**
* Indicates if the enumeration element is contained in one of the enumset groupings above.
* @return true if the element is grouped.
*/
public boolean isGrouped()
{
return VOICE_CALLS.contains(this) || VOICE_CALLS_ENCRYPTED.contains(this) || COMMANDS.contains(this) ||
DATA_CALLS.contains(this) || REGISTRATION.contains(this);
}
/**
* Label or pretty value for the element
* @return label
*/
public String getLabel()
{
return mLabel;
}
/**
* Uses label as the default string value.
*/
public String toString()
{
return mLabel;

View File

@@ -0,0 +1,212 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event;
import io.github.dsheirer.filter.FilterEditor;
import io.github.dsheirer.filter.FilterSet;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import net.miginfocom.swing.MigLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
/**
* History management panel with controls for managing item histories.
*/
public class HistoryManagementPanel<T> extends JPanel
{
private ClearableHistoryModel mModel;
private FilterSet<T> mFilterSet;
private FilterEditor<T> mFilterEditor;
private JButton mClearButton;
private JButton mFilterButton;
private JSlider mHistorySlider;
private JLabel mHistoryTitleLabel;
private JLabel mHistoryValueLabel;
private String mFilterEditorTitle;
/**
* Constructs an instance
* @param model to manage
*/
public HistoryManagementPanel(ClearableHistoryModel model, String filterEditorTitle)
{
mModel = model;
mFilterEditorTitle = filterEditorTitle;
setLayout(new MigLayout("insets 6 1 5 5", "[]5[]10[]5[]5[][grow]", ""));
add(getFilterButton());
add(getClearButton());
add(getHistoryTitleLabel());
add(getHistorySlider());
add(getHistoryValueLabel());
setEnabled(false);
}
/**
* Updates the filter set
* @param filterSet to use
*/
public void updateFilterSet(FilterSet<T> filterSet)
{
mFilterSet = filterSet;
if(mFilterEditor != null)
{
getFilterEditor().updateFilterSet(filterSet);
}
}
/**
* Overrides the panel method to also set the enabled state for the child controls.
* @param enabled true if this component should be enabled, false otherwise
*/
@Override
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
getClearButton().setEnabled(enabled);
getFilterButton().setEnabled(enabled);
getHistoryValueLabel().setEnabled(enabled);
getHistoryTitleLabel().setEnabled(enabled);
getHistorySlider().setEnabled(enabled);
}
/**
* Filter editor
* @return editor lazily constructed
*/
private FilterEditor<T> getFilterEditor()
{
if(mFilterEditor == null)
{
mFilterEditor = new FilterEditor<>(mFilterEditorTitle, getFilterButton(), mFilterSet);
}
return mFilterEditor;
}
/**
* Filter button for accessing the filter editor
* @return filter button
*/
private JButton getFilterButton()
{
if(mFilterButton == null)
{
mFilterButton = new JButton("Filters");
mFilterButton.setToolTipText("Edit filters");
mFilterButton.addActionListener(arg0 -> EventQueue.invokeLater(() -> getFilterEditor().setVisible(true)));
}
return mFilterButton;
}
/**
* Clear button
* @return clear button
*/
private JButton getClearButton()
{
if(mClearButton == null)
{
mClearButton = new JButton("Clear");
mClearButton.addActionListener(e -> mModel.clear());
mClearButton.setToolTipText("Clears the history");
}
return mClearButton;
}
/**
* History value label.
* @return label
*/
private JLabel getHistoryValueLabel()
{
if(mHistoryValueLabel == null)
{
mHistoryValueLabel = new JLabel(String.valueOf(ClearableHistoryModel.DEFAULT_HISTORY_SIZE));
}
return mHistoryValueLabel;
}
/**
* History title label
* @return label
*/
private JLabel getHistoryTitleLabel()
{
if(mHistoryTitleLabel == null)
{
mHistoryTitleLabel = new JLabel("History:");
}
return mHistoryTitleLabel;
}
/**
* History value slider control
* @return slider
*/
private JSlider getHistorySlider()
{
if(mHistorySlider == null)
{
mHistorySlider = new JSlider();
mHistorySlider.setToolTipText("Adjust history size. Double-click to reset to default 200");
mHistorySlider.setMinimum(0);
mHistorySlider.setMaximum(2000);
mHistorySlider.setMinorTickSpacing(25);
mHistorySlider.setMajorTickSpacing(500);
mHistorySlider.setPaintTicks(false);
mHistorySlider.setPaintLabels(false);
mHistorySlider.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent arg0)
{
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
{
mHistorySlider.setValue(ClearableHistoryModel.DEFAULT_HISTORY_SIZE);
}
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0) {}
});
mHistorySlider.setValue(mModel.getHistorySize());
mHistorySlider.addChangeListener(e -> {
mModel.setHistorySize(mHistorySlider.getValue());
getHistoryValueLabel().setText(String.valueOf(mHistorySlider.getValue()));
});
}
return mHistorySlider;
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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,11 +48,6 @@ public interface IDecodeEvent
*/
long getDuration();
/**
* A description of the event or event type
*/
String getEventDescription();
/**
* Collection of identifiers associated with the event. This collection should contain a
* Role.FROM and a Role.TO identifier, a Decoder Type identifier, and (optionally) an Alias List
@@ -86,7 +81,7 @@ public interface IDecodeEvent
* Timeslot for the event.
* @return timeslot or default of 0
*/
Integer getTimeslot();
int getTimeslot();
/**
* Indicates if the event has a timeslot specified

View File

@@ -18,19 +18,16 @@
*/
package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.filter.AllPassFilter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.StuffBitsMessage;
import io.github.dsheirer.sample.Listener;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public class MessageActivityModel extends AbstractTableModel implements Listener<IMessage>
/**
* Table Model for decoded IMessages.
*/
public class MessageActivityModel extends ClearableHistoryModel<MessageItem> implements Listener<IMessage>
{
private static final long serialVersionUID = 1L;
private static final int TIME = 0;
@@ -38,95 +35,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
private static final int TIMESLOT = 2;
private static final int MESSAGE = 3;
protected int mMaxMessages = 200;
protected LinkedList<MessageItem> mMessageItems = new LinkedList<>();
protected int[] mColumnWidths = {20, 20, 500};
protected String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
private String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
private SimpleDateFormat mSDFTime = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
private FilterSet<IMessage> mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
/**
* Constructor
*/
public MessageActivityModel()
{
}
/**
* Applies the filter set
* Implements the listener interface and wraps the IMessage in table-compatible message item wrapper.
* @param message to add to the model
*/
public void setFilters(FilterSet filterSet)
{
mMessageFilterSet = filterSet;
}
public void clearFilters()
{
mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
}
/**
* Clears all messages from history
*/
public void clear()
{
EventQueue.invokeLater(() -> {
mMessageItems.clear();
fireTableDataChanged();
});
}
/**
* Clears the current messages and loads the messages argument
*/
public void clearAndSet(List<IMessage> messages)
{
EventQueue.invokeLater(() -> {
mMessageItems.clear();
fireTableDataChanged();
for(IMessage message: messages)
{
receive(message);
}
});
}
public FilterSet<IMessage> getMessageFilterSet()
{
return mMessageFilterSet;
}
public void dispose()
{
mMessageItems.clear();
}
public int[] getColumnWidths()
{
return mColumnWidths;
}
public void setColumnWidths(int[] widths)
{
if(widths.length != 3)
{
throw new IllegalArgumentException("MessageActivityModel - column widths array should have 3 elements");
}
else
{
mColumnWidths = widths;
}
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount(int count)
{
mMaxMessages = count;
}
public void receive(final IMessage message)
{
//Don't process tail bits or stuff bits message fragments
@@ -135,39 +57,7 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
return;
}
if(mMessageFilterSet.passes(message))
{
final MessageItem messageItem = new MessageItem(message);
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
mMessageItems.addFirst(messageItem);
MessageActivityModel.this.fireTableRowsInserted(0, 0);
prune();
}
});
}
}
private void prune()
{
while(mMessageItems.size() > mMaxMessages)
{
MessageItem removed = mMessageItems.removeLast();
removed.dispose();
super.fireTableRowsDeleted(mMessageItems.size() - 1, mMessageItems.size() - 1);
}
}
@Override
public int getRowCount()
{
return mMessageItems.size();
EventQueue.invokeLater(() -> add(new MessageItem(message)));
}
@Override
@@ -184,20 +74,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(0 <= rowIndex && rowIndex < mMessageItems.size())
{
MessageItem messageItem = mMessageItems.get(rowIndex);
MessageItem item = getItem(rowIndex);
if(item != null)
{
switch(columnIndex)
{
case TIME:
return messageItem.getTimestamp(mSDFTime);
return item.getTimestamp(mSDFTime);
case PROTOCOL:
return messageItem.getProtocol();
return item.getProtocol();
case TIMESLOT:
return messageItem.getTimeslot();
return item.getTimeslot();
case MESSAGE:
return messageItem.getText();
return item.getText();
default:
break;
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,13 +14,10 @@
*
* 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 io.github.dsheirer.module.decode.event;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSplitButton;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageHistory;
@@ -30,37 +26,35 @@ import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import java.util.ArrayList;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
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 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.RowFilter;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
/**
* Panel to display decoded messages/activity.
*/
public class MessageActivityPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
private static final String TABLE_PREFERENCE_KEY = "message.activity.panel";
private static MessageActivityModel mMessageModel = new MessageActivityModel();
private static final Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
private final String TABLE_PREFERENCE_KEY = "message.activity.panel";
private MessageActivityModel mMessageModel = new MessageActivityModel();
private MessageHistory mCurrentMessageHistory;
private JTable mTable = new JTable(mMessageModel);
private TableRowSorter<TableModel> mTableRowSorter;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private UserPreferences mUserPreferences;
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
private FilterSet<IMessage> mMessageFilterSet;
private HistoryManagementPanel<IMessage> mHistoryManagementPanel;
/**
* Constructs an instance
@@ -69,13 +63,14 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
public MessageActivityPanel(UserPreferences userPreferences)
{
mUserPreferences = userPreferences;
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
add(mManagementPanel, "span,growx");
add(new JScrollPane(mTable), "span,grow");
mTableRowSorter = new TableRowSorter<>(mMessageModel);
mTableRowSorter.setRowFilter(new MessageRowFilter());
mTable.setRowSorter(mTableRowSorter);
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
mHistoryManagementPanel = new HistoryManagementPanel<>(mMessageModel, "Message Filter Editor");
add(mHistoryManagementPanel, "span,growx");
add(new JScrollPane(mTable), "span,grow");
}
/**
@@ -89,182 +84,62 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
mCurrentMessageHistory.removeListener(mMessageModel);
}
//Unregister from changes made to the filter set
if(mMessageFilterSet != null)
{
mMessageFilterSet.register(null);
}
if(processingChain != null)
{
mCurrentMessageHistory = processingChain.getMessageHistory();
mMessageModel.setFilters(DecoderFactory.getMessageFilters(processingChain.getModules()));
mMessageModel.clearAndSet(mCurrentMessageHistory.getItems());
mMessageFilterSet = DecoderFactory.getMessageFilters(processingChain.getModules());
//Register filter change listener to refresh the table any time the event filters are changed.
mMessageFilterSet.register(() -> mMessageModel.fireTableDataChanged());
if(mHistoryManagementPanel != null)
{
mHistoryManagementPanel.updateFilterSet(mMessageFilterSet);
}
List<MessageItem> currentHistory = new ArrayList<>();
for(IMessage message: mCurrentMessageHistory.getItems())
{
currentHistory.add(new MessageItem(message));
}
mMessageModel.clearAndSet(currentHistory);
mCurrentMessageHistory.addListener(mMessageModel);
mManagementPanel.enableButtons();
mHistoryManagementPanel.setEnabled(true);
}
else
{
mCurrentMessageHistory = null;
mMessageModel.clearFilters();
mMessageFilterSet = null;
mMessageModel.clear();
mManagementPanel.disableButtons();
mHistoryManagementPanel.setEnabled(false);
}
}
public class MessageManagementPanel extends JPanel
/**
* Row visibility filter for messages
*/
public class MessageRowFilter extends RowFilter<TableModel, Integer>
{
private static final long serialVersionUID = 1L;
private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
private MessageFilterButton mFilterButton = new MessageFilterButton();
public MessageManagementPanel()
@Override
public boolean include(Entry<? extends TableModel, ? extends Integer> entry)
{
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
disableButtons();
add(mFilterButton);
add(mHistoryButton);
}
public void enableButtons()
{
mHistoryButton.setEnabled(true);
mFilterButton.setEnabled(true);
}
public void disableButtons()
{
mHistoryButton.setEnabled(false);
mFilterButton.setEnabled(false);
}
}
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()
if(entry.getModel() instanceof MessageActivityModel model)
{
@Override
public void mouseClicked(MouseEvent arg0)
MessageItem item = model.getItem(entry.getIdentifier());
if(mMessageFilterSet != null && item != null && item.getMessage() != null)
{
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
{
slider.setValue(500); //default
}
IMessage message = item.getMessage();
return mMessageFilterSet.canProcess(message) && mMessageFilterSet.passes(message);
}
}
public void mouseEntered(MouseEvent arg0)
{
}
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)
{
((MessageActivityModel) mTable.getModel()).clear();
}
});
}
}
public class MessageFilterButton extends JideButton
{
private static final long serialVersionUID = 1L;
public MessageFilterButton()
{
super("Filter");
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
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][][]"));
@SuppressWarnings("unchecked")
FilterSet<IMessage> filter = (FilterSet<IMessage>) ((MessageActivityModel) mTable.getModel())
.getMessageFilterSet();
FilterEditorPanel<IMessage> panel = new FilterEditorPanel<>(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);
}
});
}
});
return false;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.event;
@@ -31,19 +30,20 @@ public class PlottableDecodeEvent extends DecodeEvent
private double mHeading;
private double mSpeed;
public PlottableDecodeEvent(long start)
public PlottableDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param decodeEventType for the event
* @param timeStart for the event
* @return builder
*/
public static PlottableDecodeEventBuilder plottableBuilder(long timeStart)
public static PlottableDecodeEventBuilder plottableBuilder(DecodeEventType decodeEventType, long timeStart)
{
return new PlottableDecodeEventBuilder(timeStart);
return new PlottableDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -102,7 +102,7 @@ public class PlottableDecodeEvent extends DecodeEvent
{
private long mTimeStart;
private long mDuration;
private String mEventDescription;
private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
private String mDetails;
@@ -114,8 +114,9 @@ public class PlottableDecodeEvent extends DecodeEvent
/**
* Constructs a builder instance with the specified start time in milliseconds
*/
public PlottableDecodeEventBuilder(long timeStart)
public PlottableDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -149,16 +150,6 @@ public class PlottableDecodeEvent extends DecodeEvent
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public PlottableDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -222,11 +213,10 @@ public class PlottableDecodeEvent extends DecodeEvent
*/
public PlottableDecodeEvent build()
{
PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart);
PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setLocation(mGeoPosition);
@@ -235,5 +225,4 @@ public class PlottableDecodeEvent extends DecodeEvent
return decodeEvent;
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
/**
* Event filter for decode event types that are not specified in other event filters.
*
* This is intended as a catch-all for any elements of the DecodeEventType enumeration that may get added in the
* future and are not deliberately added to the various event filter groupings listed in the enumeration.
*/
public class AllOtherEventFilter extends EventFilter
{
/**
* Constructor
*/
public AllOtherEventFilter()
{
super("Other", DecodeEventType.OTHERS);
}
}

View File

@@ -1,16 +1,40 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent> {
public DecodeEventFilterSet() {
super ("All Messages");
/**
* Top-level decode event filter set.
*/
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent>
{
public DecodeEventFilterSet()
{
super ("All Events");
addFilter(new DecodedCallEventFilter());
addFilter(new DecodedCallEncryptedEventFilter());
addFilter(new DecodedDataEventFilter());
addFilter(new DecodedCommandEventFilter());
addFilter(new DecodedRegistrationEventFilter());
addFilter(new AllOtherEventFilter());
}
}

View File

@@ -1,19 +1,36 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Encrypted call events filter
*/
public class DecodedCallEncryptedEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedCallEncryptedEventFilter()
{
super("Voice Calls - Encrypted", Arrays.asList(
DecodeEventType.CALL_ENCRYPTED,
DecodeEventType.CALL_GROUP_ENCRYPTED,
DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED,
DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED
));
super("Voice Calls - Encrypted", DecodeEventType.VOICE_CALLS_ENCRYPTED);
}
}

View File

@@ -1,25 +1,36 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Call events filter
*/
public class DecodedCallEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedCallEventFilter()
{
super("Voice Calls", Arrays.asList(
DecodeEventType.CALL_GROUP,
DecodeEventType.CALL_PATCH_GROUP,
DecodeEventType.CALL_ALERT,
DecodeEventType.CALL_DETECT,
DecodeEventType.CALL_DO_NOT_MONITOR,
DecodeEventType.CALL_END,
DecodeEventType.CALL_INTERCONNECT,
DecodeEventType.CALL_UNIQUE_ID,
DecodeEventType.CALL_UNIT_TO_UNIT,
DecodeEventType.CALL_NO_TUNER,
DecodeEventType.CALL_TIMEOUT
));
super("Voice Calls", DecodeEventType.VOICE_CALLS);
}
}

View File

@@ -1,24 +1,36 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Event filter for network commands.
*/
public class DecodedCommandEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedCommandEventFilter()
{
super("Commands", Arrays.asList(
DecodeEventType.ANNOUNCEMENT,
DecodeEventType.STATION_ID,
DecodeEventType.PAGE,
DecodeEventType.QUERY,
DecodeEventType.RADIO_CHECK,
DecodeEventType.STATUS,
DecodeEventType.COMMAND,
DecodeEventType.EMERGENCY,
DecodeEventType.NOTIFICATION,
DecodeEventType.FUNCTION
));
super("Commands", DecodeEventType.COMMANDS);
}
}

View File

@@ -1,23 +1,36 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Event filter for data call event types
*/
public class DecodedDataEventFilter extends EventFilter
{
/**
* Constructs an instance
*/
public DecodedDataEventFilter()
{
super("Data Calls", Arrays.asList(
DecodeEventType.DATA_CALL,
DecodeEventType.DATA_CALL_ENCRYPTED,
DecodeEventType.DATA_PACKET,
DecodeEventType.GPS,
DecodeEventType.IP_PACKET,
DecodeEventType.UDP_PACKET,
DecodeEventType.SDM,
DecodeEventType.ID_ANI,
DecodeEventType.ID_UNIQUE
));
super("Data Calls", DecodeEventType.DATA_CALLS);
}
}

View File

@@ -1,22 +1,36 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import java.util.Arrays;
/**
* Event filter for registration related events
*/
public class DecodedRegistrationEventFilter extends EventFilter
{
/**
* Constructor
*/
public DecodedRegistrationEventFilter()
{
super("Registrations", Arrays.asList(
DecodeEventType.AFFILIATE,
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE,
DecodeEventType.REGISTER,
DecodeEventType.REGISTER_ESN,
DecodeEventType.DEREGISTER,
DecodeEventType.REQUEST,
DecodeEventType.RESPONSE,
DecodeEventType.RESPONSE_PACKET
));
super("Registrations", DecodeEventType.REGISTRATION);
}
}

View File

@@ -1,15 +1,33 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideSplitButton;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class EventClearButton extends JideSplitButton
{
private static final long serialVersionUID = 1L;
@@ -21,37 +39,29 @@ public class EventClearButton extends JideSplitButton
JPanel historyPanel = new JPanel();
historyPanel.add(new JLabel("History Entries:"));
historyPanel.add(new JLabel("History Size:"));
final JSlider historySlider = initializeHistorySlider();
JLabel valueLabel = new JLabel(String.valueOf(maxHistoryCount));
historySlider.setValue(maxHistoryCount);
historySlider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent arg0)
historySlider.addChangeListener(arg0 -> {
if (mEventClearHandler != null)
{
if (mEventClearHandler != null)
{
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
}
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
}
valueLabel.setText(String.valueOf(historySlider.getValue()));
});
historyPanel.add(historySlider);
historyPanel.add(valueLabel);
add(historyPanel);
/* This handles the click action on the main button. Clear messages */
addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
addActionListener(e -> {
if (mEventClearHandler != null)
{
if (mEventClearHandler != null)
{
mEventClearHandler.onClearHistoryClicked();
}
mEventClearHandler.onClearHistoryClicked();
}
});
}

View File

@@ -1,6 +1,37 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
public interface EventClearHandler {
/**
* Handler interface for changes to the event history size or content.
*/
public interface EventClearHandler
{
/**
* Invoked when the retained history size limit is changed
* @param newHistoryLimit new size
*/
void onHistoryLimitChanged(int newHistoryLimit);
/**
* Invoked when the history is cleared
*/
void onClearHistoryClicked();
}

View File

@@ -1,56 +1,74 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.function.Function;
/**
* This is used as a base for {@link IDecodeEvent} types.
* This will take a list of {@link IDecodeEvent}s and generate filters for the list.
* This will default canProcess to TRUE. Override if other value or evaluation is needed.
*
*/
public class EventFilter extends Filter<IDecodeEvent>
public class EventFilter extends Filter<IDecodeEvent,DecodeEventType>
{
private Map<DecodeEventType, FilterElement<DecodeEventType>> mElements = new EnumMap<>(DecodeEventType.class);
private EventKeyExtractor mKeyExtractor = new EventKeyExtractor();
public EventFilter(String name, List<DecodeEventType> eventTypes)
/**
* Constructs an instance
* @param name of this filter
* @param decodeEventTypes to filter against
*/
public EventFilter(String name, Collection<DecodeEventType> decodeEventTypes)
{
super(name);
for (DecodeEventType eventType : eventTypes) {
mElements.put(eventType, new FilterElement<>(eventType));
}
}
@Override
public boolean passes(IDecodeEvent iDecodeEvent)
{
if (mEnabled && canProcess(iDecodeEvent))
for (DecodeEventType decodeEventType : decodeEventTypes)
{
if (mElements.containsKey(iDecodeEvent.getEventType()))
{
return mElements.get(iDecodeEvent.getEventType()).isEnabled();
}
add(new FilterElement<>(decodeEventType));
}
return false;
}
/**
* Key extractor function
* @return function.
*/
@Override
public boolean canProcess(IDecodeEvent iDecodeEvent)
public Function<IDecodeEvent, DecodeEventType> getKeyExtractor()
{
return true;
return mKeyExtractor;
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor function for decode events to extract the decode event type
*/
public static class EventKeyExtractor implements Function<IDecodeEvent,DecodeEventType>
{
return new ArrayList<>(mElements.values());
@Override
public DecodeEventType apply(IDecodeEvent iDecodeEvent)
{
return iDecodeEvent.getEventType();
}
}
}

View File

@@ -1,84 +1,103 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.event.filter;
import com.jidesoft.swing.JideButton;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import net.miginfocom.swing.MigLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import net.miginfocom.swing.MigLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
/**
* Event filter button that includes split button functionality to allow user to select filter items.
*
* @param <T> type of filter.
*/
public class EventFilterButton<T> extends JideButton
{
private static final long serialVersionUID = 1L;
/**
* Constructs an instance
* @param dialogTitle for the dialog/panel that appears
* @param filterSet to include in the editor panel.
*/
public EventFilterButton(String dialogTitle, FilterSet<T> filterSet)
{
this("Filter", dialogTitle, filterSet);
}
/**
* Constructs an instance
* @param buttonLabel to use on the button
* @param dialogTitle for the dialog/panel that appears
* @param filterSet to include in the editor panel.
*/
public EventFilterButton(String buttonLabel, String dialogTitle, FilterSet<T> filterSet)
{
super(buttonLabel);
addActionListener(
new EventFilterActionHandler(dialogTitle, filterSet)
);
addActionListener(new EventFilterActionHandler(dialogTitle, filterSet));
}
/**
* Action handler for the button
*/
public class EventFilterActionHandler implements ActionListener
{
private String title;
private FilterSet<T> filterSet;
private String mTitle;
private FilterSet<T> mFilterSet;
public EventFilterActionHandler( String title, FilterSet<T> filterSet)
/**
* Constructs an instance
* @param title for this panel
* @param filterSet to edit
*/
public EventFilterActionHandler(String title, FilterSet<T> filterSet)
{
this.title = title;
this.filterSet = filterSet;
mTitle = title;
mFilterSet = filterSet;
}
@Override
public void actionPerformed(ActionEvent e)
{
final JFrame editor = new JFrame();
editor.setTitle(title);
editor.setTitle(mTitle);
editor.setLocationRelativeTo(EventFilterButton.this);
editor.setSize(600, 400);
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
editor.setLayout(new MigLayout("", "[grow,fill]",
"[grow,fill][][]"));
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(filterSet);
editor.setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][][]"));
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(mFilterSet);
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();
}
});
close.addActionListener(e1 -> editor.dispose());
editor.add(close);
EventQueue.invokeLater(new Runnable()
{
public void run()
{
editor.setVisible(true);
}
});
EventQueue.invokeLater(() -> editor.setVisible(true));
}
}
}

View File

@@ -1,8 +0,0 @@
package io.github.dsheirer.module.decode.event.filter;
import io.github.dsheirer.filter.FilterSet;
public interface EventFilterProvider<T> {
FilterSet<T> getFilterSet();
void setFilterSet(FilterSet<T> filterSet);
}

View File

@@ -107,13 +107,7 @@ public class Fleetsync2DecoderState extends DecoderState
case ANI:
case EMERGENCY:
case LONE_WORKER_EMERGENCY:
DecodeEvent aniEvent = DecodeEvent.builder(fleetsync.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(fleetsync.getMessageType().toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.build();
DecodeEvent aniEvent = getDecodeEvent(fleetsync, getDecodeEventType(fleetsync.getMessageType()));
broadcast(aniEvent);
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.CALL));
break;
@@ -121,19 +115,13 @@ public class Fleetsync2DecoderState extends DecoderState
case PAGING:
case STATUS:
case UNKNOWN:
DecodeEvent statusEvent = DecodeEvent.builder(fleetsync.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(fleetsync.getMessageType().toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.build();
DecodeEvent statusEvent = getDecodeEvent(fleetsync, getDecodeEventType(fleetsync.getMessageType()));
broadcast(statusEvent);
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA));
break;
case GPS:
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp())
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, fleetsync.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.protocol(Protocol.FLEETSYNC)
@@ -147,6 +135,35 @@ public class Fleetsync2DecoderState extends DecoderState
}
}
private DecodeEvent getDecodeEvent(Fleetsync2Message fleetsync, DecodeEventType eventType) {
return DecodeEvent.builder(eventType, fleetsync.getTimestamp())
.channel(getCurrentChannel())
.details(fleetsync.getMessageType() + " " + fleetsync)
.identifiers(getIdentifierCollection().copyOf())
.protocol(Protocol.FLEETSYNC)
.build();
}
private DecodeEventType getDecodeEventType(FleetsyncMessageType fleetsyncMessageType) {
switch (fleetsyncMessageType) {
case ANI:
return DecodeEventType.ID_ANI;
case EMERGENCY:
case LONE_WORKER_EMERGENCY:
return DecodeEventType.EMERGENCY;
case ACKNOWLEDGE:
return DecodeEventType.ACKNOWLEDGE;
case PAGING:
return DecodeEventType.PAGE;
case STATUS:
return DecodeEventType.STATUS;
case GPS:
return DecodeEventType.GPS;
default:
return DecodeEventType.UNKNOWN;
}
}
/**
* Responds to reset events issued by the channel state
*/

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.fleetsync2;
@@ -24,52 +23,63 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message;
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
import java.util.function.Function;
import java.util.*;
public class FleetsyncMessageFilter extends Filter<IMessage>
/**
* Filter for Fleetsync messages.
*/
public class FleetsyncMessageFilter extends Filter<IMessage,FleetsyncMessageType>
{
private Map<FleetsyncMessageType,FilterElement<FleetsyncMessageType>> mElements = new EnumMap<>(FleetsyncMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public FleetsyncMessageFilter()
{
super("Fleetsync Message Filter");
super("Fleetsync Messages");
for(FleetsyncMessageType type : FleetsyncMessageType.values())
{
if(type != FleetsyncMessageType.UNKNOWN)
{
mElements.put(type, new FilterElement<FleetsyncMessageType>(type));
}
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
Fleetsync2Message fleet = (Fleetsync2Message)message;
if(mElements.containsKey(fleet.getMessageType()))
{
return mElements.get(fleet.getMessageType()).isEnabled();
}
}
return false;
}
/**
* Limits processing to only fleetsync messages.
* @param message to test
* @return true if message type is correct.
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof Fleetsync2Message;
return message instanceof Fleetsync2Message && super.canProcess(message);
}
/**
* Key extractor for fleetsync messages
* @return key extractor
*/
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, FleetsyncMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor for Fleetsync 2 Messages.
*/
private class KeyExtractor implements Function<IMessage, FleetsyncMessageType>
{
@Override
public FleetsyncMessageType apply(IMessage message)
{
if(message instanceof Fleetsync2Message f2m)
{
return f2m.getMessageType();
}
return null;
}
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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 io.github.dsheirer.module.decode.lj1200;
@@ -30,14 +27,15 @@ import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LJ1200DecoderState extends DecoderState
{
@@ -72,11 +70,11 @@ public class LJ1200DecoderState extends DecoderState
ic.remove(IdentifierClass.USER);
ic.update(lj.getIdentifiers());
DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis())
DecodeEvent event = DecodeEvent.builder(DecodeEventType.DATA_PACKET, System.currentTimeMillis())
.protocol(Protocol.LOJACK)
.identifiers(ic)
.channel(getCurrentChannel())
.details("LOJACK")
.eventDescription(lj.toString())
.details("LOJACK " + lj)
.build();
broadcast(event);
@@ -91,11 +89,11 @@ public class LJ1200DecoderState extends DecoderState
ic.remove(IdentifierClass.USER);
ic.update(transponder.getIdentifiers());
DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis())
DecodeEvent transponderEvent = DecodeEvent.builder(DecodeEventType.GPS, System.currentTimeMillis())
.protocol(Protocol.LOJACK)
.identifiers(ic)
.channel(getCurrentChannel())
.details("LOJACK TRANSPONDER")
.eventDescription(transponder.toString())
.details("LOJACK TRANSPONDER " + transponder)
.build();
broadcast(transponderEvent);
@@ -114,7 +112,7 @@ public class LJ1200DecoderState extends DecoderState
{
sb.append("Transponder Addresses:\n");
List<String> addresses = new ArrayList<String>(mAddresses);
List<String> addresses = new ArrayList<>(mAddresses);
Collections.sort(addresses);

View File

@@ -1,52 +1,66 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.lj1200;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class LJ1200MessageFilter extends Filter<IMessage>
/**
* Filter for LJ1200 messages
*/
public class LJ1200MessageFilter extends Filter<IMessage,String>
{
private static final String LJ1200_KEY = "LJ-1200";
private final LJ1200KeyExtractor mKeyExtractor = new LJ1200KeyExtractor();
/**
* Constructor
*/
public LJ1200MessageFilter()
{
super("LJ-1200 Message Filter");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
super("LJ-1200 Messages");
add(new FilterElement<>(LJ1200_KEY));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage;
return (message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, String> getKeyExtractor()
{
return Collections.EMPTY_LIST;
return mKeyExtractor;
}
/**
* Key extractor
*/
public class LJ1200KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
return LJ1200_KEY;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.ltrnet;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* LTRNet decode event
*/
public class LTRNetDecodeEvent extends DecodeEvent
{
/**
* Constructs an LTR NET decode event
* @param start
*/
public LTRNetDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(decodeEventType, start);
setProtocol(Protocol.LTR_NET);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.LTR_NET);
return decodeEventBuilder;
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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 io.github.dsheirer.module.decode.ltrnet;
@@ -38,6 +35,7 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageDirection;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.ltrnet.channel.LtrNetChannel;
import io.github.dsheirer.module.decode.ltrnet.identifier.LtrNetRadioIdentifier;
import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage;
@@ -60,9 +58,6 @@ import io.github.dsheirer.module.decode.ltrnet.message.osw.SystemIdle;
import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh;
import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow;
import io.github.dsheirer.protocol.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -71,6 +66,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LTRNetDecoderState extends DecoderState
{
@@ -166,10 +163,24 @@ public class LTRNetDecoderState extends DecoderState
if(mCurrentCallTalkgroup == null || !mCurrentCallTalkgroup.equals(talkgroup))
{
DecodeEventType decodeEventType = null;
if(talkgroup.getTalkgroup() == 253)
{
decodeEventType = DecodeEventType.REGISTER;
}
else if(talkgroup.getTalkgroup() == 254)
{
decodeEventType = DecodeEventType.STATION_ID;
}
else
{
decodeEventType = DecodeEventType.CALL;
}
getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(talkgroup);
mCurrentCallEvent = DecodeEvent.builder(timestamp)
.protocol(Protocol.LTR_NET)
mCurrentCallEvent = LTRNetDecodeEvent.builder(decodeEventType, timestamp)
.channel(getCurrentChannel())
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -181,17 +192,14 @@ public class LTRNetDecoderState extends DecoderState
if(talkgroup.getTalkgroup() == 253)
{
mCurrentCallEvent.setEventDescription("Register");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else if(talkgroup.getTalkgroup() == 254)
{
mCurrentCallEvent.setEventDescription("FCC CWID");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else
{
mCurrentCallEvent.setEventDescription("Call");
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
@@ -203,16 +211,7 @@ public class LTRNetDecoderState extends DecoderState
if(decodeEvent == null)
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
decodeEvent = DecodeEvent.builder(timestamp)
.eventDescription("Call Detect")
.channel(mChannelMap.get(channel))
.protocol(Protocol.LTR_NET)
.identifiers(ic)
.build();
decodeEvent = getDecodeEvent(talkgroup, channel, timestamp, DecodeEventType.CALL_DETECT);
mCallDetectMap.put(channel, decodeEvent);
}
else
@@ -223,16 +222,7 @@ public class LTRNetDecoderState extends DecoderState
if(eventTalkgroup == null || !eventTalkgroup.equals(talkgroup) ||
(timestamp - decodeEvent.getTimeStart() - decodeEvent.getDuration() > 2000))
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
decodeEvent = DecodeEvent.builder(timestamp)
.eventDescription("Call Detect")
.channel(mChannelMap.get(channel))
.protocol(Protocol.LTR_NET)
.identifiers(ic)
.build();
decodeEvent = getDecodeEvent(talkgroup, channel, timestamp, DecodeEventType.CALL_DETECT);
mCallDetectMap.put(channel, decodeEvent);
}
}
@@ -307,89 +297,59 @@ public class LTRNetDecoderState extends DecoderState
switch(((LtrNetMessage)message).getLtrNetMessageType())
{
case ISW_CALL_END:
if(message instanceof IswCallEnd)
if(message instanceof IswCallEnd callEnd)
{
IswCallEnd callEnd = (IswCallEnd)message;
mTalkgroups.add(callEnd.getTalkgroup());
processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp());
}
break;
case ISW_CALL_START:
if(message instanceof IswCallStart)
if(message instanceof IswCallStart callStart)
{
IswCallStart callStart = (IswCallStart)message;
mTalkgroups.add(callStart.getTalkgroup());
processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(),
MessageDirection.ISW);
}
break;
case ISW_REGISTRATION_REQUEST_ESN_HIGH:
if(message instanceof RegistrationRequestEsnHigh)
if(message instanceof RegistrationRequestEsnHigh registrationRequestEsn)
{
RegistrationRequestEsnHigh registrationRequestEsn = (RegistrationRequestEsnHigh)message;
if(registrationRequestEsn.isCompleteEsn())
{
getIdentifierCollection().update(registrationRequestEsn.getESN());
mESNIdentifiers.add(registrationRequestEsn.getESN());
}
broadcast(DecodeEvent.builder(message.getTimestamp())
.eventDescription("Registration Request")
.details(registrationRequestEsn.toString())
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.protocol(Protocol.LTR_NET)
.build());
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
break;
case ISW_REGISTRATION_REQUEST_ESN_LOW:
if(message instanceof RegistrationRequestEsnLow)
if(message instanceof RegistrationRequestEsnLow registrationRequestEsn)
{
RegistrationRequestEsnLow registrationRequestEsn = (RegistrationRequestEsnLow)message;
if(registrationRequestEsn.isCompleteEsn())
{
getIdentifierCollection().update(registrationRequestEsn.getESN());
mESNIdentifiers.add(registrationRequestEsn.getESN());
}
broadcast(DecodeEvent.builder(message.getTimestamp())
.eventDescription("Registration Request")
.details(registrationRequestEsn.toString())
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.protocol(Protocol.LTR_NET)
.build());
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
break;
case ISW_REQUEST_ACCESS:
if(message instanceof RequestAccess)
if(message instanceof RequestAccess requestAccess)
{
RequestAccess requestAccess = (RequestAccess)message;
getIdentifierCollection().update(requestAccess.getTalkgroup());
mTalkgroups.add(requestAccess.getTalkgroup());
broadcast(DecodeEvent.builder(message.getTimestamp())
.eventDescription("Access Request")
.details(requestAccess.toString())
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.protocol(Protocol.LTR_NET)
.build());
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "ACCESS: " + requestAccess));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
}
break;
case ISW_UNIQUE_ID:
if(message instanceof IswUniqueId)
if(message instanceof IswUniqueId iswUniqueId)
{
IswUniqueId iswUniqueId = (IswUniqueId)message;
getIdentifierCollection().update(iswUniqueId.getUniqueID());
mLtrNetRadioIdentifiers.add(iswUniqueId.getUniqueID());
updateCurrentCallIdentifiers();
@@ -400,104 +360,90 @@ public class LTRNetDecoderState extends DecoderState
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_CALL_END:
if(message instanceof OswCallEnd)
if(message instanceof OswCallEnd callEnd)
{
OswCallEnd callEnd = (OswCallEnd)message;
mTalkgroups.add(callEnd.getTalkgroup());
processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp());
}
break;
case OSW_CALL_START:
if(message instanceof OswCallStart)
if(message instanceof OswCallStart callStart)
{
OswCallStart callStart = (OswCallStart)message;
mTalkgroups.add(callStart.getTalkgroup());
processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(),
MessageDirection.OSW);
}
break;
case OSW_CHANNEL_MAP_HIGH:
if(message instanceof ChannelMapHigh)
if(message instanceof ChannelMapHigh channelMapHigh)
{
mChannelMapHigh = (ChannelMapHigh)message;
mChannelMapHigh = channelMapHigh;
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_CHANNEL_MAP_LOW:
if(message instanceof ChannelMapLow)
if(message instanceof ChannelMapLow channelMapLow)
{
mChannelMapLow = (ChannelMapLow)message;
mChannelMapLow = channelMapLow;
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_SYSTEM_IDLE:
if(message instanceof SystemIdle)
if(message instanceof SystemIdle systemIdle)
{
setCurrentChannelNumber(((SystemIdle)message).getChannel());
setCurrentChannelNumber(systemIdle.getChannel());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_NEIGHBOR_ID:
if(message instanceof NeighborId)
if(message instanceof NeighborId neighborId)
{
NeighborId neighborId = (NeighborId)message;
mNeighborMap.put(neighborId.getNeighborRank(), neighborId);
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_RECEIVE_FREQUENCY_HIGH:
if(message instanceof ReceiveFrequencyHigh)
if(message instanceof ReceiveFrequencyHigh receiveFrequency)
{
ReceiveFrequencyHigh receiveFrequency = (ReceiveFrequencyHigh)message;
updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_RECEIVE_FREQUENCY_LOW:
if(message instanceof ReceiveFrequencyLow)
if(message instanceof ReceiveFrequencyLow receiveFrequency)
{
ReceiveFrequencyLow receiveFrequency = (ReceiveFrequencyLow)message;
updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_REGISTRATION_ACCEPT:
if(message instanceof RegistrationAccept)
if(message instanceof RegistrationAccept registrationAccept)
{
RegistrationAccept registrationAccept = (RegistrationAccept)message;
mLtrNetRadioIdentifiers.add(registrationAccept.getUniqueID());
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.LTR_NET)
.channel(getCurrentChannel())
.identifiers(ic)
.eventDescription("Registration Accept")
.details(registrationAccept.toString())
.build());
broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE, "REGISTRATION ACCEPT: " + registrationAccept));
}
break;
case OSW_SITE_ID:
if(message instanceof SiteId)
if(message instanceof SiteId siteId)
{
mCurrentSite = (SiteId)message;
mCurrentSite = siteId;
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_TRANSMIT_FREQUENCY_HIGH:
if(message instanceof TransmitFrequencyHigh)
if(message instanceof TransmitFrequencyHigh transmitFrequency)
{
TransmitFrequencyHigh transmitFrequency = (TransmitFrequencyHigh)message;
updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
break;
case OSW_TRANSMIT_FREQUENCY_LOW:
if(message instanceof TransmitFrequencyLow)
if(message instanceof TransmitFrequencyLow transmitFrequency)
{
TransmitFrequencyLow transmitFrequency = (TransmitFrequencyLow)message;
updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency());
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
@@ -509,6 +455,27 @@ public class LTRNetDecoderState extends DecoderState
}
}
private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String details)
{
return LTRNetDecodeEvent.builder(decodeEventType, message.getTimestamp())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.build();
}
private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType)
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(talkgroup);
return LTRNetDecodeEvent.builder(decodeEventType, timestamp)
.channel(mChannelMap.get(channel))
.identifiers(ic)
.build();
}
@Override
public String getActivitySummary()
{

View File

@@ -1,52 +1,85 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.ltrnet;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage;
import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType;
import java.util.function.Function;
import java.util.*;
public class LTRNetMessageFilter extends Filter<IMessage>
/**
* Filter for LTR-Net messages
*/
public class LTRNetMessageFilter extends Filter<IMessage,LtrNetMessageType>
{
private Map<LtrNetMessageType, FilterElement<LtrNetMessageType>> mElements = new EnumMap<>(LtrNetMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public LTRNetMessageFilter()
{
super("LTR-Net Message Filter");
super("LTR-Net Messages");
for(LtrNetMessageType type: LtrNetMessageType.values())
{
mElements.put(type, new FilterElement<>(type));
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
LtrNetMessage ltr = (LtrNetMessage) message;
if(mElements.containsKey(ltr.getLtrNetMessageType()))
{
return mElements.get(ltr.getLtrNetMessageType()).isEnabled();
}
}
return false;
}
/**
* Tests for instance of LTR-Net message
* @param message to test
* @return true if correct type.
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LtrNetMessage;
return message instanceof LtrNetMessage && super.canProcess(message);
}
/**
* Key extractor for LTR-Net messages
* @return extractor
*/
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, LtrNetMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LtrNetMessageType>
{
@Override
public LtrNetMessageType apply(IMessage message)
{
if(message instanceof LtrNetMessage ltr)
{
return ltr.getLtrNetMessageType();
}
return null;
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.ltrstandard;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* LTR decode event
*/
public class LTRStandardDecodeEvent extends DecodeEvent
{
/**
* Constructs an LTR Standard decode event
* @param start
*/
public LTRStandardDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(decodeEventType, start);
setProtocol(Protocol.LTR);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.LTR);
return decodeEventBuilder;
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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 io.github.dsheirer.module.decode.ltrstandard;
@@ -35,15 +32,13 @@ import io.github.dsheirer.identifier.talkgroup.LTRTalkgroup;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.ltrstandard.channel.LtrChannel;
import io.github.dsheirer.module.decode.ltrstandard.message.Call;
import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd;
import io.github.dsheirer.module.decode.ltrstandard.message.Idle;
import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage;
import io.github.dsheirer.protocol.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -51,6 +46,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LTRStandardDecoderState extends DecoderState
{
@@ -81,10 +78,8 @@ public class LTRStandardDecoderState extends DecoderState
switch(((LTRMessage)message).getMessageType())
{
case CALL:
if(message instanceof Call)
if(message instanceof Call start)
{
Call start = (Call)message;
int channel = start.getChannel();
setChannelNumber(channel);
mLCNTracker.processFreeChannel(start.getFree());
@@ -97,11 +92,9 @@ public class LTRStandardDecoderState extends DecoderState
mCurrentTalkgroup = start.getTalkgroup();
getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(start.getTalkgroup());
mCurrentCallEvent = DecodeEvent.builder(start.getTimestamp())
.protocol(Protocol.LTR)
mCurrentCallEvent = LTRStandardDecodeEvent.builder(DecodeEventType.CALL, start.getTimestamp())
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
.eventDescription("Call")
.build();
}
else
@@ -114,10 +107,8 @@ public class LTRStandardDecoderState extends DecoderState
}
break;
case CALL_END:
if(message instanceof CallEnd)
if(message instanceof CallEnd end)
{
CallEnd end = (CallEnd)message;
mCurrentTalkgroup = null;
//Home channel is 31 for call end -- use the free channel as the call end channel
@@ -135,10 +126,10 @@ public class LTRStandardDecoderState extends DecoderState
}
break;
case IDLE:
if(message instanceof Idle)
if(message instanceof Idle idle)
{
mCurrentTalkgroup = null;
mLCNTracker.processCallChannel(((Idle)message).getChannel());
mLCNTracker.processCallChannel(idle.getChannel());
}
break;
default:

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.ltrstandard;
@@ -24,47 +23,53 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage;
import java.util.function.Function;
import java.util.*;
public class LTRStandardMessageFilter extends Filter<IMessage>
/**
* Filter for LTR standard messages.
*/
public class LTRStandardMessageFilter extends Filter<IMessage,LtrStandardMessageType>
{
private Map<LtrStandardMessageType,FilterElement<LtrStandardMessageType>> mElements = new EnumMap<>(LtrStandardMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public LTRStandardMessageFilter()
{
super("LTR Message Filter");
super("LTR Messages");
mElements.put(LtrStandardMessageType.CALL, new FilterElement<>(LtrStandardMessageType.CALL));
mElements.put(LtrStandardMessageType.CALL_END, new FilterElement<>(LtrStandardMessageType.CALL_END));
mElements.put(LtrStandardMessageType.IDLE, new FilterElement<>(LtrStandardMessageType.IDLE));
mElements.put(LtrStandardMessageType.UNKNOWN, new FilterElement<>(LtrStandardMessageType.UNKNOWN));
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
for(LtrStandardMessageType type: LtrStandardMessageType.values())
{
LTRMessage ltr = (LTRMessage)message;
if(mElements.containsKey(ltr.getMessageType()))
{
return mElements.get(ltr.getMessageType()).isEnabled();
}
add(new FilterElement<>(type));
}
return false;
}
public boolean canProcess(IMessage message)
{
return message instanceof LTRMessage;
return message instanceof LTRMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, LtrStandardMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LtrStandardMessageType>
{
@Override
public LtrStandardMessageType apply(IMessage message)
{
if(message instanceof LTRMessage ltr)
{
return ltr.getMessageType();
}
return null;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.mdc1200;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* MDC-1200 decode event
*/
public class MDCDecodeEvent extends DecodeEvent
{
/**
* Constucts an MDC1200 decode event
* @param decodeEventType for the event
* @param start for the event
*/
public MDCDecodeEvent(DecodeEventType decodeEventType, long start)
{
super(decodeEventType, start);
setProtocol(Protocol.MDC1200);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.MDC1200);
return decodeEventBuilder;
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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 io.github.dsheirer.module.decode.mdc1200;
@@ -29,9 +26,8 @@ import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.MutableIdentifierCollection;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
@@ -114,8 +110,7 @@ public class MDCDecoderState extends DecoderState
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
broadcast(DecodeEvent.builder(mdc.getTimestamp())
.eventDescription(type.getLabel())
broadcast(MDCDecodeEvent.builder(getDecodeEventType(type), mdc.getTimestamp())
.details(mdc.toString())
.identifiers(ic)
.build());
@@ -195,4 +190,22 @@ public class MDCDecoderState extends DecoderState
break;
}
}
private DecodeEventType getDecodeEventType(MDCMessageType messageType) {
switch(messageType)
{
case ANI:
return DecodeEventType.ID_ANI;
case ACKNOWLEDGE:
return DecodeEventType.RESPONSE;
case EMERGENCY:
return DecodeEventType.EMERGENCY;
case PAGING:
return DecodeEventType.PAGE;
case STATUS:
return DecodeEventType.STATUS;
default:
return DecodeEventType.UNKNOWN;
}
}
}

View File

@@ -1,55 +1,75 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.mdc1200;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.Message;
import io.github.dsheirer.module.decode.mpt1327.MPT1327Message;
import java.util.function.Function;
import java.util.*;
public class MDCMessageFilter extends Filter<IMessage>
/**
* Message filter for MDC-1200 messages
*/
public class MDCMessageFilter extends Filter<IMessage,MDCMessageType>
{
private Map<MDCMessageType, FilterElement<MDCMessageType>> mElements = new EnumMap<>(MDCMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public MDCMessageFilter()
{
super("MDC-1200 Message Filter");
super("MDC-1200 Messages");
for(MDCMessageType type : MDCMessageType.values())
{
if(type != MDCMessageType.UNKNOWN)
{
mElements.put(type, new FilterElement<MDCMessageType>(type));
}
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
MDCMessage mdc = (MDCMessage) message;
if(mElements.containsKey(mdc.getMessageType()))
{
return mElements.get(mdc.getMessageType()).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof MDCMessage;
return message instanceof MDCMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, MDCMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,MDCMessageType>
{
@Override
public MDCMessageType apply(IMessage message)
{
if(message instanceof MDCMessage mdc)
{
return mdc.getMessageType();
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,20 +14,23 @@
*
* 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 io.github.dsheirer.module.decode.mpt1327;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.p25.P25DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
public class MPT1327ChannelGrantEvent extends P25DecodeEvent
/**
* MPT1327 Channel Grant Event
*/
public class MPT1327ChannelGrantEvent extends MPT1327DecodeEvent
{
public MPT1327ChannelGrantEvent(long timestamp)
public MPT1327ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
super(timestamp);
super(decodeEventType, timestamp);
}
/**
@@ -36,9 +38,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(long timeStart)
public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(DecodeEventType decodeEventType, long timeStart)
{
return new MPT1327ChannelGrantDecodeEventBuilder(timeStart);
return new MPT1327ChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
}
/**
@@ -48,7 +50,7 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
protected String mDetails;
@@ -58,8 +60,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
*
* @param timeStart
*/
public MPT1327ChannelGrantDecodeEventBuilder(long timeStart)
public MPT1327ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -93,16 +96,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public MPT1327ChannelGrantDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -129,12 +122,11 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
*/
public MPT1327ChannelGrantEvent build()
{
MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mTimeStart);
MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setProtocol(Protocol.MPT1327);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}

View File

@@ -0,0 +1,52 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.mpt1327;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
/**
* MPT1327 Decode Event
*/
public class MPT1327DecodeEvent extends DecodeEvent
{
/**
* Constructs a MPT1327 decode event
* @param start
*/
public MPT1327DecodeEvent(DecodeEventType decodeEventType, long start)
{
super(decodeEventType, start);
setProtocol(Protocol.MPT1327);
}
/**
* Creates a new decode event builder with the specified start timestamp.
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.MPT1327);
return decodeEventBuilder;
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -30,17 +30,16 @@ import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MPT1327DecoderState extends DecoderState
{
@@ -110,28 +109,11 @@ public class MPT1327DecoderState extends DecoderState
if(identType == MPT1327Message.IdentType.REGI)
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Register")
.identifiers(ic)
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.REGISTER, null));
}
else
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Response")
.details("ACK " + identType.getLabel())
.identifiers(ic)
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.RESPONSE, "ACK " + identType.getLabel()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
@@ -143,45 +125,15 @@ public class MPT1327DecoderState extends DecoderState
case ACKT:
case ACKV:
case ACKX:
MutableIdentifierCollection icACK = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icACK.remove(IdentifierClass.USER);
icACK.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Acknowledge")
.details(mpt.getMessageType().getDescription())
.identifiers(icACK)
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.ACKNOWLEDGE, mpt.getMessageType().getDescription()));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
case AHYC:
MutableIdentifierCollection icAHY = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icAHY.remove(IdentifierClass.USER);
icAHY.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Command")
.details("Send Short Data Message")
.identifiers(icAHY)
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.COMMAND, "Send Short Data Message"));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
case AHYQ:
MutableIdentifierCollection icAHYQ = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icAHYQ.remove(IdentifierClass.USER);
icAHYQ.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Command")
.details("Send Status Message")
.identifiers(icAHYQ)
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.COMMAND, "Send Status Message"));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
case ALH:
@@ -197,41 +149,22 @@ public class MPT1327DecoderState extends DecoderState
case GTC:
if(mMPT1327TrafficChannelManager != null)
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
mMPT1327TrafficChannelManager.processChannelGrant(mpt, ic);
}
else
{
MPT1327Channel channel = MPT1327Channel.create(mpt.getChannel());
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(mpt.getTimestamp())
.eventDescription("Call Detect")
.details(mpt.getMessage())
.channel(channel)
.identifiers(ic)
.build());
DecodeEvent decodeEvent = getDecodeEvent(message, mpt, DecodeEventType.CALL_DETECT, mpt.getMessage());
decodeEvent.setChannelDescriptor(channel);
broadcast(decodeEvent);
}
break;
case HEAD_PLUS1:
case HEAD_PLUS2:
case HEAD_PLUS3:
case HEAD_PLUS4:
MutableIdentifierCollection icHEAD = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icHEAD.remove(IdentifierClass.USER);
icHEAD.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(message.getTimestamp())
.protocol(Protocol.MPT1327)
.eventDescription("Short Data Message")
.details(mpt.getMessage())
.identifiers(icHEAD)
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.SDM, mpt.getMessage()));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
@@ -250,14 +183,7 @@ public class MPT1327DecoderState extends DecoderState
// timeout specified by the user. Otherwise we'll be using the shorter default call timeout
broadcast(new ChangeChannelTimeoutEvent(this, mChannelType, mCallTimeoutMilliseconds));
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
broadcast(DecodeEvent.builder(mpt.getTimestamp())
.identifiers(ic)
.eventDescription("Call In Progress")
.build());
broadcast(getDecodeEvent(message, mpt, DecodeEventType.CALL_IN_PROGRESS, null));
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
break;
@@ -268,6 +194,22 @@ public class MPT1327DecoderState extends DecoderState
}
}
private DecodeEvent getDecodeEvent(IMessage message, MPT1327Message mpt, DecodeEventType decodeEventType, String details)
{
MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
return MPT1327DecodeEvent.builder(decodeEventType, message.getTimestamp())
.details(details)
.identifiers(ic)
.build();
}
private MutableIdentifierCollection getUpdatedMutableIdentifierCollection(MPT1327Message mpt) {
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(mpt.getIdentifiers());
return ic;
}
public void reset()
{
super.reset();

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.mpt1327;
@@ -23,56 +22,55 @@ package io.github.dsheirer.module.decode.mpt1327;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.MessageType;
import java.util.function.Function;
import java.util.*;
public class MPT1327MessageFilter extends Filter<IMessage>
/**
* Filter for MPT1327 messages
*/
public class MPT1327MessageFilter extends Filter<IMessage, MPT1327Message.MPTMessageType>
{
private Map<MPT1327Message.MPTMessageType, FilterElement<MPT1327Message.MPTMessageType>> mFilterElements = new EnumMap<>(MPT1327Message.MPTMessageType.class);
private final KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public MPT1327MessageFilter()
{
super("MPT1327 Message Type Filter");
super("MPT1327 Messages");
for(MPT1327Message.MPTMessageType type : MPT1327Message.MPTMessageType.values())
{
if(type != MPT1327Message.MPTMessageType.UNKN)
{
mFilterElements.put(type, new FilterElement<MPT1327Message.MPTMessageType>(type));
}
add(new FilterElement<>(type));
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
MPT1327Message mpt = (MPT1327Message) message;
FilterElement<MPT1327Message.MPTMessageType> element =
mFilterElements.get(mpt.getMessageType());
if(element != null)
{
return element.isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof MPT1327Message;
return message instanceof MPT1327Message && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, MPT1327Message.MPTMessageType> getKeyExtractor()
{
return new ArrayList<FilterElement<?>>(mFilterElements.values());
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage, MPT1327Message.MPTMessageType>
{
@Override
public MPT1327Message.MPTMessageType apply(IMessage message)
{
if(message instanceof MPT1327Message mpt)
{
return mpt.getMessageType();
}
return null;
}
}
}

View File

@@ -30,6 +30,7 @@ import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventProvider;
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
@@ -103,9 +104,9 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
}
}
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp())
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent
.mpt1327Builder(DecodeEventType.CALL, mpt1327Message.getTimestamp())
.channel(mpt1327Channel)
.eventDescription("Call")
.details("Traffic Channel Grant")
.identifiers(identifierCollection)
.build();
@@ -123,7 +124,7 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
channelGrantEvent.setEventDescription("Detect:" + channelGrantEvent.getEventDescription());
channelGrantEvent.setDetails("Detect:" + channelGrantEvent.getDetails());
return;
}
@@ -150,10 +151,8 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration();
List<Channel> trafficChannelList = new ArrayList<>();
if(decodeConfiguration instanceof DecodeConfigMPT1327)
if(decodeConfiguration instanceof DecodeConfigMPT1327 decodeConfigMPT1327)
{
DecodeConfigMPT1327 decodeConfigMPT1327 = (DecodeConfigMPT1327)decodeConfiguration;
int maxTrafficChannels = decodeConfigMPT1327.getTrafficChannelPoolSize();
for(int x = 0; x < maxTrafficChannels; x++)

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.p25;
@@ -29,9 +28,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
{
private ServiceOptions mServiceOptions;
public P25ChannelGrantEvent(long timestamp)
public P25ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
{
super(timestamp);
super(decodeEventType, timestamp);
}
/**
@@ -39,9 +38,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static P25ChannelGrantDecodeEventBuilder builder(long timeStart, ServiceOptions serviceOptions)
public static P25ChannelGrantDecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions)
{
return new P25ChannelGrantDecodeEventBuilder(timeStart, serviceOptions);
return new P25ChannelGrantDecodeEventBuilder(decodeEventType, timeStart, serviceOptions);
}
/**
@@ -76,7 +75,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
{
protected long mTimeStart;
protected long mDuration;
protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
@@ -88,8 +86,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
*
* @param timeStart
*/
public P25ChannelGrantDecodeEventBuilder(long timeStart, ServiceOptions serviceOptions)
public P25ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions)
{
mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
mServiceOptions = serviceOptions;
}
@@ -124,26 +123,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
return this;
}
/**
* Sets the event description text
* @param description of the event
*/
public P25ChannelGrantDecodeEventBuilder eventDescription(String description)
{
mEventDescription = description;
return this;
}
/**
* Sets the {@link DecodeEventType}
* @param decodeEventType of the event
*/
public P25ChannelGrantDecodeEventBuilder eventType(DecodeEventType decodeEventType)
{
mDecodeEventType = decodeEventType;
return this;
}
/**
* Sets the identifier collection.
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
@@ -170,13 +149,11 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
*/
public P25ChannelGrantEvent build()
{
P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mTimeStart);
P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mDecodeEventType, mTimeStart);
decodeEvent.setProtocol(Protocol.APCO25);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
decodeEvent.setEventType(mDecodeEventType);
decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setServiceOptions(mServiceOptions);
return decodeEvent;

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,23 +14,24 @@
*
* 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 io.github.dsheirer.module.decode.p25;
import io.github.dsheirer.module.decode.event.DecodeEvent;
import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.protocol.Protocol;
public class P25DecodeEvent extends DecodeEvent
{
/**
* Constucts a P25 decode event
* Constructs a P25 decode event
* @param start
*/
public P25DecodeEvent(long start)
public P25DecodeEvent(DecodeEventType decodeEventType, long start)
{
super(start);
super(decodeEventType, start);
setProtocol(Protocol.APCO25);
}
@@ -40,9 +40,9 @@ public class P25DecodeEvent extends DecodeEvent
* @param timeStart for the event
* @return builder
*/
public static DecodeEventBuilder builder(long timeStart)
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
{
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
decodeEventBuilder.protocol(Protocol.APCO25);
return decodeEventBuilder;
}

View File

@@ -281,11 +281,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Continue")
.details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.details("CONTINUE - PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -306,7 +304,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel != null)
{
event.setEventDescription(decodeEventType.toString());
event.setDetails("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""));
event.setChannelDescriptor(apco25Channel);
broadcast(event);
@@ -327,10 +324,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(mIgnoreDataCalls && opcode.isDataChannelGrant())
{
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Ignored")
.details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -340,10 +335,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
return;
}
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -357,8 +350,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED");
return;
}
@@ -427,11 +419,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
{
event.end(timestamp);
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Continue")
.details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.details("CONTINUE - PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -460,7 +450,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel != null)
{
event.setEventDescription(decodeEventType.toString());
event.setDetails("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""));
event.setChannelDescriptor(apco25Channel);
broadcast(event);
@@ -488,10 +477,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(mIgnoreDataCalls && opcode.isDataChannelGrant())
{
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString() + " - Ignored")
.details("PHASE 2 DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -502,10 +489,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
return;
}
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
.channel(apco25Channel)
.eventType(decodeEventType)
.eventDescription(decodeEventType.toString())
.details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
.identifiers(identifierCollection)
.build();
@@ -526,8 +511,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED");
return;
}
@@ -836,17 +820,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
if (event != null)
{
event.setEventDescription(event.getEventDescription() + " - Rejected");
if (channelEvent.getDescription() != null)
{
event.setDetails(channelEvent.getDescription() + " - " + event.getDetails());
}
else
{
event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails());
}
event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails());
broadcast(event);
}
});

View File

@@ -654,10 +654,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
private void processBroadcast(List<Identifier> identifiers, long timestamp, DecodeEventType request, String s) {
MutableIdentifierCollection requestCollection = getMutableIdentifierCollection(identifiers);
broadcast(P25DecodeEvent.builder(timestamp)
broadcast(P25DecodeEvent.builder(request, timestamp)
.channel(getCurrentChannel())
.eventType(request)
.eventDescription(request.toString())
.details(s)
.identifiers(requestCollection)
.build());
@@ -767,17 +765,16 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
/**
* Updates or creates a current call event.
*
* @param type of call that will be used as an event description
* @param decodeEventType of call that will be used as an event description
* @param details of the call (optional)
* @param timestamp of the message indicating a call or continuation
*/
private void updateCurrentCall(DecodeEventType type, String details, long timestamp)
private void updateCurrentCall(DecodeEventType decodeEventType, String details, long timestamp)
{
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = P25DecodeEvent.builder(timestamp)
mCurrentCallEvent = P25DecodeEvent.builder(DecodeEventType.CALL, timestamp)
.channel(getCurrentChannel())
.eventDescription(type.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -791,9 +788,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
mCurrentCallEvent.end(timestamp);
broadcast(mCurrentCallEvent);
if(type == DecodeEventType.CALL_ENCRYPTED)
if(decodeEventType == DecodeEventType.CALL_ENCRYPTED)
{
mCurrentCallEvent.setEventDescription(type.toString());
mCurrentCallEvent.setDetails(details);
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ENCRYPTED));
}
@@ -838,12 +834,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processPDU(P25Message message)
{
if(message.isValid() && message instanceof PDUMessage)
if(message.isValid() && message instanceof PDUMessage pdu)
{
PDUMessage pdu = (PDUMessage)message;
processBroadcast(pdu.getIdentifiers(), message.getTimestamp(), DecodeEventType.DATA_PACKET, pdu.toString());
}
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
@@ -856,10 +849,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
*/
private void processUMBTC(P25Message message)
{
if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing)
if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing tired)
{
UMBTCTelephoneInterconnectRequestExplicitDialing tired = (UMBTCTelephoneInterconnectRequestExplicitDialing)message;
processBroadcast(tired.getIdentifiers(), tired.getTimestamp(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT:" + tired.getTelephoneNumber());
}
@@ -910,11 +901,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString())
.identifiers(ic)
.details(arsPacket.toString() + " " + ipv4.toString())
.details(arsPacket + " " + ipv4)
.build();
broadcast(packetEvent);
@@ -929,11 +919,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(DecodeEventType.CELLOCATOR, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription("Cellocator")
.identifiers(ic)
.details(mcgpPacket.toString() + " " + ipv4.toString())
.details(mcgpPacket + " " + ipv4)
.build();
broadcast(cellocatorEvent);
@@ -942,10 +931,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(packet.getIdentifiers());
DecodeEvent lrrpEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent lrrpEvent = P25DecodeEvent.builder(DecodeEventType.LRRP, message.getTimestamp())
.channel(getCurrentChannel())
.details(lrrpPacket + " " + ipv4)
.eventDescription("LRRP")
.identifiers(ic)
.protocol(Protocol.LRRP)
.build();
@@ -956,9 +944,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
if(geoPosition != null)
{
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(message.getTimestamp())
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent
.plottableBuilder(DecodeEventType.GPS, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.GPS.toString())
.identifiers(ic)
.protocol(Protocol.LRRP)
.location(geoPosition)
@@ -975,9 +963,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UDP_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.UDP_PACKET.toString())
.identifiers(ic)
.details(ipv4.toString())
.build();
@@ -993,9 +980,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.ICMP_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.ICMP_PACKET.toString())
.identifiers(ic)
.details(ipv4.toString())
.build();
@@ -1010,9 +996,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.IP_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.IP_PACKET.toString())
.identifiers(ic)
.details(ipv4.toString())
.build();
@@ -1028,9 +1013,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
ic.update(identifier);
}
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.UNKNOWN_PACKET.toString())
.identifiers(ic)
.details(packet.toString())
.build();
@@ -1376,9 +1360,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
private void processTSBKRoamingAddressRequest(TSBKMessage tsbk) {
//TODO: not sure if this should be used or not.
broadcast(P25DecodeEvent.builder(tsbk.getTimestamp())
broadcast(P25DecodeEvent.builder(DecodeEventType.REQUEST, tsbk.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.REQUEST.toString())
.details("ROAMING ADDRESS")
// TODO: This identifierCollection is different from all the others.
.identifiers(new IdentifierCollection(tsbk.getIdentifiers()))

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import java.util.Collections;
import java.util.List;
public class AMBTCMessageFilter extends Filter<IMessage>
{
public AMBTCMessageFilter()
{
super("Alternate Multi-Block Trunking Control");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof AMBTCMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,51 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class HDUMessageFilter extends Filter<IMessage>
/**
* Filter for HDU header messages
*/
public class HeaderMessageFilter extends Filter<IMessage,String>
{
public HDUMessageFilter()
{
super("Header Data Unit");
}
private static final String HDU_KEY = "Header Data Unit (HDU)";
private KeyExtractor mKeyExtractor = new KeyExtractor();
@Override
public boolean passes(IMessage message)
/**
* Constructor
*/
public HeaderMessageFilter()
{
return mEnabled && canProcess(message);
super("Header Messages");
add(new FilterElement<>(HDU_KEY));
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof HDUMessage;
return message instanceof HDUMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, String> getKeyExtractor()
{
return Collections.EMPTY_LIST;
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof HDUMessage)
{
return HDU_KEY;
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -23,33 +22,52 @@ package io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class LDUMessageFilter extends Filter<IMessage>
/**
* Filter for unknown messages, meaning messages not handled by any other filter.
*/
public class P25OtherMessageFilter extends Filter<IMessage,String>
{
public LDUMessageFilter()
private static final String OTHER_KEY = "Other/Unknown Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public P25OtherMessageFilter()
{
super("Link Data Unit");
super("Other P25 messages");
add(new FilterElement<>(OTHER_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
return mEnabled && canProcess(message);
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LDUMessage;
return message instanceof P25Message && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return Collections.EMPTY_LIST;
@Override
public String apply(IMessage message)
{
if(message instanceof P25Message)
{
return OTHER_KEY;
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,35 +14,42 @@
*
* 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.filter.SyncLossMessageFilter;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.SyncLossMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
public class P25MessageFilterSet extends FilterSet<IMessage>
/**
* Filter set for P25 messages
*/
public class P25P1MessageFilterSet extends FilterSet<IMessage>
{
public P25MessageFilterSet()
public P25P1MessageFilterSet()
{
super("P25 Message Filter");
addFilter(new AMBTCMessageFilter());
addFilter(new HDUMessageFilter());
addFilter(new IPPacketMessageFilter());
addFilter(new LDUMessageFilter());
addFilter(new HeaderMessageFilter());
addFilter(new PacketMessageFilter());
addFilter(new PDUMessageFilter());
addFilter(new SNDCPMessageFilter());
addFilter(new SyncLossMessageFilter());
addFilter(new TDUMessageFilter());
addFilter(new TDULCMessageFilter());
addFilter(new TSBKMessageFilterSet());
addFilter(new UMBTCMessageFilter());
addFilter(new TerminatorMessageFilterSet());
addFilter(new TrunkingOpcodeMessageFilterSet());
addFilter(new VoiceMessageFilter());
addFilter(new P25OtherMessageFilter());
}
/**
* Override default to descope handling to P25 or sync-loss messages.
* @param message to test
* @return true if the message can be processed
*/
@Override
public boolean canProcess(IMessage message)
{

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,49 +23,52 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.function.Function;
import java.util.*;
public class PDUMessageFilter extends Filter<IMessage>
/**
* Filter for PDU packet data unit messages
*/
public class PDUMessageFilter extends Filter<IMessage,String>
{
private Map<Opcode, FilterElement<Opcode>> mOpcodeFilterElements = new EnumMap<>(Opcode.class);
private static final String PDU_KEY = "Packet Data Unit (PDU)";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public PDUMessageFilter()
{
super("PDU - Packet Data Unit");
for(Opcode opcode : Opcode.values())
{
mOpcodeFilterElements.put(opcode, new FilterElement<Opcode>(opcode));
}
super("Packet Data Unit messages");
add(new FilterElement<>(PDU_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
if(mEnabled && message instanceof PDUMessage)
{
PDUMessage pdu = (PDUMessage) message;
Opcode opcode = pdu.getOpcode();
return mOpcodeFilterElements.get(opcode).isEnabled();
}
return false;
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof PDUMessage;
return message instanceof TDUMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return new ArrayList<FilterElement<?>>(mOpcodeFilterElements.values());
@Override
public String apply(IMessage message)
{
if(message instanceof PDUMessage)
{
return PDU_KEY;
}
return null;
}
}
}

View File

@@ -0,0 +1,135 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.ip.IPacket;
import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket;
import io.github.dsheirer.module.decode.ip.icmp.ICMPPacket;
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage;
import java.util.function.Function;
/**
* Filter for packet messages
*/
public class PacketMessageFilter extends Filter<IMessage,String>
{
private static final String KEY_CELLOCATOR = "Cellocator Packet";
private static final String KEY_IPV4_OTHER = "IPV4 Other Packet";
private static final String KEY_IPV4_UDP_ICMP = "IPV4 UDP/IP ICMP Packet";
private static final String KEY_IPV4_UDP_OTHER = "IPV4 UDP/IP Other Packet";
private static final String KEY_MOTOROLA_ARS = "Motorola Registration (ARS) Packet";
private static final String KEY_MOTOROLA_LRRP = "Motorola Location Report (LRRP) Packet";
private static final String KEY_MOTOROLA_XCMP = "Motorola Extensible (XCMP) Packet";
private static final String KEY_SNDCP = "SNDCP Data Packet";
private static final String KEY_UNKNOWN = "Unknown Packet";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public PacketMessageFilter()
{
super("Packet Messages");
add(new FilterElement<>(KEY_CELLOCATOR));
add(new FilterElement<>(KEY_IPV4_OTHER));
add(new FilterElement<>(KEY_IPV4_UDP_ICMP));
add(new FilterElement<>(KEY_IPV4_UDP_OTHER));
add(new FilterElement<>(KEY_MOTOROLA_ARS));
add(new FilterElement<>(KEY_MOTOROLA_LRRP));
add(new FilterElement<>(KEY_MOTOROLA_XCMP));
add(new FilterElement<>(KEY_SNDCP));
add(new FilterElement<>(KEY_UNKNOWN));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof PacketMessage && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof PacketMessage packetMessage)
{
IPacket packet = packetMessage.getPacket();
if(packet instanceof MCGPPacket)
{
return KEY_CELLOCATOR;
}
else if(packet instanceof ICMPPacket)
{
return KEY_IPV4_UDP_ICMP;
}
else if(packet instanceof UDPPacket)
{
return KEY_IPV4_UDP_OTHER;
}
else if(packet instanceof IPV4Packet)
{
return KEY_IPV4_OTHER;
}
else if(packet instanceof SNDCPPacketMessage)
{
return KEY_SNDCP;
}
else if(packet instanceof ARSPacket)
{
return KEY_MOTOROLA_ARS;
}
else if(packet instanceof LRRPPacket)
{
return KEY_MOTOROLA_LRRP;
}
else if(packet instanceof XCMPPacket)
{
return KEY_MOTOROLA_XCMP;
}
else
{
return KEY_UNKNOWN;
}
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,55 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPMessage;
import io.github.dsheirer.module.decode.p25.reference.PDUType;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class SNDCPMessageFilter extends Filter<IMessage>
/**
* Filter for SNDCP messages
*/
public class SNDCPMessageFilter extends Filter<IMessage, PDUType>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public SNDCPMessageFilter()
{
super("Sub-Network Dependent Convergence Protocol");
}
super("Sub-Network Dependent Convergence Protocol (SNDCP) Messages");
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
for(PDUType pduType: PDUType.values())
{
add(new FilterElement<>(pduType));
}
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof SNDCPMessage;
return message instanceof SNDCPMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
public Function<IMessage, PDUType> getKeyExtractor()
{
return Collections.EMPTY_LIST;
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,PDUType>
{
@Override
public PDUType apply(IMessage message)
{
if(message instanceof SNDCPMessage sndcp)
{
return sndcp.getPDUType();
}
return null;
}
}
}

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import java.util.Collections;
import java.util.List;
public class TDULCMessageFilter extends Filter<IMessage>
{
public TDULCMessageFilter()
{
super("TDU Terminator Data Unit with Link Control");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof TDULinkControlMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,7 +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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,51 @@ import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.function.Function;
import java.util.Collections;
import java.util.List;
public class TDUMessageFilter extends Filter<IMessage>
/**
* Filter for TDU terminator (no link control) messages
*/
public class TDUMessageFilter extends Filter<IMessage,String>
{
private static final String TDU_KEY = "Terminator Data Unit (TDU)";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public TDUMessageFilter()
{
super("TDU Terminator Data Unit");
super("Terminator/No Link Control messages");
add(new FilterElement<>(TDU_KEY));
}
@Override
public boolean passes(IMessage message)
public Function<IMessage, String> getKeyExtractor()
{
return mEnabled && canProcess(message);
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof TDUMessage;
return message instanceof TDUMessage && super.canProcess(message);
}
@Override
public List<FilterElement<?>> getFilterElements()
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
return Collections.EMPTY_LIST;
@Override
public String apply(IMessage message)
{
if(message instanceof TDUMessage)
{
return TDU_KEY;
}
return null;
}
}
}

View File

@@ -1,113 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.*;
public class TSBKMessageFilterSet extends FilterSet<IMessage>
{
public TSBKMessageFilterSet()
{
super("TSBK Trunking Signalling Block");
addFilter(new StandardOpcodeFilter());
}
// @Override
// public boolean passes( Message message )
// {
// if( mEnabled && canProcess( message ) )
// {
// TSBKMessage tsbk = (TSBKMessage) message;
//
// switch( tsbk.getVendor() )
// {
// case STANDARD:
// return
// }
// Opcode opcode = tsbk.getOpcode();
//
// return mFilterElements.get( opcode ).isEnabled();
// }
//
// return false;
// }
@Override
public boolean canProcess(IMessage message)
{
return message instanceof TSBKMessage;
}
public class StandardOpcodeFilter extends Filter<IMessage>
{
private Map<Opcode, FilterElement<Opcode>> mStandardElements = new EnumMap<>(Opcode.class);
public StandardOpcodeFilter()
{
super("Standard Opcode Filter");
for(Opcode opcode : Opcode.values())
{
if(opcode != Opcode.OSP_UNKNOWN)
{
mStandardElements.put(opcode, new FilterElement<Opcode>(opcode));
}
}
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
Opcode opcode = ((TSBKMessage) message).getOpcode();
if(mStandardElements.containsKey(opcode))
{
return mStandardElements.get(opcode).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return ((TSBKMessage) message).getVendor() == Vendor.STANDARD;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>(mStandardElements.values());
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import java.util.Collection;
import java.util.function.Function;
/**
* Filter for TDU terminator and TDULC terminator with link control messages
*/
public class TerminatorMessageFilter extends Filter<IMessage, LinkControlOpcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public TerminatorMessageFilter(String name, Collection<LinkControlOpcode> opcodes)
{
super(name);
for(LinkControlOpcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
@Override
public Function<IMessage, LinkControlOpcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,LinkControlOpcode>
{
@Override
public LinkControlOpcode apply(IMessage message)
{
if(message instanceof TDULinkControlMessage tdulc)
{
return tdulc.getLinkControlWord().getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for terminator/link control messages
*/
public class TerminatorMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public TerminatorMessageFilterSet()
{
super("Terminator Messages");
addFilter(new TDUMessageFilter());
addFilter(new TerminatorMessageFilter("Command/Status", LinkControlOpcode.COMMAND_STATUS_OPCODES));
addFilter(new TerminatorMessageFilter("Network/Channel", LinkControlOpcode.NETWORK_OPCODES));
addFilter(new TerminatorMessageFilter("Voice", LinkControlOpcode.VOICE_OPCODES));
addFilter(new TerminatorMessageFilter("Motorola", LinkControlOpcode.MOTOROLA_OPCODES));
List<LinkControlOpcode> others = new ArrayList<>();
for(LinkControlOpcode opcode: LinkControlOpcode.values())
{
if(!opcode.isGrouped())
{
others.add(opcode);
}
}
addFilter(new TerminatorMessageFilter("Other/Reserved", others));
}
@Override
public boolean canProcess(IMessage message)
{
return (message instanceof TDUMessage || message instanceof TDULinkControlMessage) && super.canProcess(message);
}
}

View File

@@ -0,0 +1,91 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import java.util.Collection;
import java.util.function.Function;
/**
* Message filter for P25 TSBK, AMBTC and UMBTC trunking control messages.
*/
public class TrunkingOpcodeMessageFilter extends Filter<IMessage, Opcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
private Collection<Opcode> mOpcodes;
/**
* Constructs an instance
*
* @param name of this filter
*/
public TrunkingOpcodeMessageFilter(String name, Collection<Opcode> opcodes)
{
super(name);
mOpcodes = opcodes;
for(Opcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
/**
* Key extractor
* @return opcode extractor
*/
@Override
public Function<IMessage, Opcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor for Opcode values from TSBK, AMBTC and UMBTC messages.
*/
private class KeyExtractor implements Function<IMessage,Opcode>
{
@Override
public Opcode apply(IMessage message)
{
if(message instanceof TSBKMessage tsbk)
{
return tsbk.getOpcode();
}
else if(message instanceof AMBTCMessage ambtc)
{
return ambtc.getHeader().getOpcode();
}
else if(message instanceof UMBTCMessage umbtc)
{
return umbtc.getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import java.util.List;
/**
* Filter set for trunking messages
*/
public class TrunkingOpcodeMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public TrunkingOpcodeMessageFilterSet()
{
super("Trunking Messages");
addFilter(new TrunkingOpcodeMessageFilter("Mobile Data Requests", Opcode.MOBILE_DATA_REQUESTS));
addFilter(new TrunkingOpcodeMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE));
addFilter(new TrunkingOpcodeMessageFilter("Mobile Voice Requests", Opcode.MOBILE_VOICE_REQUESTS));
addFilter(new TrunkingOpcodeMessageFilter("Network/Channel Status", Opcode.NETWORK_CHANNEL_STATUS));
addFilter(new TrunkingOpcodeMessageFilter("Network Data Grants", Opcode.NETWORK_DATA_GRANTS));
addFilter(new TrunkingOpcodeMessageFilter("Network Command/Request/Response", Opcode.NETWORK_COMMAND_REQUEST_RESPONSE));
addFilter(new TrunkingOpcodeMessageFilter("Network Voice Grants", Opcode.NETWORK_VOICE_GRANTS));
addFilter(new TrunkingOpcodeMessageFilter("Vendor-Harris Messages", Opcode.HARRIS));
addFilter(new TrunkingOpcodeMessageFilter("Vendor-Motorola Messages", Opcode.MOTOROLA));
addFilter(new TrunkingOpcodeMessageFilter("Vendor-Unknown Messages", Opcode.UNKNOWN));
List<Opcode> ungrouped = Opcode.getUngrouped();
if(!ungrouped.isEmpty())
{
addFilter(new TrunkingOpcodeMessageFilter("Unknown Opcode Messages", ungrouped));
}
}
@Override
public boolean canProcess(IMessage message)
{
return (message instanceof TSBKMessage || message instanceof AMBTCMessage || message instanceof UMBTCMessage) &&
super.canProcess(message);
}
}

View File

@@ -1,55 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
import java.util.Collections;
import java.util.List;
public class UMBTCMessageFilter extends Filter<IMessage>
{
public UMBTCMessageFilter()
{
super("Unconfirmed Multi-Block Trunking Control");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof UMBTCMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -0,0 +1,83 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
import java.util.function.Function;
/**
* Filter for LDU1 and LDU2 voice messages
*/
public class VoiceMessageFilter extends Filter<IMessage,String>
{
private static final String LDU1_KEY = "LDU1 Voice Message";
private static final String LDU2_KEY = "LDU2 Voice Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructs an instance
*
* @param name of this filter
*/
public VoiceMessageFilter()
{
super("Voice Messages");
add(new FilterElement<>(LDU1_KEY));
add(new FilterElement<>(LDU2_KEY));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof LDUMessage && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof LDU1Message)
{
return LDU1_KEY;
}
else if(message instanceof LDU2Message)
{
return LDU2_KEY;
}
return null;
}
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,13 +14,17 @@
*
* 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 io.github.dsheirer.module.decode.p25.phase1.message.lc;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.EnumSet;
/**
* Opcodes used for Link Control Words.
*/
public enum LinkControlOpcode
{
GROUP_VOICE_CHANNEL_USER("GROUP VOICE CHANNEL USER", 0),
@@ -101,22 +104,82 @@ public enum LinkControlOpcode
private String mLabel;
private int mCode;
/**
* Constructor
* @param label to display for the opcode
* @param code value.
*/
LinkControlOpcode(String label, int code)
{
mLabel = label;
mCode = code;
}
/**
* Command, response and status opcodes
*/
public static final EnumSet<LinkControlOpcode> COMMAND_STATUS_OPCODES = EnumSet.of(UNIT_TO_UNIT_ANSWER_REQUEST,
TELEPHONE_INTERCONNECT_ANSWER_REQUEST, CALL_TERMINATION_OR_CANCELLATION, GROUP_AFFILIATION_QUERY,
UNIT_AUTHENTICATION_COMMAND, UNIT_REGISTRATION_COMMAND, STATUS_QUERY, STATUS_UPDATE, MESSAGE_UPDATE,
EXTENDED_FUNCTION_COMMAND, CALL_ALERT);
/**
* Motorola Opcodes
*/
public static final EnumSet<LinkControlOpcode> MOTOROLA_OPCODES =
EnumSet.range(MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER, MOTOROLA_UNKNOWN);
/**
* Network/channel related opcodes
*/
public static final EnumSet<LinkControlOpcode> NETWORK_OPCODES = EnumSet.of(CHANNEL_IDENTIFIER_UPDATE,
CHANNEL_IDENTIFIER_UPDATE_EXPLICIT, SYSTEM_SERVICE_BROADCAST, SECONDARY_CONTROL_CHANNEL_BROADCAST,
SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, ADJACENT_SITE_STATUS_BROADCAST,
ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT, RFSS_STATUS_BROADCAST, RFSS_STATUS_BROADCAST_EXPLICIT,
NETWORK_STATUS_BROADCAST, NETWORK_STATUS_BROADCAST_EXPLICIT, PROTECTION_PARAMETER_BROADCAST);
/**
* Voice/call opcodes
*/
public static final EnumSet<LinkControlOpcode> VOICE_OPCODES = EnumSet.of(GROUP_VOICE_CHANNEL_USER,
GROUP_VOICE_CHANNEL_UPDATE, GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT, UNIT_TO_UNIT_VOICE_CHANNEL_USER,
TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER);
/**
* Indicates if the enumeration element is contained in one of the enumset groupings above.
* @return true if the element is grouped.
*/
public boolean isGrouped()
{
return COMMAND_STATUS_OPCODES.contains(this) || MOTOROLA_OPCODES.contains(this) ||
NETWORK_OPCODES.contains(this) || VOICE_OPCODES.contains(this);
}
/**
* Pretty label/string for the element
* @return label
*/
public String getLabel()
{
return mLabel;
}
/**
* Numeric value of the opcode.
* @return code value.
*/
public int getCode()
{
return mCode;
}
/**
* Lookup method for finding an opcode for a given vendor and value combination.
* @param value of the opcode
* @param vendor for the opcode
* @return matching element or UNKNOWN.
*/
public static LinkControlOpcode fromValue(int value, Vendor vendor)
{
switch(vendor)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2021 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -21,8 +21,9 @@ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk;
import io.github.dsheirer.module.decode.p25.reference.Direction;
import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
public enum Opcode
{
@@ -192,13 +193,12 @@ public enum Opcode
private String mLabel;
private String mDescription;
public static final EnumSet<Opcode> STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT,
OSP_PROTECTION_PARAMETER_UPDATE);
public static final EnumSet<Opcode> STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST,
ISP_RESERVED_3F);
public static final EnumSet<Opcode> DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(OSP_SNDCP_DATA_CHANNEL_GRANT,
OSP_INDIVIDUAL_DATA_CHANNEL_GRANT, OSP_GROUP_DATA_CHANNEL_GRANT);
/**
* Constructor
* @param code value
* @param label or pretty value.
* @param description of the opcode
*/
Opcode(int code, String label, String description)
{
mCode = code;
@@ -206,6 +206,135 @@ public enum Opcode
mDescription = description;
}
/**
* OSP standard opcodes
*/
public static final EnumSet<Opcode> STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT,
OSP_PROTECTION_PARAMETER_UPDATE);
/**
* ISP standard opcodes
*/
public static final EnumSet<Opcode> STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST,
ISP_RESERVED_3F);
/**
* ISP mobile data requests
*/
public static final EnumSet<Opcode> MOBILE_DATA_REQUESTS = EnumSet.of(ISP_INDIVIDUAL_DATA_SERVICE_REQUEST,
ISP_GROUP_DATA_SERVICE_REQUEST, ISP_SNDCP_DATA_CHANNEL_REQUEST, ISP_SNDCP_RECONNECT_REQUEST);
/**
* ISP mobile voice requests
*/
public static final EnumSet<Opcode> MOBILE_VOICE_REQUESTS = EnumSet.of(ISP_GROUP_VOICE_SERVICE_REQUEST,
ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST, ISP_TELEPHONE_INTERCONNECT_EXPLICIT_DIAL_REQUEST,
ISP_TELEPHONE_INTERCONNECT_PSTN_REQUEST);
/**
* ISP mobile request, response and status.
*/
public static final EnumSet<Opcode> MOBILE_REQUEST_RESPONSE = EnumSet.of(ISP_UNIT_TO_UNIT_ANSWER_RESPONSE,
ISP_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE, ISP_SNDCP_DATA_PAGE_RESPONSE, ISP_STATUS_UPDATE_REQUEST,
ISP_STATUS_QUERY_REQUEST, ISP_STATUS_QUERY_RESPONSE, ISP_MESSAGE_UPDATE_REQUEST,
ISP_RADIO_UNIT_MONITOR_REQUEST, ISP_CALL_ALERT_REQUEST, ISP_UNIT_ACKNOWLEDGE_RESPONSE,
ISP_CANCEL_SERVICE_REQUEST, ISP_EXTENDED_FUNCTION_RESPONSE, ISP_EMERGENCY_ALARM_REQUEST,
ISP_GROUP_AFFILIATION_QUERY_RESPONSE, ISP_GROUP_AFFILIATION_REQUEST, ISP_UNIT_DE_REGISTRATION_REQUEST,
ISP_UNIT_REGISTRATION_REQUEST, ISP_LOCATION_REGISTRATION_REQUEST, ISP_AUTHENTICATION_QUERY_OBSOLETE,
ISP_AUTHENTICATION_RESPONSE_OBSOLETE, ISP_PROTECTION_PARAMETER_REQUEST, ISP_IDENTIFIER_UPDATE_REQUEST,
ISP_ROAMING_ADDRESS_REQUEST, ISP_ROAMING_ADDRESS_RESPONSE, ISP_AUTHENTICATION_RESPONSE,
ISP_AUTHENTICATION_RESPONSE_MUTUAL, ISP_AUTHENTICATION_FNE_RESULT, ISP_AUTHENTICATION_SU_DEMAND);
/**
* OSP network data channel grants.
*/
public static final EnumSet<Opcode> NETWORK_DATA_GRANTS = EnumSet.of(OSP_SNDCP_DATA_CHANNEL_GRANT,
OSP_INDIVIDUAL_DATA_CHANNEL_GRANT, OSP_GROUP_DATA_CHANNEL_GRANT);
/**
* OSP network status
*/
public static final EnumSet<Opcode> NETWORK_CHANNEL_STATUS = EnumSet.of(OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT,
OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT, OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT,
OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, OSP_TDMA_SYNC_BROADCAST, OSP_IDENTIFIER_UPDATE_TDMA,
OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS, OSP_TIME_DATE_ANNOUNCEMENT, OSP_SYSTEM_SERVICE_BROADCAST,
OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST, OSP_RFSS_STATUS_BROADCAST, OSP_NETWORK_STATUS_BROADCAST,
OSP_ADJACENT_STATUS_BROADCAST, OSP_IDENTIFIER_UPDATE, OSP_PROTECTION_PARAMETER_BROADCAST,
OSP_PROTECTION_PARAMETER_UPDATE);
/**
* OSP network command, request and response
*/
public static final EnumSet<Opcode> NETWORK_COMMAND_REQUEST_RESPONSE = EnumSet.of(OSP_UNIT_TO_UNIT_ANSWER_REQUEST,
OSP_TELEPHONE_INTERCONNECT_ANSWER_REQUEST, OSP_SNDCP_DATA_PAGE_REQUEST, OSP_STATUS_UPDATE,
OSP_STATUS_QUERY, OSP_MESSAGE_UPDATE, OSP_RADIO_UNIT_MONITOR_COMMAND, OSP_CALL_ALERT,
OSP_ACKNOWLEDGE_RESPONSE, OSP_QUEUED_RESPONSE, OSP_EXTENDED_FUNCTION_COMMAND, OSP_DENY_RESPONSE,
OSP_GROUP_AFFILIATION_QUERY, OSP_GROUP_AFFILIATION_RESPONSE, OSP_LOCATION_REGISTRATION_RESPONSE,
OSP_UNIT_REGISTRATION_COMMAND, OSP_UNIT_REGISTRATION_RESPONSE, OSP_AUTHENTICATION_COMMAND,
OSP_UNIT_DEREGISTRATION_ACKNOWLEDGE, OSP_AUTHENTICATION_DEMAND, OSP_AUTHENTICATION_FNE_RESPONSE,
OSP_ROAMING_ADDRESS_COMMAND, OSP_ROAMING_ADDRESS_UPDATE);
/**
* OSP network voice channel grants.
*/
public static final EnumSet<Opcode> NETWORK_VOICE_GRANTS = EnumSet.of(OSP_GROUP_VOICE_CHANNEL_GRANT,
OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE, OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT,
OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT, OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE,
OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT, OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE);
/**
* Motorola opcodes
*/
public static final EnumSet<Opcode> MOTOROLA = EnumSet.of(MOTOROLA_ISP_UNKNOWN, MOTOROLA_OSP_PATCH_GROUP_ADD,
MOTOROLA_OSP_PATCH_GROUP_DELETE, MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT,
MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE, MOTOROLA_OSP_TRAFFIC_CHANNEL_ID,
MOTOROLA_OSP_DENY_RESPONSE, MOTOROLA_OSP_SYSTEM_LOADING, MOTOROLA_OSP_BASE_STATION_ID,
MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN, MOTOROLA_OSP_UNKNOWN);
/**
* Harris opcodes
*/
public static final EnumSet<Opcode> HARRIS = EnumSet.of(HARRIS_ISP_UNKNOWN, HARRIS_OSP_TDMA_SYNC,
HARRIS_OSP_UNKNOWN);
/**
* Unknown vendor ISP/OSP opcodes
*/
public static final EnumSet<Opcode> UNKNOWN = EnumSet.of(UNKNOWN_VENDOR_ISP, UNKNOWN_VENDOR_OSP);
/**
* Indicates if the enumeration element is contained in one of the enumset groupings above.
* @return true if the element is grouped.
*/
public boolean isGrouped()
{
return MOBILE_DATA_REQUESTS.contains(this) || MOBILE_VOICE_REQUESTS.contains(this) ||
MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_DATA_GRANTS.contains(this) ||
NETWORK_CHANNEL_STATUS.contains(this) || NETWORK_COMMAND_REQUEST_RESPONSE.contains(this) ||
NETWORK_VOICE_GRANTS.contains(this) || MOTOROLA.contains(this) || HARRIS.contains(this) ||
UNKNOWN.contains(this);
}
/**
* List of opcodes that are not grouped. This will catch any opcodes that are added to this enumeration where
* the opcode is not added to one of the existing groups. This is used for message filtering to ensure that all
* opcodes and handled by the filter/filterset.
* @return list of ungrouped opcodes.
*/
public static List<Opcode> getUngrouped()
{
List<Opcode> ungrouped = new ArrayList<>();
for(Opcode opcode: ungrouped)
{
if(!opcode.isGrouped())
{
ungrouped.add(opcode);
}
}
return ungrouped;
}
/**
* Formatted, fixed-length label
*/
@@ -235,7 +364,7 @@ public enum Opcode
*/
public boolean isDataChannelGrant()
{
return DATA_CHANNEL_GRANT_OPCODES.contains(this);
return NETWORK_DATA_GRANTS.contains(this);
}
@Override
@@ -244,17 +373,13 @@ public enum Opcode
return mLabel;
}
@Deprecated //use the ISP/OSP version of this method instead
public static Opcode fromValue(int value)
{
if(0 <= value && value <= 63)
{
return values()[value];
}
return OSP_UNKNOWN;
}
/**
* Lookup an opcode from a numeric value, vendor, and inbound/outbound direction.
* @param value for the opcode
* @param direction indicator - inbound (mobile) or outbound (tower)
* @param vendor identifier
* @return opcode or UNKNOWN
*/
public static Opcode fromValue(int value, Direction direction, Vendor vendor)
{
switch(vendor)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -169,9 +169,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
{
if(message.isValid() && message.getTimeslot() == getTimeslot())
{
if(message instanceof MacMessage)
if(message instanceof MacMessage macMessage)
{
MacMessage macMessage = (MacMessage)message;
processMacMessage(macMessage);
MacPduType macPduType = macMessage.getMacPduType();
@@ -315,31 +314,26 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
processMacRelease(message, mac);
break;
case PHASE1_65_GROUP_VOICE_SERVICE_REQUEST:
if(mac instanceof GroupVoiceServiceRequest)
if(mac instanceof GroupVoiceServiceRequest gvsr)
{
GroupVoiceServiceRequest gvsr = (GroupVoiceServiceRequest)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "GROUP VOICE SERVICE " + gvsr.getServiceOptions());
}
break;
case PHASE1_68_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_ABBREVIATED:
if(mac instanceof UnitToUnitVoiceChannelGrantAbbreviated)
if(mac instanceof UnitToUnitVoiceChannelGrantAbbreviated uuvcga)
{
UnitToUnitVoiceChannelGrantAbbreviated uuvcga = (UnitToUnitVoiceChannelGrantAbbreviated)mac;
broadcast(message, mac, uuvcga.getChannel(), DecodeEventType.CALL_UNIT_TO_UNIT, "VOICE CHANNEL GRANT");
}
break;
case PHASE1_69_UNIT_TO_UNIT_ANSWER_REQUEST_ABBREVIATED:
if(mac instanceof UnitToUnitAnswerRequestAbbreviated)
if(mac instanceof UnitToUnitAnswerRequestAbbreviated uuara)
{
UnitToUnitAnswerRequestAbbreviated uuara = (UnitToUnitAnswerRequestAbbreviated)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "UNIT-TO-UNIT ANSWER REQUEST - " + uuara.getServiceOptions());
}
break;
case PHASE1_74_TELEPHONE_INTERCONNECT_ANSWER_REQUEST:
if(mac instanceof TelephoneInterconnectAnswerRequest)
if(mac instanceof TelephoneInterconnectAnswerRequest tiar)
{
TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT ANSWER REQUEST");
}
break;
@@ -350,9 +344,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
processSDCG(message, mac);
break;
case PHASE1_85_SNDCP_DATA_PAGE_REQUEST:
if(mac instanceof SNDCPDataPageRequest)
if(mac instanceof SNDCPDataPageRequest sdpr)
{
SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.PAGE, "SNDCP DATA PAGE " + sdpr.getServiceOptions());
}
break;
@@ -366,9 +359,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
break;
case PHASE1_92_MESSAGE_UPDATE_ABBREVIATED:
if(mac instanceof MessageUpdateAbbreviated)
if(mac instanceof MessageUpdateAbbreviated mua)
{
MessageUpdateAbbreviated mua = (MessageUpdateAbbreviated)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.SDM, "MESSAGE UPDATE - " + mua.getShortDataMessage());
}
break;
@@ -379,9 +371,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
processCallAlert(message, mac);
break;
case PHASE1_96_ACK_RESPONSE:
if(mac instanceof AcknowledgeResponse)
if(mac instanceof AcknowledgeResponse ar)
{
AcknowledgeResponse ar = (AcknowledgeResponse)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.RESPONSE, "ACKNOWLEDGE: " + ar.getServiceType());
}
break;
@@ -401,10 +392,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
broadcast(message, mac, getCurrentChannel(), DecodeEventType.COMMAND, "UNIT REGISTRATION");
break;
case PHASE1_197_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED:
if(mac instanceof UnitToUnitAnswerRequestExtended)
if(mac instanceof UnitToUnitAnswerRequestExtended uuare)
{
UnitToUnitAnswerRequestExtended uuare = (UnitToUnitAnswerRequestExtended)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "UNIT-TO-UNIT ANSWER REQUEST " + uuare.getServiceOptions());
}
break;
@@ -418,9 +407,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
broadcast(message, mac, getCurrentChannel(), DecodeEventType.STATUS, "STATUS QUERY");
break;
case PHASE1_220_MESSAGE_UPDATE_EXTENDED:
if(mac instanceof MessageUpdateExtended)
if(mac instanceof MessageUpdateExtended mue)
{
MessageUpdateExtended mue = (MessageUpdateExtended)mac;
broadcast(message, mac, getCurrentChannel(), DecodeEventType.SDM, "MESSAGE UPDATE - " + mue.getShortDataMessage());
}
break;
@@ -468,270 +456,153 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processQueuedResponse(MacMessage message, MacStructure mac) {
if(mac instanceof QueuedResponse)
if(mac instanceof QueuedResponse qr)
{
QueuedResponse qr = (QueuedResponse)mac;
MutableIdentifierCollection icQueuedResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icQueuedResponse.remove(IdentifierClass.USER);
icQueuedResponse.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.RESPONSE.toString())
.details("QUEUED - " + qr.getQueuedResponseServiceType() +
" REASON:" + qr.getQueuedResponseReason() + " ADDL:" + qr.getAdditionalInfo())
.identifiers(icQueuedResponse)
.build());
broadcast(message, mac, DecodeEventType.RESPONSE,
"QUEUED - " + qr.getQueuedResponseServiceType() +
" REASON:" + qr.getQueuedResponseReason() + " ADDL:" + qr.getAdditionalInfo());
}
}
private void processEFCA(MacMessage message, MacStructure mac) {
if(mac instanceof ExtendedFunctionCommand)
if(mac instanceof ExtendedFunctionCommand efc)
{
ExtendedFunctionCommand efc = (ExtendedFunctionCommand)mac;
MutableIdentifierCollection icExtendedFunction = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icExtendedFunction.remove(IdentifierClass.USER);
icExtendedFunction.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("EXTENDED FUNCTION: " + efc.getExtendedFunction() + " ARGUMENTS:" + efc.getArguments())
.identifiers(icExtendedFunction)
.build());
broadcast(message, mac, DecodeEventType.COMMAND,
"EXTENDED FUNCTION: " + efc.getExtendedFunction() + " ARGUMENTS:" + efc.getArguments());
}
}
private void processDenyResponse(MacMessage message, MacStructure mac) {
if(mac instanceof DenyResponse)
if(mac instanceof DenyResponse dr)
{
DenyResponse dr = (DenyResponse)mac;
MutableIdentifierCollection icDenyResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icDenyResponse.remove(IdentifierClass.USER);
icDenyResponse.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.RESPONSE.toString())
.details("DENY: " + dr.getDeniedServiceType() + " REASON:" + dr.getDenyReason() + " ADDL:" + dr.getAdditionalInfo())
.identifiers(icDenyResponse)
.build());
broadcast(message, mac, DecodeEventType.RESPONSE,
"DENY: " + dr.getDeniedServiceType() + " REASON:" + dr.getDenyReason() + " ADDL:" + dr.getAdditionalInfo());
}
}
private void processRUMCExtended(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommandExtended)
if(mac instanceof RadioUnitMonitorCommandExtended rumce)
{
RadioUnitMonitorCommandExtended rumce = (RadioUnitMonitorCommandExtended)mac;
MutableIdentifierCollection icRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icRequest.remove(IdentifierClass.USER);
icRequest.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("RADIO UNIT MONITOR" + (rumce.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumce.getTransmitTime() + "MULTIPLIER:" + rumce.getTransmitMultiplier())
.identifiers(icRequest)
.build());
broadcast(message, mac, DecodeEventType.COMMAND,
"RADIO UNIT MONITOR" + (rumce.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumce.getTransmitTime() + "MULTIPLIER:" + rumce.getTransmitMultiplier());
}
}
private void processSUE(MacMessage message, MacStructure mac) {
if(mac instanceof StatusUpdateExtended)
if(mac instanceof StatusUpdateExtended sue)
{
StatusUpdateExtended sue = (StatusUpdateExtended)mac;
MutableIdentifierCollection icStatusUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icStatusUpdate.remove(IdentifierClass.USER);
icStatusUpdate.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.STATUS.toString())
.details("STATUS UPDATE - UNIT:" + sue.getUnitStatus() + " USER:" + sue.getUserStatus())
.identifiers(icStatusUpdate)
.build());
broadcast(message, mac, DecodeEventType.STATUS,
"STATUS UPDATE - UNIT:" + sue.getUnitStatus() + " USER:" + sue.getUserStatus());
}
}
private void processEFCE(MacMessage message, MacStructure mac) {
if(mac instanceof ExtendedFunctionCommandExtended)
if(mac instanceof ExtendedFunctionCommandExtended efce)
{
ExtendedFunctionCommandExtended efce = (ExtendedFunctionCommandExtended)mac;
MutableIdentifierCollection icExtendedFunction = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icExtendedFunction.remove(IdentifierClass.USER);
icExtendedFunction.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("EXTENDED FUNCTION: " + efce.getExtendedFunction() + " ARGUMENTS:" + efce.getArguments())
.identifiers(icExtendedFunction)
.build());
broadcast(message, mac, DecodeEventType.COMMAND,
"EXTENDED FUNCTION: " + efce.getExtendedFunction() + " ARGUMENTS:" + efce.getArguments());
}
}
private void processCallAlert(MacMessage message, MacStructure mac) {
MutableIdentifierCollection icCallAlert = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icCallAlert.remove(IdentifierClass.USER);
icCallAlert.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.CALL_ALERT.toString())
.identifiers(icCallAlert)
.build());
broadcast(message, mac, DecodeEventType.CALL_ALERT, null);
}
private void processRUMCE(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommandEnhanced)
if(mac instanceof RadioUnitMonitorCommandEnhanced rumc)
{
RadioUnitMonitorCommandEnhanced rumc = (RadioUnitMonitorCommandEnhanced)mac;
MutableIdentifierCollection icRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icRequest.remove(IdentifierClass.USER);
icRequest.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("RADIO UNIT MONITOR" + (rumc.isStealthMode() ? " (STEALTH)" : "") +
broadcast(message, mac, DecodeEventType.COMMAND,
"RADIO UNIT MONITOR" + (rumc.isStealthMode() ? " (STEALTH)" : "") +
" ENCRYPTION:" + rumc.getEncryption() +
" TIME:" + rumc.getTransmitTime())
.identifiers(icRequest)
.build());
" TIME:" + rumc.getTransmitTime());
}
}
private void processSUA(MacMessage message, MacStructure mac) {
if(mac instanceof StatusUpdateAbbreviated)
if(mac instanceof StatusUpdateAbbreviated sua)
{
StatusUpdateAbbreviated sua = (StatusUpdateAbbreviated)mac;
MutableIdentifierCollection icStatusUpdate = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icStatusUpdate.remove(IdentifierClass.USER);
icStatusUpdate.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.STATUS.toString())
.details("STATUS UPDATE - UNIT:" + sua.getUnitStatus() + " USER:" + sua.getUserStatus())
.identifiers(icStatusUpdate)
.build());
broadcast(message, mac, DecodeEventType.STATUS,
"STATUS UPDATE - UNIT:" + sua.getUnitStatus() + " USER:" + sua.getUserStatus());
}
}
private void processSDCG(MacMessage message, MacStructure mac) {
if(mac instanceof SNDCPDataChannelGrant)
if(mac instanceof SNDCPDataChannelGrant sdcg)
{
SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)mac;
MutableIdentifierCollection icGrant = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icGrant.remove(IdentifierClass.USER);
icGrant.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(sdcg.getChannel())
.eventDescription(sdcg.getServiceOptions().isEncrypted() ? DecodeEventType.DATA_CALL_ENCRYPTED.toString() : DecodeEventType.DATA_CALL.toString())
.details("SNDCP CHANNEL GRANT " + sdcg.getServiceOptions())
.identifiers(icGrant)
.build());
broadcast(message, mac,
sdcg.getServiceOptions().isEncrypted() ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL,
"SNDCP CHANNEL GRANT " + sdcg.getServiceOptions());
}
}
private void processRUMCA(MacMessage message, MacStructure mac) {
if(mac instanceof RadioUnitMonitorCommand)
if(mac instanceof RadioUnitMonitorCommand rumc)
{
RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)mac;
MutableIdentifierCollection icRequest = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icRequest.remove(IdentifierClass.USER);
icRequest.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("RADIO UNIT MONITOR" + (rumc.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumc.getTransmitTime() + " MULTIPLIER:" + rumc.getTransmitMultiplier())
.identifiers(icRequest)
.build());
broadcast(message, mac, DecodeEventType.COMMAND,
"RADIO UNIT MONITOR" + (rumc.isSilentMonitor() ? " (STEALTH)" : "") +
" TIME:" + rumc.getTransmitTime() + " MULTIPLIER:" + rumc.getTransmitMultiplier());
}
}
private void processMacRelease(MacMessage message, MacStructure mac) {
if(mac instanceof MacRelease)
if(mac instanceof MacRelease mr)
{
MacRelease mr = (MacRelease)mac;
MutableIdentifierCollection icMacRelease = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icMacRelease.remove(IdentifierClass.USER);
icMacRelease.update(mac.getIdentifiers());
closeCurrentCallEvent(message.getTimestamp(), true, message.getMacPduType());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details((mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isTalkerPreemption() ? " BY USER" : ""))
.identifiers(icMacRelease)
.build());
broadcast(message, mac, DecodeEventType.COMMAND,
(mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isTalkerPreemption() ? " BY USER" : ""));
}
}
private void processPCSQ(MacMessage message, MacStructure mac) {
if(mac instanceof PowerControlSignalQuality)
if(mac instanceof PowerControlSignalQuality pcsq)
{
PowerControlSignalQuality pcsq = (PowerControlSignalQuality)mac;
MutableIdentifierCollection icPowerControl = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icPowerControl.remove(IdentifierClass.USER);
icPowerControl.update(mac.getIdentifiers());
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.COMMAND.toString())
.details("ADJUST TRANSMIT POWER - RF:" + pcsq.getRFLevel() + " BER:" + pcsq.getBitErrorRate())
.identifiers(icPowerControl)
.build());
broadcast(message, mac, DecodeEventType.COMMAND,
"ADJUST TRANSMIT POWER - RF:" + pcsq.getRFLevel() + " BER:" + pcsq.getBitErrorRate());
}
}
private void processIPMWP(MacMessage message, MacStructure mac) {
if(mac instanceof IndividualPagingMessage)
if(mac instanceof IndividualPagingMessage ipm)
{
IndividualPagingMessage ipm = (IndividualPagingMessage)mac;
MutableIdentifierCollection icIndividualPaging = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icIndividualPaging.remove(IdentifierClass.USER);
icIndividualPaging.update(mac.getIdentifiers());
boolean priority = ipm.isTalkgroupPriority1() || ipm.isTalkgroupPriority2() ||
ipm.isTalkgroupPriority3() || ipm.isTalkgroupPriority4();
broadcast(P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
.eventDescription(DecodeEventType.PAGE.toString())
.details((priority ? "PRIORITY " : "") + "USER PAGE")
.identifiers(icIndividualPaging)
.build());
broadcast(message, mac, DecodeEventType.PAGE,
(priority ? "PRIORITY " : "") + "USER PAGE");
}
}
private void broadcast(MacMessage message, MacStructure mac, IChannelDescriptor currentChannel, DecodeEventType page, String s) {
MutableIdentifierCollection collection = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
collection.remove(IdentifierClass.USER);
collection.update(mac.getIdentifiers());
private void broadcast(MacMessage message, MacStructure mac, IChannelDescriptor currentChannel, DecodeEventType eventType, String details) {
MutableIdentifierCollection collection = getUpdatedMutableIdentifierCollection(mac);
broadcast(P25DecodeEvent.builder(message.getTimestamp())
broadcast(P25DecodeEvent.builder(eventType, message.getTimestamp())
.channel(currentChannel)
.eventDescription(page.toString())
.details(s)
.details(details)
.identifiers(collection)
.build());
}
private void processUTUVCGUExtended(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended)
{
UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = (UnitToUnitVoiceChannelGrantUpdateExtended)mac;
private void broadcast(MacMessage message, MacStructure structure, DecodeEventType eventType, String details) {
MutableIdentifierCollection icQueuedResponse = getUpdatedMutableIdentifierCollection(structure);
broadcast(P25DecodeEvent.builder(eventType, message.getTimestamp())
.channel(getCurrentChannel())
.details(details)
.identifiers(icQueuedResponse)
.build());
}
private MutableIdentifierCollection getUpdatedMutableIdentifierCollection(MacStructure mac) {
MutableIdentifierCollection icQueuedResponse = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
icQueuedResponse.remove(IdentifierClass.USER);
icQueuedResponse.update(mac.getIdentifiers());
return icQueuedResponse;
}
private void processUTUVCGUExtended(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue)
{
if(isCurrentGroup(uuvcgue.getTargetAddress()))
{
broadcastCurrentChannel(uuvcgue.getChannel());
@@ -740,10 +611,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUExplicit(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateExplicit)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateExplicit gvcgue)
{
GroupVoiceChannelGrantUpdateExplicit gvcgue = (GroupVoiceChannelGrantUpdateExplicit)mac;
if(isCurrentGroup(gvcgue.getGroupAddress()))
{
broadcastCurrentChannel(gvcgue.getChannel());
@@ -752,10 +621,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processUTUVCGUAbbreviated(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantAbbreviated)
if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantAbbreviated uuvcga)
{
UnitToUnitVoiceChannelGrantAbbreviated uuvcga = (UnitToUnitVoiceChannelGrantAbbreviated)mac;
if(isCurrentGroup(uuvcga.getSourceAddress()) || isCurrentGroup(uuvcga.getTargetAddress()))
{
broadcastCurrentChannel(uuvcga.getChannel());
@@ -764,10 +631,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUpdate(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdate)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdate gvcgu)
{
GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)mac;
if(isCurrentGroup(gvcgu.getGroupAddressA()))
{
broadcastCurrentChannel(gvcgu.getChannelA());
@@ -781,10 +646,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGAbbreviated(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantAbbreviated)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantAbbreviated gvcga)
{
GroupVoiceChannelGrantAbbreviated gvcga = (GroupVoiceChannelGrantAbbreviated)mac;
if(isCurrentGroup(gvcga.getGroupAddress()))
{
broadcastCurrentChannel(gvcga.getChannel());
@@ -793,10 +656,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUMExplicit(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultipleExplicit)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultipleExplicit gvcgume)
{
GroupVoiceChannelGrantUpdateMultipleExplicit gvcgume = (GroupVoiceChannelGrantUpdateMultipleExplicit)mac;
if(isCurrentGroup(gvcgume.getGroupAddressA()))
{
broadcastCurrentChannel(gvcgume.getChannelA());
@@ -810,10 +671,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
private void processGVCGUM(MacStructure mac) {
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultiple)
if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultiple gvcgum)
{
GroupVoiceChannelGrantUpdateMultiple gvcgum = (GroupVoiceChannelGrantUpdateMultiple)mac;
if(isCurrentGroup(gvcgum.getGroupAddressA()))
{
broadcastCurrentChannel(gvcgum.getChannelA());
@@ -850,10 +709,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
getIdentifierCollection().update(mPatchGroupManager.update(identifier));
}
if(mac instanceof TelephoneInterconnectVoiceChannelUser)
if(mac instanceof TelephoneInterconnectVoiceChannelUser tivcu)
{
TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)mac;
if(tivcu.getServiceOptions().isEncrypted())
{
updateCurrentCall(DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, null, message.getTimestamp());
@@ -885,10 +742,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
getIdentifierCollection().update(mPatchGroupManager.update(identifier));
}
if(mac instanceof UnitToUnitVoiceChannelUserExtended)
if(mac instanceof UnitToUnitVoiceChannelUserExtended uuvcue)
{
UnitToUnitVoiceChannelUserExtended uuvcue = (UnitToUnitVoiceChannelUserExtended)mac;
if(uuvcue.getServiceOptions().isEncrypted())
{
updateCurrentCall(DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED, null, message.getTimestamp());
@@ -1144,9 +999,13 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
{
if(mCurrentCallEvent == null)
{
mCurrentCallEvent = P25DecodeEvent.builder(timestamp)
if(type == null)
{
type = DecodeEventType.CALL;
}
mCurrentCallEvent = P25DecodeEvent.builder(type, timestamp)
.channel(getCurrentChannel())
.eventDescription(type != null ? type.toString() : DecodeEventType.CALL.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -1164,11 +1023,6 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie
}
else
{
if(type != null)
{
mCurrentCallEvent.setEventDescription(type.toString());
}
if(details != null)
{
mCurrentCallEvent.setDetails(details);

View File

@@ -1,28 +0,0 @@
/*
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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 io.github.dsheirer.module.decode.p25.phase2.message;
public class P25MessageFactory
{
}

View File

@@ -0,0 +1,81 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase2.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacOpcode;
import java.util.Collection;
import java.util.function.Function;
/**
* Message filter for P25 Phase 2 MAC opcode messages.
*/
public class MacOpcodeMessageFilter extends Filter<IMessage, MacOpcode>
{
private KeyExtractor mKeyExtractor = new KeyExtractor();
private Collection<MacOpcode> mOpcodes;
/**
* Constructs an instance
*
* @param name of this filter
*/
public MacOpcodeMessageFilter(String name, Collection<MacOpcode> opcodes)
{
super(name);
mOpcodes = opcodes;
for(MacOpcode opcode: opcodes)
{
add(new FilterElement<>(opcode));
}
}
/**
* Key extractor
* @return opcode extractor
*/
@Override
public Function<IMessage, MacOpcode> getKeyExtractor()
{
return mKeyExtractor;
}
/**
* Key extractor for MacOpcode values from MacMessage.
*/
private class KeyExtractor implements Function<IMessage,MacOpcode>
{
@Override
public MacOpcode apply(IMessage message)
{
if(message instanceof MacMessage mac)
{
return mac.getMacStructure().getOpcode();
}
return null;
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase2.message.filter;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacOpcode;
import java.util.ArrayList;
import java.util.List;
/**
* Filter set for P25 Phase 2 MAC messages
*/
public class MacOpcodeMessageFilterSet extends FilterSet<IMessage>
{
/**
* Constructor
*/
public MacOpcodeMessageFilterSet()
{
super("Trunking Messages");
addFilter(new MacOpcodeMessageFilter("Call Maintenance", MacOpcode.CALL_MAINTENANCE));
addFilter(new MacOpcodeMessageFilter("Data Channel Grants", MacOpcode.DATA_GRANTS));
addFilter(new MacOpcodeMessageFilter("Mobile Request & Response", MacOpcode.MOBILE_REQUEST_RESPONSE));
addFilter(new MacOpcodeMessageFilter("Network Request & Response", MacOpcode.NETWORK_REQUEST_RESPONSE));
addFilter(new MacOpcodeMessageFilter("Network Status & Announcements", MacOpcode.NETWORK_STATUS));
addFilter(new MacOpcodeMessageFilter("Voice Channel Grants", MacOpcode.VOICE_GRANTS));
List<MacOpcode> ungrouped = new ArrayList<>();
for(MacOpcode opcode: ungrouped)
{
if(!opcode.isGrouped())
{
ungrouped.add(opcode);
}
}
addFilter(new MacOpcodeMessageFilter("Other/Unknown", ungrouped));
}
@Override
public boolean canProcess(IMessage message)
{
return (message instanceof MacMessage) && super.canProcess(message);
}
}

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,41 +14,38 @@
*
* 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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
package io.github.dsheirer.module.decode.p25.phase2.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
import io.github.dsheirer.message.SyncLossMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.P25P2Message;
import java.util.Collections;
import java.util.List;
public class IPPacketMessageFilter extends Filter<IMessage>
/**
* Filter set for P25 Phase 2 messages
*/
public class P25P2MessageFilterSet extends FilterSet<IMessage>
{
public IPPacketMessageFilter()
public P25P2MessageFilterSet()
{
super("Packet Message");
}
@Override
public boolean passes(IMessage message)
{
return mEnabled && canProcess(message);
super("P25 Phase 2 Message Filter");
addFilter(new MacOpcodeMessageFilterSet());
addFilter(new VoiceMessageFilter());
addFilter(new P25P2OtherMessageFilter());
}
/**
* Override default to descope handling to P25P2 or sync-loss messages.
* @param message to test
* @return true if the message can be processed
*/
@Override
public boolean canProcess(IMessage message)
{
return message instanceof PacketMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
return message instanceof P25P2Message || message instanceof SyncLossMessage;
}
}

View File

@@ -0,0 +1,73 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.p25.phase2.message.filter;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.p25.phase2.message.P25P2Message;
import java.util.function.Function;
/**
* Filter for unknown messages, meaning messages not handled by any other filter.
*/
public class P25P2OtherMessageFilter extends Filter<IMessage,String>
{
private static final String OTHER_KEY = "Other/Unknown Message";
private KeyExtractor mKeyExtractor = new KeyExtractor();
/**
* Constructor
*/
public P25P2OtherMessageFilter()
{
super("Other P25 messages");
add(new FilterElement<>(OTHER_KEY));
}
@Override
public Function<IMessage, String> getKeyExtractor()
{
return mKeyExtractor;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof P25P2Message && super.canProcess(message);
}
/**
* Key extractor
*/
private class KeyExtractor implements Function<IMessage,String>
{
@Override
public String apply(IMessage message)
{
if(message instanceof P25P2Message)
{
return OTHER_KEY;
}
return null;
}
}
}

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