Skip to content

Commit

Permalink
#1618 Update Cap+ radio & group parsing, process TXI & voice frame en…
Browse files Browse the repository at this point in the history
…cryption parameters, and many other changes to the DMR message processing.
  • Loading branch information
sheirerd authored and Dennis Sheirer committed Aug 13, 2023
1 parent 227c88c commit 2ffb296
Show file tree
Hide file tree
Showing 64 changed files with 2,039 additions and 684 deletions.
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

125 changes: 125 additions & 0 deletions src/main/java/io/github/dsheirer/edac/BPTC_16_2.java
@@ -0,0 +1,125 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/

package io.github.dsheirer.edac;

import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage;

/**
* Block Product Turbo Code 16/2 for decoding a DMR Voice Frame F payload from the 32-bit EMB field.
* <p>
* See ETSI TS 102 361-1 B.2.2.1 and B.2.2.2
*/
public class BPTC_16_2
{
private static final int[] DEINTERLEAVE = new int[]{0, 24, 1, 25, 2, 26, 3, 27, 4, 28, 5, 29, 6, 30, 7, 31, 8, 16,
9, 17, 10, 18, 11, 19, 12, 20, 13, 21, 14, 22, 15, 23};

/**
* Unscramble and perform FEC checks per paragraph B.2.2.1 for Non-Reverse Channel Single Burst
*
* @param message with 32 interleaved bits.
* @return descrambled and error checked message or null if the process fails or there are too many errors.
*/
public static CorrectedBinaryMessage decodeShortBurst(CorrectedBinaryMessage message)
{
CorrectedBinaryMessage deinterleaved = deinterleave(message);
int fec = Hamming16.checkAndCorrect(deinterleaved, 0);

if(fec == 2) //0 or 1 is good, 2 = uncorrectable errors
{
return null;
}

//Check for even parity. Bits 0-15 should be the same as bits 16-31.
for(int x = 0; x < 16; x++)
{
if(deinterleaved.get(x) ^ deinterleaved.get(x + 16))
{
return null;
}
}

return deinterleaved;
}

/**
* Unscramble and perform FEC checks per paragraph B.2.2.2 for Reverse Channel Single Burst
*
* @param binaryMessage with 32 interleaved bits.
* @return descrambled and error checked message or null if the process fails or there are too many errors.
*/
public static CorrectedBinaryMessage decodeReverseChannel(CorrectedBinaryMessage message)
{
CorrectedBinaryMessage deinterleaved = deinterleave(message);
System.out.println(" DEINTER: " + deinterleaved.toHexString());
int fec = Hamming16.checkAndCorrect(deinterleaved, 0);
System.out.println(" DECODED: " + deinterleaved.toHexString());
System.out.println("FEC:" + fec);
if(fec == 2) //0 or 1 is good, 2 = uncorrectable errors
{
return null;
}

//Check for odd parity. Bits 0-15 should be opposite of bits 16-31.
for(int x = 0; x < 16; x++)
{
if(deinterleaved.get(x) == deinterleaved.get(x + 16))
{
return null;
}
}

return deinterleaved;
}

/**
* Performs deinterleave of the interleaved message.
*
* @param original to deinterleave
* @return deinterleaved message
*/
public static CorrectedBinaryMessage deinterleave(CorrectedBinaryMessage original)
{
CorrectedBinaryMessage delinterleaved = new CorrectedBinaryMessage(32);
for(int x = 0; x < 32; x++)
{
if(original.get(x))
{
delinterleaved.set(DEINTERLEAVE[x]);
}
}

return delinterleaved;
}

public static void main(String[] args)
{
String[] msgs = new String[]{"05030A03", "35003A00", "1C6D2C9E"};

for(String msg : msgs)
{
CorrectedBinaryMessage original = new CorrectedBinaryMessage(BinaryMessage.loadHex(msg));
System.out.println("ORIGINAL: " + original.toHexString());
CorrectedBinaryMessage decoded = decodeReverseChannel(original);
System.out.println("-------------------------");
}
}
}
28 changes: 15 additions & 13 deletions src/main/java/io/github/dsheirer/edac/CRCUtil.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -226,17 +226,19 @@ public static void main(String[] args)
{
mLog.debug("Starting");

//DMR message
// String raw = "100110010001000000001001000000001111011011010000011001010000000000000000000000001100001000001011";
String raw = "101010000000000000110110100000101101011011010000011001111100000000000011100101001001111010001110";
mLog.debug(raw);
raw = "101010000000000000110110100000101101011011010000011001111100000000000011100101000000000000000000";
BinaryMessage message = BinaryMessage.load(raw);
mLog.debug(message.toString());

long polynomial = 0x11021l;
decode(message, 0, 80, polynomial, 16);
mLog.debug(message.toString());
mLog.debug("Finished");
long poly = 0x13l;
long[] checksums = generate(32, 4, poly, 0, true);

StringBuilder sb = new StringBuilder();
sb.append("private static int[] CHECKSUMS = new int[]{");
for(long checksum: checksums)
{
sb.append("0x").append(Long.toHexString(checksum).toUpperCase());
sb.append(",");
}

sb.append("};");

System.out.println("Checksums:\n" + sb);
}
}
53 changes: 32 additions & 21 deletions src/main/java/io/github/dsheirer/edac/Golay24.java
@@ -1,31 +1,29 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/

package io.github.dsheirer.edac;

import io.github.dsheirer.bits.BinaryMessage;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 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/>
* -----------------------------------------------------------------------
* Galois24 decoder based on Hank Wallace's tutorial/algorithm located at:
* http://www.aqdi.com/golay.htm
******************************************************************************/

/**
* Galois 24/12/7 decoder
*/
Expand Down Expand Up @@ -160,4 +158,17 @@ private static int getSyndrome(BinaryMessage message, int startIndex)

return (checksum ^ calculated);
}

public static void main(String[] args)
{
// CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("F3BB20"));
// CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("F0C5C0"));
CorrectedBinaryMessage bm = new CorrectedBinaryMessage(BinaryMessage.loadHex("AFAC00"));

System.out.println("M:" + bm.toHexString());
int a = Golay24.checkAndCorrect(bm, 0);
System.out.println("M:" + bm.toHexString());

System.out.println("A:" + a);
}
}
Expand Up @@ -459,6 +459,12 @@ private TextField getFindText()
if(mFindText == null)
{
mFindText = new TextField();
mFindText.setOnKeyPressed(event -> {
if(event.getCode().equals(KeyCode.ENTER))
{
getFindButton().fire();
}
});
mFindText.textProperty().addListener((observable, oldValue, newValue) -> updateFilters());
}

Expand Down
Expand Up @@ -65,8 +65,8 @@
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraGroupVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraUnitToUnitVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusEncryptedVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusGroupVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusWideAreaVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaGroupVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.CapacityPlusRestChannel;
import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
Expand Down Expand Up @@ -646,12 +646,12 @@ private void processCSBK(CSBKMessage csbk)
}
else
{
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue());

if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
mDetectedCallEventsMap.put(channel.getValue(), event);
}
else
{
Expand Down Expand Up @@ -679,12 +679,12 @@ private void processCSBK(CSBKMessage csbk)
}
else
{
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue());

if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
mDetectedCallEventsMap.put(channel.getValue(), event);
}
else
{
Expand All @@ -711,12 +711,12 @@ private void processCSBK(CSBKMessage csbk)
}
else
{
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue());

if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = getDecodeEvent(csbk, DecodeEventType.CALL_UNIT_TO_UNIT, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
mDetectedCallEventsMap.put(channel.getValue(), event);
}
else
{
Expand Down Expand Up @@ -756,12 +756,12 @@ private void processCSBK(CSBKMessage csbk)
}
else
{
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
DecodeEvent event = mDetectedCallEventsMap.get(channel.getValue());

if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
mDetectedCallEventsMap.put(channel.getValue(), event);
}
else
{
Expand Down Expand Up @@ -807,12 +807,12 @@ private void processCSBK(CSBKMessage csbk)
}
else
{
DecodeEvent detectedEvent = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
DecodeEvent detectedEvent = mDetectedCallEventsMap.get(channel.getValue());

if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers()))
{
detectedEvent = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent);
mDetectedCallEventsMap.put(channel.getValue(), detectedEvent);
}
else
{
Expand Down Expand Up @@ -979,10 +979,8 @@ private void processLinkControl(LCMessage message, boolean isTerminator)
}
break;
case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER:
if(message instanceof CapacityPlusGroupVoiceChannelUser cpgvcu)
if(message instanceof MotorolaGroupVoiceChannelUser cpgvcu)
{
updateRestChannel(cpgvcu.getRestChannel());

if(isTerminator)
{
getIdentifierCollection().remove(Role.FROM);
Expand Down

0 comments on commit 2ffb296

Please sign in to comment.