Skip to content

Commit

Permalink
#1761 DMR decoder supports Hytera Tier 3 UTF-16 Talker Alias & new Tr…
Browse files Browse the repository at this point in the history
…affic Channel Talker Status CSBKO 47. Implements a tracking feature to detect when vendors are playing games using non-standard CSBK CRC mask values by tracking when a non-standard mask value is consistently detected and applying that mask when the initial CRC check fails.
  • Loading branch information
Dennis Sheirer committed Dec 14, 2023
1 parent b6c6505 commit ec9ceee
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 32 deletions.
3 changes: 2 additions & 1 deletion src/main/java/io/github/dsheirer/bits/BinaryMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import io.github.dsheirer.edac.CRC;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.BitSet;
import org.apache.commons.lang3.Validate;
import org.apache.commons.math3.util.FastMath;
Expand Down Expand Up @@ -1278,7 +1279,7 @@ public String parseUnicode(int offset, int characterCount)
bytes[x] = getByte(CHARACTER_8_BIT, x * 8 + offset);
}

return new String(bytes);
return new String(bytes, Charset.forName("UTF-16"));
}

/**
Expand Down
49 changes: 37 additions & 12 deletions src/main/java/io/github/dsheirer/edac/CRCDMR.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 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 the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
* 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.
* 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/>
*
******************************************************************************/
* 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;
Expand Down Expand Up @@ -177,6 +180,28 @@ public static int correctCCITT80(CorrectedBinaryMessage message, int messageStar
return 2;
}

/**
* Calculates the residual CRC value using a mask of zero which should identify the mask value when an alternate
* mask value is employed.
* @param message to be checked
* @param messageStart location
* @param crcStart location
* @return residual CRC check value.
*/
public static int calculateResidual(CorrectedBinaryMessage message, int messageStart, int crcStart)
{
int calculated = 0; //Starting value

/* Iterate the set bits and XOR running checksum with lookup value */
for(int i = message.nextSetBit(messageStart); i >= messageStart && i < crcStart; i = message.nextSetBit(i + 1))
{
calculated ^= CCITT_80_CHECKSUMS[i - messageStart];
}

int checksum = getIntChecksum(message, crcStart, 16);
return calculated ^ checksum;
}

/**
* Error detection for CRC-9 protected Confirmed Packet Data blocks. These
* data blocks have a slightly complicated structure because the checksum
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@
import io.github.dsheirer.source.ISourceEventListener;
import io.github.dsheirer.source.ISourceEventProvider;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.wave.ComplexWaveSource;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -332,4 +335,38 @@ public Listener<ComplexSamples> getComplexSamplesListener()
{
return DMRDecoder.this;
}

public static void main(String[] args)
{
File file = new File("/home/denny/Downloads/TIII,HYT414.585000,.wav");

try
{
ComplexWaveSource source = new ComplexWaveSource(file);
source.open();
System.out.println("Source Sample Rate: " + source.getSampleRate());
DMRDecoder decoder = new DMRDecoder(new DecodeConfigDMR());
decoder.setMessageListener(message -> System.out.println("TS:" + message.getTimeslot() + " " + message));
source.setListener(iNativeBuffer -> {
Iterator<ComplexSamples> it = iNativeBuffer.iterator();
while(it.hasNext())
{
ComplexSamples samples = it.next();
decoder.receive(samples);
}
});
decoder.setSampleRate(source.getSampleRate());
decoder.start();
source.start();

while(true)
{
source.next(65535);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.hytera.HyteraTrafficChannelTalkerStatus;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityMaxAloha;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusNeighbors;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.motorola.CapacityPlusSiteStatus;
Expand Down Expand Up @@ -708,7 +709,22 @@ private void processCSBK(CSBKMessage csbk)
case HYTERA_08_ANNOUNCEMENT:
case HYTERA_68_ANNOUNCEMENT:
case HYTERA_68_XPT_SITE_STATE:

break;
case HYTERA_08_TRAFFIC_CHANNEL_TALKER_STATUS:
if(csbk instanceof HyteraTrafficChannelTalkerStatus status)
{
if(status.isChannelActive())
{
getIdentifierCollection().update(status.getIdentifiers());
updateCurrentCall(DecodeEventType.CALL_GROUP, "HYTERA TIER 3 CALL", status.getTimestamp());
}
else
{
getIdentifierCollection().remove(Role.FROM);
getIdentifierCollection().update(status.getDestinationRadio());
}
}
break;
case MOTOROLA_CAPPLUS_NEIGHBOR_REPORT:
if(csbk instanceof CapacityPlusNeighbors)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.github.dsheirer.module.decode.dmr.message.DMRBurst;
import io.github.dsheirer.module.decode.dmr.message.data.IDLEMessage;
import io.github.dsheirer.module.decode.dmr.message.data.block.DataBlock;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Aloha;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.Preamble;
import io.github.dsheirer.module.decode.dmr.message.data.csbk.standard.announcement.Announcement;
Expand Down Expand Up @@ -71,6 +72,7 @@ public class DMRMessageProcessor implements Listener<IMessage>
private TalkerAliasAssembler mTalkerAliasAssembler = new TalkerAliasAssembler();
private Listener<IMessage> mMessageListener;
private Map<Integer,TimeslotFrequency> mTimeslotFrequencyMap = new TreeMap<>();
private DmrCrcMaskManager mCrcMaskManager = new DmrCrcMaskManager();

/**
* Constructs an instance
Expand All @@ -93,6 +95,17 @@ public DMRMessageProcessor(DecodeConfigDMR config)
@Override
public void receive(IMessage message)
{
if(message == null)
{
return;
}

//Detect and correct messages employing an alternate CRC mask pattern.
if(!message.isValid() && message instanceof CSBKMessage csbk)
{
mCrcMaskManager.check(csbk);
}

if(message instanceof FullLCMessage flc)
{
if(flc.getTimeslot() == 1)
Expand Down

0 comments on commit ec9ceee

Please sign in to comment.