#1746 DMR Decoder now supports simplex repeater and DMDC modes of operation.

This commit is contained in:
Dennis Sheirer
2023-12-01 02:58:27 -05:00
parent d2580f8a99
commit 834846d339
7 changed files with 110 additions and 25 deletions

View File

@@ -69,9 +69,17 @@ public class RecordingViewer extends VBox
Menu createNewViewerMenu = new Menu("New Viewer ..."); Menu createNewViewerMenu = new Menu("New Viewer ...");
MenuItem dmrMenuItem = new MenuItem("DMR"); MenuItem dmrMenuItem = new MenuItem("DMR");
dmrMenuItem.onActionProperty().set(event -> getTabPane().getTabs().add(new LabeledTab("DMR-" + mTabCounterDmr++, new DmrViewer()))); dmrMenuItem.onActionProperty().set(event -> {
Tab tab = new LabeledTab("DMR-" + mTabCounterDmr++, new DmrViewer());
getTabPane().getTabs().add(tab);
getTabPane().getSelectionModel().select(tab);
});
MenuItem p25p1MenuItem = new MenuItem("P25P1"); MenuItem p25p1MenuItem = new MenuItem("P25P1");
p25p1MenuItem.onActionProperty().set(event -> getTabPane().getTabs().add(new LabeledTab("P25P1-" + mTabCounterP25P1++, new P25P1Viewer()))); p25p1MenuItem.onActionProperty().set(event -> {
Tab tab = new LabeledTab("P25P1-" + mTabCounterP25P1++, new P25P1Viewer());
getTabPane().getTabs().add(tab);
getTabPane().getSelectionModel().select(tab);
});
createNewViewerMenu.getItems().addAll(dmrMenuItem, p25p1MenuItem); createNewViewerMenu.getItems().addAll(dmrMenuItem, p25p1MenuItem);
MenuItem exitMenu = new MenuItem("Exit"); MenuItem exitMenu = new MenuItem("Exit");

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 * 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 * it under the terms of the GNU General Public License as published by
@@ -284,11 +284,27 @@ public class DMRBurstFramer implements Listener<Dibit>
} }
else else
{ {
//When we don't have sufficient timeslot tracking data, dump the messages to timeslot 0 so that they //When we don't have sufficient timeslot tracking data, we either have misaligned timeslots from a
//don't corrupt the decoder states for either timeslot, until we positively regain timeslot tracking //repeater or we have some form of mobile burst. If mobile, process to the correct timeslto, otherwise
//dump the messages to timeslot 0 so that the burst doesn't corrupt the decoder states for either
// timeslot, until we positively regain timeslot tracking
if(burst1 != null) if(burst1 != null)
{ {
mBurstDetectListener.burstDetected(burst1, mSyncTrackerTimeslot1.getSyncPattern(), 0); DMRSyncPattern sync1 = mSyncTrackerTimeslot1.getSyncPattern();
if(sync1.isMobileStationSyncPattern() || sync1.isDirectModeTS1())
{
mBurstDetectListener.burstDetected(burst1, sync1, 1);
}
else if(sync1.isDirectModeTS2())
{
mBurstDetectListener.burstDetected(burst1, sync1, 2);
}
else
{
mBurstDetectListener.burstDetected(burst1, sync1, 0);
}
mDibitCounter -= BURST_DIBIT_LENGTH; mDibitCounter -= BURST_DIBIT_LENGTH;
} }
else else
@@ -298,7 +314,21 @@ public class DMRBurstFramer implements Listener<Dibit>
if(burst2 != null) if(burst2 != null)
{ {
mBurstDetectListener.burstDetected(burst2, mSyncTrackerTimeslot2.getSyncPattern(), 0); DMRSyncPattern sync2 = mSyncTrackerTimeslot2.getSyncPattern();
if(sync2.isMobileStationSyncPattern() || sync2.isDirectModeTS1())
{
mBurstDetectListener.burstDetected(burst2, sync2, 1);
}
else if(sync2.isDirectModeTS2())
{
mBurstDetectListener.burstDetected(burst2, sync2, 2);
}
else
{
mBurstDetectListener.burstDetected(burst2, sync2, 0);
}
mDibitCounter -= BURST_DIBIT_LENGTH; mDibitCounter -= BURST_DIBIT_LENGTH;
} }
else else

View File

@@ -445,11 +445,18 @@ public class DMRDecoderState extends TimeslotDecoderState
*/ */
private void processVoice(VoiceMessage message) private void processVoice(VoiceMessage message)
{ {
if(message.getSyncPattern().isDirectMode()) if(message.getSyncPattern().isMobileSyncPattern())
{ {
updateCurrentCall(DecodeEventType.CALL, "DIRECT MODE", message.getTimestamp()); if(message.getSyncPattern().isDirectMode())
{
updateCurrentCall(DecodeEventType.CALL, "DIRECT MODE", message.getTimestamp());
}
else
{
updateCurrentCall(DecodeEventType.CALL, "REPEATER", message.getTimestamp());
}
//Use the timeslot as the talkgroup identifier since DCDM mode doesn't use talkgroups //Use the timeslot as the talkgroup identifier since DCDM & simple repeater modes don't use talkgroups
getIdentifierCollection().update(DMRTalkgroup.create(getTimeslot())); getIdentifierCollection().update(DMRTalkgroup.create(getTimeslot()));
} }
else else

View File

@@ -153,10 +153,8 @@ public class DMRMessageProcessor implements Listener<IMessage>
dispatch(message); dispatch(message);
//Extract the Full Link Control message fragment from the Voice with embedded signalling message //Extract the Full Link Control message fragment from the Voice with embedded signalling message
if(message instanceof VoiceEMBMessage) if(message instanceof VoiceEMBMessage voice)
{ {
VoiceEMBMessage voice = (VoiceEMBMessage)message;
if(message.getTimeslot() == 1) if(message.getTimeslot() == 1)
{ {
FullLCMessage flco = mFLCAssemblerTimeslot1.process(voice.getEMB().getLCSS(), FullLCMessage flco = mFLCAssemblerTimeslot1.process(voice.getEMB().getLCSS(),

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 * 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 * it under the terms of the GNU General Public License as published by
@@ -91,9 +91,17 @@ public enum DMRSyncPattern
private static final EnumSet<DMRSyncPattern> CACH_PATTERNS = EnumSet.of(BASE_STATION_DATA, BASE_STATION_VOICE, private static final EnumSet<DMRSyncPattern> CACH_PATTERNS = EnumSet.of(BASE_STATION_DATA, BASE_STATION_VOICE,
BS_VOICE_FRAME_B, BS_VOICE_FRAME_C, BS_VOICE_FRAME_D, BS_VOICE_FRAME_E, BS_VOICE_FRAME_F); BS_VOICE_FRAME_B, BS_VOICE_FRAME_C, BS_VOICE_FRAME_D, BS_VOICE_FRAME_E, BS_VOICE_FRAME_F);
//Direct Mode Sync Patterns //Direct Mode TS 1 Sync Patterns
private static final EnumSet<DMRSyncPattern> DIRECT_MODE_PATTERNS = EnumSet.of(DIRECT_MODE_DATA_TIMESLOT_1, private static final EnumSet<DMRSyncPattern> DIRECT_MODE_TS1_PATTERNS = EnumSet.of(DIRECT_MODE_VOICE_TIMESLOT_1,
DIRECT_MODE_DATA_TIMESLOT_2, DIRECT_MODE_VOICE_TIMESLOT_1, DIRECT_MODE_VOICE_TIMESLOT_2); DIRECT_MODE_DATA_TIMESLOT_1);
//Direct Mode TS 2 Sync Patterns
private static final EnumSet<DMRSyncPattern> DIRECT_MODE_TS2_PATTERNS = EnumSet.of(DIRECT_MODE_VOICE_TIMESLOT_2,
DIRECT_MODE_DATA_TIMESLOT_2);
//Mobile Station Sync Patterns
private static final EnumSet<DMRSyncPattern> MOBILE_STATION_PATTERNS = EnumSet.of(MOBILE_STATION_DATA,
MOBILE_STATION_VOICE, MS_VOICE_FRAME_B, MS_VOICE_FRAME_C, MS_VOICE_FRAME_D, MS_VOICE_FRAME_E, MS_VOICE_FRAME_F);
/** /**
* Pattern that represents the enum entry * Pattern that represents the enum entry
@@ -140,7 +148,43 @@ public enum DMRSyncPattern
*/ */
public boolean isDirectMode() public boolean isDirectMode()
{ {
return DIRECT_MODE_PATTERNS.contains(this); return isDirectModeTS1() || isDirectModeTS2();
}
/**
* Indicates if this is Direct Mode sync pattern for timestot 1
* @return true if DMTS1
*/
public boolean isDirectModeTS1()
{
return DIRECT_MODE_TS1_PATTERNS.contains(this);
}
/**
* Indicates if this is Direct Mode sync pattern for timestot 2
* @return true if DMTS2
*/
public boolean isDirectModeTS2()
{
return DIRECT_MODE_TS2_PATTERNS.contains(this);
}
/**
* Indicates if this is a mobile station sync pattern
* @return true if a mobile station sync pattern.
*/
public boolean isMobileStationSyncPattern()
{
return MOBILE_STATION_PATTERNS.contains(this);
}
/**
* Indicates if this is a mobile sync pattern, either mobile station or direct mode.
* @return true if a mobile sync pattern.
*/
public boolean isMobileSyncPattern()
{
return isMobileStationSyncPattern() || isDirectMode();
} }
/** /**

View File

@@ -48,15 +48,13 @@ public class VoiceAMessage extends VoiceMessage
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if(getSyncPattern().isDirectMode()) if(!getSyncPattern().isMobileSyncPattern())
{ {
sb.append(getSyncPattern()); sb.append("CC:- ");
}
else
{
sb.append("CC:- ").append(getSyncPattern());
} }
sb.append(getSyncPattern());
return sb.toString(); return sb.toString();
} }

View File

@@ -56,7 +56,7 @@ public class VoiceEMBMessage extends VoiceMessage
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if(getEMB().isValid()) if(!getSyncPattern().isMobileSyncPattern() && getEMB().isValid())
{ {
sb.append("CC:").append(getEMB().getColorCode()).append(" "); sb.append("CC:").append(getEMB().getColorCode()).append(" ");
} }