Compare commits

...

22 Commits

Author SHA1 Message Date
Denny
5efd89632e Ensures that any leftover cached LDU1 message is reset when the call ends. 2017-05-28 07:30:05 -04:00
Denny
1dc3863489 Updates build to 0.3.0 Beta 12 2017-05-28 07:22:43 -04:00
Denny Sheirer
441286ba13 Merge pull request #247 from DSheirer/244-ldu1-encryption-flag
244 Prevent P25 Encrypted Audio Decoding/Playback
2017-05-28 11:20:20 +00:00
Denny
e4323df347 Resolves issue #244 - updates the P25 IMBE audio module to correctly
detect encrypted audio state so that encrypted audio is not (incorrectly)
decoded and played back to the user.

Processes HDU and LDU2 messages to identify encrypted call state.  Once
established, all subsequent LDU1/2 messages are decoded only if the call
is unencrypted.  If an LDU1 message is received out-of-sequence, meaning
that the initial HDU message was missed, then the LDU1 is cached until
we receive an LDU2 message and correctly determine the call encryption
state.
2017-05-28 07:17:09 -04:00
Denny
a872e0d664 Updates version to Beta 0.3.0 Beta 11 2017-05-20 07:24:27 -04:00
Denny Sheirer
fbc92cfe31 Merge pull request #246 from DSheirer/245-sound-card-source-issue
Using sound card or mixer as a source throws an error
2017-05-20 10:55:23 +00:00
Denny
1a432197ac Resolves #245 issue with NPE for frequency change event listener when using the sound card/mixer as a source. 2017-05-20 06:51:53 -04:00
Denny
fbc6e508bd Increments build to 0.3.0-beta10 2017-04-14 07:46:18 -04:00
Denny Sheirer
f9b6e67b00 Merge pull request #240 from DSheirer/234-long-alias-names-cause-audio-label-to-wrap
Long alias names cause audio label to wrap
2017-03-31 01:13:17 +00:00
Denny
a628800a71 Resolves #234 - updates the miglayout layout manager to allow jlabel text strings to be automatically truncated (with ellipses) when the alias name is too long. 2017-03-30 21:12:06 -04:00
Denny
25f71a76c4 Resolves #234 - hard codes alias name truncation when name exceeds 33 characters in length. 2017-03-30 20:03:48 -04:00
Denny
5fe8ccd478 Resolves #234 - change the order of assigning an icon and alias text to the audio panel in an attempt to prevent the audio channel panel from wrapping to the next line when the text is too long to display. 2017-03-30 19:52:35 -04:00
Denny Sheirer
069f951a86 Merge pull request #239 from DSheirer/228-traffic-channel-allocation-for-do-not-monitor
Traffic channel manager now allocates traffic channels for do not monitor aliases
2017-03-30 23:27:38 +00:00
Denny
dab6df7836 Resolves #228 - traffic channel manager now allocates traffic channels even when the alias is marked as do not monitor 2017-03-30 19:26:35 -04:00
Denny Sheirer
89da46b63c Merge pull request #238 from DSheirer/235-lojack-decoder-error-on-construction
Resolves error on constructing a LoJack decoder state.
2017-03-30 23:19:29 +00:00
Denny
5f2ce2a9f2 Resolves #235 - fixes error on constructing a LoJack decoder state. 2017-03-30 19:18:39 -04:00
Denny Sheirer
7a0b377971 Merge pull request #232 from DSheirer/229-p25-phase2-event-duplicates
Resolves #229 - duplicate P25 TDMA call events.
2017-03-23 21:52:47 +00:00
Denny
5dbe0ecce3 Resolves #229 - duplicate P25 TDMA call events. 2017-03-23 21:48:52 +00:00
Denny Sheirer
6647c581a6 Merge pull request #231 from DSheirer/230-p25-tdma-frequency-bands
P25 Phase II TDMA frequency bands are now displayed in details summary
2017-03-23 21:01:58 +00:00
Denny
a2ab5bfa95 Resolves #230 - P25 Phase II TDMA frequency bands are now displayed in the channel details summary. 2017-03-23 20:55:42 +00:00
Denny
ed486440b8 Merge remote-tracking branch 'origin/master' 2017-03-12 11:33:17 -04:00
Denny
13464e01fc Updates build to 0.3.0 Beta 9 2017-03-12 11:33:04 -04:00
14 changed files with 380 additions and 337 deletions

View File

@@ -7,7 +7,7 @@
<property name="major" value="0"/> <property name="major" value="0"/>
<property name="minor" value="3"/> <property name="minor" value="3"/>
<property name="patch" value="0"/> <property name="patch" value="0"/>
<property name="release" value="-beta8"/> <property name="release" value="-beta12"/>
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/> <property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
<!-- Build properties --> <!-- Build properties -->

View File

@@ -90,7 +90,8 @@ public class AudioChannelPanel extends JPanel
private void init() private void init()
{ {
setLayout(new MigLayout("align center center, insets 0 0 0 0", "[][][align right]0[grow,fill]", "")); setLayout(new MigLayout("align center center, insets 0 0 0 0",
"[][][align right]0[grow,fill]", ""));
setBackground(mBackgroundColor); setBackground(mBackgroundColor);
mMutedLabel.setFont(mFont); mMutedLabel.setFont(mFont);
@@ -109,7 +110,7 @@ public class AudioChannelPanel extends JPanel
mTo.setFont(mFont); mTo.setFont(mFont);
mTo.setForeground(mValueColor); mTo.setForeground(mValueColor);
add(mTo); add(mTo,"wmin 10lp");
} }
@Override @Override
@@ -159,30 +160,40 @@ public class AudioChannelPanel extends JPanel
* does not occur on the Swing event thread -- wrap any calls to this * does not occur on the Swing event thread -- wrap any calls to this
* method with an event thread call. * method with an event thread call.
*/ */
private void updateLabel(JLabel valueLabel, String value, JLabel aliasLabel, Alias alias) private void updateLabel(JLabel textLabel, String value, JLabel iconLabel, Alias alias)
{ {
if(value != null) if(value != null)
{ {
if(alias != null) if(alias != null)
{ {
valueLabel.setText(alias.getName()); iconLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18));
aliasLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18));
String aliasName = alias.getName();
//Truncate label if length exceeds 33 characters - hack!
if(aliasName.length() > 33)
{
textLabel.setText(aliasName.substring(0,30) + "...");
}
else
{
textLabel.setText(aliasName);
}
} }
else else
{ {
valueLabel.setText(value); iconLabel.setIcon(null);
aliasLabel.setIcon(null); textLabel.setText(value);
} }
} }
else else
{ {
valueLabel.setText("-----"); iconLabel.setIcon(null);
aliasLabel.setIcon(null); textLabel.setText("-----");
} }
} }
/** /**
* Processes audio metadata to update this panel's display values * Processes audio metadata to update this panel's display values
*/ */

View File

@@ -33,8 +33,7 @@ public class AudioChannelsPanel extends JPanel
public AudioChannelsPanel(IconManager iconManager, SettingsManager settingsManager, IAudioController controller) public AudioChannelsPanel(IconManager iconManager, SettingsManager settingsManager, IAudioController controller)
{ {
// setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill][][grow,fill]", "[grow,fill]")); setLayout(new MigLayout("insets 0 0 0 0", "[][sg abc,grow,fill][][sg abc,grow,fill]", "[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0", "[][sg,grow,fill][][sg,grow,fill]", "[grow,fill]"));
setBackground(Color.BLACK); setBackground(Color.BLACK);
@@ -44,7 +43,7 @@ public class AudioChannelsPanel extends JPanel
for(int x = 0; x < outputs.size(); x++) for(int x = 0; x < outputs.size(); x++)
{ {
add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x)), "growx"); add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x)));
if(x < outputs.size() - 1) if(x < outputs.size() - 1)
{ {

View File

@@ -63,13 +63,11 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
private Listener<CallEvent> mCallEventListener; private Listener<CallEvent> mCallEventListener;
private ChannelModel mChannelModel; private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private DecodeConfiguration mDecodeConfiguration; private DecodeConfiguration mDecodeConfiguration;
private RecordConfiguration mRecordConfiguration; private RecordConfiguration mRecordConfiguration;
private String mSystem; private String mSystem;
private String mSite; private String mSite;
private String mAliasListName; private String mAliasListName;
private CallEvent mPreviousDoNotMonitorCallEvent;
/** /**
* Monitors call events and allocates traffic decoder channels in response * Monitors call events and allocates traffic decoder channels in response
@@ -77,7 +75,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
* traffic channel allocations. * traffic channel allocations.
* *
* @param channelModel containing channels currently in use * @param channelModel containing channels currently in use
* @param channelProcessingManager
* @param decodeConfiguration - decoder configuration to use for each * @param decodeConfiguration - decoder configuration to use for each
* traffic channel allocation. * traffic channel allocation.
* @param recordConfiguration - recording options for each traffic channel * @param recordConfiguration - recording options for each traffic channel
@@ -88,7 +85,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
* in the pool * in the pool
*/ */
public TrafficChannelManager(ChannelModel channelModel, public TrafficChannelManager(ChannelModel channelModel,
ChannelProcessingManager channelProcessingManager,
DecodeConfiguration decodeConfiguration, DecodeConfiguration decodeConfiguration,
RecordConfiguration recordConfiguration, RecordConfiguration recordConfiguration,
String system, String system,
@@ -97,7 +93,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
int trafficChannelPoolSize) int trafficChannelPoolSize)
{ {
mChannelModel = channelModel; mChannelModel = channelModel;
mChannelProcessingManager = channelProcessingManager;
mDecodeConfiguration = decodeConfiguration; mDecodeConfiguration = decodeConfiguration;
mRecordConfiguration = recordConfiguration; mRecordConfiguration = recordConfiguration;
mSystem = system; mSystem = system;
@@ -120,7 +115,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
mCallEventListener = null; mCallEventListener = null;
mDecodeConfiguration = null; mDecodeConfiguration = null;
mPreviousDoNotMonitorCallEvent = null;
} }
/** /**
@@ -197,21 +191,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
long frequency = callEvent.getFrequency(); long frequency = callEvent.getFrequency();
/* Check the from/to aliases for do not monitor priority */ if(frequency > 0)
if(isDoNotMonitor(callEvent))
{
callEvent.setCallEventType(CallEventType.CALL_DO_NOT_MONITOR);
if(isSameCallEvent(mPreviousDoNotMonitorCallEvent, callEvent))
{
return;
}
else
{
mPreviousDoNotMonitorCallEvent = callEvent;
}
}
else if(frequency > 0)
{ {
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC, Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC,
frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth())); frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
@@ -330,31 +310,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
return false; return false;
} }
/**
* Checks for aliases that contain a do not follow/process priority.
*
* @param event
* @return - true if there is an alias that has a do not process alias id
*/
private boolean isDoNotMonitor(CallEvent event)
{
Alias to = event.getToIDAlias();
if(to != null && to.hasCallPriority() && to.getCallPriority() == Priority.DO_NOT_MONITOR)
{
return true;
}
Alias from = event.getFromIDAlias();
if(from != null && from.hasCallPriority() && from.getCallPriority() == Priority.DO_NOT_MONITOR)
{
return true;
}
return false;
}
@Override @Override
public Listener<DecoderStateEvent> getDecoderStateListener() public Listener<DecoderStateEvent> getDecoderStateListener()
{ {

View File

@@ -320,7 +320,13 @@ public class ProcessingChain implements IChannelEventListener
if(module instanceof IFrequencyChangeListener) if(module instanceof IFrequencyChangeListener)
{ {
mFrequencyChangeEventBroadcaster.addListener(((IFrequencyChangeListener) module).getFrequencyChangeListener()); Listener<FrequencyChangeEvent> frequencyChangeEventListener =
((IFrequencyChangeListener)module).getFrequencyChangeListener();
if(frequencyChangeEventListener != null)
{
mFrequencyChangeEventBroadcaster.addListener(frequencyChangeEventListener);
}
} }
if(module instanceof IMessageListener) if(module instanceof IMessageListener)

View File

@@ -222,7 +222,7 @@ public class DecoderFactory
if(channelType == ChannelType.STANDARD) if(channelType == ChannelType.STANDARD)
{ {
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig, modules.add(new TrafficChannelManager(channelModel, decodeConfig,
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize())); (aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize()));
} }
@@ -263,7 +263,7 @@ public class DecoderFactory
if(channelType == ChannelType.STANDARD) if(channelType == ChannelType.STANDARD)
{ {
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig, modules.add(new TrafficChannelManager(channelModel, decodeConfig,
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
(aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize())); (aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize()));
} }

View File

@@ -54,7 +54,7 @@ public class LJ1200DecoderState extends DecoderState
super(aliasList); super(aliasList);
mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO, mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.LOJACK); getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
} }
@Override @Override

View File

@@ -315,6 +315,8 @@ public class P25DecoderState extends DecoderState
*/ */
public void registerCallEvent(P25CallEvent event) public void registerCallEvent(P25CallEvent event)
{ {
String channel = event.getChannel();
if(mChannelCallMap.containsKey(event.getChannel())) if(mChannelCallMap.containsKey(event.getChannel()))
{ {
P25CallEvent previousEvent = mChannelCallMap.remove(event.getChannel()); P25CallEvent previousEvent = mChannelCallMap.remove(event.getChannel());
@@ -419,7 +421,7 @@ public class P25DecoderState extends DecoderState
mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL) mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(mCurrentChannel) .channel(mCurrentChannel)
.details((hdu.isEncrypted() ? "ENCRYPTED " : "")) .details((hdu.isEncryptedAudio() ? "ENCRYPTED " : ""))
.frequency(mCurrentChannelFrequency) .frequency(mCurrentChannelFrequency)
.to(to) .to(to)
.build(); .build();
@@ -1105,7 +1107,7 @@ public class P25DecoderState extends DecoderState
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(gvcu.getChannelA()) .channel(gvcu.getChannelA())
.details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : ""))
.frequency(gvcu.getDownlinkFrequencyA()) .frequency(gvcu.getDownlinkFrequencyA())
.to(userA) .to(userA)
.build()); .build());
@@ -1120,7 +1122,7 @@ public class P25DecoderState extends DecoderState
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(gvcu.getChannelB()) .channel(gvcu.getChannelB())
.details((gvcu.isEncrypted() ? "ENCRYPTED" : "")) .details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : ""))
.frequency(gvcu.getDownlinkFrequencyB()) .frequency(gvcu.getDownlinkFrequencyB())
.to(userB) .to(userB)
.build()); .build());
@@ -1149,7 +1151,7 @@ public class P25DecoderState extends DecoderState
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT) broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(gvcue.getTransmitChannel()) .channel(gvcue.getTransmitChannel())
.details((gvcue.isEncrypted() ? "ENCRYPTED" : "")) .details((gvcue.isEncryptedLinkControlWord() ? "ENCRYPTED" : ""))
.frequency(gvcue.getDownlinkFrequency()) .frequency(gvcue.getDownlinkFrequency())
.to(group) .to(group)
.build()); .build());
@@ -1185,7 +1187,7 @@ public class P25DecoderState extends DecoderState
if(mCurrentCallEvent.getDetails() == null) if(mCurrentCallEvent.getDetails() == null)
{ {
mCurrentCallEvent.setDetails( mCurrentCallEvent.setDetails(
(gvcuser.isEncrypted() ? "ENCRYPTED " : "") + (gvcuser.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") +
(gvcuser.isEmergency() ? "EMERGENCY " : "")); (gvcuser.isEmergency() ? "EMERGENCY " : ""));
broadcast(mCurrentCallEvent); broadcast(mCurrentCallEvent);
@@ -1397,7 +1399,7 @@ public class P25DecoderState extends DecoderState
if(mCurrentCallEvent.getDetails() == null) if(mCurrentCallEvent.getDetails() == null)
{ {
mCurrentCallEvent.setDetails( mCurrentCallEvent.setDetails(
(tivcu.isEncrypted() ? "ENCRYPTED " : "") + (tivcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") +
(tivcu.isEmergency() ? "EMERGENCY " : "")); (tivcu.isEmergency() ? "EMERGENCY " : ""));
broadcast(mCurrentCallEvent); broadcast(mCurrentCallEvent);
@@ -1480,7 +1482,7 @@ public class P25DecoderState extends DecoderState
if(mCurrentCallEvent.getDetails() == null) if(mCurrentCallEvent.getDetails() == null)
{ {
mCurrentCallEvent.setDetails( mCurrentCallEvent.setDetails(
(uuvcu.isEncrypted() ? "ENCRYPTED " : "") + (uuvcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") +
(uuvcu.isEmergency() ? "EMERGENCY " : "")); (uuvcu.isEmergency() ? "EMERGENCY " : ""));
broadcast(mCurrentCallEvent); broadcast(mCurrentCallEvent);
@@ -1557,6 +1559,7 @@ public class P25DecoderState extends DecoderState
break; break;
case IDENTIFIER_UPDATE_NON_VUHF: case IDENTIFIER_UPDATE_NON_VUHF:
case IDENTIFIER_UPDATE_VHF_UHF_BANDS: case IDENTIFIER_UPDATE_VHF_UHF_BANDS:
case IDENTIFIER_UPDATE_TDMA:
IdentifierUpdate iu = (IdentifierUpdate)tsbk; IdentifierUpdate iu = (IdentifierUpdate)tsbk;
if(!mBands.containsKey(iu.getIdentifier())) if(!mBands.containsKey(iu.getIdentifier()))
@@ -2735,6 +2738,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(pgvcg.isTDMAChannel() ? "TDMA " : "");
details.append(pgvcg.isEmergency() ? "EMERGENCY " : ""); details.append(pgvcg.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(pgvcg.getPriority()); details.append("PRI:").append(pgvcg.getPriority());
details.append(pgvcg.isEncryptedChannel() ? " ENCRYPTED" : ""); details.append(pgvcg.isEncryptedChannel() ? " ENCRYPTED" : "");
@@ -2742,7 +2746,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (pgvcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(pgvcg.getDownlinkFrequency()) .frequency(pgvcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -2775,8 +2779,9 @@ public class P25DecoderState extends DecoderState
{ {
P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (gvcgu.isTDMAChannel1() ? " TDMA" : "")) .channel(channel)
.details((gvcgu.isEncrypted() ? "ENCRYPTED " : "")) .details((gvcgu.isTDMAChannel1() ? "TDMA " : "") +
(gvcgu.isEncrypted() ? "ENCRYPTED " : ""))
.frequency(gvcgu.getDownlinkFrequency1()) .frequency(gvcgu.getDownlinkFrequency1())
.to(to) .to(to)
.build(); .build();
@@ -2801,8 +2806,9 @@ public class P25DecoderState extends DecoderState
{ {
P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel2 + (gvcgu.isTDMAChannel2() ? " TDMA" : "")) .channel(channel2)
.details((gvcgu.isEncrypted() ? "ENCRYPTED " : "")) .details((gvcgu.isTDMAChannel2() ? "TDMA " : "") +
(gvcgu.isEncrypted() ? "ENCRYPTED " : ""))
.frequency(gvcgu.getDownlinkFrequency2()) .frequency(gvcgu.getDownlinkFrequency2())
.to(to2) .to(to2)
.build(); .build();
@@ -2866,6 +2872,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(gdcg.isTDMAChannel() ? "TDMA " : "");
details.append(gdcg.isEmergency() ? "EMERGENCY " : ""); details.append(gdcg.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(gdcg.getPriority()).append(" "); details.append("PRI:").append(gdcg.getPriority()).append(" ");
details.append(gdcg.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(gdcg.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -2873,7 +2880,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (gdcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(gdcg.getDownlinkFrequency()) .frequency(gdcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -2904,6 +2911,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(gvcg.isTDMAChannel() ? "TDMA " : "");
details.append(gvcg.isEmergency() ? "EMERGENCY " : ""); details.append(gvcg.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(gvcg.getPriority()).append(" "); details.append("PRI:").append(gvcg.getPriority()).append(" ");
details.append(gvcg.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(gvcg.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -2911,7 +2919,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (gvcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(gvcg.getDownlinkFrequency()) .frequency(gvcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -2928,8 +2936,7 @@ public class P25DecoderState extends DecoderState
} }
break; break;
case GROUP_VOICE_CHANNEL_GRANT_UPDATE: case GROUP_VOICE_CHANNEL_GRANT_UPDATE:
GroupVoiceChannelGrantUpdate gvcgu = GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)message;
(GroupVoiceChannelGrantUpdate)message;
channel = gvcgu.getChannel1(); channel = gvcgu.getChannel1();
from = null; from = null;
@@ -2942,6 +2949,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(gvcgu.isTDMAChannel() ? "TDMA " : "");
details.append(gvcgu.isEmergency() ? "EMERGENCY " : ""); details.append(gvcgu.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(gvcgu.getPriority()).append(" "); details.append("PRI:").append(gvcgu.getPriority()).append(" ");
details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -2949,7 +2957,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(gvcgu.getChannel1() + (gvcgu.isTDMAChannel1() ? " TDMA" : "")) .channel(gvcgu.getChannel1())
.details(details.toString()) .details(details.toString())
.frequency(gvcgu.getDownlinkFrequency1()) .frequency(gvcgu.getDownlinkFrequency1())
.from(from) .from(from)
@@ -2977,6 +2985,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(gvcgu.isTDMAChannel() ? "TDMA " : "");
details.append(gvcgu.isEmergency() ? "EMERGENCY " : ""); details.append(gvcgu.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(gvcgu.getPriority()).append(" "); details.append("PRI:").append(gvcgu.getPriority()).append(" ");
details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -2984,7 +2993,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event2 = new P25CallEvent.Builder(CallEventType.GROUP_CALL) P25CallEvent event2 = new P25CallEvent.Builder(CallEventType.GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(gvcgu.getChannel2() + (gvcgu.isTDMAChannel2() ? " TDMA" : "")) .channel(gvcgu.getChannel2())
.details(details.toString()) .details(details.toString())
.frequency(gvcgu.getDownlinkFrequency2()) .frequency(gvcgu.getDownlinkFrequency2())
.to(gvcgu.getGroupAddress2()) .to(gvcgu.getGroupAddress2())
@@ -3016,6 +3025,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(gvcgue.isTDMAChannel() ? "TDMA " : "");
details.append(gvcgue.isEmergency() ? "EMERGENCY " : ""); details.append(gvcgue.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(gvcgue.getPriority()).append(" "); details.append("PRI:").append(gvcgue.getPriority()).append(" ");
details.append(gvcgue.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(gvcgue.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -3023,7 +3033,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (gvcgue.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(gvcgue.getDownlinkFrequency()) .frequency(gvcgue.getDownlinkFrequency())
.from(from) .from(from)
@@ -3053,6 +3063,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(idcg.isTDMAChannel() ? "TDMA " : "");
details.append(idcg.isEmergency() ? "EMERGENCY " : ""); details.append(idcg.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(idcg.getPriority()).append(" "); details.append("PRI:").append(idcg.getPriority()).append(" ");
details.append(idcg.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(idcg.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -3060,7 +3071,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (idcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(idcg.getDownlinkFrequency()) .frequency(idcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -3090,13 +3101,14 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(sdcg.isTDMAChannel() ? "TDMA " : "");
details.append(sdcg.isEmergency() ? "EMERGENCY " : ""); details.append(sdcg.isEmergency() ? "EMERGENCY " : "");
details.append(sdcg.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(sdcg.isEncryptedChannel() ? "ENCRYPTED " : "");
details.append(sdcg.getSessionMode().name()); details.append(sdcg.getSessionMode().name());
P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL) P25CallEvent event = new P25CallEvent.Builder(CallEventType.DATA_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (sdcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(sdcg.getDownlinkFrequency()) .frequency(sdcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -3128,6 +3140,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(tivcg.isTDMAChannel() ? "TDMA " : "");
details.append(tivcg.isEmergency() ? "EMERGENCY " : ""); details.append(tivcg.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(tivcg.getPriority()).append(" "); details.append("PRI:").append(tivcg.getPriority()).append(" ");
details.append(tivcg.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(tivcg.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -3137,7 +3150,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder( P25CallEvent event = new P25CallEvent.Builder(
CallEventType.TELEPHONE_INTERCONNECT) CallEventType.TELEPHONE_INTERCONNECT)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (tivcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(tivcg.getDownlinkFrequency()) .frequency(tivcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -3170,6 +3183,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(tivcgu.isTDMAChannel() ? "TDMA " : "");
details.append(tivcgu.isEmergency() ? "EMERGENCY " : ""); details.append(tivcgu.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(tivcgu.getPriority()).append(" "); details.append("PRI:").append(tivcgu.getPriority()).append(" ");
details.append(tivcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(tivcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -3179,7 +3193,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder( P25CallEvent event = new P25CallEvent.Builder(
CallEventType.TELEPHONE_INTERCONNECT) CallEventType.TELEPHONE_INTERCONNECT)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (tivcgu.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(tivcgu.getDownlinkFrequency()) .frequency(tivcgu.getDownlinkFrequency())
.from(from) .from(from)
@@ -3209,6 +3223,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(uuvcg.isTDMAChannel() ? "TDMA " : "");
details.append(uuvcg.isEmergency() ? "EMERGENCY " : ""); details.append(uuvcg.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(uuvcg.getPriority()).append(" "); details.append("PRI:").append(uuvcg.getPriority()).append(" ");
details.append(uuvcg.isEncryptedChannel() ? " ENCRYPTED " : ""); details.append(uuvcg.isEncryptedChannel() ? " ENCRYPTED " : "");
@@ -3217,7 +3232,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder( P25CallEvent event = new P25CallEvent.Builder(
CallEventType.UNIT_TO_UNIT_CALL) CallEventType.UNIT_TO_UNIT_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (uuvcg.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(uuvcg.getDownlinkFrequency()) .frequency(uuvcg.getDownlinkFrequency())
.from(from) .from(from)
@@ -3247,6 +3262,7 @@ public class P25DecoderState extends DecoderState
else else
{ {
StringBuilder details = new StringBuilder(); StringBuilder details = new StringBuilder();
details.append(uuvcgu.isTDMAChannel() ? "TDMA " : "");
details.append(uuvcgu.isEmergency() ? "EMERGENCY " : ""); details.append(uuvcgu.isEmergency() ? "EMERGENCY " : "");
details.append("PRI:").append(uuvcgu.getPriority()).append(" "); details.append("PRI:").append(uuvcgu.getPriority()).append(" ");
details.append(uuvcgu.isEncryptedChannel() ? "ENCRYPTED " : ""); details.append(uuvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
@@ -3255,7 +3271,7 @@ public class P25DecoderState extends DecoderState
P25CallEvent event = new P25CallEvent.Builder( P25CallEvent event = new P25CallEvent.Builder(
CallEventType.UNIT_TO_UNIT_CALL) CallEventType.UNIT_TO_UNIT_CALL)
.aliasList(getAliasList()) .aliasList(getAliasList())
.channel(channel + (uuvcgu.isTDMAChannel() ? " TDMA" : "")) .channel(channel)
.details(details.toString()) .details(details.toString())
.frequency(uuvcgu.getDownlinkFrequency()) .frequency(uuvcgu.getDownlinkFrequency())
.from(from) .from(from)

View File

@@ -12,6 +12,9 @@ import jmbe.iface.AudioConverter;
import message.IMessageListener; import message.IMessageListener;
import message.Message; import message.Message;
import module.Module; import module.Module;
import module.decode.p25.message.hdu.HDUMessage;
import module.decode.p25.message.ldu.LDU1Message;
import module.decode.p25.message.ldu.LDU2Message;
import module.decode.p25.message.ldu.LDUMessage; import module.decode.p25.message.ldu.LDUMessage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -25,14 +28,18 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class); private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class);
private static final String IMBE_CODEC = "IMBE"; private static final String IMBE_CODEC = "IMBE";
private static boolean mLibraryLoadStatusLogged = false; private static boolean mLibraryLoadStatusLogged = false;
private boolean mCanConvertAudio = false; private boolean mCanConvertAudio = false;
private boolean mEncryptedCall = false;
private boolean mEncryptedCallStateEstablished = false;
private AudioConverter mAudioConverter; private AudioConverter mAudioConverter;
private Listener<AudioPacket> mAudioPacketListener; private Listener<AudioPacket> mAudioPacketListener;
private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f); private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
private Metadata mMetadata; private Metadata mMetadata;
private LDU1Message mCachedLDU1Message = null;
public P25AudioModule(Metadata metadata) public P25AudioModule(Metadata metadata)
{ {
@@ -76,32 +83,73 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
} }
/** /**
* Primary processing method for p25 imbe audio frame messages. Each LDU * Processes call header (HDU) and voice frame (LDU1/LDU2) messages to decode audio and to determine the
* audio message contains 9 imbe voice frames. Each frame is converted to * encrypted audio status of a call event. Only the HDU and LDU2 messages convey encrypted call status. If an
* audio when the JMBE library is loaded, processed by auto-gain, and * LDU1 message is received without a preceding HDU message, then the LDU1 message is cached until the first
* broadcast to the audio packet listener. * LDU2 message is received and the encryption state can be determined. Both the LDU1 and the LDU2 message are
* then processed for audio if the call is unencrypted.
*/ */
public void receive(Message message) public void receive(Message message)
{ {
if (mCanConvertAudio && mAudioPacketListener != null) if(mCanConvertAudio && mAudioPacketListener != null)
{ {
if (message instanceof LDUMessage) if(mEncryptedCallStateEstablished)
{ {
LDUMessage ldu = (LDUMessage) message; if(message instanceof LDUMessage)
/* Only process unencrypted audio frames */
if (!(ldu.isValid() && ldu.isEncrypted()))
{ {
for (byte[] frame : ldu.getIMBEFrames()) processAudio((LDUMessage)message);
{
float[] audio = mAudioConverter.decode(frame);
audio = mGain.apply(audio);
mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf()));
}
} }
} }
else
{
if(message instanceof HDUMessage)
{
mEncryptedCallStateEstablished = true;
mEncryptedCall = ((HDUMessage)message).isEncryptedAudio();
}
else if(message instanceof LDU1Message)
{
//When we receive an LDU1 message with first receiving the HDU message, cache the LDU1 Message
//until we can determine the encrypted call state from the next LDU2 message
mCachedLDU1Message = (LDU1Message)message;
}
else if(message instanceof LDU2Message)
{
mEncryptedCallStateEstablished = true;
LDU2Message ldu2 = (LDU2Message)message;
mEncryptedCall = ldu2.isEncryptedAudio();
if(mCachedLDU1Message != null)
{
processAudio(mCachedLDU1Message);
mCachedLDU1Message = null;
}
processAudio(ldu2);
}
}
}
}
/**
* Processes an audio packet by decoding the IMBE audio frames and rebroadcasting them as PCM audio packets.
*/
private void processAudio(LDUMessage ldu)
{
if(!mEncryptedCall)
{
for(byte[] frame : ldu.getIMBEFrames())
{
float[] audio = mAudioConverter.decode(frame);
audio = mGain.apply(audio);
mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf()));
}
}
else
{
//Encrypted audio processing not implemented
} }
} }
@@ -119,18 +167,18 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
Class temp = Class.forName("jmbe.JMBEAudioLibrary"); Class temp = Class.forName("jmbe.JMBEAudioLibrary");
library = (AudioConversionLibrary) temp.newInstance(); library = (AudioConversionLibrary)temp.newInstance();
if ((library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 && if((library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 &&
library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1) library.getBuildVersion() >= 3) || library.getMajorVersion() >= 1)
{ {
mAudioConverter = library.getAudioConverter(IMBE_CODEC, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO); mAudioConverter = library.getAudioConverter(IMBE_CODEC, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO);
if (mAudioConverter != null) if(mAudioConverter != null)
{ {
mCanConvertAudio = true; mCanConvertAudio = true;
if (!mLibraryLoadStatusLogged) if(!mLibraryLoadStatusLogged)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("JMBE audio conversion library ["); sb.append("JMBE audio conversion library [");
@@ -144,7 +192,7 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
} }
else else
{ {
if (!mLibraryLoadStatusLogged) if(!mLibraryLoadStatusLogged)
{ {
mLog.info("JMBE audio conversion library NOT FOUND"); mLog.info("JMBE audio conversion library NOT FOUND");
mLibraryLoadStatusLogged = true; mLibraryLoadStatusLogged = true;
@@ -156,28 +204,28 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
mLog.warn("JMBE library version 0.3.3 or higher is required - found: " + library.getVersion()); mLog.warn("JMBE library version 0.3.3 or higher is required - found: " + library.getVersion());
} }
} }
catch (ClassNotFoundException e1) catch(ClassNotFoundException e1)
{ {
if (!mLibraryLoadStatusLogged) if(!mLibraryLoadStatusLogged)
{ {
mLog.error("Couldn't find/load JMBE audio conversion library"); mLog.error("Couldn't find/load JMBE audio conversion library");
mLibraryLoadStatusLogged = true; mLibraryLoadStatusLogged = true;
} }
} }
catch (InstantiationException e1) catch(InstantiationException e1)
{ {
if (!mLibraryLoadStatusLogged) if(!mLibraryLoadStatusLogged)
{ {
mLog.error("Couldn't instantiate JMBE audio conversion library class"); mLog.error("Couldn't instantiate JMBE audio conversion library class");
mLibraryLoadStatusLogged = true; mLibraryLoadStatusLogged = true;
} }
} }
catch (IllegalAccessException e1) catch(IllegalAccessException e1)
{ {
if (!mLibraryLoadStatusLogged) if(!mLibraryLoadStatusLogged)
{ {
mLog.error("Couldn't load JMBE audio conversion library due to " mLog.error("Couldn't load JMBE audio conversion library due to "
+ "security restrictions"); + "security restrictions");
mLibraryLoadStatusLogged = true; mLibraryLoadStatusLogged = true;
} }
} }
@@ -196,19 +244,25 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
} }
/** /**
* Wrapper for squelch state listener. Internally, the P25 audio module * Wrapper for squelch state to process end of call actions. At call end the encrypted call state established
* doesn't have a squelch state. If there are IMBE audio frames, we have * flag is reset so that the encrypted audio state for the next call can be properly detected and we send an
* audio. We use this listener to signal to the recorder manager that the * END audio packet so that downstream processors like the audio recorder can properly close out a call sequence.
* channel state is resetting and it should end the recording.
*/ */
public class SquelchStateListener implements Listener<SquelchState> public class SquelchStateListener implements Listener<SquelchState>
{ {
@Override @Override
public void receive(SquelchState state) public void receive(SquelchState state)
{ {
if (state == SquelchState.SQUELCH && mAudioPacketListener != null) if(state == SquelchState.SQUELCH)
{ {
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); if(mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
}
mEncryptedCallStateEstablished = false;
mEncryptedCall = false;
mCachedLDU1Message = null;
} }
} }
} }

View File

@@ -245,7 +245,7 @@ public class HDUMessage extends P25Message
return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) ); return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) );
} }
public boolean isEncrypted() public boolean isEncryptedAudio()
{ {
return getEncryption() != Encryption.UNENCRYPTED; return getEncryption() != Encryption.UNENCRYPTED;
} }

View File

@@ -1,244 +1,246 @@
package module.decode.p25.message.ldu; package module.decode.p25.message.ldu;
import module.decode.p25.reference.DataUnitID;
import module.decode.p25.reference.LinkControlOpcode;
import module.decode.p25.reference.Vendor;
import module.decode.p25.reference.VendorLinkControlOpcode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.AliasList; import alias.AliasList;
import bits.BinaryMessage; import bits.BinaryMessage;
import edac.CRC; import edac.CRC;
import edac.Hamming10; import edac.Hamming10;
import edac.ReedSolomon_63_47_17; import edac.ReedSolomon_63_47_17;
import module.decode.p25.reference.DataUnitID;
import module.decode.p25.reference.LinkControlOpcode;
import module.decode.p25.reference.Vendor;
import module.decode.p25.reference.VendorLinkControlOpcode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LDU1Message extends LDUMessage public class LDU1Message extends LDUMessage
{ {
private final static Logger mLog = LoggerFactory.getLogger( LDU1Message.class ); private final static Logger mLog = LoggerFactory.getLogger(LDU1Message.class);
public static final int ENCRYPTION_FLAG = 352; public static final int ENCRYPTED_LINK_CONTROL_WORD_FLAG = 352;
public static final int IMPLICIT_VENDOR_FLAG = 353; public static final int IMPLICIT_VENDOR_FLAG = 353;
public static final int[] OPCODE = { 354,355,356,357,362,363 }; public static final int[] OPCODE = {354, 355, 356, 357, 362, 363};
public static final int[] VENDOR = { 364,365,366,367,372,373,374,375 }; public static final int[] VENDOR = {364, 365, 366, 367, 372, 373, 374, 375};
public static final int[] GOLAY_WORD_STARTS = { public static final int[] GOLAY_WORD_STARTS = {
352,362,372,382,536,546,556,566,720,730,740,750,904,914,924,934,1088, 352, 362, 372, 382, 536, 546, 556, 566, 720, 730, 740, 750, 904, 914, 924, 934, 1088,
1098,1108,1118,1272,1282,1292,1302 }; 1098, 1108, 1118, 1272, 1282, 1292, 1302};
public static final int[] CW_HEX_0 = { 352,353,354,355,356,357 }; public static final int[] CW_HEX_0 = {352, 353, 354, 355, 356, 357};
public static final int[] CW_HEX_1 = { 362,363,364,365,366,367 }; public static final int[] CW_HEX_1 = {362, 363, 364, 365, 366, 367};
public static final int[] CW_HEX_2 = { 372,373,374,375,376,377 }; public static final int[] CW_HEX_2 = {372, 373, 374, 375, 376, 377};
public static final int[] CW_HEX_3 = { 382,383,384,385,386,387 }; public static final int[] CW_HEX_3 = {382, 383, 384, 385, 386, 387};
public static final int[] CW_HEX_4 = { 536,537,538,539,540,541 }; public static final int[] CW_HEX_4 = {536, 537, 538, 539, 540, 541};
public static final int[] CW_HEX_5 = { 546,547,548,549,550,551 }; public static final int[] CW_HEX_5 = {546, 547, 548, 549, 550, 551};
public static final int[] CW_HEX_6 = { 556,557,558,559,560,561 }; public static final int[] CW_HEX_6 = {556, 557, 558, 559, 560, 561};
public static final int[] CW_HEX_7 = { 566,567,568,569,570,571 }; public static final int[] CW_HEX_7 = {566, 567, 568, 569, 570, 571};
public static final int[] CW_HEX_8 = { 720,721,722,723,724,725 }; public static final int[] CW_HEX_8 = {720, 721, 722, 723, 724, 725};
public static final int[] CW_HEX_9 = { 730,731,732,733,734,735 }; public static final int[] CW_HEX_9 = {730, 731, 732, 733, 734, 735};
public static final int[] CW_HEX_10 = { 740,741,742,743,744,745 }; public static final int[] CW_HEX_10 = {740, 741, 742, 743, 744, 745};
public static final int[] CW_HEX_11 = { 750,751,752,753,754,755 }; public static final int[] CW_HEX_11 = {750, 751, 752, 753, 754, 755};
public static final int[] RS_HEX_0 = { 904,905,906,907,908,909 }; public static final int[] RS_HEX_0 = {904, 905, 906, 907, 908, 909};
public static final int[] RS_HEX_1 = { 914,915,916,917,918,919 }; public static final int[] RS_HEX_1 = {914, 915, 916, 917, 918, 919};
public static final int[] RS_HEX_2 = { 924,925,926,927,928,929 }; public static final int[] RS_HEX_2 = {924, 925, 926, 927, 928, 929};
public static final int[] RS_HEX_3 = { 934,935,936,937,938,939 }; public static final int[] RS_HEX_3 = {934, 935, 936, 937, 938, 939};
public static final int[] RS_HEX_4 = { 1088,1089,1090,1091,1092,1093 }; public static final int[] RS_HEX_4 = {1088, 1089, 1090, 1091, 1092, 1093};
public static final int[] RS_HEX_5 = { 1098,1099,1100,1101,1102,1103 }; public static final int[] RS_HEX_5 = {1098, 1099, 1100, 1101, 1102, 1103};
public static final int[] RS_HEX_6 = { 1108,1109,1110,1111,1112,1113 }; public static final int[] RS_HEX_6 = {1108, 1109, 1110, 1111, 1112, 1113};
public static final int[] RS_HEX_7 = { 1118,1119,1120,1121,1122,1123 }; public static final int[] RS_HEX_7 = {1118, 1119, 1120, 1121, 1122, 1123};
public static final int[] RS_HEX_8 = { 1272,1273,1274,1275,1276,1277 }; public static final int[] RS_HEX_8 = {1272, 1273, 1274, 1275, 1276, 1277};
public static final int[] RS_HEX_9 = { 1282,1283,1284,1285,1286,1287 }; public static final int[] RS_HEX_9 = {1282, 1283, 1284, 1285, 1286, 1287};
public static final int[] RS_HEX_10 = { 1292,1293,1294,1295,1296,1297 }; public static final int[] RS_HEX_10 = {1292, 1293, 1294, 1295, 1296, 1297};
public static final int[] RS_HEX_11 = { 1302,1303,1304,1305,1306,1307 }; public static final int[] RS_HEX_11 = {1302, 1303, 1304, 1305, 1306, 1307};
/* Reed-Solomon(24,12,13) code protects the link control word. Maximum /* Reed-Solomon(24,12,13) code protects the link control word. Maximum
* correctable errors are: Hamming Distance(13) / 2 = 6 */ * correctable errors are: Hamming Distance(13) / 2 = 6 */
public static final ReedSolomon_63_47_17 mReedSolomonDecoder = public static final ReedSolomon_63_47_17 mReedSolomonDecoder =
new ReedSolomon_63_47_17( 6 ); new ReedSolomon_63_47_17(6);
public LDU1Message( BinaryMessage message, DataUnitID duid, public LDU1Message(BinaryMessage message, DataUnitID duid,
AliasList aliasList ) AliasList aliasList)
{ {
super( message, duid, aliasList ); super(message, duid, aliasList);
/* NID CRC is checked in the message framer, thus a constructed message /* NID CRC is checked in the message framer, thus a constructed message
* means it passed the CRC */ * means it passed the CRC */
mCRC = new CRC[ 3 ]; mCRC = new CRC[3];
mCRC[ 0 ] = CRC.PASSED; mCRC[0] = CRC.PASSED;
checkCRC(); checkCRC();
} }
/** /**
* Subclass constructor from an existing LDU1Message * Subclass constructor from an existing LDU1Message
*/ */
protected LDU1Message( LDU1Message message ) protected LDU1Message(LDU1Message message)
{ {
super( message.getSourceMessage(), DataUnitID.LDU1, message.getAliasList() ); super(message.getSourceMessage(), DataUnitID.LDU1, message.getAliasList());
mCRC = message.mCRC; mCRC = message.mCRC;
} }
@Override /**
public boolean isEncrypted() * Indicates if the contents of the Link Control Word are encrypted.
{ *
return mMessage.get( ENCRYPTION_FLAG ); * @return true if the contents of the LCW are encrypted.
} */
public boolean isEncryptedLinkControlWord()
{
return mMessage.get(ENCRYPTED_LINK_CONTROL_WORD_FLAG);
}
private void checkCRC() private void checkCRC()
{ {
mCRC[ 1 ] = CRC.PASSED; mCRC[1] = CRC.PASSED;
/* Hamming( 10,6,3 ) error detection and correction */ /* Hamming( 10,6,3 ) error detection and correction */
for( int index: GOLAY_WORD_STARTS ) for(int index : GOLAY_WORD_STARTS)
{ {
int errors = Hamming10.checkAndCorrect( mMessage, index ); int errors = Hamming10.checkAndCorrect(mMessage, index);
if( errors > 1 ) if(errors > 1)
{ {
mCRC[ 1 ] = CRC.FAILED_CRC; mCRC[1] = CRC.FAILED_CRC;
} }
} }
/* Reed-Solomon( 24,16,9 ) error detection and correction /* Reed-Solomon( 24,16,9 ) error detection and correction
* Check the Reed-Solomon parity bits. The RS decoder expects the code * Check the Reed-Solomon parity bits. The RS decoder expects the code
* words and reed solomon parity hex codewords in reverse order. * words and reed solomon parity hex codewords in reverse order.
* *
* Since this is a truncated RS(63) codes, we pad the code with zeros */ * Since this is a truncated RS(63) codes, we pad the code with zeros */
int[] input = new int [63]; int[] input = new int[63];
int[] output = new int [63]; int[] output = new int[63];
input[ 0 ] = mMessage.getInt( RS_HEX_11 ); input[0] = mMessage.getInt(RS_HEX_11);
input[ 1 ] = mMessage.getInt( RS_HEX_10 ); input[1] = mMessage.getInt(RS_HEX_10);
input[ 2 ] = mMessage.getInt( RS_HEX_9 ); input[2] = mMessage.getInt(RS_HEX_9);
input[ 3 ] = mMessage.getInt( RS_HEX_8 ); input[3] = mMessage.getInt(RS_HEX_8);
input[ 4 ] = mMessage.getInt( RS_HEX_7 ); input[4] = mMessage.getInt(RS_HEX_7);
input[ 5 ] = mMessage.getInt( RS_HEX_6 ); input[5] = mMessage.getInt(RS_HEX_6);
input[ 6 ] = mMessage.getInt( RS_HEX_5 ); input[6] = mMessage.getInt(RS_HEX_5);
input[ 7 ] = mMessage.getInt( RS_HEX_4 ); input[7] = mMessage.getInt(RS_HEX_4);
input[ 8 ] = mMessage.getInt( RS_HEX_3 ); input[8] = mMessage.getInt(RS_HEX_3);
input[ 9 ] = mMessage.getInt( RS_HEX_2 ); input[9] = mMessage.getInt(RS_HEX_2);
input[ 10 ] = mMessage.getInt( RS_HEX_1 ); input[10] = mMessage.getInt(RS_HEX_1);
input[ 11 ] = mMessage.getInt( RS_HEX_0 ); input[11] = mMessage.getInt(RS_HEX_0);
input[ 12 ] = mMessage.getInt( CW_HEX_11 ); input[12] = mMessage.getInt(CW_HEX_11);
input[ 13 ] = mMessage.getInt( CW_HEX_10 ); input[13] = mMessage.getInt(CW_HEX_10);
input[ 14 ] = mMessage.getInt( CW_HEX_9 ); input[14] = mMessage.getInt(CW_HEX_9);
input[ 15 ] = mMessage.getInt( CW_HEX_8 ); input[15] = mMessage.getInt(CW_HEX_8);
input[ 16 ] = mMessage.getInt( CW_HEX_7 ); input[16] = mMessage.getInt(CW_HEX_7);
input[ 17 ] = mMessage.getInt( CW_HEX_6 ); input[17] = mMessage.getInt(CW_HEX_6);
input[ 18 ] = mMessage.getInt( CW_HEX_5 ); input[18] = mMessage.getInt(CW_HEX_5);
input[ 19 ] = mMessage.getInt( CW_HEX_4 ); input[19] = mMessage.getInt(CW_HEX_4);
input[ 20 ] = mMessage.getInt( CW_HEX_3 ); input[20] = mMessage.getInt(CW_HEX_3);
input[ 21 ] = mMessage.getInt( CW_HEX_2 ); input[21] = mMessage.getInt(CW_HEX_2);
input[ 22 ] = mMessage.getInt( CW_HEX_1 ); input[22] = mMessage.getInt(CW_HEX_1);
input[ 23 ] = mMessage.getInt( CW_HEX_0 ); input[23] = mMessage.getInt(CW_HEX_0);
/* indexes 24 - 62 are defaulted to zero */ /* indexes 24 - 62 are defaulted to zero */
boolean irrecoverableErrors = mReedSolomonDecoder.decode( input, output ); boolean irrecoverableErrors = mReedSolomonDecoder.decode(input, output);
if( irrecoverableErrors ) if(irrecoverableErrors)
{ {
mCRC[ 2 ] = CRC.FAILED_CRC; mCRC[2] = CRC.FAILED_CRC;
} }
else else
{ {
/* Since we've detected that we can correct the RS words, mark the /* Since we've detected that we can correct the RS words, mark the
* hamming crc as corrected as well, if it is currently failed */ * hamming crc as corrected as well, if it is currently failed */
if( mCRC[ 1 ] == CRC.FAILED_CRC ) if(mCRC[1] == CRC.FAILED_CRC)
{ {
mCRC[ 1 ] = CRC.CORRECTED; mCRC[1] = CRC.CORRECTED;
} }
mCRC[ 2 ] = CRC.PASSED; mCRC[2] = CRC.PASSED;
/* Only fix the codeword data hex codewords */ /* Only fix the codeword data hex codewords */
repairHexCodeword( input, output, 12, CW_HEX_11 ); repairHexCodeword(input, output, 12, CW_HEX_11);
repairHexCodeword( input, output, 13, CW_HEX_10 ); repairHexCodeword(input, output, 13, CW_HEX_10);
repairHexCodeword( input, output, 14, CW_HEX_9 ); repairHexCodeword(input, output, 14, CW_HEX_9);
repairHexCodeword( input, output, 15, CW_HEX_8 ); repairHexCodeword(input, output, 15, CW_HEX_8);
repairHexCodeword( input, output, 16, CW_HEX_7 ); repairHexCodeword(input, output, 16, CW_HEX_7);
repairHexCodeword( input, output, 17, CW_HEX_6 ); repairHexCodeword(input, output, 17, CW_HEX_6);
repairHexCodeword( input, output, 18, CW_HEX_5 ); repairHexCodeword(input, output, 18, CW_HEX_5);
repairHexCodeword( input, output, 19, CW_HEX_4 ); repairHexCodeword(input, output, 19, CW_HEX_4);
repairHexCodeword( input, output, 20, CW_HEX_3 ); repairHexCodeword(input, output, 20, CW_HEX_3);
repairHexCodeword( input, output, 21, CW_HEX_2 ); repairHexCodeword(input, output, 21, CW_HEX_2);
repairHexCodeword( input, output, 22, CW_HEX_1 ); repairHexCodeword(input, output, 22, CW_HEX_1);
repairHexCodeword( input, output, 23, CW_HEX_0 ); repairHexCodeword(input, output, 23, CW_HEX_0);
} }
} }
private void repairHexCodeword( int[] input, int[] output, int index, int[] indexSet ) private void repairHexCodeword(int[] input, int[] output, int index, int[] indexSet)
{ {
if( input[ index ] != output[ index ] ) if(input[index] != output[index])
{ {
mMessage.load( indexSet[ 0 ], 6, output[ index ] ); mMessage.load(indexSet[0], 6, output[index]);
mCRC[ 2 ] = CRC.CORRECTED; mCRC[2] = CRC.CORRECTED;
} }
} }
@Override @Override
public String getMessage() public String getMessage()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append( getMessageStub() ); sb.append(getMessageStub());
sb.append( " " ); sb.append(" ");
sb.append( mMessage.toString() ); sb.append(mMessage.toString());
return sb.toString(); return sb.toString();
} }
public String getMessageStub() public String getMessageStub()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append( super.getMessageStub() ); sb.append(super.getMessageStub());
if( isEncrypted() ) if(isEncryptedLinkControlWord())
{ {
sb.append( "ENCRYPTED" ); sb.append("ENCRYPTED");
} }
else else
{ {
if( isImplicitFormat() || getVendor() == Vendor.STANDARD ) if(isImplicitFormat() || getVendor() == Vendor.STANDARD)
{ {
sb.append( getOpcode().getLabel() ); sb.append(getOpcode().getLabel());
} }
else else
{ {
sb.append( "VENDOR:" + getVendor().getLabel() + " " ); sb.append("VENDOR:" + getVendor().getLabel() + " ");
sb.append( getVendorOpcode().getLabel() ); sb.append(getVendorOpcode().getLabel());
} }
} }
return sb.toString(); return sb.toString();
} }
public boolean isImplicitFormat() public boolean isImplicitFormat()
{ {
return mMessage.get( IMPLICIT_VENDOR_FLAG ); return mMessage.get(IMPLICIT_VENDOR_FLAG);
} }
public LinkControlOpcode getOpcode() public LinkControlOpcode getOpcode()
{ {
return LinkControlOpcode.fromValue( mMessage.getInt( OPCODE ) ); return LinkControlOpcode.fromValue(mMessage.getInt(OPCODE));
} }
public VendorLinkControlOpcode getVendorOpcode() public VendorLinkControlOpcode getVendorOpcode()
{ {
return VendorLinkControlOpcode.fromValue( mMessage.getInt( OPCODE ) ); return VendorLinkControlOpcode.fromValue(mMessage.getInt(OPCODE));
} }
public Vendor getVendor() public Vendor getVendor()
{ {
if( isImplicitFormat() ) if(isImplicitFormat())
{ {
return Vendor.STANDARD; return Vendor.STANDARD;
} }
return Vendor.fromValue( mMessage.getInt( VENDOR ) ); return Vendor.fromValue(mMessage.getInt(VENDOR));
} }
} }

View File

@@ -73,8 +73,10 @@ public class LDU2Message extends LDUMessage
checkCRC(); checkCRC();
} }
@Override /**
public boolean isEncrypted() * Indicates if the audio stream is encrypted
*/
public boolean isEncryptedAudio()
{ {
Encryption encryption = getEncryption(); Encryption encryption = getEncryption();

View File

@@ -35,8 +35,6 @@ public abstract class LDUMessage extends P25Message
super( message, duid, aliasList ); super( message, duid, aliasList );
} }
public abstract boolean isEncrypted();
@Override @Override
public String getMessage() public String getMessage()
{ {

View File

@@ -41,7 +41,7 @@ public class IdentifierUpdateTDMA extends IdentifierUpdate
sb.append( " ID:" + getIdentifier() ); sb.append( " ID:" + getIdentifier() );
sb.append( " BASE:" + getBaseFrequency() ); sb.append( " BASE:" + getBaseFrequency() );
sb.append( " BW:" + getBandwidth() ); sb.append( " BANDWIDTH:" + getBandwidth() );
sb.append( " SPACING:" + getChannelSpacing() ); sb.append( " SPACING:" + getChannelSpacing() );
sb.append( " OFFSET:" + getTransmitOffset() ); sb.append( " OFFSET:" + getTransmitOffset() );
sb.append( " TYPE:" + getChannelType().toString() ); sb.append( " TYPE:" + getChannelType().toString() );
@@ -137,7 +137,7 @@ public class IdentifierUpdateTDMA extends IdentifierUpdate
sb.append( mAccessType.name() ); sb.append( mAccessType.name() );
sb.append( " BANDWIDTH:" + mBandwidth ); sb.append( " BANDWIDTH:" + mBandwidth );
sb.append( " SLOT/CARRIER:" + mSlotsPerCarrier ); sb.append( " TIMESLOTS PER CARRIER:" + mSlotsPerCarrier );
sb.append( " VOCODER:" + mVocoder.name() ); sb.append( " VOCODER:" + mVocoder.name() );
return sb.toString(); return sb.toString();