Skip to content

Commit

Permalink
#1939 Duplicate call detector test cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dennis Sheirer committed Sep 4, 2024
1 parent e6a8048 commit 0cee16e
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 14 deletions.
7 changes: 6 additions & 1 deletion src/main/java/io/github/dsheirer/audio/AudioSegment.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
Expand Down Expand Up @@ -386,6 +386,11 @@ public void addAudio(float[] audioBuffer)
throw new IllegalStateException("Can't add audio to an audio segment that is being disposed");
}

if(mAudioBuffers.isEmpty())
{
mStartTimestamp = System.currentTimeMillis() - 20;
}

mAudioBuffers.add(audioBuffer);
mSampleCount += audioBuffer.length;
}
Expand Down
29 changes: 18 additions & 11 deletions src/main/java/io/github/dsheirer/audio/DuplicateCallDetector.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
Expand Down Expand Up @@ -29,6 +29,7 @@
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.duplicate.CallManagementPreference;
import io.github.dsheirer.preference.duplicate.ICallManagementProvider;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.util.ThreadPool;
import java.util.ArrayList;
Expand Down Expand Up @@ -77,7 +78,7 @@ public void receive(AudioSegment audioSegment)

if(detector == null)
{
detector = new SystemDuplicateCallDetector();
detector = new SystemDuplicateCallDetector(mCallManagementPreference);
mDetectorMap.put(system, detector);
}

Expand All @@ -87,15 +88,21 @@ public void receive(AudioSegment audioSegment)
}
}

public class SystemDuplicateCallDetector
public static class SystemDuplicateCallDetector
{
private LinkedTransferQueue<AudioSegment> mAudioSegmentQueue = new LinkedTransferQueue<>();
private List<AudioSegment> mAudioSegments = new ArrayList<>();
private AtomicBoolean mMonitoring = new AtomicBoolean();
private final LinkedTransferQueue<AudioSegment> mAudioSegmentQueue = new LinkedTransferQueue<>();
private final List<AudioSegment> mAudioSegments = new ArrayList<>();
private final AtomicBoolean mMonitoring = new AtomicBoolean();
private ScheduledFuture<?> mProcessorFuture;
private final ICallManagementProvider mCallManagementProvider;

public SystemDuplicateCallDetector()
/**
* Constructs an instance
* @param callManagementProvider to check for duplicate monitoring preferences
*/
public SystemDuplicateCallDetector(ICallManagementProvider callManagementProvider)
{
mCallManagementProvider = callManagementProvider;
}

public void add(AudioSegment audioSegment)
Expand All @@ -112,7 +119,7 @@ private void startMonitoring()
{
if(mMonitoring.compareAndSet(false, true))
{
mProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> process(),
mProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(this::process,
0, 25, TimeUnit.MILLISECONDS);
}
}
Expand All @@ -136,7 +143,7 @@ private void stopMonitoring()
*/
private boolean isDuplicate(AudioSegment segment1, AudioSegment segment2)
{
if(mCallManagementPreference.isDuplicateCallDetectionByTalkgroupEnabled())
if(mCallManagementProvider.isDuplicateCallDetectionByTalkgroupEnabled())
{
//Step 1 check for duplicate TO values
List<Identifier> to1 = segment1.getIdentifierCollection().getIdentifiers(Role.TO);
Expand All @@ -148,7 +155,7 @@ private boolean isDuplicate(AudioSegment segment1, AudioSegment segment2)
}
}

if(mCallManagementPreference.isDuplicateCallDetectionByRadioEnabled())
if(mCallManagementProvider.isDuplicateCallDetectionByRadioEnabled())
{
//Step 2 check for duplicate FROM values
List<Identifier> from1 = segment1.getIdentifierCollection().getIdentifiers(Role.FROM);
Expand All @@ -168,7 +175,7 @@ private boolean isDuplicate(AudioSegment segment1, AudioSegment segment2)
* @param identifiers2
* @return
*/
private boolean isDuplicate(List<Identifier> identifiers1, List<Identifier> identifiers2)
public static boolean isDuplicate(List<Identifier> identifiers1, List<Identifier> identifiers2)
{
for(Identifier identifier1: identifiers1)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
Expand Down Expand Up @@ -30,7 +30,7 @@
/**
* User preferences for call management
*/
public class CallManagementPreference extends Preference
public class CallManagementPreference extends Preference implements ICallManagementProvider
{
private static final String PREFERENCE_KEY_DETECT_DUPLICATE_TALKGROUP = "duplicate.call.detect.talkgroup";
private static final String PREFERENCE_KEY_DETECT_DUPLICATE_RADIO = "duplicate.call.detect.radio";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.preference.duplicate;

/**
* Interface for a call management value provider.
*/
public interface ICallManagementProvider
{
boolean isDuplicateCallDetectionEnabled();
boolean isDuplicateCallDetectionByTalkgroupEnabled();
boolean isDuplicateCallDetectionByRadioEnabled();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.preference.duplicate;

/**
* Test implementation of duplicate call detection preferences.
*/
public class TestCallManagementProvider implements ICallManagementProvider
{
private final boolean mByTalkgroup;
private final boolean mByRadio;

/**
* Constructs an instance
* @param byTalkgroup to enable duplicate detection by talkgroup
* @param byRadio to enable duplicate detection by radio
*/
public TestCallManagementProvider(boolean byTalkgroup, boolean byRadio)
{
mByTalkgroup = byTalkgroup;
mByRadio = byRadio;
}

@Override
public boolean isDuplicateCallDetectionEnabled()
{
return mByTalkgroup || mByRadio;
}

@Override
public boolean isDuplicateCallDetectionByTalkgroupEnabled()
{
return mByTalkgroup;
}

@Override
public boolean isDuplicateCallDetectionByRadioEnabled()
{
return mByRadio;
}
}
106 changes: 106 additions & 0 deletions src/test/java/io/github/dsheirer/audio/DuplicateCallDetectionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.audio;

import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier;
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.radio.APCO25RadioIdentifier;
import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup;
import io.github.dsheirer.preference.duplicate.ICallManagementProvider;
import io.github.dsheirer.preference.duplicate.TestCallManagementProvider;
import java.util.List;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class DuplicateCallDetectionTest
{
@Test
void sameCallOnDifferentSites()
{
AliasList aliasList = new AliasList("test");
AudioSegment audioSegment1 = new AudioSegment(aliasList, 1);
audioSegment1.addIdentifier(SystemConfigurationIdentifier.create("Test System"));
audioSegment1.addIdentifier(SiteConfigurationIdentifier.create("Test Site 1"));
audioSegment1.addIdentifier(APCO25Talkgroup.create(1));
audioSegment1.addIdentifier(APCO25RadioIdentifier.createFrom(2));

AudioSegment audioSegment2 = new AudioSegment(aliasList, 2);
audioSegment2.addIdentifier(SystemConfigurationIdentifier.create("Test System"));
audioSegment2.addIdentifier(SiteConfigurationIdentifier.create("Test Site 2"));
audioSegment2.addIdentifier(APCO25Talkgroup.create(1));
audioSegment2.addIdentifier(APCO25RadioIdentifier.createFrom(2));

boolean testByTalkgroup = true;
boolean testByRadio = false;
ICallManagementProvider provider = new TestCallManagementProvider(testByTalkgroup, testByRadio);
DuplicateCallDetector.SystemDuplicateCallDetector detector = new DuplicateCallDetector.SystemDuplicateCallDetector(provider);

List<Identifier> to1 = audioSegment1.getIdentifierCollection().getIdentifiers(Role.TO);
List<Identifier> to2 = audioSegment2.getIdentifierCollection().getIdentifiers(Role.TO);
//Test for duplicate by TO identifiers
assertTrue(DuplicateCallDetector.SystemDuplicateCallDetector.isDuplicate(to1, to2), "TO identifiers should be equivalent");

List<Identifier> from1 = audioSegment1.getIdentifierCollection().getIdentifiers(Role.FROM);
List<Identifier> from2 = audioSegment2.getIdentifierCollection().getIdentifiers(Role.FROM);
//Test for duplicate by FROM identifiers
assertTrue(DuplicateCallDetector.SystemDuplicateCallDetector.isDuplicate(from1, from2), "FROM identifiers should be equivalent");
}

/**
* Test: same call, same site, same source radio, simulcasting to two different talkgroups.
*/
@Test
void sameCallSameSiteSameRadioSimulcastToDifferentTalkgroups()
{
AliasList aliasList = new AliasList("test");

AudioSegment audioSegment1 = new AudioSegment(aliasList, 1);
audioSegment1.addIdentifier(SystemConfigurationIdentifier.create("Test System"));
audioSegment1.addIdentifier(SiteConfigurationIdentifier.create("Test Site 1"));
audioSegment1.addIdentifier(APCO25Talkgroup.create(1));
audioSegment1.addIdentifier(APCO25RadioIdentifier.createFrom(2));

AudioSegment audioSegment2 = new AudioSegment(aliasList, 2);
audioSegment2.addIdentifier(SystemConfigurationIdentifier.create("Test System"));
audioSegment2.addIdentifier(SiteConfigurationIdentifier.create("Test Site 1"));
audioSegment2.addIdentifier(APCO25Talkgroup.create(2));
audioSegment2.addIdentifier(APCO25RadioIdentifier.createFrom(2));

boolean testByTalkgroup = true;
boolean testByRadio = true;
ICallManagementProvider provider = new TestCallManagementProvider(testByTalkgroup, testByRadio);
DuplicateCallDetector.SystemDuplicateCallDetector detector = new DuplicateCallDetector.SystemDuplicateCallDetector(provider);

List<Identifier> to1 = audioSegment1.getIdentifierCollection().getIdentifiers(Role.TO);
List<Identifier> to2 = audioSegment2.getIdentifierCollection().getIdentifiers(Role.TO);
//Test for duplicate by TO identifiers
assertFalse(DuplicateCallDetector.SystemDuplicateCallDetector.isDuplicate(to1, to2), "TO identifiers should not be equivalent");

List<Identifier> from1 = audioSegment1.getIdentifierCollection().getIdentifiers(Role.FROM);
List<Identifier> from2 = audioSegment2.getIdentifierCollection().getIdentifiers(Role.FROM);
//Test for duplicate by FROM identifiers
assertTrue(DuplicateCallDetector.SystemDuplicateCallDetector.isDuplicate(from1, from2), "FROM identifiers should be equivalent");
}
}

0 comments on commit 0cee16e

Please sign in to comment.