Skip to content

Commit 3c8cd03

Browse files
DSheirerDennis Sheirer
andauthored
#1914 P25 P1 & P2 Talker Alias Support for Motorola and L3Harris. Talker alias manager caches aliases and applies to future events. Active (cached) talker aliases now listed in the channel Details tab. Enables multi-select in message viewer for all supported protocols. Note: decoding of Motorola's talker alias format not yet implemented - only recognition and reassembly of the encoded sequence is currently supported. (#2001)
Co-authored-by: Dennis Sheirer <[email protected]>
1 parent 600db3f commit 3c8cd03

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2047
-445
lines changed

src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadata.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.github.dsheirer.alias.Alias;
2323
import io.github.dsheirer.alias.AliasList;
2424
import io.github.dsheirer.alias.AliasModel;
25+
import io.github.dsheirer.identifier.Form;
2526
import io.github.dsheirer.identifier.Identifier;
2627
import io.github.dsheirer.identifier.IdentifierUpdateListener;
2728
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
@@ -57,6 +58,7 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
5758
private DecoderTypeConfigurationIdentifier mDecoderTypeConfigurationIdentifier;
5859
private Identifier mFromIdentifier;
5960
private List<Alias> mFromIdentifierAliases;
61+
private Identifier mTalkerAliasIdentifier;
6062
private Identifier mToIdentifier;
6163
private List<Alias> mToIdentifierAliases;
6264
private Integer mTimeslot;
@@ -238,6 +240,22 @@ public List<Alias> getFromIdentifierAliases()
238240
return mFromIdentifierAliases;
239241
}
240242

243+
/**
244+
* Optional talker alias identifier
245+
*/
246+
public Identifier getTalkerAliasIdentifier()
247+
{
248+
return mTalkerAliasIdentifier;
249+
}
250+
251+
/**
252+
* Indicates if we have a non-null talker alias identifier
253+
*/
254+
public boolean hasTalkerAliasIdentifier()
255+
{
256+
return mTalkerAliasIdentifier != null;
257+
}
258+
241259
/**
242260
* Current call event TO identifier
243261
*/
@@ -409,15 +427,22 @@ public void receive(IdentifierUpdateNotification update)
409427
case USER:
410428
if(identifier.getRole() == Role.FROM)
411429
{
412-
mFromIdentifier = update.isAdd() ? identifier : null;
413-
414-
if(mAliasList != null && mFromIdentifier != null)
430+
if(identifier.getForm() == Form.TALKER_ALIAS)
415431
{
416-
mFromIdentifierAliases = mAliasList.getAliases(mFromIdentifier);
432+
mTalkerAliasIdentifier = identifier;
417433
}
418434
else
419435
{
420-
mFromIdentifierAliases = Collections.EMPTY_LIST;
436+
mFromIdentifier = update.isAdd() ? identifier : null;
437+
438+
if(mAliasList != null && mFromIdentifier != null)
439+
{
440+
mFromIdentifierAliases = mAliasList.getAliases(mFromIdentifier);
441+
}
442+
else
443+
{
444+
mFromIdentifierAliases = Collections.EMPTY_LIST;
445+
}
421446
}
422447

423448
broadcastUpdate(ChannelMetadataField.USER_FROM);

src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataPanel.java

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* *****************************************************************************
3-
* Copyright (C) 2014-2020 Dennis Sheirer
3+
* Copyright (C) 2014-2024 Dennis Sheirer
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -38,6 +38,14 @@
3838
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
3939
import io.github.dsheirer.sample.Broadcaster;
4040
import io.github.dsheirer.sample.Listener;
41+
import java.awt.Color;
42+
import java.awt.Component;
43+
import java.awt.event.MouseAdapter;
44+
import java.awt.event.MouseEvent;
45+
import java.text.DecimalFormat;
46+
import java.util.EnumMap;
47+
import java.util.List;
48+
import java.util.Map;
4149
import net.miginfocom.swing.MigLayout;
4250
import org.slf4j.Logger;
4351
import org.slf4j.LoggerFactory;
@@ -52,14 +60,6 @@
5260
import javax.swing.event.ListSelectionEvent;
5361
import javax.swing.event.ListSelectionListener;
5462
import javax.swing.table.DefaultTableCellRenderer;
55-
import java.awt.Color;
56-
import java.awt.Component;
57-
import java.awt.event.MouseAdapter;
58-
import java.awt.event.MouseEvent;
59-
import java.text.DecimalFormat;
60-
import java.util.EnumMap;
61-
import java.util.List;
62-
import java.util.Map;
6363

6464
public class ChannelMetadataPanel extends JPanel implements ListSelectionListener
6565
{
@@ -294,6 +294,10 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
294294
{
295295
text = EMPTY_VALUE;
296296
}
297+
else if(hasAdditionalIdentifier(channelMetadata))
298+
{
299+
text = text + " " + getAdditionalIdentifier(channelMetadata);
300+
}
297301

298302
label.setText(text);
299303
}
@@ -306,6 +310,8 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
306310
}
307311

308312
public abstract Identifier getIdentifier(ChannelMetadata channelMetadata);
313+
public abstract boolean hasAdditionalIdentifier(ChannelMetadata channelMetadata);
314+
public abstract Identifier getAdditionalIdentifier(ChannelMetadata channelMetadata);
309315
}
310316

311317
/**
@@ -323,6 +329,18 @@ public Identifier getIdentifier(ChannelMetadata channelMetadata)
323329
{
324330
return channelMetadata.getFromIdentifier();
325331
}
332+
333+
@Override
334+
public Identifier getAdditionalIdentifier(ChannelMetadata channelMetadata)
335+
{
336+
return channelMetadata.getTalkerAliasIdentifier();
337+
}
338+
339+
@Override
340+
public boolean hasAdditionalIdentifier(ChannelMetadata channelMetadata)
341+
{
342+
return channelMetadata.hasTalkerAliasIdentifier();
343+
}
326344
}
327345

328346
/**
@@ -340,6 +358,11 @@ public Identifier getIdentifier(ChannelMetadata channelMetadata)
340358
{
341359
return channelMetadata.getToIdentifier();
342360
}
361+
362+
@Override
363+
public Identifier getAdditionalIdentifier(ChannelMetadata channelMetadata) {return null;}
364+
@Override
365+
public boolean hasAdditionalIdentifier(ChannelMetadata channelMetadata) {return false;}
343366
}
344367

345368
public class ColoredStateCellRenderer extends DefaultTableCellRenderer

src/main/java/io/github/dsheirer/gui/viewer/DmrViewer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* *****************************************************************************
3-
* Copyright (C) 2014-2023 Dennis Sheirer
3+
* Copyright (C) 2014-2024 Dennis Sheirer
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -46,6 +46,7 @@
4646
import javafx.scene.control.CheckBox;
4747
import javafx.scene.control.Label;
4848
import javafx.scene.control.ProgressIndicator;
49+
import javafx.scene.control.SelectionMode;
4950
import javafx.scene.control.TableColumn;
5051
import javafx.scene.control.TablePosition;
5152
import javafx.scene.control.TableView;
@@ -284,6 +285,7 @@ private TableView<IMessage> getMessageTableView()
284285
if(mMessageTableView == null)
285286
{
286287
mMessageTableView = new TableView<>();
288+
mMessageTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
287289
mMessageTableView.setPlaceholder(getLoadingIndicator());
288290
SortedList<IMessage> sortedList = new SortedList<>(mFilteredMessages);
289291
sortedList.comparatorProperty().bind(mMessageTableView.comparatorProperty());

src/main/java/io/github/dsheirer/gui/viewer/P25P1Viewer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import javafx.scene.control.Button;
6363
import javafx.scene.control.Label;
6464
import javafx.scene.control.ProgressIndicator;
65+
import javafx.scene.control.SelectionMode;
6566
import javafx.scene.control.TableColumn;
6667
import javafx.scene.control.TablePosition;
6768
import javafx.scene.control.TableView;
@@ -380,6 +381,7 @@ private TableView<MessagePackage> getMessagePackageTableView()
380381
if(mMessagePackageTableView == null)
381382
{
382383
mMessagePackageTableView = new TableView<>();
384+
mMessagePackageTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
383385
mMessagePackageTableView.setPlaceholder(getLoadingIndicator());
384386
SortedList<MessagePackage> sortedList = new SortedList<>(mFilteredMessagePackages);
385387
sortedList.comparatorProperty().bind(mMessagePackageTableView.comparatorProperty());

src/main/java/io/github/dsheirer/gui/viewer/P25P2Viewer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import javafx.scene.control.CheckBox;
6464
import javafx.scene.control.Label;
6565
import javafx.scene.control.ProgressIndicator;
66+
import javafx.scene.control.SelectionMode;
6667
import javafx.scene.control.TableColumn;
6768
import javafx.scene.control.TablePosition;
6869
import javafx.scene.control.TableView;
@@ -459,6 +460,7 @@ private TableView<MessagePackage> getMessagePackageTableView()
459460
if(mMessagePackageTableView == null)
460461
{
461462
mMessagePackageTableView = new TableView<>();
463+
mMessagePackageTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
462464
mMessagePackageTableView.setPlaceholder(getLoadingIndicator());
463465
SortedList<MessagePackage> sortedList = new SortedList<>(mFilteredMessagePackages);
464466
sortedList.comparatorProperty().bind(mMessagePackageTableView.comparatorProperty());
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* *****************************************************************************
3+
* Copyright (C) 2014-2024 Dennis Sheirer
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>
17+
* ****************************************************************************
18+
*/
19+
20+
package io.github.dsheirer.identifier.alias;
21+
22+
import io.github.dsheirer.identifier.Identifier;
23+
import io.github.dsheirer.identifier.IdentifierCollection;
24+
import io.github.dsheirer.identifier.MutableIdentifierCollection;
25+
import io.github.dsheirer.identifier.Role;
26+
import io.github.dsheirer.identifier.radio.RadioIdentifier;
27+
import java.util.ArrayList;
28+
import java.util.Collections;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.concurrent.ConcurrentHashMap;
32+
33+
/**
34+
* Talker alias cache manager. Collects observed talker aliases and inserts them into an identifier collection when
35+
* the corresponding radio is active in a FROM role.
36+
*
37+
* This implementation is thread safe and is intended to be used across control and traffic channels.
38+
*/
39+
public class TalkerAliasManager
40+
{
41+
private Map<Integer,TalkerAliasIdentifier> mAliasMap = new ConcurrentHashMap<>();
42+
43+
/**
44+
* Constructs an instance
45+
*/
46+
public TalkerAliasManager()
47+
{
48+
}
49+
50+
/**
51+
* Updates the alias for the
52+
* @param identifier
53+
* @param alias
54+
*/
55+
public void update(RadioIdentifier identifier, TalkerAliasIdentifier alias)
56+
{
57+
if(identifier.getRole() == Role.FROM)
58+
{
59+
mAliasMap.put(identifier.getValue(), alias);
60+
}
61+
}
62+
63+
/**
64+
* Indicates if an alias exists for the identifier
65+
* @param radioIdentifier to test
66+
* @return true if an alias exists.
67+
*/
68+
public boolean hasAlias(RadioIdentifier radioIdentifier)
69+
{
70+
return mAliasMap.containsKey(radioIdentifier.getValue());
71+
}
72+
73+
/**
74+
* Enriches the immutable identifier collection by detecting a radio identifier with the FROM role, lookup a
75+
* matching alias, and insert the alias into a new mutable identifier collection.
76+
* @param originalIC to enrich
77+
* @return enriched identifier collection or the original identifier collection if we don't have an alias.
78+
*/
79+
public synchronized IdentifierCollection enrich(IdentifierCollection originalIC)
80+
{
81+
Identifier fromRadio = originalIC.getFromIdentifier();
82+
83+
if(fromRadio instanceof RadioIdentifier rid)
84+
{
85+
TalkerAliasIdentifier alias = mAliasMap.get(rid.getValue());
86+
87+
if(alias != null)
88+
{
89+
MutableIdentifierCollection enrichedIC = new MutableIdentifierCollection(originalIC.getIdentifiers());
90+
enrichedIC.update(alias);
91+
return enrichedIC;
92+
}
93+
}
94+
95+
return originalIC;
96+
}
97+
98+
/**
99+
* Enriches the mutable identifier collection by detecting a radio identifier with the FROM role, lookup a
100+
* matching alias, and insert the alias into the mutable identifier collection argument.
101+
* @param mic to enrich
102+
*/
103+
public synchronized void enrichMutable(MutableIdentifierCollection mic)
104+
{
105+
Identifier fromRadio = mic.getFromIdentifier();
106+
107+
if(fromRadio instanceof RadioIdentifier rid)
108+
{
109+
TalkerAliasIdentifier alias = mAliasMap.get(rid.getValue());
110+
111+
if(alias != null)
112+
{
113+
mic.update(alias);
114+
}
115+
}
116+
}
117+
118+
/**
119+
* Creates a summary listing of talker aliases
120+
* @return summary.
121+
*/
122+
public synchronized String getAliasSummary()
123+
{
124+
StringBuilder sb = new StringBuilder();
125+
sb.append("Active System Radio Aliases\n");
126+
List<Integer> radios = new ArrayList<>(mAliasMap.keySet());
127+
128+
if(radios.size() > 0)
129+
{
130+
Collections.sort(radios);
131+
for(Integer radio : radios)
132+
{
133+
sb.append(" ").append(radio);
134+
sb.append("\t").append(mAliasMap.get(radio).getValue());
135+
sb.append("\n");
136+
}
137+
}
138+
else
139+
{
140+
sb.append(" None\n");
141+
}
142+
143+
return sb.toString();
144+
}
145+
}

src/main/java/io/github/dsheirer/identifier/patch/PatchGroupManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ public String getPatchGroupSummary()
6363

6464
if(trackers.isEmpty())
6565
{
66-
sb.append("None");
66+
sb.append(" None\n");
6767
}
6868
else
6969
{
7070
for(PatchGroupTracker tracker : trackers)
7171
{
72-
sb.append(" ").append(tracker.mPatchGroupIdentifier).append("\n");
72+
sb.append(" ").append(tracker.mPatchGroupIdentifier).append("\n");
7373
}
7474
}
7575

0 commit comments

Comments
 (0)