Compare commits

...

11 Commits

Author SHA1 Message Date
Denny Sheirer
108691dc3c Merge pull request #274 from DSheirer/272-java-9-compatibility
272 java 9 compatibility
2017-10-09 12:40:04 -04:00
Denny
455aa97286 Updates build number to 'final' 2017-10-09 12:35:28 -04:00
Denny
d608834214 Resolves #272. Updates the windows launch script to perform conditional
launching based on the JRE version 8 or 9.
2017-10-09 16:33:58 +00:00
Denny
41660e1881 Updates the build number to 0.3.1 for the next patch release
Updates the build script for conditional compilation using either Java
8 or Java 9 options.  Java 9 deprecated the java.xml.bind package and
removed it as a default Java SE package, so we have to reference it via
the --add-modules argument.

Updates the linux/mac launch script to check the java runtime version
and use either a Java 8 or Java 9 launch command.
2017-10-08 09:00:10 -04:00
Denny Sheirer
110ccb158c Update README.md
Removed Version 0.2.0 user manual link
2017-09-22 06:16:34 -04:00
Denny
64ccacf04a Releasing Version 0.3.0 (final). 2017-09-22 05:56:27 -04:00
Denny Sheirer
f00b9555fe Merge pull request #271 from DSheirer/269-viterbi-3-4-rate
269 Viterbi decoder for DMR 3/4-rate packet messages
2017-09-15 11:50:19 -04:00
Denny
85442a9648 Resolves #269 - creates a generic Viterbi decoder and a Digital Mobile Radio (DMR) 3/4 Rate Viterbi decoder implementation for decoding Trellis Coded Modulation (TCM) encoded DMR Data packets. 2017-09-15 11:40:23 -04:00
Denny Sheirer
c67a0b329d Merge pull request #267 from DSheirer/266-licence-directory
266 Update build script to use gitlab license file
2017-09-10 04:55:57 -04:00
Denny
8035d64fcd Resolves #266 - updates build script to use new gitlab license file and
removes previous license file.
2017-09-10 04:54:26 -04:00
Denny Sheirer
df2a061a08 Create LICENSE 2017-08-28 19:51:13 -04:00
14 changed files with 927 additions and 151 deletions

View File

@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> {one line to give the program's name and a brief idea of what it does.}
Copyright (C) <year> <name of author> Copyright (C) {year} {name of author}
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
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode: notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author> {project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.

View File

@@ -2,7 +2,6 @@
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR). A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted) * [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
* [User's Manual Version 0.2.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0) * [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0)
* [Download](https://github.com/DSheirer/sdrtrunk/releases) * [Download](https://github.com/DSheirer/sdrtrunk/releases)
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk) * [Support](https://groups.google.com/forum/#!forum/sdrtrunk)

View File

@@ -1,121 +1,142 @@
<project name="SDRTrunk" default="SDRTrunk" basedir="."> <project name="SDRTrunk" default="SDRTrunk" basedir=".">
<description> <description>
Ant build script for SDRTrunk Decoder Application Ant build script for SDRTrunk Decoder Application
</description> </description>
<!-- version properties --> <!-- version properties -->
<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="1"/>
<property name="release" value="-beta14"/> <property name="release" value="final"/>
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/> <property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
<!-- Build properties --> <!-- Build properties -->
<property name="repo" location=".."/> <property name="repo" location=".."/>
<property name="source.code" location="${repo}/src"/> <property name="source.code" location="${repo}/src"/>
<property name="source.configs" location="${repo}/config"/> <property name="source.configs" location="${repo}/config"/>
<property name="source.images" location="${repo}/images"/> <property name="source.images" location="${repo}/images"/>
<property name="source.libs" location="${repo}/imports"/> <property name="source.libs" location="${repo}/imports"/>
<property name="source.license" location="${repo}/license"/> <property name="source.scripts" location="${repo}/scripts"/>
<property name="source.scripts" location="${repo}/scripts"/>
<property name="output" location="${repo}/product"/> <property name="output" location="${repo}/product"/>
<property name="output.classes" location="${repo}/classes"/> <property name="output.classes" location="${repo}/classes"/>
<property name="output.folder" location="${output}/sdrtrunk"/> <property name="output.folder" location="${output}/sdrtrunk"/>
<property name="output.config" location="${output.folder}/config"/> <property name="output.config" location="${output.folder}/config"/>
<property name="output.images" location="${output.folder}/images"/> <property name="output.images" location="${output.folder}/images"/>
<property name="output.libs" location="${output.folder}/libs"/> <property name="output.libs" location="${output.folder}/libs"/>
<path id="classpath"> <path id="classpath">
<fileset dir="${source.libs}" includes="*.jar" /> <fileset dir="${source.libs}" includes="*.jar"/>
</path> </path>
<target name="init"> <target name="init">
<!-- Create the classes folder to contain the compiled java classes --> <!-- Create the classes folder to contain the compiled java classes -->
<mkdir dir="${output.classes}"/> <mkdir dir="${output.classes}"/>
<!-- Create version file --> <!-- Create version file -->
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/> <echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
</target> </target>
<target name="compile" depends="clean,init" description="Compile java classes" > <target name="compile" depends="clean,init,compile-java-8,compile-java-9"
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false" /> description="Compile java classes" />
</target>
<target name="SDRTrunk" depends="compile" description="Create SDRTrunk application" > <target name="compile-java-8" depends="check-java-version" if="java.8" description="Compiles code with Java 8 arguments">
<!-- Create the distribution directory --> <echo message="Compiling with Java 8 Options"/>
<mkdir dir="${output.folder}"/> <javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false"/>
</target>
<!-- Create jar file --> <target name="compile-java-9" depends="check-java-version" if="java.9" description="Compiles code with Java 9 arguments">
<jar jarfile="${output.folder}/sdrtrunk.jar" basedir="${output.classes}"> <echo message="Compiling with Java 9 Options"/>
<manifest> <javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false">
<attribute name="Title" value="sdrtrunk"/> <compilerarg value="--add-modules"/>
<attribute name="Main-Class" value="gui.SDRTrunk"/> <compilerarg value="java.xml.bind"/>
<attribute name="Version" value="${major}.${minor}.${patch}-${release}"/> </javac>
</manifest> </target>
<fileset dir="${source.configs}">
<exclude name="*.rules"/>
</fileset>
<fileset dir="${source.license}"> <target name="check-java-version" description="Determines if the compiler is Java 8 or 9">
<include name="license.txt"/> <condition property="java.8" value="true">
</fileset> <contains string="${java.version}" substring="1.8"/>
</jar> </condition>
<condition property="java.9" value="true">
<equals arg1="${java.version}" arg2="9"/>
</condition>
<echo message="Java Version: ${java.version}"/>
</target>
<!-- Copy third-party libraries --> <target name="SDRTrunk" depends="compile" description="Create SDRTrunk application">
<mkdir dir="${output.libs}"/> <!-- Create the distribution directory -->
<copy todir="${output.libs}"> <mkdir dir="${output.folder}"/>
<fileset dir="${source.libs}" includes="*.jar" />
</copy>
<!-- Copy config files --> <!-- Create jar file -->
<mkdir dir="${output.config}"/> <jar jarfile="${output.folder}/sdrtrunk.jar" basedir="${output.classes}">
<manifest>
<attribute name="Title" value="sdrtrunk"/>
<attribute name="Main-Class" value="gui.SDRTrunk"/>
<attribute name="Version" value="${major}.${minor}.${patch}-${release}"/>
</manifest>
<fileset dir="${source.configs}">
<exclude name="*.rules"/>
</fileset>
<copy todir="${output.config}"> <fileset dir="${repo}">
<fileset dir="${source.configs}"> <include name="LICENSE"/>
<include name="*.rules"/> </fileset>
</fileset> </jar>
</copy>
<!-- Copy image files --> <!-- Copy third-party libraries -->
<mkdir dir="${output.images}"/> <mkdir dir="${output.libs}"/>
<copy todir="${output.images}"> <copy todir="${output.libs}">
<fileset dir="${source.images}" /> <fileset dir="${source.libs}" includes="*.jar"/>
</copy> </copy>
<!-- Copy license file --> <!-- Copy config files -->
<copy todir="${output.folder}"> <mkdir dir="${output.config}"/>
<fileset dir="${source.license}">
<include name="license.txt"/>
</fileset>
</copy>
<!-- Copy program scripts --> <copy todir="${output.config}">
<copy todir="${output.folder}"> <fileset dir="${source.configs}">
<fileset dir="${source.scripts}" /> <include name="*.rules"/>
</copy> </fileset>
</copy>
<!-- Copy image files -->
<mkdir dir="${output.images}"/>
<copy todir="${output.images}">
<fileset dir="${source.images}"/>
</copy>
<!-- Copy license file -->
<copy todir="${output.folder}">
<fileset dir="${repo}">
<include name="LICENSE"/>
</fileset>
</copy>
<!-- Copy program scripts -->
<copy todir="${output.folder}">
<fileset dir="${source.scripts}"/>
</copy>
<!-- Bundle the files into .zip --> <!-- Bundle the files into .zip -->
<zip destfile="${output}/${filename}.zip" basedir="${output}" /> <zip destfile="${output}/${filename}.zip" basedir="${output}"/>
<!-- Remove linux start script before tar task --> <!-- Remove linux start script before tar task -->
<delete file="${output.folder}/run_sdrtrunk_linux.sh"/> <delete file="${output.folder}/run_sdrtrunk_linux.sh"/>
<!-- Bundle files into .tar.gz and copy/set permissions on linux start script --> <!-- Bundle files into .tar.gz and copy/set permissions on linux start script -->
<tar destfile="${output}/${filename}.tar.gz" compression="gzip"> <tar destfile="${output}/${filename}.tar.gz" compression="gzip">
<tarfileset dir="${output.folder}" prefix="sdrtrunk" /> <tarfileset dir="${output.folder}" prefix="sdrtrunk"/>
<tarfileset dir="${source.scripts}" prefix="sdrtrunk" filemode="777"> <tarfileset dir="${source.scripts}" prefix="sdrtrunk" filemode="777">
<include name="run_sdrtrunk_linux.sh"/> <include name="run_sdrtrunk_linux.sh"/>
</tarfileset> </tarfileset>
</tar> </tar>
<delete dir="${output.folder}"/> <delete dir="${output.folder}"/>
</target> </target>
<target name="clean" description="Clean previous build artifacts" > <target name="clean" description="Clean previous build artifacts">
<delete dir="${output.classes}"/> <delete dir="${output.classes}"/>
<delete dir="${output}"/> <delete dir="${output}"/>
</target> </target>
</project> </project>

View File

@@ -1,2 +1,16 @@
#!/bin/bash #!/bin/bash
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk
java -version
# Test for java version 9
JAVA_9=$(java -version 2>&1 | grep -i version | sed 's/.* "\([9]\).*/\1/')
if [ -z ${JAVA_9+x} ]; then
echo Using Java 9 program options
# Java 9 uses the G1 garbage collector by default and requires that we specify the
# java.xml.bind package since it's been removed from the java se core library
java -cp "*:libs/*" --add-modules java.xml.bind gui.SDRTrunk;
else
echo Using Java 8 program options
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk;
fi

View File

@@ -1 +1,25 @@
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk @echo off
java -version
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "usebackq tokens=3" %%a in (`java -version 2^>^&1 ^| find "version"`) do (
rem set b to the unquoted version string parsed from the java version command
set b=%%~a
rem set c to the first character of the unquoted version string
set c=!b:~0,1!
if !c! equ 9 (
echo Using Java 9 options
java -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" --add-modules java.xml.bind gui.SDRTrunk
) else (
echo Using Java 8 options
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
)
)
ENDLOCAL
@echo on

View File

@@ -137,6 +137,17 @@ public class BinaryMessage extends BitSet
mCRC = crc; mCRC = crc;
} }
/**
* Indicates the number of bit errors that were corrected in this message.
*
* @see CorrectedBinaryMessage subclass that supports setting this value
* @return number of corrected bits in this message
*/
public int getCorrectedBitCount()
{
return 0;
}
/** /**
* Current pointer index * Current pointer index
*/ */

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 bits;
import java.util.BitSet;
public class CorrectedBinaryMessage extends BinaryMessage
{
private int mCorrectedBitCount;
/**
* Subclass of binary message class to allow capturing a corrected bits metric.
* @param size of message to create.
*/
public CorrectedBinaryMessage(int size)
{
super(size);
}
public CorrectedBinaryMessage(int size, boolean[] bitsToPreload)
{
super(size, bitsToPreload);
}
public CorrectedBinaryMessage(BitSet bitset, int size)
{
super(bitset, size);
}
public CorrectedBinaryMessage(byte[] data)
{
super(data);
}
@Override
public int getCorrectedBitCount()
{
return mCorrectedBitCount;
}
/**
* Sets the number of bits that were corrected in this message
* @param count of corrected bits
*/
public void setCorrectedBitCount(int count)
{
mCorrectedBitCount = count;
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 edac.trellis;
public class DMRNode extends Node
{
//Hamming error values indicating the number of bits set in each indexed value, 0-15
public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
/**
* Transmitted quad-bit value, derived from ETSI TS 102-361-1, Table B.7 Trellis encoder state transition table
*
* Note: the B.7 table contains constellation point ID numbers that have to be converted into actual bit values
* where the bits represent the dibit pair for the constellation point.
*/
public static final int[][] DMR_TRANSITION_MATRIX = new int[][]
{
{2,13,14,1,7,8,11,4},
{14,1,7,8,11,4,2,13},
{10,5,6,9,15,0,3,12},
{6,9,15,0,3,12,10,5},
{15,0,3,12,10,5,6,9},
{3,12,10,5,6,9,15,0},
{7,8,11,4,2,13,14,1},
{11,4,2,13,14,1,7,8}
};
/**
* Trellis Coded Modulation (TCM) 3/4-rate trellis node for decoding DMR
*
* @param inputValue for the trellis time instant
* @param transmittedOutputValue the actual four-bit output value that was transmitted.
*/
public DMRNode(int inputValue, int transmittedOutputValue)
{
super(inputValue, transmittedOutputValue);
}
/**
* Calculates the error between the preceeding node and this node as the Hamming distance between the expected
* four-bit output value versus the actual transmitted output value.
*
* @param precedingNode that precedes this node
* @return error value for this node relative to the preceeding node argument
*/
@Override
public int getError(Node precedingNode)
{
if(precedingNode != null)
{
int expectedOutputValue = getOutputValue(precedingNode);
int errorMask = expectedOutputValue ^ getTransmittedOutputValue();
return HAMMING_ERROR_COUNT[errorMask];
}
return 0;
}
/**
* Expected output four-bit value when this node is preceeded by the argument node.
*
* @param precedingNode that precedes this node
* @return expected output value
*/
@Override
public int getOutputValue(Node precedingNode)
{
return DMR_TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()];
}
}

View File

@@ -0,0 +1,77 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 edac.trellis;
public abstract class Node
{
private int mInputValue;
private int mTransmittedOutputValue;
/**
* Trellis Coded Modulation (TCM) node. A node represents a time instant on the trellis and contains an input
* value and the corresponding transmitted output value for the time instant. A node does not maintain the
* state at the time instant -- the state is determined by the input value of a preceeding node.
*
* @param inputValue for this node
* @param transmittedOutputValue for this node
*/
public Node(int inputValue, int transmittedOutputValue)
{
mInputValue = inputValue;
mTransmittedOutputValue = transmittedOutputValue;
}
/**
* Input value for this node.
*
* @return input value
*/
public int getInputValue()
{
return mInputValue;
}
/**
* Transmitted output value for this node
* @return transmitted output value
*/
public int getTransmittedOutputValue()
{
return mTransmittedOutputValue;
}
/**
* Calculates the error value for this node (time instant) by determining the expected output value that would have
* been transmitted using the state from the preceding node argument and this node's input value and comparing that
* expected value to the actual transmitted output value. This error is normally represented as the Hamming
* distance between the two values.
*
* @param precedingNode that precedes this node
* @return error value for this node relative to the preceeding node
*/
public abstract int getError(Node precedingNode);
/**
* Expected output value when this node is preceeded by the node specified in the argument.
*
* @param precedingNode that precedes this node
* @return expected output value
*/
public abstract int getOutputValue(Node precedingNode);
}

131
src/edac/trellis/Path.java Normal file
View File

@@ -0,0 +1,131 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 edac.trellis;
import java.util.ArrayList;
import java.util.List;
public class Path
{
private List<Node> mNodes = new ArrayList<>();
private int mError = 0;
/**
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
* states across a trellis.
*
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
* @param startingNode to start the path. This node normally contains an input value of zero, but is implementation
* specific.
*/
public Path(Node startingNode)
{
add(startingNode);
}
/**
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
* states across a trellis.
*
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
*
* @param original path to create a copy from. Each of the nodes in the original will be added to this path and
* after construction both this path and the original will contain the exact same set of nodes.
*/
private Path(Path original)
{
for(Node node: original.getNodes())
{
add(node);
}
}
/**
* Cumulative error across all nodes contained within this path. Error value is updated as each new node is added
* to this path.
*
* @return cumulative error value
*/
public int getError()
{
return mError;
}
/**
* Length or number of nodes contained within this path. Note: this value includes the starting node and may
* also include a flushing node. The values contained in the starting and flushing nodes are normally not used
* when decoding a path.
*
* @return number of nodes in this path
*/
public int getLength()
{
return mNodes.size() - 1;
}
/**
* The set of nodes contained in this path.
* @return nodes
*/
public List<Node> getNodes()
{
return mNodes;
}
/**
* Adds the node to this path and updates the cumulative error value for this path by adding the node's error
* value to the running error total.
*
* @param node to add to this path
*/
public void add(Node node)
{
if(!mNodes.isEmpty())
{
mError += node.getError(getLastNode());
}
mNodes.add(node);
}
/**
* Last node in this path.
*
* @return last node
*/
public Node getLastNode()
{
if(!mNodes.isEmpty())
{
return mNodes.get(mNodes.size() - 1);
}
return null;
}
/**
* Utility method to create an exact copy of this path.
*
* @return a new copy of this path.
*/
public Path copyOf()
{
return new Path(this);
}
}

View File

@@ -0,0 +1,226 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 edac.trellis;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public abstract class ViterbiDecoder
{
private int mInputBitLength;
private int mInputValueCount;
private int mOutputBitLength;
private int mOutputValueCount;
/**
* Viterbi decoder for trellis coded modulation (TCM) encoded binary sequences.
*
* @param inputBitLength for coding (e.g. 3/4 rate input bit length is 3)
* @param outputBitLength for coding (e.g. 3/4 rate output bit length is 4)
*/
public ViterbiDecoder(int inputBitLength, int outputBitLength)
{
mInputBitLength = inputBitLength;
mOutputBitLength = outputBitLength;
mInputValueCount = (int)Math.pow(2.0, mInputBitLength);
mOutputValueCount = (int)Math.pow(2.0, mOutputBitLength);
}
/**
* Creates a node that represents an input value and the actual/transmitted output value.
*
* Note: this method must be implemented by a sub-class since the node requires a specific state transition
* table/matrix implementation.
*
* @param inputValue for the specific trellis node
* @param transmittedOutputValue the actual transmitted output value
* @return node for decoding
*/
protected abstract Node createNode(int inputValue, int transmittedOutputValue);
/**
* Creates a flushing or final node that represents an actual/transmitted output value and a flushing input value.
*
* The final flushing input value is implementation-specific, therefore this will be implemented in any subclass
* of this decoder. A normal flushing input value is zero.
*
* @param transmittedOutputValue the actual transmitted output value
* @return flushing node
*/
protected abstract Node createFlushingNode(int transmittedOutputValue);
/**
* Creates a starting node that represents a starting state and notional transmitted output value.
*
* The starting input value is implementation-specific, therefore this will be implemented in any subclass
* of this decoder. A normal starting input value is zero.
*
* @return starting node
*/
protected abstract Node createStartingNode();
/**
* Size in bits of the input and state values (e.g. 3/4 rate input size is 3)
*
* @return input bit size
*/
public int getInputBitLength()
{
return mInputBitLength;
}
/**
* Count of input or state values where the set of values is in the range of 0 <> count - 1
*
* @return input or state values count
*/
public int getInputValueCount()
{
return mInputValueCount;
}
/**
* Size in bits of the output constellation or transmitted symbol value (e.g. 3/4 rate output size is 4)
*
* @return output size in bits
*/
public int getOutputBitLength()
{
return mOutputBitLength;
}
/**
* Count of input or state values where the set of values is in the range of 0 <> count - 1
*
* @return input or state values count
*/
public int getOutputValueCount()
{
return mOutputValueCount;
}
/**
* Decodes the TCM encoded transmitted output values and returns a path the represents the most likely transmitted
* sequence of nodes.
*
* @param transmittedOutputValues from the encoded message
* @return most likely path representing the transmitted values
*/
public Path decode(int[] transmittedOutputValues)
{
Collection<Path> survivingPaths = new ArrayList<>();
survivingPaths.add(new Path(createStartingNode()));
//Add all but the last transmitted value to the starting path, creating a set of survivors at each iteration.
for(int x = 0; x < transmittedOutputValues.length - 1; x++)
{
survivingPaths = add(survivingPaths, transmittedOutputValues[x]);
}
//Flush the survivors with the final transmitted value and return the lone surviving path
return flush(survivingPaths, transmittedOutputValues[transmittedOutputValues.length - 1]);
}
/**
* Creates a set of new nodes representing all possible input values and the transmitted output value argument
* and iteratively adds the set of nodes to a copy of each of the surviving paths. A set of surviving paths is
* returned that represents each path that terminates at a distinct input/state value that has the least error
* value.
*
* @param survivingPaths from the previous time instant to evaluate for the next time instant in the trellis
* using the transmitted output value.
*
* @param transmittedOutputValue that will be decoded for this time instant.
*
* @return paths that have survived (ie have the lowest error value) after adding nodes for the transmitted
* output value
*
*/
public Collection<Path> add(Collection<Path> survivingPaths, int transmittedOutputValue)
{
Map<Integer,Path> survivingPathMap = new HashMap<>();
for(int x = 0; x < getInputValueCount(); x++)
{
//Create a single/unique node for each possible input value combined with the transmitted output value
Node node = createNode(x, transmittedOutputValue);
//Evaluate each surviving path by creating a copy and adding the node to it and see if it survives
for(Path path: survivingPaths)
{
Path pathToEvaluate = path.copyOf();
pathToEvaluate.add(node);
if(survivingPathMap.containsKey(node.getInputValue()))
{
Path survivingPath = survivingPathMap.get(node.getInputValue());
//Replace the current survivor path if this evaluate path has a lower error value
if(pathToEvaluate.getError() < survivingPath.getError())
{
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
}
}
else
{
//No survivor yet -- set this evaluate path as the first survivor
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
}
}
}
return survivingPathMap.values();
}
/**
* Flushes each of the surviving paths with a final/flushing node using the transmitted symbol. A final flushing
* input value or state is implementation-specific, but normally uses a value of zero. Since all paths will be
* evaluated against each other as terminating at the same state, only the path with the lowest error value will
* be returned from this method.
*
* @param survivingPaths to flush with the final flushing node
* @param transmittedOutputValue to use for the final flushing node
*
* @return the single survivor path that remains after adding the flushing node to each of the candidate argument
* survivor paths.
*/
public Path flush(Collection<Path> survivingPaths, int transmittedOutputValue)
{
Node flushingNode = createFlushingNode(transmittedOutputValue);
Path bestPath = null;
for(Path survivingPath: survivingPaths)
{
survivingPath.add(flushingNode);
if(bestPath == null || (survivingPath.getError() < bestPath.getError()))
{
bestPath = survivingPath;
}
}
return bestPath;
}
}

View File

@@ -0,0 +1,159 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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 edac.trellis;
import bits.BinaryMessage;
import bits.CorrectedBinaryMessage;
import java.util.List;
public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
{
/**
* Viterbi decoder for Digital Mobile Radio (DMR) 3/4 rate Trellis Coded Modulation (TCM) encoded messages.
*/
public ViterbiDecoder_3_4_DMR()
{
super(3,4);
}
/**
* Decodes a 3/4 rate trellis coded modulation (TCM) encoded DMR binary message containing 196 bits that have
* already been deinterleaved.
*
* @param encodedMessage to decode that has already been deinterleaved.
* @return decoded message
*/
public BinaryMessage decode(BinaryMessage encodedMessage)
{
int[] symbols = getSymbols(encodedMessage);
Path mostLikelyPath = decode(symbols);
return getMessage(mostLikelyPath);
}
/**
* Extracts the decoded/corrected message from the surviving path argument.
*
* @param path to extract a message from
* @return corrected binary message.
*/
private static CorrectedBinaryMessage getMessage(Path path)
{
List<Node> mNodes = path.getNodes();
//Each node contains an input value excluding the starting(0) and final flushing(0) nodes.
CorrectedBinaryMessage message = new CorrectedBinaryMessage((mNodes.size() - 2) * 3);
if(mNodes.size() > 2)
{
for(int x = 1; x < mNodes.size() - 1; x++)
{
int inputValue = mNodes.get(x).getInputValue();
int messageOffset = 3 * (x - 1);
if((inputValue & 4) == 4)
{
message.set(messageOffset);
}
if((inputValue & 2) == 2)
{
message.set(messageOffset + 1);
}
if((inputValue & 1) == 1)
{
message.set(messageOffset + 2);
}
}
}
//Transfer the corrected error count to the message
message.setCorrectedBitCount(path.getError());
return message;
}
/**
* Extracts (49) four-bit symbols (196 bits / 4) from the deinterleaved DMR binary message
*
* @param encodedMessage that deinterleaved and contains 196-bits of 3/4 TCM encoding
* @return symbols
*/
public int[] getSymbols(BinaryMessage encodedMessage)
{
//Ensure we have an integral number of transmitted symbols (nibbles) in the message
if(encodedMessage.size() % getOutputBitLength() != 0)
{
throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" +
" message size: " + encodedMessage.size());
}
int[] symbols = new int[encodedMessage.size() / getOutputBitLength()];
int index = 0;
int pointer = 0;
while(index < encodedMessage.size())
{
int symbol = encodedMessage.getInt(index, index + (getOutputBitLength() - 1));
symbols[pointer++] = symbol;
index += getOutputBitLength();
}
return symbols;
}
/**
* Creates a DMR node
* @param inputValue for the specific trellis node
* @param transmittedOutputValue the actual transmitted output value
* @return created node
*/
@Override
protected Node createNode(int inputValue, int transmittedOutputValue)
{
return new DMRNode(inputValue, transmittedOutputValue);
}
/**
* Creates a DMR flushing node. DMR uses an input value of zero to flush the final output value out of the
* encoder.
*
* @param transmittedOutputValue the actual transmitted output value
* @return
*/
@Override
protected Node createFlushingNode(int transmittedOutputValue)
{
return new DMRNode(0, transmittedOutputValue);
}
/**
* Creates a DMR starting node. DMR uses a starting input value of zero for the initial node.
*/
@Override
protected Node createStartingNode()
{
return new DMRNode(0,0);
}
}

View File

@@ -151,41 +151,4 @@ public class RadioReferenceService
return mClient.getUserFeedBroadcasts(mUserAuthorization); return mClient.getUserFeedBroadcasts(mUserAuthorization);
} }
public static void main(String[] args)
{
try
{
RadioReferenceService service = new RadioReferenceService("dsheirer", "1dodgeram");
Long expiration = service.getAccountExpirationDate();
mLog.debug("Expiration Date:" + (expiration != null ? new Date(expiration).toString() : "unknown"));
mLog.debug("Is Active: " + service.isAccountActive());
UserFeedBroadcast[] feeds = service.getUserFeedBroadcasts();
for(UserFeedBroadcast feed: feeds)
{
mLog.debug("Feed ID: " + feed.getFeedId() + " Desc:" + feed.getDescr());
}
}
catch(AxisFault af)
{
mLog.debug("Hey, I caught an axis fault!");
mLog.debug("Reason:" + af.getFaultReason());
}
catch(RemoteException re)
{
re.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
mLog.debug("Finished!");
}
} }