mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Compare commits
8 Commits
v1.0.0-bet
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a53f6e9d99 | ||
|
|
e7f4f655e5 | ||
|
|
000ac51ff0 | ||
|
|
23cee388de | ||
|
|
5c73fb318f | ||
|
|
066554393f | ||
|
|
303c0a98cc | ||
|
|
02c62d012f |
240
build.gradle
240
build.gradle
@@ -95,35 +95,34 @@ test {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
// JUnit Tests
|
// JUnit Tests
|
||||||
testImplementation(platform("org.junit:junit-bom:6.0.1"))
|
testImplementation(platform("org.junit:junit-bom:6.1.3"))
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:6.0.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:6.0.1")
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||||
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:6.0.1")
|
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
|
||||||
//Jitpack imports
|
//Jitpack imports
|
||||||
implementation 'com.github.dnault:libresample4j:master'
|
implementation 'com.github.dnault:libresample4j:master'
|
||||||
implementation 'com.github.DSheirer.jmbe:api:v2.1.0'
|
implementation 'com.github.DSheirer.jmbe:api:v2.1.1'
|
||||||
implementation 'com.github.nwaldispuehl:java-lame:v3.98.4'
|
implementation 'com.github.nwaldispuehl:java-lame:v3.98.4'
|
||||||
|
|
||||||
//mavenCentral/jcenter imports
|
//mavenCentral/jcenter imports
|
||||||
implementation 'ch.qos.logback:logback-classic:1.6.3'
|
implementation 'ch.qos.logback:logback-classic:1.6.6'
|
||||||
implementation 'ch.qos.logback:logback-core:1.5.38'
|
implementation 'ch.qos.logback:logback-core:1.6.6'
|
||||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.22.4'
|
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.23.0'
|
||||||
implementation 'com.fazecast:jSerialComm:2.11.4'
|
implementation 'com.formdev:flatlaf:3.8'
|
||||||
implementation 'com.formdev:flatlaf:3.7.2'
|
implementation 'com.formdev:flatlaf-intellij-themes:3.8' //Not imported - Theme.java loads these themes by class name
|
||||||
implementation 'com.formdev:flatlaf-intellij-themes:3.7.2'
|
|
||||||
implementation 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1'
|
implementation 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1'
|
||||||
implementation 'com.github.jiconfont:jiconfont-javafx:1.0.0'
|
implementation 'com.github.jiconfont:jiconfont-javafx:1.0.0'
|
||||||
implementation 'com.github.jiconfont:jiconfont-swing:1.0.1'
|
implementation 'com.github.jiconfont:jiconfont-swing:1.0.1'
|
||||||
implementation 'com.github.wendykierp:JTransforms:3.2'
|
implementation('com.github.wendykierp:JTransforms:3.2') {
|
||||||
implementation 'com.google.code.gson:gson:2.14.0'
|
exclude group: 'junit' //JLargeArrays 1.7 declares junit as a compile dependency
|
||||||
implementation 'com.google.guava:guava:33.6.0-jre'
|
}
|
||||||
|
implementation 'com.google.code.gson:gson:2.15.0'
|
||||||
|
implementation 'com.google.guava:guava:33.8.0-jre'
|
||||||
implementation 'com.jidesoft:jide-oss:3.6.18'
|
implementation 'com.jidesoft:jide-oss:3.6.18'
|
||||||
implementation 'com.miglayout:miglayout-swing:11.4.3'
|
implementation 'com.miglayout:miglayout-swing:11.5.2'
|
||||||
implementation 'com.mpatric:mp3agic:0.9.1'
|
implementation 'com.mpatric:mp3agic:0.9.1'
|
||||||
implementation 'commons-io:commons-io:2.22.0'
|
implementation 'commons-io:commons-io:2.22.0'
|
||||||
implementation 'eu.hansolo:charts:1.0.5'
|
|
||||||
implementation 'io.github.dsheirer:jairspy:1.0.12.1' //Airspy tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jairspy:1.0.12.1' //Airspy tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jairspyhf:1.8.1.1' //Airspy HF+ tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jairspyhf:1.8.1.1' //Airspy HF+ tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jbladerf:2.6.0.1' //bladeRF tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jbladerf:2.6.0.1' //bladeRF tuners - GitHub release, see settings.gradle
|
||||||
@@ -131,20 +130,18 @@ dependencies {
|
|||||||
implementation 'io.github.dsheirer:jlibusb:1.0.30.1' //libusb and USB hotplug for the tuner libraries - GitHub release; not pulled in transitively
|
implementation 'io.github.dsheirer:jlibusb:1.0.30.1' //libusb and USB hotplug for the tuner libraries - GitHub release; not pulled in transitively
|
||||||
implementation 'io.github.dsheirer:jhydrasdr:1.1.4.1' //HydraSDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jhydrasdr:1.1.4.1' //HydraSDR tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jlimesdr:25.1.0.2' //LimeSDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jlimesdr:25.1.0.2' //LimeSDR tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jrtl:1.4.0.2' //RTL-SDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jrtl:1.4.0.3' //RTL-SDR tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jsdrplay:3.15.3' //SDRplay RSP tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jsdrplay:3.15.3' //SDRplay RSP tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
||||||
implementation 'net.coderazzi:tablefilter-swing:5.5.4'
|
implementation 'org.apache.commons:commons-compress:1.28.0' //Not used directly - overrides the vulnerable 1.21 version from jarchivelib
|
||||||
implementation 'org.apache.commons:commons-compress:1.28.0'
|
implementation 'org.apache.commons:commons-lang3:3.21.0'
|
||||||
implementation 'org.apache.commons:commons-lang3:3.20.0'
|
|
||||||
implementation 'org.apache.commons:commons-math3:3.6.1'
|
implementation 'org.apache.commons:commons-math3:3.6.1'
|
||||||
implementation 'org.apache.commons:commons-csv:1.14.1'
|
implementation 'org.apache.commons:commons-csv:1.14.1'
|
||||||
implementation 'org.apache.mina:mina-core:2.2.9'
|
implementation 'org.apache.mina:mina-core:2.2.10'
|
||||||
implementation 'org.apache.mina:mina-http:2.2.9'
|
implementation 'org.apache.mina:mina-http:2.2.10'
|
||||||
implementation 'org.controlsfx:controlsfx:11.2.3'
|
implementation 'org.controlsfx:controlsfx:11.2.6'
|
||||||
implementation 'org.rauschig:jarchivelib:1.2.0'
|
implementation 'org.rauschig:jarchivelib:1.2.0'
|
||||||
implementation 'org.slf4j:slf4j-api:2.0.17'
|
implementation 'org.slf4j:slf4j-api:2.0.20'
|
||||||
implementation 'pl.edu.icm:JLargeArrays:1.6'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def os = DefaultNativePlatform.currentOperatingSystem
|
def os = DefaultNativePlatform.currentOperatingSystem
|
||||||
@@ -235,18 +232,6 @@ String targetOsxX86_64 = 'osx-x86_64-v' + version
|
|||||||
String targetWindowsX86_64 = 'windows-x86_64-v' + version
|
String targetWindowsX86_64 = 'windows-x86_64-v' + version
|
||||||
String targetWindowsAarch64 = 'windows-aarch64-v' + version
|
String targetWindowsAarch64 = 'windows-aarch64-v' + version
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional pre-downloaded Java Development Kit (JDK) for each target OS and CPU. These locations are hard-coded to the
|
|
||||||
* author's development environment, but can be changed to your environment if you want to use this build pattern.
|
|
||||||
*/
|
|
||||||
String jdk_base = '/home/denny/java_jdks/'
|
|
||||||
String jdk_linux_aarch64 = jdk_base + 'linux-arm64/jdk-25.0.1-full'
|
|
||||||
String jdk_linux_x86_64 = jdk_base + 'linux-x64/jdk-25.0.1-full'
|
|
||||||
String jdk_osx_x86_64 = jdk_base + 'osx-x64/jdk-25.0.1-full.jdk'
|
|
||||||
String jdk_osx_aarch64 = jdk_base + 'osx-arm64/jdk-25.0.1-full.jdk'
|
|
||||||
String jdk_windows_x86_64 = jdk_base + 'windows-x64/jdk-25.0.1-full'
|
|
||||||
String jdk_windows_aarch64 = jdk_base + 'windows-arm64/jdk-25.0.1-full'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download URLs to download the JDK as part of the gradle build packaging process
|
* Download URLs to download the JDK as part of the gradle build packaging process
|
||||||
*/
|
*/
|
||||||
@@ -275,19 +260,138 @@ def configure(RuntimeZipTask rt, List<String> jvmArgs) {
|
|||||||
//jdk.incubator.vector - needed for Project Panama foreign function and vector apis
|
//jdk.incubator.vector - needed for Project Panama foreign function and vector apis
|
||||||
//jdk.accessibility is used with assistive technologies like screen readers
|
//jdk.accessibility is used with assistive technologies like screen readers
|
||||||
//java.management for JVM resource monitoring
|
//java.management for JVM resource monitoring
|
||||||
rt.extension.addModules('jdk.charsets', 'jdk.crypto.ec', 'jdk.incubator.vector', 'jdk.accessibility', 'java.management')
|
//java.base, java.logging, java.net.http, java.prefs, javafx.controls, javafx.swing - required by sdrtrunk (jdeps)
|
||||||
|
rt.extension.addModules('jdk.charsets', 'jdk.crypto.ec', 'jdk.incubator.vector', 'jdk.accessibility', 'java.management',
|
||||||
|
'java.base', 'java.logging', 'java.net.http', 'java.prefs', 'javafx.controls', 'javafx.swing')
|
||||||
|
|
||||||
//Use auto-detected modules and 'add' any specified modules.
|
//Use only the modules listed above. The runtime plugin's module auto-detection uses a bundled ASM library that
|
||||||
rt.extension.additive.set(true)
|
//can't read Java 27 class files and logs a 'Failed to scan' error for each sdrtrunk class. The jdeps module check
|
||||||
|
//on the runtime task verifies that this list is complete.
|
||||||
|
rt.extension.additive.set(false)
|
||||||
|
|
||||||
//Recommended options for reducing/shrinking runtime target image size
|
//Recommended options for reducing/shrinking runtime target image size
|
||||||
rt.extension.addOptions('--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages')
|
rt.extension.addOptions('--strip-debug', '--compress', 'zip-6', '--no-header-files', '--no-man-pages')
|
||||||
|
|
||||||
//Runtime images have auto appended version in image zip name. Override to include version in single target image.
|
//Runtime images have auto appended version in image zip name. Override to include version in single target image.
|
||||||
// rt.extension.imageDir=layout.buildDirectory.dir('image')
|
// rt.extension.imageDir=layout.buildDirectory.dir('image')
|
||||||
rt.extension.imageZip.set(file(layout.buildDirectory.dir('image').get().file('sdr-trunk.zip')))
|
rt.extension.imageZip.set(file(layout.buildDirectory.dir('image').get().file('sdr-trunk.zip')))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrites the jar file without the native library folders for other operating systems and architectures. Fails the
|
||||||
|
* build if a jar that contains native libraries doesn't contain the folder for the target.
|
||||||
|
* @param jar file to rewrite
|
||||||
|
* @param keep native folder for the target
|
||||||
|
* @return number of bytes removed from the jar
|
||||||
|
*/
|
||||||
|
long stripJar(File jar, String keep) {
|
||||||
|
def tunerPattern = ~/^[^\/]+\/native\/([^\/]+)\/.*/
|
||||||
|
def removed = [] as Set
|
||||||
|
def hadTuner = false, keptTuner = false
|
||||||
|
File temp = new File(jar.parentFile, jar.name + '.tmp')
|
||||||
|
|
||||||
|
new java.util.zip.ZipFile(jar).withCloseable { zip ->
|
||||||
|
def entries = zip.entries().toList()
|
||||||
|
|
||||||
|
entries.each { e ->
|
||||||
|
def tuner = tunerPattern.matcher(e.name)
|
||||||
|
|
||||||
|
if(tuner.matches()) {
|
||||||
|
hadTuner = true
|
||||||
|
if(tuner.group(1) == keep) { keptTuner = true } else { removed << e.name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hadTuner && !keptTuner) {
|
||||||
|
throw new GradleException("Native strip: ${jar.name} has no native/${keep} folder")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!removed.isEmpty()) {
|
||||||
|
new java.util.zip.ZipOutputStream(new FileOutputStream(temp)).withCloseable { out ->
|
||||||
|
entries.findAll { !removed.contains(it.name) }.each { e ->
|
||||||
|
def copy = new java.util.zip.ZipEntry(e)
|
||||||
|
if(copy.method == java.util.zip.ZipEntry.DEFLATED) {
|
||||||
|
copy.compressedSize = -1
|
||||||
|
}
|
||||||
|
out.putNextEntry(copy)
|
||||||
|
zip.getInputStream(e).withCloseable { out << it }
|
||||||
|
out.closeEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(removed.isEmpty()) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
long before = jar.length()
|
||||||
|
java.nio.file.Files.move(temp.toPath(), jar.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
return before - jar.length()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the native libraries for other operating systems and architectures from the jars in each runtime target
|
||||||
|
* image, before the runtimeZip tasks package the images. The tuner libraries (jRTL, jAirspy, etc.) package their
|
||||||
|
* native libraries under '<library>/native/<os>-<arch>/', where the folder name matches the runtime target name
|
||||||
|
* '<os>-<arch>-v<version>' without the version suffix, except that the libraries use 'macos' where the target uses 'osx'.
|
||||||
|
*/
|
||||||
|
tasks.named('runtime') {rt ->
|
||||||
|
rt.doLast {
|
||||||
|
rt.targetPlatforms.get().keySet().each { target ->
|
||||||
|
def nativeFolder = (target - "-v${version}").replaceFirst(/^osx-/, 'macos-')
|
||||||
|
def lib = new File(rt.imageDir.asFile, "${project.name}-${target}/lib")
|
||||||
|
long total = 0
|
||||||
|
lib.listFiles({ File f -> f.name.endsWith('.jar') } as FileFilter).each { jar ->
|
||||||
|
total += stripJar(jar, nativeFolder)
|
||||||
|
}
|
||||||
|
logger.lifecycle("Native strip: removed ${String.format('%.1f', total / 1048576)} MB of other " +
|
||||||
|
"platform native libraries from [${target}]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that each runtime target image contains every module that the sdrtrunk jar requires, as reported by jdeps.
|
||||||
|
* This guards against the runtime plugin's module auto-detection silently missing modules (e.g. when it can't parse
|
||||||
|
* class files compiled for a newer Java version).
|
||||||
|
*/
|
||||||
|
def jdepsHome = javaToolchains.launcherFor(java.toolchain).map { it.metadata.installationPath.asFile }
|
||||||
|
def mainJar = tasks.named('jar').flatMap { it.archiveFile }
|
||||||
|
|
||||||
|
tasks.named('runtime') {rt ->
|
||||||
|
rt.doLast {
|
||||||
|
def jdeps = new File(jdepsHome.get(), 'bin/' + (os.isWindows() ? 'jdeps.exe' : 'jdeps')).absolutePath
|
||||||
|
def process = new ProcessBuilder(jdeps, '--multi-release', java.toolchain.languageVersion.get().toString(),
|
||||||
|
'--ignore-missing-deps', '--print-module-deps', '-q', mainJar.get().asFile.absolutePath)
|
||||||
|
.redirectErrorStream(true).start()
|
||||||
|
def output = process.inputStream.text.trim()
|
||||||
|
|
||||||
|
if(process.waitFor() != 0) {
|
||||||
|
throw new GradleException("Module check: jdeps failed: ${output}")
|
||||||
|
}
|
||||||
|
|
||||||
|
def required = output.split(',')*.trim().findAll { it } as Set
|
||||||
|
|
||||||
|
rt.targetPlatforms.get().keySet().each { target ->
|
||||||
|
def release = new File(rt.imageDir.asFile, "${project.name}-${target}/release")
|
||||||
|
def match = release.text =~ /(?m)^MODULES="([^"]*)"/
|
||||||
|
|
||||||
|
if(!match.find()) {
|
||||||
|
throw new GradleException("Module check: no MODULES entry in ${release}")
|
||||||
|
}
|
||||||
|
|
||||||
|
def missing = required - (match.group(1).split(' ') as Set)
|
||||||
|
|
||||||
|
if(missing) {
|
||||||
|
throw new GradleException("Module check: runtime image [${target}] is missing required modules " +
|
||||||
|
"${missing.sort()} - add them to addModules() in build.gradle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.lifecycle("Module check: runtime images contain all modules required by sdrtrunk: ${required.sort()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a release package for the current operating system.
|
* Creates a release package for the current operating system.
|
||||||
*
|
*
|
||||||
@@ -344,22 +448,12 @@ tasks.register('runtimeZipCurrent', RuntimeZipTask) {rt ->
|
|||||||
*/
|
*/
|
||||||
tasks.register('runtimeZipWindows', RuntimeZipTask) {rt ->
|
tasks.register('runtimeZipWindows', RuntimeZipTask) {rt ->
|
||||||
//Windows aarch64
|
//Windows aarch64
|
||||||
if(file(jdk_windows_aarch64).exists()) {
|
rt.extension.targetPlatform(targetWindowsAarch64) {
|
||||||
rt.extension.targetPlatform(targetWindowsAarch64, jdk_windows_aarch64, [])
|
jdkHome = jdkDownload(jdk_download_windows_aarch64)
|
||||||
}
|
|
||||||
else {
|
|
||||||
rt.extension.targetPlatform(targetWindowsAarch64) {
|
|
||||||
jdkHome = jdkDownload(jdk_download_windows_aarch64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//Windows x86-64
|
//Windows x86-64
|
||||||
if(file(jdk_windows_x86_64).exists()) {
|
rt.extension.targetPlatform(targetWindowsX86_64) {
|
||||||
rt.extension.targetPlatform(targetWindowsX86_64, jdk_windows_x86_64, [])
|
jdkHome = jdkDownload(jdk_download_windows_x86_64)
|
||||||
}
|
|
||||||
else {
|
|
||||||
rt.extension.targetPlatform(targetWindowsX86_64) {
|
|
||||||
jdkHome = jdkDownload(jdk_download_windows_x86_64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(rt, jvmArgsWindows)
|
configure(rt, jvmArgsWindows)
|
||||||
@@ -376,40 +470,20 @@ tasks.register('runtimeZipWindows', RuntimeZipTask) {rt ->
|
|||||||
*/
|
*/
|
||||||
tasks.register('runtimeZipOthers', RuntimeZipTask) {rt ->
|
tasks.register('runtimeZipOthers', RuntimeZipTask) {rt ->
|
||||||
//Linux aarch64
|
//Linux aarch64
|
||||||
if(file(jdk_linux_aarch64).exists()) {
|
rt.extension.targetPlatform(targetLinuxAarch64) {
|
||||||
rt.extension.targetPlatform(targetLinuxAarch64, jdk_linux_aarch64, [])
|
jdkHome = jdkDownload(jdk_download_linux_aarch64)
|
||||||
}
|
|
||||||
else {
|
|
||||||
rt.extension.targetPlatform(targetLinuxAarch64) {
|
|
||||||
jdkHome = jdkDownload(jdk_download_linux_aarch64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//Linux x86-64
|
//Linux x86-64
|
||||||
if(file(jdk_linux_x86_64).exists()) {
|
rt.extension.targetPlatform(targetLinuxX86_64) {
|
||||||
rt.extension.targetPlatform(targetLinuxX86_64, jdk_linux_x86_64, [])
|
jdkHome = jdkDownload(jdk_download_linux_x86_64)
|
||||||
}
|
|
||||||
else {
|
|
||||||
rt.extension.targetPlatform(targetLinuxX86_64) {
|
|
||||||
jdkHome = jdkDownload(jdk_download_linux_x86_64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//OSX aarch64
|
//OSX aarch64
|
||||||
if(file(jdk_osx_aarch64).exists()) {
|
rt.extension.targetPlatform(targetOsxAarch64) {
|
||||||
rt.extension.targetPlatform(targetOsxAarch64, jdk_osx_aarch64, [])
|
jdkHome = jdkDownload(jdk_download_osx_aarch64)
|
||||||
}
|
|
||||||
else {
|
|
||||||
rt.extension.targetPlatform(targetOsxAarch64) {
|
|
||||||
jdkHome = jdkDownload(jdk_download_osx_aarch64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//OSX x86-64
|
//OSX x86-64
|
||||||
if(file(jdk_osx_x86_64).exists()) {
|
rt.extension.targetPlatform(targetOsxX86_64) {
|
||||||
rt.extension.targetPlatform(targetOsxX86_64, jdk_osx_x86_64, [])
|
jdkHome = jdkDownload(jdk_download_osx_x86_64)
|
||||||
}
|
|
||||||
else {
|
|
||||||
rt.extension.targetPlatform(targetOsxX86_64) {
|
|
||||||
jdkHome = jdkDownload(jdk_download_osx_x86_64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(rt, jvmArgsLinux)
|
configure(rt, jvmArgsLinux)
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.9.0-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
retries=0
|
retries=0
|
||||||
retryBackOffMs=500
|
retryBackOffMs=500
|
||||||
|
|||||||
52
gradlew.bat
vendored
52
gradlew.bat
vendored
@@ -26,6 +26,33 @@
|
|||||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||||
setlocal EnableExtensions
|
setlocal EnableExtensions
|
||||||
|
|
||||||
|
@rem Catch executions from older scripts and ensure they exit cleanly.
|
||||||
|
@rem This can be removed once we can be reasonably confident that few people
|
||||||
|
@rem will be migrating directly to this new wrapper.
|
||||||
|
goto afterSafetyNet
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||||
|
goto exitWithErrorLevel
|
||||||
|
:afterSafetyNet
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
@rem This is normally unused
|
@rem This is normally unused
|
||||||
@@ -45,13 +72,14 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
1>&2 echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo. 1>&2
|
1>&2 echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
1>&2 echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
1>&2 echo location of your Java installation.
|
||||||
|
|
||||||
"%COMSPEC%" /c exit 1
|
"%COMSPEC%" /c exit 1
|
||||||
|
goto exitWithErrorLevel
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
@@ -59,13 +87,14 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
1>&2 echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo. 1>&2
|
1>&2 echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
1>&2 echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
1>&2 echo location of your Java installation.
|
||||||
|
|
||||||
"%COMSPEC%" /c exit 1
|
"%COMSPEC%" /c exit 1
|
||||||
|
goto exitWithErrorLevel
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
@@ -75,8 +104,9 @@ echo location of your Java installation. 1>&2
|
|||||||
@rem Execute gradlew
|
@rem Execute gradlew
|
||||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||||
@rem which allows us to clear the local environment before executing the java command
|
@rem which allows us to clear the local environment before executing the java command
|
||||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel
|
||||||
|
|
||||||
|
@rem This label must not be changed. We rely on old scripts being able to jump to this point.
|
||||||
:exitWithErrorLevel
|
:exitWithErrorLevel
|
||||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||||
|
|||||||
1023
sdr-trunk.ipr
1023
sdr-trunk.ipr
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url = 'https://jitpack.io' }
|
||||||
|
|
||||||
//jAirspy is published as a GitHub release, with the native libraries for all platforms in the jar
|
//jAirspy is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||||
ivy {
|
ivy {
|
||||||
|
|||||||
@@ -1,483 +0,0 @@
|
|||||||
/*
|
|
||||||
* *****************************************************************************
|
|
||||||
* Copyright (C) 2014-2022 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.audio.convert.thumbdv;
|
|
||||||
|
|
||||||
import com.fazecast.jSerialComm.SerialPort;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.AmbeMessage;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.AmbeMessageFactory;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.VocoderRate;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.request.AmbeRequest;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.request.DecodeSpeechRequest;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.request.EncodeSpeechRequest;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.request.ResetRequest;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.request.SetVocoderParametersRequest;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.request.SetVocoderRequest;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.DecodeSpeechResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.ReadyResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetVocoderParameterResponse;
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.LinkedTransferQueue;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Northwest Digital Radio (NWDR) ThumbDv dongle.
|
|
||||||
*
|
|
||||||
* Note: linux users must allow access to the serial port:
|
|
||||||
*
|
|
||||||
* sudo usermod -a -G uucp username
|
|
||||||
* sudo usermod -a -G dialout username
|
|
||||||
* sudo usermod -a -G lock username
|
|
||||||
* sudo usermod -a -G tty username
|
|
||||||
*/
|
|
||||||
public class ThumbDv implements AutoCloseable
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(ThumbDv.class);
|
|
||||||
private static final String PORT_DESCRIPTION = "USB-to-Serial Port (ftdi_sio)";
|
|
||||||
private static final String PORT_DESCRIPTION_FRAGMENT = "USB Serial Port";
|
|
||||||
private static final int BAUD_RATE = 460800;
|
|
||||||
private static final byte PACKET_START = (byte) 0x61;
|
|
||||||
|
|
||||||
public enum AudioProtocol
|
|
||||||
{
|
|
||||||
DMR,
|
|
||||||
DSTAR,
|
|
||||||
NXDN,
|
|
||||||
P25_PHASE2,
|
|
||||||
}
|
|
||||||
|
|
||||||
private SerialPort mSerialPort;
|
|
||||||
private ScheduledFuture mSerialPortReaderHandle;
|
|
||||||
private ScheduledFuture mAudioDecodeProcessorHandle;
|
|
||||||
private LinkedTransferQueue<DecodeSpeechRequest> mDecodeSpeechRequests = new LinkedTransferQueue<>();
|
|
||||||
private AudioProtocol mAudioProtocol;
|
|
||||||
private Listener<float[]> mAudioBufferListener;
|
|
||||||
private boolean mStarted;
|
|
||||||
|
|
||||||
public ThumbDv(AudioProtocol audioProtocol, Listener<float[]> listener)
|
|
||||||
{
|
|
||||||
mAudioProtocol = audioProtocol;
|
|
||||||
mAudioBufferListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enqueues the audio code frame for decoding. Decoded PCM speech packet will be sent to the registered
|
|
||||||
* audio packet listener.
|
|
||||||
*
|
|
||||||
* @param codecFrame
|
|
||||||
*/
|
|
||||||
public void decode(byte[] codecFrame)
|
|
||||||
{
|
|
||||||
if(!mStarted || mSerialPort == null)
|
|
||||||
{
|
|
||||||
throw new IllegalStateException("Must invoke start() method and thumbdv serial device must be available");
|
|
||||||
}
|
|
||||||
|
|
||||||
mDecodeSpeechRequests.offer(new DecodeSpeechRequest(codecFrame));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException
|
|
||||||
{
|
|
||||||
if(mSerialPortReaderHandle != null)
|
|
||||||
{
|
|
||||||
mSerialPortReaderHandle.cancel(true);
|
|
||||||
mSerialPortReaderHandle = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mSerialPort != null)
|
|
||||||
{
|
|
||||||
mSerialPort.closePort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() throws IOException
|
|
||||||
{
|
|
||||||
if(mSerialPort == null)
|
|
||||||
{
|
|
||||||
SerialPort[] ports = SerialPort.getCommPorts();
|
|
||||||
|
|
||||||
for(SerialPort port : ports)
|
|
||||||
{
|
|
||||||
mLog.debug("Serial Port Name:" + port.getDescriptivePortName());
|
|
||||||
if(port.getDescriptivePortName().contentEquals(PORT_DESCRIPTION) ||
|
|
||||||
port.getDescriptivePortName().contains(PORT_DESCRIPTION_FRAGMENT))
|
|
||||||
{
|
|
||||||
mSerialPort = port;
|
|
||||||
mSerialPort.setBaudRate(BAUD_RATE);
|
|
||||||
mSerialPort.openPort(0, 5000, 10000);
|
|
||||||
|
|
||||||
if(mSerialPort.isOpen())
|
|
||||||
{
|
|
||||||
mLog.info("Resetting ThumbDv Device");
|
|
||||||
send(new ResetRequest());
|
|
||||||
|
|
||||||
mLog.info("Creating Serial Port Reader");
|
|
||||||
final Runnable r = new SerialPortReader(mSerialPort.getInputStream());
|
|
||||||
mLog.info("Starting Serial Port Reader");
|
|
||||||
mSerialPortReaderHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(r, 0,
|
|
||||||
5, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
mStarted = true;
|
|
||||||
mLog.info("Startup complete - awaiting reset response");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.warn("Could not open serial port: " + mSerialPort.getSystemPortName());
|
|
||||||
throw new IOException("Could not open serial port:" + mSerialPort.getSystemPortName());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mSerialPort == null)
|
|
||||||
{
|
|
||||||
throw new IOException("ThumbDV serial port not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(byte[] message) throws IOException
|
|
||||||
{
|
|
||||||
if(mSerialPort == null)
|
|
||||||
{
|
|
||||||
throw new IOException("ThumbDv must be started before use");
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesWritten = mSerialPort.writeBytes(message, message.length);
|
|
||||||
|
|
||||||
if(bytesWritten < 0)
|
|
||||||
{
|
|
||||||
throw new IOException("Unable to write message:" + Arrays.toString(message));
|
|
||||||
}
|
|
||||||
else if(bytesWritten != message.length)
|
|
||||||
{
|
|
||||||
throw new IOException("Unable to write message:" + Arrays.toString(message) + " Bytes Written:" + bytesWritten);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the AMBE request message
|
|
||||||
*
|
|
||||||
* @param request message
|
|
||||||
* @throws IOException if there is an error
|
|
||||||
*/
|
|
||||||
public void send(AmbeRequest request) throws IOException
|
|
||||||
{
|
|
||||||
send(request.getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void receive(byte[] bytes)
|
|
||||||
{
|
|
||||||
AmbeMessage message = AmbeMessageFactory.getMessage(bytes);
|
|
||||||
|
|
||||||
if(message instanceof DecodeSpeechResponse && mAudioBufferListener != null)
|
|
||||||
{
|
|
||||||
float[] samples = ((DecodeSpeechResponse)message).getSamples();
|
|
||||||
mAudioBufferListener.receive(samples);
|
|
||||||
}
|
|
||||||
else if(message instanceof ReadyResponse && mAudioDecodeProcessorHandle == null)
|
|
||||||
{
|
|
||||||
if(mAudioDecodeProcessorHandle == null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch(mAudioProtocol)
|
|
||||||
{
|
|
||||||
case DSTAR:
|
|
||||||
send(new SetVocoderRequest(VocoderRate.RATE_33));
|
|
||||||
send(new SetVocoderParametersRequest(0x0130, 0x0763, 0x4000, 0x0000, 0x0000, 0x0048));
|
|
||||||
break;
|
|
||||||
case DMR:
|
|
||||||
case NXDN:
|
|
||||||
case P25_PHASE2:
|
|
||||||
send(new SetVocoderRequest(VocoderRate.RATE_33));
|
|
||||||
send(new SetVocoderParametersRequest(0x0431, 0x0754, 0x2400, 0x0000, 0x0000, 0x6F48));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException("Unrecognized audio protocol:" + mAudioProtocol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
mLog.error("Error setting audio protocol vocoder parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
mLog.info("ThumbDv Reset Complete");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.info("ThumbDv Reset Detected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(message instanceof SetVocoderParameterResponse)
|
|
||||||
{
|
|
||||||
if(((SetVocoderParameterResponse)message).isSuccessful())
|
|
||||||
{
|
|
||||||
mLog.info("Audio vocoder parameters configured for " + mAudioProtocol);
|
|
||||||
//Start the audio frame decode processor
|
|
||||||
mAudioDecodeProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioDecodeProcessor(), 0,
|
|
||||||
10, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(message != null)
|
|
||||||
{
|
|
||||||
mLog.debug("RECEIVED:" + message.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs the current settings of the serial port. Note: you must invoke start() before this method so that
|
|
||||||
* the serial port can be discovered and opened.
|
|
||||||
*/
|
|
||||||
public void logSerialPort()
|
|
||||||
{
|
|
||||||
if(mSerialPort != null)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("\nPort:\t\t\t").append(mSerialPort.getSystemPortName()).append("\n");
|
|
||||||
sb.append("Name:\t\t\t").append(mSerialPort.getDescriptivePortName()).append("\n");
|
|
||||||
sb.append("Baud Rate:\t\t").append(mSerialPort.getBaudRate()).append("\n");
|
|
||||||
sb.append("Data Bits:\t\t").append(mSerialPort.getNumDataBits()).append("\n");
|
|
||||||
sb.append("Parity Bits:\t").append(mSerialPort.getParity()).append("\n");
|
|
||||||
sb.append("Stop Bits:\t\t").append(mSerialPort.getNumStopBits()).append("\n");
|
|
||||||
sb.append("Flow Control:\t").append(mSerialPort.getFlowControlSettings()).append("\n");
|
|
||||||
sb.append("Is Open:\t\t").append(mSerialPort.isOpen()).append("\n");
|
|
||||||
|
|
||||||
mLog.info(sb.toString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.info("No serial port found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes the audio frame decode queue
|
|
||||||
*/
|
|
||||||
public class AudioDecodeProcessor implements Runnable
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DecodeSpeechRequest request = mDecodeSpeechRequests.poll();
|
|
||||||
|
|
||||||
if(request != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
send(request);
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
mLog.error("Error decoding audio frame", ioe);
|
|
||||||
}
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// //Force a 20ms sleep after submitting an audio frame to avoid overflowing the thumbdv
|
|
||||||
// Thread.sleep(20);
|
|
||||||
// }
|
|
||||||
// catch(InterruptedException ie)
|
|
||||||
// {
|
|
||||||
// //Do nothing
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Throwable t)
|
|
||||||
{
|
|
||||||
mLog.error("Error", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerialPortReader implements Runnable
|
|
||||||
{
|
|
||||||
private InputStream mInputStream;
|
|
||||||
|
|
||||||
public SerialPortReader(InputStream inputStream)
|
|
||||||
{
|
|
||||||
mInputStream = inputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(mInputStream != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while(mInputStream.available() > 0)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[400];
|
|
||||||
|
|
||||||
int bytesRead = mInputStream.readNBytes(buffer, 0, 1);
|
|
||||||
|
|
||||||
if(bytesRead == 1 && buffer[0] == PACKET_START)
|
|
||||||
{
|
|
||||||
while(mInputStream.available() < 2)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch(InterruptedException ie)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRead = mInputStream.readNBytes(buffer, 1, 2);
|
|
||||||
|
|
||||||
if(bytesRead == 2)
|
|
||||||
{
|
|
||||||
int length = (0xFF & buffer[1]) << 8;
|
|
||||||
length += (0xFF & buffer[2]);
|
|
||||||
|
|
||||||
if(0 < length && length < 400)
|
|
||||||
{
|
|
||||||
length++; //Add a byte for the type byte
|
|
||||||
|
|
||||||
while(mInputStream.available() < length)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(1);
|
|
||||||
}
|
|
||||||
catch(InterruptedException ie)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesRead = mInputStream.readNBytes(buffer, 3, length);
|
|
||||||
|
|
||||||
if(bytesRead == length)
|
|
||||||
{
|
|
||||||
receive(Arrays.copyOf(buffer, length + 3));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.debug("Expected [" + length + "] but received [" + bytesRead + "] bytes - " + Arrays.toString(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.error("Received packet with unexpected length: " + length);
|
|
||||||
//Don't process the buffer ... let the reader read and inspect 1 byte at a time
|
|
||||||
//to regain sync on the packet start byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.debug("Expected [2] but received [" + bytesRead + "] -- this shouldn't happen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.debug("Unrecognized byte: " + Arrays.toString(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
mLog.error("Error while reading ThumbDv serial port", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Throwable t)
|
|
||||||
{
|
|
||||||
mLog.error("Error", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
{
|
|
||||||
mLog.debug("Starting");
|
|
||||||
|
|
||||||
String silence = "BEDDEA821EFD660C08";
|
|
||||||
|
|
||||||
String[] frames = {"0E46122323067C60F8", "0E469433C1067CF1BC", "0E46122B23067C60F8", "0E67162BE08874E2B4",
|
|
||||||
"0E46163BE1067CF1BC", "0E46122B23067C60F8", "0A06163BE00A5C303E", "0E46122B23067C60F8", "0E46163BE1847CE1FC",
|
|
||||||
"0E46122B23067C60F8"};
|
|
||||||
|
|
||||||
List<byte[]> frameData = new ArrayList<>();
|
|
||||||
|
|
||||||
for(String frame : frames)
|
|
||||||
{
|
|
||||||
byte[] bytes = new byte[frame.length() / 2];
|
|
||||||
for(int x = 0; x < frame.length(); x += 2)
|
|
||||||
{
|
|
||||||
String hex = frame.substring(x, x + 2);
|
|
||||||
bytes[x / 2] = (byte) (0xFF & Integer.parseInt(hex, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
frameData.add(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
mLog.debug("Starting thumb dv thread(s)");
|
|
||||||
|
|
||||||
final Listener<float[]> listener = packet -> {
|
|
||||||
mLog.info("Got an audio packet!");
|
|
||||||
};
|
|
||||||
|
|
||||||
try(ThumbDv thumbDv = new ThumbDv(AudioProtocol.P25_PHASE2, listener))
|
|
||||||
{
|
|
||||||
thumbDv.start();
|
|
||||||
|
|
||||||
Thread.sleep(6000);
|
|
||||||
|
|
||||||
for(int x = 0; x < 20; x++)
|
|
||||||
{
|
|
||||||
thumbDv.send(new EncodeSpeechRequest(new short[160]));
|
|
||||||
Thread.sleep(20);
|
|
||||||
}
|
|
||||||
// for(byte[] frame : frameData)
|
|
||||||
// {
|
|
||||||
// thumbDv.decode(frame);
|
|
||||||
// }
|
|
||||||
|
|
||||||
while(true);
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
mLog.error("Error", ioe);
|
|
||||||
}
|
|
||||||
catch(InterruptedException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMBE-3000R Message
|
|
||||||
*/
|
|
||||||
public abstract class AmbeMessage
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Converts the byte array to a hexadecimal string array
|
|
||||||
*/
|
|
||||||
public static String toHex(byte[] data)
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
for(byte b: data)
|
|
||||||
{
|
|
||||||
sb.append(String.format("%02X", b));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.DecodeSpeechResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.EncodeSpeechResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.GetConfigResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.InitializeCodecResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.ProductIdResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.ReadyResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetChannelFormatResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetChannelResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetPacketModeResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetSpeechFormatResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetVocoderParameterResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.SetVocoderResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.UnknownResponse;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.VersionResponse;
|
|
||||||
|
|
||||||
public class AmbeMessageFactory
|
|
||||||
{
|
|
||||||
private static final byte CONTROL_PACKET = (byte)0x00;
|
|
||||||
private static final byte CHANNEL_PACKET = (byte)0x01;
|
|
||||||
private static final byte SPEECH_PACKET = (byte)0x02;
|
|
||||||
private static final int INDEX_PACKET_TYPE = 3;
|
|
||||||
private static final int INDEX_CONTROL_PACKET_TYPE = 4;
|
|
||||||
|
|
||||||
|
|
||||||
public static AmbeMessage getMessage(byte[] data)
|
|
||||||
{
|
|
||||||
if(data != null && data.length >= 5)
|
|
||||||
{
|
|
||||||
if(data[INDEX_PACKET_TYPE] == CONTROL_PACKET)
|
|
||||||
{
|
|
||||||
PacketField packetField = PacketField.fromValue(data[INDEX_CONTROL_PACKET_TYPE]);
|
|
||||||
|
|
||||||
switch(packetField)
|
|
||||||
{
|
|
||||||
case PKT_CHANNEL_0:
|
|
||||||
return new SetChannelResponse(data);
|
|
||||||
case PKT_CHANNEL_FORMAT:
|
|
||||||
return new SetChannelFormatResponse(data);
|
|
||||||
case PKT_CODEC_STOP:
|
|
||||||
return new SetPacketModeResponse(data);
|
|
||||||
case PKT_GET_CONFIG:
|
|
||||||
return new GetConfigResponse(data);
|
|
||||||
case PKT_INIT:
|
|
||||||
return new InitializeCodecResponse(data);
|
|
||||||
case PKT_PRODUCT_ID:
|
|
||||||
return new ProductIdResponse(data);
|
|
||||||
case PKT_RATE_PARAMETER:
|
|
||||||
return new SetVocoderParameterResponse(data);
|
|
||||||
case PKT_RATE_TABLE:
|
|
||||||
return new SetVocoderResponse(data);
|
|
||||||
case PKT_READY:
|
|
||||||
return new ReadyResponse(data);
|
|
||||||
case PKT_SPEECH_FORMAT:
|
|
||||||
return new SetSpeechFormatResponse(data);
|
|
||||||
case PKT_VERSION_STRING:
|
|
||||||
return new VersionResponse(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(data[INDEX_PACKET_TYPE] == CHANNEL_PACKET)
|
|
||||||
{
|
|
||||||
return new EncodeSpeechResponse(data);
|
|
||||||
}
|
|
||||||
else if(data[INDEX_PACKET_TYPE] == SPEECH_PACKET)
|
|
||||||
{
|
|
||||||
return new DecodeSpeechResponse(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UnknownResponse(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMBE-3000 Initialization Options
|
|
||||||
*/
|
|
||||||
public enum InitializeOption
|
|
||||||
{
|
|
||||||
ENCODER((byte)0x01),
|
|
||||||
DECODER((byte)0x02),
|
|
||||||
ENCODER_AND_DECODER((byte)0x03),
|
|
||||||
ECHO_CANCELLER((byte)0x04),
|
|
||||||
ENCODER_DECODER_ECHO_CANCELLER((byte)0x07);
|
|
||||||
|
|
||||||
private byte mCode;
|
|
||||||
|
|
||||||
InitializeOption(byte code)
|
|
||||||
{
|
|
||||||
mCode = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getCode()
|
|
||||||
{
|
|
||||||
return mCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
/*
|
|
||||||
* *****************************************************************************
|
|
||||||
* Copyright (C) 2014-2022 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.audio.convert.thumbdv.message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMBE-3000R Control Packet Field enumeration
|
|
||||||
*/
|
|
||||||
public enum PacketField
|
|
||||||
{
|
|
||||||
PKT_ENCODER_CMODE((byte)0x05),
|
|
||||||
PKT_DECODER_CMODE((byte)0x06),
|
|
||||||
PKT_RATE_TABLE((byte)0x09),
|
|
||||||
PKT_RATE_PARAMETER((byte)0x0A),
|
|
||||||
PKT_INIT((byte)0x0B),
|
|
||||||
PKT_LOW_POWER((byte)0x10),
|
|
||||||
PKT_CHANNEL_FORMAT((byte)0x15),
|
|
||||||
PKT_SPEECH_FORMAT((byte)0x16),
|
|
||||||
PKT_CODEC_START((byte)0x2A),
|
|
||||||
PKT_CODEC_STOP((byte)0x2B),
|
|
||||||
PKT_PRODUCT_ID((byte)0x30),
|
|
||||||
PKT_VERSION_STRING((byte)0x31),
|
|
||||||
PKT_COMPAND((byte)0x32),
|
|
||||||
PKT_RESET((byte)0x33),
|
|
||||||
PKT_RESET_TO_SOFTWARE_CONFIG((byte)0x34),
|
|
||||||
PKT_HALT((byte)0x35),
|
|
||||||
PKT_GET_CONFIG((byte)0x36),
|
|
||||||
PKT_READ_CONFIG((byte)0x37),
|
|
||||||
PKT_CODEC_CFG((byte)0x38),
|
|
||||||
PKT_READY((byte)0x39),
|
|
||||||
PKT_PARITY_MODE((byte)0x3F),
|
|
||||||
PKT_CHANNEL_0((byte)0x40),
|
|
||||||
PKT_WRITE_I2C((byte)0x44),
|
|
||||||
PKT_CLEAR_CODEC_RESET((byte)0x46),
|
|
||||||
PKT_SET_CODEC_RESET((byte)0x47),
|
|
||||||
PKT_DISCARD_CODEC_SAMPLES((byte)0x48),
|
|
||||||
PKT_DELAY_NUMBER_MICRO_SECONDS((byte)0x49),
|
|
||||||
PKT_DELAY_NUMBER_NANO_SECONDS((byte)0x4A),
|
|
||||||
PKT_GAIN((byte)0x4B),
|
|
||||||
PKT_RTS_THRESHOLD((byte)0x4E),
|
|
||||||
|
|
||||||
//Speech packet values
|
|
||||||
SPEECH_DATA((byte)0x00),
|
|
||||||
CHANNEL_DATA_HARD_SYMBOL((byte)0x01),
|
|
||||||
CMODE((byte)0x02),
|
|
||||||
SAMPLE_COUNT((byte)0x03),
|
|
||||||
TONE((byte)0x08),
|
|
||||||
CHANNEL_DATA_SOFT_SYMBOL((byte)0x17),
|
|
||||||
VOCODER((byte)0x40),
|
|
||||||
|
|
||||||
//Packet Types
|
|
||||||
PACKET_TYPE_CONTROL((byte)0x00),
|
|
||||||
PACKET_TYPE_ENCODE_SPEECH((byte)0x01),
|
|
||||||
PACKET_TYPE_DECODE_SPEECH((byte)0x02),
|
|
||||||
|
|
||||||
UNKNOWN((byte)0x00);
|
|
||||||
|
|
||||||
private byte mCode;
|
|
||||||
|
|
||||||
PacketField(byte code)
|
|
||||||
{
|
|
||||||
mCode = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packet type byte code value
|
|
||||||
*/
|
|
||||||
public byte getCode()
|
|
||||||
{
|
|
||||||
return mCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup a packet type from the code byte value
|
|
||||||
*/
|
|
||||||
public static PacketField fromValue(byte value)
|
|
||||||
{
|
|
||||||
switch(value)
|
|
||||||
{
|
|
||||||
case (byte)0x05:
|
|
||||||
return PKT_ENCODER_CMODE;
|
|
||||||
case (byte)0x06:
|
|
||||||
return PKT_DECODER_CMODE;
|
|
||||||
case (byte)0x09:
|
|
||||||
return PKT_RATE_TABLE;
|
|
||||||
case (byte)0x0A:
|
|
||||||
return PKT_RATE_PARAMETER;
|
|
||||||
case (byte)0x0B:
|
|
||||||
return PKT_INIT;
|
|
||||||
case (byte)0x10:
|
|
||||||
return PKT_LOW_POWER;
|
|
||||||
case (byte)0x15:
|
|
||||||
return PKT_CHANNEL_FORMAT;
|
|
||||||
case (byte)0x16:
|
|
||||||
return PKT_SPEECH_FORMAT;
|
|
||||||
case (byte)0x2A:
|
|
||||||
return PKT_CODEC_START;
|
|
||||||
case (byte)0x2B:
|
|
||||||
return PKT_CODEC_STOP;
|
|
||||||
case (byte)0x30:
|
|
||||||
return PKT_PRODUCT_ID;
|
|
||||||
case (byte)0x31:
|
|
||||||
return PKT_VERSION_STRING;
|
|
||||||
case (byte)0x32:
|
|
||||||
return PKT_COMPAND;
|
|
||||||
case (byte)0x33:
|
|
||||||
return PKT_RESET;
|
|
||||||
case (byte)0x34:
|
|
||||||
return PKT_RESET_TO_SOFTWARE_CONFIG;
|
|
||||||
case (byte)0x35:
|
|
||||||
return PKT_HALT;
|
|
||||||
case (byte)0x36:
|
|
||||||
return PKT_GET_CONFIG;
|
|
||||||
case (byte)0x37:
|
|
||||||
return PKT_READ_CONFIG;
|
|
||||||
case (byte)0x38:
|
|
||||||
return PKT_CODEC_CFG;
|
|
||||||
case (byte)0x39:
|
|
||||||
return PKT_READY;
|
|
||||||
case (byte)0x3F:
|
|
||||||
return PKT_PARITY_MODE;
|
|
||||||
case (byte)0x40:
|
|
||||||
return PKT_CHANNEL_0;
|
|
||||||
case (byte)0x44:
|
|
||||||
return PKT_WRITE_I2C;
|
|
||||||
case (byte)0x46:
|
|
||||||
return PKT_CLEAR_CODEC_RESET;
|
|
||||||
case (byte)0x47:
|
|
||||||
return PKT_SET_CODEC_RESET;
|
|
||||||
case (byte)0x48:
|
|
||||||
return PKT_DISCARD_CODEC_SAMPLES;
|
|
||||||
case (byte)0x49:
|
|
||||||
return PKT_DELAY_NUMBER_MICRO_SECONDS;
|
|
||||||
case (byte)0x4A:
|
|
||||||
return PKT_DELAY_NUMBER_NANO_SECONDS;
|
|
||||||
case (byte)0x4B:
|
|
||||||
return PKT_GAIN;
|
|
||||||
case (byte)0x4E:
|
|
||||||
return PKT_RTS_THRESHOLD;
|
|
||||||
default:
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message;
|
|
||||||
|
|
||||||
public enum VocoderRate
|
|
||||||
{
|
|
||||||
//AMBE-1000 Standard Rates
|
|
||||||
RATE_0((byte)0x00, 2400,0),
|
|
||||||
RATE_1((byte)0x01, 3600,0),
|
|
||||||
RATE_2((byte)0x02, 3600,1200),
|
|
||||||
RATE_3((byte)0x03, 4800,0),
|
|
||||||
RATE_4((byte)0x04, 9600,0),
|
|
||||||
RATE_5((byte)0x05, 2350,50),
|
|
||||||
RATE_6((byte)0x06, 4850,4750),
|
|
||||||
RATE_7((byte)0x07, 4550,250),
|
|
||||||
RATE_8((byte)0x08, 3100,1700),
|
|
||||||
RATE_9((byte)0x09, 4400,2800),
|
|
||||||
RATE_10((byte)0x0A, 4150,2250),
|
|
||||||
RATE_11((byte)0x0B, 3350,250),
|
|
||||||
RATE_12((byte)0x0C, 7750,250),
|
|
||||||
RATE_13((byte)0x0D, 4650,3350),
|
|
||||||
RATE_14((byte)0x0E, 3750,250),
|
|
||||||
RATE_15((byte)0x0F, 4000,0),
|
|
||||||
|
|
||||||
//AMBE-2000 Standard Rates
|
|
||||||
RATE_16((byte)0x10, 3600,0),
|
|
||||||
RATE_17((byte)0x11, 4000,0),
|
|
||||||
RATE_18((byte)0x12, 4800,0),
|
|
||||||
RATE_19((byte)0x13, 6400,0),
|
|
||||||
RATE_20((byte)0x14, 8000,0),
|
|
||||||
RATE_21((byte)0x15, 9600,0),
|
|
||||||
RATE_22((byte)0x16, 2400,1600),
|
|
||||||
RATE_23((byte)0x17, 3600,1200),
|
|
||||||
RATE_24((byte)0x18, 4000,800),
|
|
||||||
RATE_25((byte)0x19, 2400,2400),
|
|
||||||
RATE_26((byte)0x1A, 4000,2400),
|
|
||||||
RATE_27((byte)0x1B, 4400,2800), //Is this compatible with APCO25 Phase 1?
|
|
||||||
RATE_28((byte)0x1C, 4000,4000),
|
|
||||||
RATE_29((byte)0x1D, 2400,7200),
|
|
||||||
RATE_30((byte)0x1E, 3600,6000),
|
|
||||||
RATE_31((byte)0x1F, 2000,0),
|
|
||||||
RATE_32((byte)0x20, 3600,2800),
|
|
||||||
|
|
||||||
//AMBE-3000 Standard Rates (AMBE+2)
|
|
||||||
RATE_33((byte)0x21, 2450,1150), //APCO25 HR (Phase 2), DMR, NXDN, DSTAR
|
|
||||||
RATE_34((byte)0x22, 2450,0),
|
|
||||||
RATE_35((byte)0x23, 2250,1150),
|
|
||||||
RATE_36((byte)0x24, 2250,0),
|
|
||||||
RATE_37((byte)0x25, 2400,0),
|
|
||||||
RATE_38((byte)0x26, 3000,0),
|
|
||||||
RATE_39((byte)0x27, 3600,0),
|
|
||||||
RATE_40((byte)0x28, 4000,0),
|
|
||||||
RATE_41((byte)0x29, 4400,0),
|
|
||||||
RATE_42((byte)0x2A, 4800,0),
|
|
||||||
RATE_43((byte)0x2B, 6400,0),
|
|
||||||
RATE_44((byte)0x2C, 7200,0),
|
|
||||||
RATE_45((byte)0x2D, 8000,0),
|
|
||||||
RATE_46((byte)0x2E, 9600,0),
|
|
||||||
RATE_47((byte)0x2F, 2450,250),
|
|
||||||
RATE_48((byte)0x30, 3350,250),
|
|
||||||
RATE_49((byte)0x31, 3750,250),
|
|
||||||
RATE_50((byte)0x32, 4550,250),
|
|
||||||
RATE_51((byte)0x33, 2450,1950),
|
|
||||||
RATE_52((byte)0x34, 2450,2350),
|
|
||||||
RATE_53((byte)0x35, 2450,3550),
|
|
||||||
RATE_54((byte)0x36, 2450,4750),
|
|
||||||
RATE_55((byte)0x37, 2600,1400),
|
|
||||||
RATE_56((byte)0x38, 3600,1200),
|
|
||||||
RATE_57((byte)0x39, 4000,800),
|
|
||||||
RATE_58((byte)0x3A, 4000,2400),
|
|
||||||
RATE_59((byte)0x3B, 4400,2800), //Is this compatible with APCO25 Phase 1?
|
|
||||||
RATE_60((byte)0x3C, 4000,4000),
|
|
||||||
RATE_61((byte)0x3D, 3600,6000);
|
|
||||||
|
|
||||||
private byte mCode;
|
|
||||||
private int mSpeechRate;
|
|
||||||
private int mFecRate;
|
|
||||||
|
|
||||||
VocoderRate(byte code, int speechRate, int fecRate)
|
|
||||||
{
|
|
||||||
mCode = code;
|
|
||||||
mSpeechRate = speechRate;
|
|
||||||
mFecRate = fecRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Byte code value for the vocoder rate
|
|
||||||
*/
|
|
||||||
public byte getCode()
|
|
||||||
{
|
|
||||||
return mCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Speech rate in bits per second (bps)
|
|
||||||
*/
|
|
||||||
public int getSpeechRate()
|
|
||||||
{
|
|
||||||
return mSpeechRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Forward Error Correction rate in bits per second (bps)
|
|
||||||
*/
|
|
||||||
public int getFecRate()
|
|
||||||
{
|
|
||||||
return mFecRate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.AmbeMessage;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMBE-3000R Request Packet
|
|
||||||
*/
|
|
||||||
public abstract class AmbeRequest extends AmbeMessage
|
|
||||||
{
|
|
||||||
private static final byte PACKET_START_BYTE = (byte)0x61;
|
|
||||||
protected static final int PACKET_TYPE_INDEX = 3;
|
|
||||||
protected static final int PAYLOAD_START_INDEX = 4;
|
|
||||||
|
|
||||||
public abstract PacketField getType();
|
|
||||||
public abstract byte[] getData();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a byte array of the specified length plus 4 packet header bytes with packet start, length and control
|
|
||||||
* bytes filled in.
|
|
||||||
*
|
|
||||||
* @param length of the message (not including the 4 byte packet header)
|
|
||||||
* @return byte array with packet header pre-filled.
|
|
||||||
*/
|
|
||||||
protected byte[] createMessage(int length, PacketField type)
|
|
||||||
{
|
|
||||||
byte[] data = new byte[length + 4];
|
|
||||||
|
|
||||||
data[0] = PACKET_START_BYTE;
|
|
||||||
data[1] = (byte)((length >> 8 & 0xFF));
|
|
||||||
data[2] = (byte)(length & 0xFF);
|
|
||||||
|
|
||||||
if(type == PacketField.PACKET_TYPE_ENCODE_SPEECH || type == PacketField.PACKET_TYPE_DECODE_SPEECH)
|
|
||||||
{
|
|
||||||
data[PACKET_TYPE_INDEX] = type.getCode();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data[PACKET_TYPE_INDEX] = PacketField.PACKET_TYPE_CONTROL.getCode();
|
|
||||||
data[PAYLOAD_START_INDEX] = type.getCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.VocoderRate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode speech request, used to request decode of an encoded audio frame.
|
|
||||||
*/
|
|
||||||
public class DecodeSpeechRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private static final int CHANNEL_DATA_IDENTIFIER_INDEX = 4;
|
|
||||||
private static final byte SAMPLE_COUNT = (byte)(0xFF & 160); //8 kHz Audio sample count for 20ms frame
|
|
||||||
|
|
||||||
private byte[] mAudioFrame;
|
|
||||||
private VocoderRate mVocoderRate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an audio frame decode request using the specified vocoder rate.
|
|
||||||
* @param audioFrame of encoded audio samples
|
|
||||||
* @param vocoderRate to use when decoding
|
|
||||||
*/
|
|
||||||
public DecodeSpeechRequest(byte[] audioFrame, VocoderRate vocoderRate)
|
|
||||||
{
|
|
||||||
mAudioFrame = audioFrame;
|
|
||||||
mVocoderRate = vocoderRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an audio frame decode request using the current vocoder rate.
|
|
||||||
* @param audioFrame of encoded audio samples
|
|
||||||
*/
|
|
||||||
public DecodeSpeechRequest(byte[] audioFrame)
|
|
||||||
{
|
|
||||||
this(audioFrame, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PACKET_TYPE_ENCODE_SPEECH;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasVocoderRate()
|
|
||||||
{
|
|
||||||
return mVocoderRate != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
if(hasVocoderRate())
|
|
||||||
{
|
|
||||||
int length = mAudioFrame.length + 9;
|
|
||||||
|
|
||||||
byte[] data = createMessage(length, getType());
|
|
||||||
int offset = CHANNEL_DATA_IDENTIFIER_INDEX;
|
|
||||||
|
|
||||||
data[offset++] = PacketField.VOCODER.getCode();
|
|
||||||
data[offset++] = mVocoderRate.getCode();
|
|
||||||
|
|
||||||
data[offset++] = PacketField.CHANNEL_DATA_HARD_SYMBOL.getCode();
|
|
||||||
data[offset++] = (byte)(0xFF & (mAudioFrame.length * 8));
|
|
||||||
System.arraycopy(mAudioFrame, 0, data, offset, mAudioFrame.length);
|
|
||||||
offset += mAudioFrame.length;
|
|
||||||
data[offset++] = PacketField.SAMPLE_COUNT.getCode();
|
|
||||||
data[offset++] = (byte)0xA0;
|
|
||||||
data[offset++] = (byte)0x02;
|
|
||||||
data[offset++] = (byte)0x00;
|
|
||||||
data[offset] = (byte)0x00;
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int length = mAudioFrame.length + 2;
|
|
||||||
byte[] data = createMessage(length, getType());
|
|
||||||
|
|
||||||
int offset = CHANNEL_DATA_IDENTIFIER_INDEX;
|
|
||||||
data[offset++] = PacketField.CHANNEL_DATA_HARD_SYMBOL.getCode();
|
|
||||||
data[offset++] = (byte)(0xFF & (mAudioFrame.length * 8));
|
|
||||||
System.arraycopy(mAudioFrame, 0, data, offset, mAudioFrame.length);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
public class EncodeSpeechRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private static final int CHANNEL_IDENTIFIER_INDEX = 4;
|
|
||||||
private static final int SPEECH_DATA_IDENTIFIER_INDEX = 5;
|
|
||||||
private static final int SAMPLE_COUNT_INDEX = 6;
|
|
||||||
private static final int SPEECH_DATA_START_INDEX = 7;
|
|
||||||
|
|
||||||
private short[] mSamples;
|
|
||||||
|
|
||||||
public EncodeSpeechRequest(short[] samples)
|
|
||||||
{
|
|
||||||
mSamples = samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EncodeSpeechRequest(float[] samples)
|
|
||||||
{
|
|
||||||
mSamples = new short[samples.length];
|
|
||||||
|
|
||||||
for(int x = 0; x < samples.length; x++)
|
|
||||||
{
|
|
||||||
if(samples[x] > 1.0f)
|
|
||||||
{
|
|
||||||
mSamples[x] = Short.MAX_VALUE;
|
|
||||||
}
|
|
||||||
else if(samples[x] < -1.0f)
|
|
||||||
{
|
|
||||||
mSamples[x] = Short.MIN_VALUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mSamples[x] = (short)(samples[x] * Short.MAX_VALUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PACKET_TYPE_DECODE_SPEECH;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
int length = (mSamples.length * 2) + 3;
|
|
||||||
byte[] data = createMessage(length, getType());
|
|
||||||
|
|
||||||
data[CHANNEL_IDENTIFIER_INDEX] = PacketField.PKT_CHANNEL_0.getCode();
|
|
||||||
data[SPEECH_DATA_IDENTIFIER_INDEX] = (byte)0x00;
|
|
||||||
data[SAMPLE_COUNT_INDEX] = (byte)(0xFF & mSamples.length);
|
|
||||||
|
|
||||||
int pointer = SPEECH_DATA_START_INDEX;
|
|
||||||
|
|
||||||
for(short sample: mSamples)
|
|
||||||
{
|
|
||||||
data[pointer++] = (byte)((sample >> 8 & 0xFF));
|
|
||||||
data[pointer++] = (byte)(sample & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get configuration request packet
|
|
||||||
*/
|
|
||||||
public class GetConfigRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_GET_CONFIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
return createMessage(1, getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.InitializeOption;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize CODEC request packet
|
|
||||||
*/
|
|
||||||
public class InitializeCodecRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private static final int INITIALIZE_OPTION_INDEX = 5;
|
|
||||||
private InitializeOption mInitializeOption;
|
|
||||||
|
|
||||||
public InitializeCodecRequest(InitializeOption option)
|
|
||||||
{
|
|
||||||
mInitializeOption = option;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
byte[] data = createMessage(2, getType());
|
|
||||||
data[INITIALIZE_OPTION_INDEX] = mInitializeOption.getCode();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Product ID request packet
|
|
||||||
*/
|
|
||||||
public class ProductIdRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_PRODUCT_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
return createMessage(1, getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset request packet
|
|
||||||
*/
|
|
||||||
public class ResetRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_RESET;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
return createMessage(1, getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set channel format request packet
|
|
||||||
*/
|
|
||||||
public class SetChannelFormatRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private static final int FORMAT_INDEX = 5;
|
|
||||||
private static final byte FORMAT = (byte)0x00;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_CHANNEL_FORMAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
byte[] data = createMessage(2, getType());
|
|
||||||
data[FORMAT_INDEX] = FORMAT;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set channel request packet
|
|
||||||
*/
|
|
||||||
public class SetChannelRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_CHANNEL_0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
return createMessage(1, getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packet mode (versus CODEC mode) request packet
|
|
||||||
*/
|
|
||||||
public class SetPacketModeRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_CODEC_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
return createMessage(1, getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set speech format request packet
|
|
||||||
*/
|
|
||||||
public class SetSpeechFormatRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private static final int FORMAT_INDEX = 5;
|
|
||||||
private static final byte FORMAT = (byte)0x00;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_SPEECH_FORMAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
byte[] data = createMessage(2, getType());
|
|
||||||
data[FORMAT_INDEX] = FORMAT;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Vocoder parameters request packet
|
|
||||||
*/
|
|
||||||
public class SetVocoderParametersRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private int mWord0;
|
|
||||||
private int mWord1;
|
|
||||||
private int mWord2;
|
|
||||||
private int mWord3;
|
|
||||||
private int mWord4;
|
|
||||||
private int mWord5;
|
|
||||||
|
|
||||||
public SetVocoderParametersRequest(int word0, int word1, int word2, int word3, int word4, int word5)
|
|
||||||
{
|
|
||||||
mWord0 = word0;
|
|
||||||
mWord1 = word1;
|
|
||||||
mWord2 = word2;
|
|
||||||
mWord3 = word3;
|
|
||||||
mWord4 = word4;
|
|
||||||
mWord5 = word5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_RATE_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
byte[] data = createMessage(14, getType());
|
|
||||||
data[PAYLOAD_START_INDEX + 1] = (byte)(mWord0 >> 8 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 2] = (byte)(mWord0 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 3] = (byte)(mWord1 >> 8 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 4] = (byte)(mWord1 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 5] = (byte)(mWord2 >> 8 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 6] = (byte)(mWord2 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 7] = (byte)(mWord3 >> 8 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 8] = (byte)(mWord3 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 9] = (byte)(mWord4 >> 8 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 10] = (byte)(mWord4 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 11] = (byte)(mWord5 >> 8 & 0xFF);
|
|
||||||
data[PAYLOAD_START_INDEX + 12] = (byte)(mWord5 & 0xFF);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.VocoderRate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Vocoder Rate request packet
|
|
||||||
*/
|
|
||||||
public class SetVocoderRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
private VocoderRate mVocoderRate;
|
|
||||||
|
|
||||||
public SetVocoderRequest(VocoderRate rate)
|
|
||||||
{
|
|
||||||
mVocoderRate = rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_RATE_TABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
byte[] data = createMessage(2, getType());
|
|
||||||
data[PAYLOAD_START_INDEX + 1] = mVocoderRate.getCode();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.request;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version string request packet
|
|
||||||
*/
|
|
||||||
public class VersionRequest extends AmbeRequest
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_VERSION_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getData()
|
|
||||||
{
|
|
||||||
return createMessage(1, getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* *****************************************************************************
|
|
||||||
* Copyright (C) 2014-2022 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.AmbeMessage;
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMBE-3000R Response Packet
|
|
||||||
*/
|
|
||||||
public abstract class AmbeResponse extends AmbeMessage
|
|
||||||
{
|
|
||||||
protected static final int PAYLOAD_START_INDEX = 5;
|
|
||||||
private byte[] mMessage;
|
|
||||||
|
|
||||||
protected AmbeResponse(byte[] message)
|
|
||||||
{
|
|
||||||
mMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control packet type
|
|
||||||
*/
|
|
||||||
public abstract PacketField getType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Received message bytes
|
|
||||||
*/
|
|
||||||
protected byte[] getMessage()
|
|
||||||
{
|
|
||||||
return mMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Payload of the packet (does not include the packet header)
|
|
||||||
*/
|
|
||||||
protected byte[] getPayload()
|
|
||||||
{
|
|
||||||
return Arrays.copyOfRange(getMessage(), PAYLOAD_START_INDEX, getMessage().length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdr-trunk
|
|
||||||
* Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
import io.github.dsheirer.sample.ConversionUtils;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode speech response
|
|
||||||
*/
|
|
||||||
public class DecodeSpeechResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public DecodeSpeechResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PACKET_TYPE_DECODE_SPEECH;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Payload of the packet (does not include the packet header)
|
|
||||||
*/
|
|
||||||
protected byte[] getPayload()
|
|
||||||
{
|
|
||||||
return Arrays.copyOfRange(getMessage(), PAYLOAD_START_INDEX + 1, getMessage().length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float[] getSamples()
|
|
||||||
{
|
|
||||||
return ConversionUtils.convertFromSigned16BitSamples(getPayload());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "DECODED SPEECH: SAMPLE COUNT:" + getSamples().length + " " + toHex(getPayload());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode speech response
|
|
||||||
*/
|
|
||||||
public class EncodeSpeechResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public EncodeSpeechResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PACKET_TYPE_ENCODE_SPEECH;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Payload of the packet (does not include the packet header)
|
|
||||||
*/
|
|
||||||
protected byte[] getPayload()
|
|
||||||
{
|
|
||||||
return Arrays.copyOfRange(getMessage(), PAYLOAD_START_INDEX + 1, getMessage().length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "ENCODED SPEECH: " + toHex(getPayload()) + " " + Arrays.toString(getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get configuration response
|
|
||||||
*/
|
|
||||||
public class GetConfigResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public GetConfigResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_GET_CONFIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "CONFIGURATION: " + toHex(getPayload());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize CODEC response
|
|
||||||
*/
|
|
||||||
public class InitializeCodecResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public InitializeCodecResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success / fail
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "INITIALIZE CODEC " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Product ID Response
|
|
||||||
*/
|
|
||||||
public class ProductIdResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public ProductIdResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_PRODUCT_ID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Product ID string
|
|
||||||
*/
|
|
||||||
public String getProductId()
|
|
||||||
{
|
|
||||||
return new String(getPayload()).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "PRODUCT ID:" + getProductId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ready response
|
|
||||||
*/
|
|
||||||
public class ReadyResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public ReadyResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_READY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "READY" + Arrays.toString(getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset response
|
|
||||||
*/
|
|
||||||
public class ResetResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public ResetResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_RESET;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success indicator
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "RESET " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set channel format response
|
|
||||||
*/
|
|
||||||
public class SetChannelFormatResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public SetChannelFormatResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_CHANNEL_FORMAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success or fail
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "SET CHANNEL FORMAT " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Vocoder rate response
|
|
||||||
*/
|
|
||||||
public class SetChannelResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public SetChannelResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_CHANNEL_0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success indicator
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "SET CHANNEL 0 " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set packet mode response
|
|
||||||
*/
|
|
||||||
public class SetPacketModeResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public SetPacketModeResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_CODEC_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success or fail
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "SET PACKET MODE " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set speech format response
|
|
||||||
*/
|
|
||||||
public class SetSpeechFormatResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public SetSpeechFormatResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_SPEECH_FORMAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success or fail
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "SET SPEECH FORMAT " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Vocoder parameter response
|
|
||||||
*/
|
|
||||||
public class SetVocoderParameterResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public SetVocoderParameterResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_RATE_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success indicator
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
|
|
||||||
return payload != null && payload.length ==3 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
if(isSuccessful())
|
|
||||||
{
|
|
||||||
return "SET VOCODER RATE SUCCESSFUL";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return "SET VOCODER RATE **FAILED** - RESPONSE:" + toHex(getPayload());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Vocoder rate response
|
|
||||||
*/
|
|
||||||
public class SetVocoderResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public SetVocoderResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_RATE_TABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Success indicator
|
|
||||||
*/
|
|
||||||
public boolean isSuccessful()
|
|
||||||
{
|
|
||||||
byte[] payload = getPayload();
|
|
||||||
|
|
||||||
return payload != null && payload.length ==1 && payload[0] == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "SET VOCODER RATE " + (isSuccessful() ? "SUCCESSFUL" : "**FAILED**");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unknown Response Message
|
|
||||||
*/
|
|
||||||
public class UnknownResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public UnknownResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "UNKNOWN MESSAGE:" + Arrays.toString(getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 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.audio.convert.thumbdv.message.response;
|
|
||||||
|
|
||||||
import io.github.dsheirer.audio.convert.thumbdv.message.PacketField;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version Response
|
|
||||||
*/
|
|
||||||
public class VersionResponse extends AmbeResponse
|
|
||||||
{
|
|
||||||
public VersionResponse(byte[] message)
|
|
||||||
{
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PacketField getType()
|
|
||||||
{
|
|
||||||
return PacketField.PKT_VERSION_STRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version string
|
|
||||||
*/
|
|
||||||
public String getVersion()
|
|
||||||
{
|
|
||||||
return new String(getPayload()).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "VERSION:" + getVersion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -257,6 +257,20 @@ public abstract class C4FMSymbolProcessor
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum bit errors allowed in a detected sync pattern while acquiring (ie not synchronized), measured from the
|
||||||
|
* symbol decisions at the optimized symbol timing. Sync detections with more bit errors are rejected. Bit errors
|
||||||
|
* include symbol magnitude (ie +3 vs +1) errors, so this rejects noise where the symbol signs correlate with the
|
||||||
|
* sync pattern but the symbol magnitudes are inconsistent. Once synchronized, the limit isn't applied since the
|
||||||
|
* sync detection timing is known and rejecting a valid sync detection costs the burst. Default implementation
|
||||||
|
* imposes no limit.
|
||||||
|
* @return maximum sync pattern bit error count while acquiring.
|
||||||
|
*/
|
||||||
|
public int getSyncAcquisitionMaxBitErrors()
|
||||||
|
{
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of consecutive sync periods (capped at 3) where a sync was detected within the max message length.
|
* Number of consecutive sync periods (capped at 3) where a sync was detected within the max message length.
|
||||||
*/
|
*/
|
||||||
@@ -945,12 +959,14 @@ public abstract class C4FMSymbolProcessor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this correction has a valid NID or the optimization (or detection) score is high quality.
|
* Indicates if the optimization score is high quality and, while acquiring, the sync pattern bit errors don't
|
||||||
|
* exceed the max.
|
||||||
* @return true if valid.
|
* @return true if valid.
|
||||||
*/
|
*/
|
||||||
public boolean isValid()
|
public boolean isValid()
|
||||||
{
|
{
|
||||||
return mOptimizationScore > getSyncThresholdEqualized();
|
return mOptimizationScore > getSyncThresholdEqualized() &&
|
||||||
|
(mSynchronized || mCorrectedBitCount <= getSyncAcquisitionMaxBitErrors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1297,7 +1313,7 @@ public abstract class C4FMSymbolProcessor
|
|||||||
|
|
||||||
for(int x = 0; x < patternSymbols.length; x++)
|
for(int x = 0; x < patternSymbols.length; x++)
|
||||||
{
|
{
|
||||||
if(bufferPointer < maxPointer)
|
if(bufferPointer >= 0 && bufferPointer < maxPointer)
|
||||||
{
|
{
|
||||||
softSymbol = interpolate(mBuffer[bufferPointer], mBuffer[bufferPointer + 1], fractional) * gain + balance;
|
softSymbol = interpolate(mBuffer[bufferPointer], mBuffer[bufferPointer + 1], fractional) * gain + balance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,7 +298,10 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
{
|
{
|
||||||
if(message instanceof VoiceMessage voice)
|
if(message instanceof VoiceMessage voice)
|
||||||
{
|
{
|
||||||
processVoice(voice);
|
if(isValid(voice))
|
||||||
|
{
|
||||||
|
processVoice(voice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(message instanceof DataMessage data)
|
else if(message instanceof DataMessage data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ public class DMRSymbolProcessor extends C4FMSymbolProcessor
|
|||||||
private static final float SYNC_THRESHOLD_EQUALIZED = 70;
|
private static final float SYNC_THRESHOLD_EQUALIZED = 70;
|
||||||
//Once synchronized, sync detections are only allowed within this many symbols of a burst boundary.
|
//Once synchronized, sync detections are only allowed within this many symbols of a burst boundary.
|
||||||
private static final int SYNC_WINDOW_SYMBOLS = 2;
|
private static final int SYNC_WINDOW_SYMBOLS = 2;
|
||||||
|
//Maximum bit errors (of 48) in a detected sync pattern while acquiring. Testing: all false sync detections in
|
||||||
|
//600 seconds of noise occurred while acquiring, with 7+ (mostly 12+) bit errors, and a max of 8 eliminated them
|
||||||
|
//(245 bursts to 0) at a cost of < 0.6% valid data bursts on clean recordings and 0.3 - 5.5% on multipath
|
||||||
|
//recordings with injected noise, where valid link control improved. A max of 10 leaks noise false syncs and 6
|
||||||
|
//costs more multipath data bursts. Applying the max once synchronized cost 3 - 20% of valid bursts.
|
||||||
|
private static final int SYNC_ACQUISITION_MAX_BIT_ERRORS = 8;
|
||||||
private final DMRSyncModeMonitor mSyncModeMonitor = new DMRSyncModeMonitor();
|
private final DMRSyncModeMonitor mSyncModeMonitor = new DMRSyncModeMonitor();
|
||||||
private final DMRMessageFramer mMessageFramer;
|
private final DMRMessageFramer mMessageFramer;
|
||||||
|
|
||||||
@@ -142,6 +148,12 @@ public class DMRSymbolProcessor extends C4FMSymbolProcessor
|
|||||||
return Math.min(offset, BURST_DIBIT_LENGTH - offset) <= SYNC_WINDOW_SYMBOLS;
|
return Math.min(offset, BURST_DIBIT_LENGTH - offset) <= SYNC_WINDOW_SYMBOLS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSyncAcquisitionMaxBitErrors()
|
||||||
|
{
|
||||||
|
return SYNC_ACQUISITION_MAX_BIT_ERRORS;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDynamicBalanceTrackingRequired()
|
public boolean isDynamicBalanceTrackingRequired()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ public class DMRAudioModule extends AmbeAudioModule implements IdentifierUpdateP
|
|||||||
{
|
{
|
||||||
if(hasAudioCodec() && message.getTimeslot() == getTimeslot())
|
if(hasAudioCodec() && message.getTimeslot() == getTimeslot())
|
||||||
{
|
{
|
||||||
|
//Ignore voice messages where none of the AMBE voice frames pass error detection, so that invalid voice
|
||||||
|
//messages (ie false sync detections) don't produce audio or establish the encryption state.
|
||||||
|
if(message instanceof VoiceMessage voiceMessage && !voiceMessage.isValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Attempt to set the audio encryption state from certain types of messages
|
//Attempt to set the audio encryption state from certain types of messages
|
||||||
if(!mEncryptedCallStateEstablished)
|
if(!mEncryptedCallStateEstablished)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2026 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.module.decode.dmr.message.voice;
|
||||||
|
|
||||||
|
import io.github.dsheirer.bits.BinaryMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error detection for the three AMBE+2 (3600 x 2450) voice frames carried in a DMR voice burst.
|
||||||
|
*
|
||||||
|
* Each 72-bit voice frame carries an extended Golay(24,12) protected C0 vector and a Golay(23,12) protected C1 vector
|
||||||
|
* that is scrambled with a pseudo-random sequence seeded by the 12 data bits of C0. The C2 and C3 vectors are not
|
||||||
|
* protected. Golay(23,12) is a perfect code where every 23-bit word decodes to a codeword within 3 bit errors, so a
|
||||||
|
* frame is only considered valid when the combined C0 and C1 error count is low, which random (ie noise) bits rarely
|
||||||
|
* achieve.
|
||||||
|
*
|
||||||
|
* Vector bit indices and the C1 scrambling sequence are from the JMBE AMBEFrame decoder. Burst bit indices of each
|
||||||
|
* voice frame match VoiceMessage.getAMBEFrames(): frames 1 and 3 are contiguous and frame 2 is split around the 48-bit
|
||||||
|
* sync/embedded signalling field in the middle of the burst.
|
||||||
|
*/
|
||||||
|
public class AMBEFrameValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Maximum combined C0 and C1 bit errors for a valid voice frame. Random 47-bit sequences have a combined error
|
||||||
|
* count of 2 or less with a probability of ~1.3e-4.
|
||||||
|
*/
|
||||||
|
public static final int MAX_ERRORS = 2;
|
||||||
|
|
||||||
|
private static final int[] VECTOR_C0 = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 1, 5,
|
||||||
|
9, 13, 17, 21};
|
||||||
|
private static final int[] VECTOR_C1 = {25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 2, 6, 10, 14, 18, 22, 26,
|
||||||
|
30, 34, 38, 42};
|
||||||
|
private static final int[] FRAME_OFFSETS = {24, 96, 216};
|
||||||
|
private static final int GOLAY_23_GENERATOR = 0xC75;
|
||||||
|
private static final int[][] C0_INDICES = new int[3][VECTOR_C0.length];
|
||||||
|
private static final int[][] C1_INDICES = new int[3][VECTOR_C1.length];
|
||||||
|
//Minimum weight error pattern for each Golay(23,12) syndrome (perfect code: all syndromes have weight <= 3)
|
||||||
|
private static final int[] ERROR_PATTERNS = new int[2048];
|
||||||
|
//C1 descrambling sequence for each 12-bit C0 data (u0) seed value, MSB first
|
||||||
|
private static final int[] SCRAMBLE_SEQUENCES = new int[4096];
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
for(int frame = 0; frame < 3; frame++)
|
||||||
|
{
|
||||||
|
for(int x = 0; x < VECTOR_C0.length; x++)
|
||||||
|
{
|
||||||
|
C0_INDICES[frame][x] = getBurstIndex(frame, VECTOR_C0[x]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int x = 0; x < VECTOR_C1.length; x++)
|
||||||
|
{
|
||||||
|
C1_INDICES[frame][x] = getBurstIndex(frame, VECTOR_C1[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Golay(23,12) is perfect: the 2048 error patterns of weight 0 - 3 map one-to-one onto the 2048 syndromes.
|
||||||
|
for(int i = 0; i < 23; i++)
|
||||||
|
{
|
||||||
|
ERROR_PATTERNS[getSyndrome(1 << i)] = 1 << i;
|
||||||
|
|
||||||
|
for(int j = i + 1; j < 23; j++)
|
||||||
|
{
|
||||||
|
ERROR_PATTERNS[getSyndrome((1 << i) | (1 << j))] = (1 << i) | (1 << j);
|
||||||
|
|
||||||
|
for(int k = j + 1; k < 23; k++)
|
||||||
|
{
|
||||||
|
int pattern = (1 << i) | (1 << j) | (1 << k);
|
||||||
|
ERROR_PATTERNS[getSyndrome(pattern)] = pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int seed = 0; seed < 4096; seed++)
|
||||||
|
{
|
||||||
|
int pr = 16 * seed;
|
||||||
|
int sequence = 0;
|
||||||
|
|
||||||
|
for(int x = 0; x < 23; x++)
|
||||||
|
{
|
||||||
|
pr = (173 * pr + 13849) % 65536;
|
||||||
|
sequence = (sequence << 1) | (pr >= 32768 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRAMBLE_SEQUENCES[seed] = sequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private AMBEFrameValidator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Burst bit index for the voice frame bit index.
|
||||||
|
* @param frame index 0-2
|
||||||
|
* @param bit index 0-71 within the (interleaved) voice frame
|
||||||
|
* @return bit index within the 288-bit burst
|
||||||
|
*/
|
||||||
|
private static int getBurstIndex(int frame, int bit)
|
||||||
|
{
|
||||||
|
//Frame 2 is split around the 48-bit sync/embedded signalling field (burst bits 132 - 179)
|
||||||
|
if(frame == 1 && bit >= 36)
|
||||||
|
{
|
||||||
|
return FRAME_OFFSETS[frame] + bit + 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FRAME_OFFSETS[frame] + bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Golay(23,12) syndrome for the 23-bit word where the MSB (bit 22) is the first codeword bit.
|
||||||
|
*/
|
||||||
|
private static int getSyndrome(int word)
|
||||||
|
{
|
||||||
|
for(int bit = 22; bit >= 11; bit--)
|
||||||
|
{
|
||||||
|
if((word & (1 << bit)) != 0)
|
||||||
|
{
|
||||||
|
word ^= GOLAY_23_GENERATOR << (bit - 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the indexed bits from the message as an integer value, with the first index as the MSB.
|
||||||
|
*/
|
||||||
|
private static int getValue(BinaryMessage message, int[] indices)
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
for(int index : indices)
|
||||||
|
{
|
||||||
|
value = (value << 1) | (message.get(index) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combined C0 and C1 bit error count for the voice frame, where C0 is decoded first so that the corrected C0 data
|
||||||
|
* bits can seed the C1 descrambling sequence. C0 errors include the extended Golay parity bit, so an even weight
|
||||||
|
* C0 error pattern (ie 4 errors) that Golay(23,12) mis-corrects is counted as 4 errors.
|
||||||
|
* @param message containing the 288-bit voice burst
|
||||||
|
* @param frame index 0-2
|
||||||
|
* @return combined C0 and C1 bit error count.
|
||||||
|
*/
|
||||||
|
public static int getErrorCount(BinaryMessage message, int frame)
|
||||||
|
{
|
||||||
|
int c0 = getValue(message, C0_INDICES[frame]);
|
||||||
|
int golay = c0 >>> 1;
|
||||||
|
int pattern = ERROR_PATTERNS[getSyndrome(golay)];
|
||||||
|
int corrected = golay ^ pattern;
|
||||||
|
int errors = Integer.bitCount(pattern);
|
||||||
|
|
||||||
|
//Extended Golay(24,12) parity bit provides even parity across the 24-bit codeword
|
||||||
|
if(((Integer.bitCount(corrected) + (c0 & 1)) & 1) != 0)
|
||||||
|
{
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Short circuit when C0 alone exceeds the max error count.
|
||||||
|
if(errors > MAX_ERRORS)
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
int c1 = getValue(message, C1_INDICES[frame]) ^ SCRAMBLE_SEQUENCES[corrected >>> 11];
|
||||||
|
return errors + Integer.bitCount(ERROR_PATTERNS[getSyndrome(c1)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if at least one of the three voice frames in the burst is valid, checking each frame in turn until a
|
||||||
|
* valid frame is found.
|
||||||
|
* @param message containing the 288-bit voice burst
|
||||||
|
* @return true if at least one voice frame has a combined C0 and C1 error count within the max error count.
|
||||||
|
*/
|
||||||
|
public static boolean isValid(BinaryMessage message)
|
||||||
|
{
|
||||||
|
for(int frame = 0; frame < 3; frame++)
|
||||||
|
{
|
||||||
|
if(getErrorCount(message, frame) <= MAX_ERRORS)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,9 +40,10 @@ public abstract class VoiceMessage extends DMRBurst
|
|||||||
public static int[] FRAME_1_IV_FRAGMENT = new int[]{95, 91, 87, 83};
|
public static int[] FRAME_1_IV_FRAGMENT = new int[]{95, 91, 87, 83};
|
||||||
public static int[] FRAME_2_IV_FRAGMENT = new int[]{215, 211, 207, 203};
|
public static int[] FRAME_2_IV_FRAGMENT = new int[]{215, 211, 207, 203};
|
||||||
public static int[] FRAME_3_IV_FRAGMENT = new int[]{287, 283, 279, 275};
|
public static int[] FRAME_3_IV_FRAGMENT = new int[]{287, 283, 279, 275};
|
||||||
|
private Boolean mValid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DMR message frame. This message is comprised of a 24-bit prefix and a 264-bit message frame. Outbound base
|
* DMR message frame. This message comprises a 24-bit prefix and a 264-bit message frame. Outbound base
|
||||||
* station frames transmit a Common Announcement Channel (CACH) in the 24-bit prefix, whereas Mobile inbound frames
|
* station frames transmit a Common Announcement Channel (CACH) in the 24-bit prefix, whereas Mobile inbound frames
|
||||||
* do not use the 24-bit prefix.
|
* do not use the 24-bit prefix.
|
||||||
*
|
*
|
||||||
@@ -73,8 +74,8 @@ public abstract class VoiceMessage extends DMRBurst
|
|||||||
{
|
{
|
||||||
frame_2[i] = getMessage().getByte(96 + i * 8);
|
frame_2[i] = getMessage().getByte(96 + i * 8);
|
||||||
}
|
}
|
||||||
// 4 bits and 4 bits
|
// 4 bits and 4 bits. Note: mask the shifted (signed) byte to prevent sign extension into the upper 4 bits.
|
||||||
frame_2[4] = (byte)((getMessage().getByte(128) & 0xF0) | (getMessage().getByte(180) >> 4));
|
frame_2[4] = (byte)((getMessage().getByte(128) & 0xF0) | ((getMessage().getByte(180) >> 4) & 0x0F));
|
||||||
// copy last 4 byte
|
// copy last 4 byte
|
||||||
for(int i = 0; i < 4; i++)
|
for(int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
@@ -109,10 +110,20 @@ public abstract class VoiceMessage extends DMRBurst
|
|||||||
return fragments;
|
return fragments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the voice burst is valid, meaning that at least one of the three AMBE voice frames passes error
|
||||||
|
* detection (see AMBEFrameValidator). Frames are checked in turn until a valid frame is found and the result is
|
||||||
|
* cached.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid()
|
public boolean isValid()
|
||||||
{
|
{
|
||||||
return true;
|
if(mValid == null)
|
||||||
|
{
|
||||||
|
mValid = AMBEFrameValidator.isValid(getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2026 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.dsp.c4fm;
|
||||||
|
|
||||||
|
import io.github.dsheirer.module.decode.p25.phase1.P25P1SymbolProcessorC4FM;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JUnit tests for the sync correlation score in the C4FM symbol processor.
|
||||||
|
*/
|
||||||
|
public class C4FMSymbolProcessorScoreTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A lagging sync detect on the first symbol after a buffer reload, followed by a coarse timing search that steps
|
||||||
|
* all the way left, scores a candidate whose first sync symbol starts just before the start of the buffer.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void scoreBeforeBufferStart()
|
||||||
|
{
|
||||||
|
float samplesPerSymbol = 25000f / 4800f;
|
||||||
|
P25P1SymbolProcessorC4FM processor = new P25P1SymbolProcessorC4FM(null, null);
|
||||||
|
processor.setSamplesPerSymbol(samplesPerSymbol);
|
||||||
|
//Sync workspace, sized as in setSamplesPerSymbol(), plus a 1024 sample buffer
|
||||||
|
int syncWorkspaceLength = (int)Math.ceil((processor.mSyncDetector.getDibitLength() + 1) * samplesPerSymbol);
|
||||||
|
processor.mBuffer = new float[syncWorkspaceLength + 1024];
|
||||||
|
|
||||||
|
//Offset to the final sync symbol that puts the first sync symbol at buffer index -0.3
|
||||||
|
double offset = (processor.mSyncDetector.getDibitLength() - 1) * samplesPerSymbol - 0.3;
|
||||||
|
assertDoesNotThrow(() -> processor.score(offset, samplesPerSymbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2026 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.module.decode.dmr.message.voice;
|
||||||
|
|
||||||
|
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||||
|
import io.github.dsheirer.module.decode.dmr.sync.DMRSyncPattern;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for AMBE voice frame error detection.
|
||||||
|
*/
|
||||||
|
public class AMBEFrameValidatorTest
|
||||||
|
{
|
||||||
|
//Base station voice frame A burst from a recording where JMBE decodes all three voice frames with zero errors.
|
||||||
|
private static final String VOICE_BURST = "5A3AC4B9E881526173002A6BB9E881526755FD7DF75F7173002A6BB9E881526173002A6B";
|
||||||
|
//Burst bit indices of the first C0 bits (voice frame bits 0, 4, 8) and first C1 bit (voice frame bit 25) of
|
||||||
|
//frame 1, which starts at burst bit 24.
|
||||||
|
private static final int FRAME_1_C0_BIT_0 = 24;
|
||||||
|
private static final int FRAME_1_C0_BIT_1 = 28;
|
||||||
|
private static final int FRAME_1_C0_BIT_2 = 32;
|
||||||
|
private static final int FRAME_1_C1_BIT_0 = 49;
|
||||||
|
|
||||||
|
private static CorrectedBinaryMessage getBurst()
|
||||||
|
{
|
||||||
|
return CorrectedBinaryMessage.loadHex(VOICE_BURST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void errorFreeBurst()
|
||||||
|
{
|
||||||
|
CorrectedBinaryMessage burst = getBurst();
|
||||||
|
|
||||||
|
for(int frame = 0; frame < 3; frame++)
|
||||||
|
{
|
||||||
|
assertEquals(0, AMBEFrameValidator.getErrorCount(burst, frame), "Frame " + frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(AMBEFrameValidator.isValid(burst));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void errorsAreCounted()
|
||||||
|
{
|
||||||
|
CorrectedBinaryMessage burst = getBurst();
|
||||||
|
burst.flip(FRAME_1_C0_BIT_0);
|
||||||
|
assertEquals(1, AMBEFrameValidator.getErrorCount(burst, 0));
|
||||||
|
burst.flip(FRAME_1_C1_BIT_0);
|
||||||
|
assertEquals(2, AMBEFrameValidator.getErrorCount(burst, 0));
|
||||||
|
burst.flip(FRAME_1_C0_BIT_1);
|
||||||
|
assertEquals(3, AMBEFrameValidator.getErrorCount(burst, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fourC0ErrorsAreDetected()
|
||||||
|
{
|
||||||
|
CorrectedBinaryMessage burst = getBurst();
|
||||||
|
burst.flip(FRAME_1_C0_BIT_0);
|
||||||
|
burst.flip(FRAME_1_C0_BIT_1);
|
||||||
|
burst.flip(FRAME_1_C0_BIT_2);
|
||||||
|
burst.flip(FRAME_1_C0_BIT_2 + 4);
|
||||||
|
assertTrue(AMBEFrameValidator.getErrorCount(burst, 0) > AMBEFrameValidator.MAX_ERRORS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validWhenAnyFrameIsValid()
|
||||||
|
{
|
||||||
|
CorrectedBinaryMessage burst = getBurst();
|
||||||
|
|
||||||
|
//Corrupt the C0 vector of each frame in turn (4 bit errors), verifying a single valid frame is sufficient.
|
||||||
|
int[] frameOffsets = {24, 96, 216};
|
||||||
|
|
||||||
|
for(int frame = 0; frame < 3; frame++)
|
||||||
|
{
|
||||||
|
for(int bit = 0; bit < 16; bit += 4)
|
||||||
|
{
|
||||||
|
burst.flip(frameOffsets[frame] + bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(AMBEFrameValidator.getErrorCount(burst, frame) > AMBEFrameValidator.MAX_ERRORS);
|
||||||
|
assertEquals(frame < 2, AMBEFrameValidator.isValid(burst), "Corrupted frames: " + (frame + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void randomBurstsAreRarelyValid()
|
||||||
|
{
|
||||||
|
Random random = new Random(1234);
|
||||||
|
int valid = 0;
|
||||||
|
int trials = 20000;
|
||||||
|
|
||||||
|
for(int trial = 0; trial < trials; trial++)
|
||||||
|
{
|
||||||
|
CorrectedBinaryMessage burst = new CorrectedBinaryMessage(288);
|
||||||
|
|
||||||
|
for(int bit = 0; bit < 288; bit++)
|
||||||
|
{
|
||||||
|
if(random.nextBoolean())
|
||||||
|
{
|
||||||
|
burst.set(bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(AMBEFrameValidator.isValid(burst))
|
||||||
|
{
|
||||||
|
valid++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Expected false valid rate is ~3 x 1.3e-4 (ie ~8 of 20,000)
|
||||||
|
assertTrue(valid < 30, "Random bursts detected as valid: " + valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void voiceMessageValidity()
|
||||||
|
{
|
||||||
|
assertTrue(new VoiceAMessage(DMRSyncPattern.BASE_STATION_VOICE, getBurst(), null, 0, 1).isValid());
|
||||||
|
|
||||||
|
CorrectedBinaryMessage corrupted = getBurst();
|
||||||
|
int[] frameOffsets = {24, 96, 216};
|
||||||
|
|
||||||
|
for(int offset : frameOffsets)
|
||||||
|
{
|
||||||
|
for(int bit = 0; bit < 16; bit += 4)
|
||||||
|
{
|
||||||
|
corrupted.flip(offset + bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(new VoiceAMessage(DMRSyncPattern.BASE_STATION_VOICE, corrupted, null, 0, 1).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the AMBE frames match the burst bits, specifically for frame 2 where bits 32-35 are sourced from burst
|
||||||
|
* bits 128-131 and bits 36-39 from burst bits 180-183, with burst bit 180 set (ie a negative signed byte).
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void ambeFramesMatchBurstBits()
|
||||||
|
{
|
||||||
|
CorrectedBinaryMessage burst = getBurst();
|
||||||
|
burst.set(180);
|
||||||
|
burst.clear(128, 132);
|
||||||
|
List<byte[]> frames = new VoiceAMessage(DMRSyncPattern.BASE_STATION_VOICE, burst, null, 0, 1).getAMBEFrames();
|
||||||
|
int[] frameOffsets = {24, 96, 216};
|
||||||
|
|
||||||
|
for(int frame = 0; frame < 3; frame++)
|
||||||
|
{
|
||||||
|
for(int bit = 0; bit < 72; bit++)
|
||||||
|
{
|
||||||
|
int burstIndex = frameOffsets[frame] + bit + ((frame == 1 && bit >= 36) ? 48 : 0);
|
||||||
|
boolean frameBit = (frames.get(frame)[bit / 8] & (0x80 >> (bit % 8))) != 0;
|
||||||
|
assertEquals(burst.get(burstIndex), frameBit, "Frame " + frame + " bit " + bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user