mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-10 23:57:25 -05:00
Compare commits
51 Commits
v0.4.0-alp
...
v0.4.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e20540ccc6 | ||
|
|
8d7e0e9f46 | ||
|
|
cc5d94f58d | ||
|
|
26197e433c | ||
|
|
f54b4dfd0e | ||
|
|
085ba52294 | ||
|
|
b8c24680a5 | ||
|
|
c658582157 | ||
|
|
83e518d503 | ||
|
|
2163d0f822 | ||
|
|
34c920eede | ||
|
|
35b0b5ef9b | ||
|
|
08672c33bd | ||
|
|
e2651d6c7c | ||
|
|
851a4e12d2 | ||
|
|
b7c66ec844 | ||
|
|
7522a3a820 | ||
|
|
6a0402d1cf | ||
|
|
5309af84f7 | ||
|
|
8d8d77fa11 | ||
|
|
284cf07e97 | ||
|
|
16414c7585 | ||
|
|
70c9eed0bf | ||
|
|
f49b1fef17 | ||
|
|
3fe070987a | ||
|
|
00d5610213 | ||
|
|
d3c1a605df | ||
|
|
85df151a96 | ||
|
|
8915797aac | ||
|
|
03a5d71a95 | ||
|
|
4158d4ac10 | ||
|
|
f934676bd0 | ||
|
|
d5b802e794 | ||
|
|
139227e46d | ||
|
|
2394d51a51 | ||
|
|
d9fa2bea5b | ||
|
|
0cb360eeed | ||
|
|
ea472f873f | ||
|
|
8cbdbd527e | ||
|
|
ff0d893bd9 | ||
|
|
7b924d0486 | ||
|
|
712eb07f4b | ||
|
|
d5c3ecc7df | ||
|
|
c4bf089667 | ||
|
|
378b03e06e | ||
|
|
34235382fb | ||
|
|
c05aff0768 | ||
|
|
f0c3e0fd1a | ||
|
|
d601cbda0e | ||
|
|
2653dc6b38 | ||
|
|
48cbbfc656 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,7 +17,7 @@ out/
|
||||
*.class
|
||||
*.log
|
||||
# Package Files #
|
||||
*.jar
|
||||
#*.jar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -4,6 +4,8 @@
|
||||
<bytecodeTargetLevel>
|
||||
<module name="sdr-trunk_main" target="11" />
|
||||
<module name="sdr-trunk_test" target="11" />
|
||||
<module name="sdrtrunk-sdr-trunk_main" target="11" />
|
||||
<module name="sdrtrunk-sdr-trunk_test" target="11" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
||||
4
.idea/libraries/imports.xml
generated
4
.idea/libraries/imports.xml
generated
@@ -1,12 +1,10 @@
|
||||
<component name="libraryTable">
|
||||
<library name="imports">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/imports/jmbe-interface-0.3.3.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/java-lame-3.98.4.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/radioreference-14.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/wsdl4j-1.5.1.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/commons-math3-3.6.1.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/libresample4j-0.1.3.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/jmbe-api-1.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
4
.idea/modules.xml
generated
4
.idea/modules.xml
generated
@@ -2,9 +2,11 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk_main.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk_main.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk_test.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk_test.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdrtrunk-sdr-trunk_main.iml" filepath="$PROJECT_DIR$/.idea/modules/sdrtrunk-sdr-trunk_main.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdrtrunk-sdr-trunk_test.iml" filepath="$PROJECT_DIR$/.idea/modules/sdrtrunk-sdr-trunk_test.iml" group="sdr-trunk" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
68
README.md
68
README.md
@@ -2,9 +2,10 @@
|
||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
||||
|
||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted_V0.3.0)
|
||||
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_V0.3.0)
|
||||
* [User's Manual Version 0.3.0 and 0.4.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_V0.3.0)
|
||||
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
* [Support Group](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||
* [Discord Support Channel](https://discord.gg/HJQaKYE)
|
||||
|
||||

|
||||
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot
|
||||
@@ -13,68 +14,53 @@ A cross-platform java application for decoding, monitoring, recording and stream
|
||||
|
||||
If you simply want to download and run the program, please follow these instructions.
|
||||
|
||||
## Install Java 8 (or newer)
|
||||
|
||||
## Download the latest sdrtrunk release
|
||||
## Download the latest sdrtrunk release for your operating system
|
||||
|
||||
All release versions of sdrtrunk are available from the [releases](https://github.com/DSheirer/sdrtrunk/releases) tab.
|
||||
|
||||
**(final)** These versions have been tested and are the current release version.
|
||||
* **(alpha)** These versions are under development feature previews and likely to contain bugs and unexpected behavior.
|
||||
* **(beta)** These versions are currently being tested for bugs and functionality prior to final release.
|
||||
* **(final)** These versions have been tested and are the current release version.
|
||||
|
||||
**(alpha)** These versions are under development feature previews and likely to contain bugs and unexpected behavior.
|
||||
## Unzip the release
|
||||
|
||||
**(beta)** These versions are currently being tested for bugs and functionality.
|
||||
Use 7-zip or any zip utility to unzip the release file
|
||||
|
||||
## Run the application
|
||||
## Start the application
|
||||
|
||||
Either double-click on the downloaded file (if supported on your operating system) or open a terminal/command window
|
||||
and change to the directory where you downloaded the release file and type:
|
||||
|
||||
```
|
||||
java -jar downloaded-jar-filename
|
||||
```
|
||||
|
||||
Note: replace _downloaded-jar-filename_ with the actual name of the sdrtrunk release version that you downloaded
|
||||
Once unzipped, open a command prompt to where you unzipped the release. Change to the **/bin** directory and use the launch script to start the application:
|
||||
* **Windows** sdr-trunk.bat
|
||||
* **Linux/OSX** ./sdr-trunk
|
||||
|
||||
## Optional - P25 Audio
|
||||
If you're using sdrtrunk with a P25 trunked radio system, the [JMBE](https://github.com/DSheirer/sdrtrunk/wiki/JMBE) wiki page contains instructions
|
||||
for downloading the JMBE audio library source code and compiling the JMBE library. Copy the resulting JMBE audio library
|
||||
jar file to the same folder containing the sdrtrunk application to use the library with sdrtrunk.
|
||||
If you're using sdrtrunk with a P25 trunked radio system, the [JMBE](https://github.com/DSheirer/sdrtrunk/wiki/JMBE) wiki page contains instructions for downloading the JMBE audio library source code and compiling the JMBE library. Once you have compiled the library, launch the sdrtrunk application. From the menu bar, choose **View >> Preferences**. In the **JMBE Audio Codec** section, update the path to where your compiled JMBE library is located. Any channels that are started after you set the path will be able to produce P25 audio.
|
||||
|
||||
## Minimum System Requirements
|
||||
* **Operating System:** Windows (32 or 64-bit), Linux (32 or 64-bit) or Mac/Linux (64-bit, 10.14 or higher)
|
||||
* **CPU:** 4-core
|
||||
* **RAM:** 8GB or more (preferred). Depending on usage, 4GB may be sufficient.
|
||||
|
||||
# Developer Instructions:
|
||||
|
||||
If you're interested in modifying and/or compiling the source code, please follow these instructions to use gradle to compile the code.
|
||||
|
||||
## Build the project
|
||||
sdrtrunk uses gradle as the build system. You can build it with locally installed gradle or if you do not have/want
|
||||
to install gradle you can use preconfigured linux/windows wrapper.
|
||||
sdrtrunk uses the gradle build system. This requires OpenJDK 11 or higher installed on your local compuber. Use the gradle wrapper to build the source code:
|
||||
|
||||
### Build with locally installed gradle 4.3.1
|
||||
### Linux
|
||||
```
|
||||
gradle clean buildSdr
|
||||
./gradlew clean build
|
||||
```
|
||||
### Build with preconfigured wrapper for linux/windows
|
||||
Linux
|
||||
### Windows
|
||||
```
|
||||
./gradlew clean buildSdr
|
||||
gradlew.bat clean build
|
||||
```
|
||||
Windows
|
||||
```
|
||||
gradlew.bat clean buildSdr
|
||||
```
|
||||
This would add gradle, download all project dependencies and build JAR
|
||||
|
||||
The **/build/distributions** folder will contain the zip file of the compiled program. Unzip it and launch the program from the scripts in the **/bin** directory.
|
||||
|
||||
## Development
|
||||
All dependencies/versions are controlled from build.gradle.
|
||||
To change the new release version tag of artifact - change property:
|
||||
```
|
||||
version = '0.3.2'
|
||||
```
|
||||
|
||||
## Run SDRTrunk
|
||||
SDRTrunk is packed into single uber-jar file. There is need to have folders with dependencies and
|
||||
adding them with classpath. Just run with java.
|
||||
### Use Java8
|
||||
```
|
||||
java -jar build/libs/sdr-trunk-all-0.3.2.jar
|
||||
version = '0.4.0'
|
||||
```
|
||||
|
||||
175
build.gradle
175
build.gradle
@@ -1,37 +1,30 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'org.openjfx.javafxplugin' version '0.0.6'
|
||||
id 'org.beryx.runtime' version '1.1.1'
|
||||
id 'org.beryx.runtime' version '1.8.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -39,7 +32,7 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
version = '0.4.0-alpha.7'
|
||||
version = '0.4.0-beta.3'
|
||||
sourceCompatibility = '11'
|
||||
|
||||
sourceSets {
|
||||
@@ -54,6 +47,7 @@ dependencies {
|
||||
compile 'ch.qos.logback:logback-core:1.2.3'
|
||||
compile 'ch.qos.logback:logback-classic:1.2.3'
|
||||
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8'
|
||||
compile 'com.fazecast:jSerialComm:2.5.0'
|
||||
compile 'com.github.jiconfont:jiconfont-font_awesome:4.7.0.1'
|
||||
compile 'com.github.jiconfont:jiconfont-javafx:1.0.0'
|
||||
compile 'com.github.jiconfont:jiconfont-swing:1.0.1'
|
||||
@@ -61,10 +55,10 @@ dependencies {
|
||||
compile 'com.google.guava:guava:27.0.1-jre'
|
||||
compile 'com.jidesoft:jide-oss:3.6.18'
|
||||
compile 'com.miglayout:miglayout-swing:5.2'
|
||||
compile 'eu.hansolo:charts:1.0.5'
|
||||
compile 'io.github.dsheirer:radio-reference-api:15.1.2'
|
||||
compile 'javax.usb:usb-api:1.0.2'
|
||||
compile 'net.coderazzi:tablefilter-swing:5.4.0'
|
||||
compile 'org.apache.axis:axis:1.4'
|
||||
compile 'org.apache.axis:axis-jaxrpc:1.4'
|
||||
compile 'org.apache.commons:commons-lang3:3.8.1'
|
||||
compile 'org.apache.commons:commons-io:1.3.2'
|
||||
compile 'org.apache.mina:mina-core:2.0.19'
|
||||
@@ -72,15 +66,15 @@ dependencies {
|
||||
compile "org.openjfx:javafx-base:11:win"
|
||||
compile "org.openjfx:javafx-base:11:linux"
|
||||
compile "org.openjfx:javafx-base:11:mac"
|
||||
compile "org.openjfx:javafx-controls:11:win"
|
||||
compile "org.openjfx:javafx-controls:11:linux"
|
||||
compile "org.openjfx:javafx-controls:11:mac"
|
||||
compile "org.openjfx:javafx-graphics:11:win"
|
||||
compile "org.openjfx:javafx-graphics:11:linux"
|
||||
compile "org.openjfx:javafx-graphics:11:mac"
|
||||
compile "org.openjfx:javafx-swing:11:win"
|
||||
compile "org.openjfx:javafx-swing:11:linux"
|
||||
compile "org.openjfx:javafx-swing:11:mac"
|
||||
compile "org.openjfx:javafx-controls:11.0.2:win"
|
||||
compile "org.openjfx:javafx-controls:11.0.2:linux"
|
||||
compile "org.openjfx:javafx-controls:11.0.2:mac"
|
||||
compile "org.openjfx:javafx-graphics:11.0.2:win"
|
||||
compile "org.openjfx:javafx-graphics:11.0.2:linux"
|
||||
compile "org.openjfx:javafx-graphics:11.0.2:mac"
|
||||
compile "org.openjfx:javafx-swing:11.0.2:win"
|
||||
compile "org.openjfx:javafx-swing:11.0.2:linux"
|
||||
compile "org.openjfx:javafx-swing:11.0.2:mac"
|
||||
compile 'org.slf4j:slf4j-api:1.7.25'
|
||||
compile 'org.usb4java:libusb4java:1.3.0'
|
||||
compile 'org.usb4java:usb4java:1.3.0'
|
||||
@@ -103,23 +97,108 @@ javafx {
|
||||
modules = ['javafx.base', 'javafx.controls', 'javafx.graphics', 'javafx.swing']
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes (
|
||||
'Implementation-Title' : 'sdrtrunk project',
|
||||
'Implementation-Version' : archiveVersion,
|
||||
'Class-Path' : 'jmbe-1.0.1.jar',
|
||||
'Created-By' : "Gradle ${gradle.gradleVersion}",
|
||||
'Build-Timestamp' : new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
|
||||
'Build-JDK' : "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']}",
|
||||
'Build-OS' : "${System.properties['os.name']} (${System.properties['os.arch']} ${System.properties['os.version']}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java Development Kit (JDK) locations. In order to build OS-specific images, these paths must point to the 'bin'
|
||||
* directory within a JDK for each of the specified architectures. These JDKs are used by the runtime and runtimeZip
|
||||
* tasks to produce platform-specific builds. If none of these paths exists, then an image will be created for the
|
||||
* host OS using the installed JDK.
|
||||
*/
|
||||
def jdk_linux_x86_64 = '/media/denny/WD250GB/java/bellsoft/linux-x64/jdk-13.0.1'
|
||||
def jdk_linux_x86_32 = '/media/denny/WD250GB/java/bellsoft/linux-x86/jdk-13.0.1'
|
||||
def jdk_osx_x86_64 = '/media/denny/WD250GB/java/bellsoft/osx-x64/jdk-13.0.1.jdk'
|
||||
def jdk_windows_x86_64 = '/media/denny/WD250GB/java/bellsoft/windows-x64/jdk-13.0.1'
|
||||
def jdk_windows_x86_32 = '/media/denny/WD250GB/java/bellsoft/windows-x86/jdk-13.0.1'
|
||||
|
||||
def directory_linux_x86_64 = new File(jdk_linux_x86_64)
|
||||
def directory_linux_x86_32 = new File(jdk_linux_x86_32)
|
||||
def directory_osx_x86_64 = new File(jdk_osx_x86_64)
|
||||
def directory_windows_x86_64 = new File(jdk_windows_x86_64)
|
||||
def directory_windows_x86_32 = new File(jdk_windows_x86_32)
|
||||
|
||||
runtime {
|
||||
targetPlatform('linux-x64', '/media/denny/WD250GB/java/linux/jdk-11.0.1')
|
||||
targetPlatform('osx-x64', '/media/denny/WD250GB/java/osx/jdk-11.0.1.jdk/Contents/Home')
|
||||
targetPlatform('windows-x64', '/media/denny/WD250GB/java/windows/jdk-11.0.1')
|
||||
if(directory_linux_x86_64.exists())
|
||||
{
|
||||
targetPlatform('linux-x86_64', jdk_linux_x86_64)
|
||||
}
|
||||
|
||||
if(directory_linux_x86_32.exists())
|
||||
{
|
||||
targetPlatform('linux-x86_32', jdk_linux_x86_32)
|
||||
}
|
||||
|
||||
if(directory_osx_x86_64.exists())
|
||||
{
|
||||
targetPlatform('osx-x86_64', jdk_osx_x86_64)
|
||||
}
|
||||
|
||||
if(directory_windows_x86_64.exists())
|
||||
{
|
||||
targetPlatform('windows-x86_64', jdk_windows_x86_64)
|
||||
}
|
||||
|
||||
if(directory_windows_x86_32.exists())
|
||||
{
|
||||
targetPlatform('windows-x86_32', jdk_windows_x86_32)
|
||||
}
|
||||
|
||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
||||
modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop']
|
||||
imageZip = file("$buildDir/image/sdr-trunk-" + version + ".zip")
|
||||
}
|
||||
|
||||
task buildSdr(type: Jar) {
|
||||
manifest {
|
||||
attributes 'Implementation-Title': 'SdrTrunk project',
|
||||
'Implementation-Version': version,
|
||||
'Main-Class': 'io.github.dsheirer.gui.SDRTrunk',
|
||||
'Class-Path': 'jmbe-0.3.2.jar jmbe-0.3.3.jar'
|
||||
/**
|
||||
* Check for existence of JDK folders for each supported platform before creating runtime images
|
||||
*/
|
||||
tasks.runtime.doFirst {
|
||||
|
||||
if(!directory_linux_x86_64.exists())
|
||||
{
|
||||
println("Skipping OS Image - Linux x86 64-bit JDK was not found at " + jdk_linux_x86_64)
|
||||
}
|
||||
|
||||
if(!directory_linux_x86_32.exists())
|
||||
{
|
||||
println("Skipping OS Image - Linux x86 32-bit JDK was not found at " + jdk_linux_x86_32)
|
||||
}
|
||||
|
||||
if(!directory_osx_x86_64.exists())
|
||||
{
|
||||
println("Skipping OS Image - OSX x86 64-bit JDK was not found at " + jdk_osx_x86_64);
|
||||
}
|
||||
|
||||
if(!directory_windows_x86_64.exists())
|
||||
{
|
||||
println("Skipping OS Image - Windows x86 64-bit JDK was not found at " + jdk_windows_x86_64)
|
||||
}
|
||||
|
||||
if(!directory_windows_x86_32.exists())
|
||||
{
|
||||
println("Skipping OS Image - Windows x86 32-bit JDK was not found at " + jdk_windows_x86_32)
|
||||
}
|
||||
baseName = project.name + '-all'
|
||||
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
with jar
|
||||
}
|
||||
|
||||
/**
|
||||
* Since we have to list each of the platform-specific JavaFX libraries as dependencies,
|
||||
* remove them from each build image
|
||||
*/
|
||||
tasks.runtime.doLast {
|
||||
delete(fileTree('build/image/sdr-trunk-linux-x86_64/lib').include { it.name ==~ /javafx.*-(win|mac)\.jar/ })
|
||||
delete(fileTree('build/image/sdr-trunk-linux-x86_32/lib').include { it.name ==~ /javafx.*-(win|mac)\.jar/ })
|
||||
delete(fileTree('build/image/sdr-trunk-osx-x86_64/lib').include { it.name ==~ /javafx.*-(win|linux)\.jar/ })
|
||||
delete(fileTree('build/image/sdr-trunk-windows-x86_64/lib').include { it.name ==~ /javafx.*-(linux|mac)\.jar/ })
|
||||
delete(fileTree('build/image/sdr-trunk-windows-x86_32/lib').include { it.name ==~ /javafx.*-(linux|mac)\.jar/ })
|
||||
}
|
||||
|
||||
36
gradle/wrapper/gradle-wrapper.properties
vendored
36
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,25 +1,27 @@
|
||||
#
|
||||
# *******************************************************************************
|
||||
# sdrtrunk
|
||||
# Copyright (C) 2014-2019 Dennis Sheirer
|
||||
# /*
|
||||
# * ******************************************************************************
|
||||
# * 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/>
|
||||
# * *****************************************************************************
|
||||
# */
|
||||
#
|
||||
# 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/>
|
||||
# ******************************************************************************
|
||||
#
|
||||
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
337
sdr-trunk.ipr
337
sdr-trunk.ipr
@@ -11,9 +11,9 @@
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.groovy" />
|
||||
</wildcardResourcePatterns>
|
||||
<bytecodeTargetLevel target="1.8">
|
||||
<module name="sdr-trunk_main" target="1.8" />
|
||||
<module name="sdr-trunk_test" target="1.8" />
|
||||
<bytecodeTargetLevel target="11">
|
||||
<module name="sdr-trunk_main" target="11" />
|
||||
<module name="sdr-trunk_test" target="11" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
<component name="CopyrightManager" default="sdrtrunk">
|
||||
@@ -36,6 +36,7 @@
|
||||
<option name="distributionType" value="WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="$USER_HOME$/gradle/gradle-2.12" />
|
||||
<option name="gradleJvm" value="11" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
@@ -206,12 +207,12 @@
|
||||
</component>
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/sdr-trunk.iml" filepath="$PROJECT_DIR$/sdr-trunk.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/sdr-trunk.iml" filepath="$PROJECT_DIR$/sdr-trunk.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/sdr-trunk_main.iml" filepath="$PROJECT_DIR$/sdr-trunk_main.iml" group="sdr-trunk" />
|
||||
<module fileurl="file://$PROJECT_DIR$/sdr-trunk_test.iml" filepath="$PROJECT_DIR$/sdr-trunk_test.iml" group="sdr-trunk" />
|
||||
</modules>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<component name="SvnBranchConfigurationManager">
|
||||
@@ -255,48 +256,48 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.9.0/a0ad4e203304ccab7e01266fa814115850edb8a9/jackson-annotations-2.9.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.fasterxml.jackson.core:jackson-core:2.9.3">
|
||||
<library name="Gradle: com.fasterxml.jackson.core:jackson-core:2.9.8">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.3/ea9b6fc7bc3ccba9777b0827091f9aa1f8580371/jackson-core-2.9.3.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.8/f5a654e4675769c716e5b387830d19b501ca191/jackson-core-2.9.8.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.3/a1c84964d4a1ef15eeb91d0c2b62460e7d7c4829/jackson-core-2.9.3-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.8/acc65b3b6e07784dc1760fa4e460238eb2a9073a/jackson-core-2.9.8-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.3/99006a307afedb30000fc93b462e98f0d822fa11/jackson-core-2.9.3-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.9.8/ecaea301e166a0b48f11615864246de739b6619b/jackson-core-2.9.8-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.fasterxml.jackson.core:jackson-databind:2.9.3">
|
||||
<library name="Gradle: com.fasterxml.jackson.core:jackson-databind:2.9.8">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.3/193b96ef555b2f2573b576887ba9a93e4bf48e8c/jackson-databind-2.9.3.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.8/11283f21cc480aa86c4df7a0a3243ec508372ed2/jackson-databind-2.9.8.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.3/486b5cb643a0c98342b6f533d9fc5aa75ab736df/jackson-databind-2.9.3-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.8/a1dea243882227fc27c350a5b25ca76ef4ecb7d7/jackson-databind-2.9.8-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.3/eab1ff15884e40c0adafe219a98c46513eb1d0e6/jackson-databind-2.9.3-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.9.8/f66792d499a6fea6c7a743558f940e0ebf775ce3/jackson-databind-2.9.8-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.3">
|
||||
<library name="Gradle: com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.8">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.3/c7d7d9f5316e0098e4d652f2d013aa9f9d69e3b1/jackson-dataformat-xml-2.9.3.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.8/6a068ddeeafe736da35fba3a7f64462081b562c9/jackson-dataformat-xml-2.9.8.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.3/9f09f5e7b1ff00b02953b0dc64e6edcc03d8dbc2/jackson-dataformat-xml-2.9.3-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.8/ba3a439d041b333550de55e34c370d9221011911/jackson-dataformat-xml-2.9.8-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.3/585e357aeb2ce0428740e88efd1443889e5834f4/jackson-dataformat-xml-2.9.3-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.8/2d48d64536472ff6138cab6ba731b29247fdb0fb/jackson-dataformat-xml-2.9.8-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.9.3">
|
||||
<library name="Gradle: com.fasterxml.jackson.module:jackson-module-jaxb-annotations:2.9.8">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.9.3/ffe9afb0a9ee84fc79a9fd451b00643da1fa2466/jackson-module-jaxb-annotations-2.9.3.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.9.8/da08815ba1c7f7b435e7df02b7bf98327bad2fd4/jackson-module-jaxb-annotations-2.9.8.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.9.3/21ec5b47ad8833bbe1ce1f99192bcb6a4d0d3846/jackson-module-jaxb-annotations-2.9.3-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.9.8/575e1a2a77b830cd3c369931b71b653114314a43/jackson-module-jaxb-annotations-2.9.8-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.9.3/660f2e6f5bc3dcd507e21ec656218d92095e4e01/jackson-module-jaxb-annotations-2.9.3-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.module/jackson-module-jaxb-annotations/2.9.8/b68061d95e61cc7c4a574f449cd9db7bad66d75b/jackson-module-jaxb-annotations-2.9.8-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.fasterxml.woodstox:woodstox-core:5.0.3">
|
||||
@@ -310,6 +311,61 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fasterxml.woodstox/woodstox-core/5.0.3/f38482e8b060d9e5354c10103f4c4db53c58db7d/woodstox-core-5.0.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.fazecast:jSerialComm:2.5.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fazecast/jSerialComm/2.5.0/df146deefea578aa38de43a0658f06ac297acb6f/jSerialComm-2.5.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fazecast/jSerialComm/2.5.0/1aa3e4d710396f7bc56661b23a6d125bf5d3749/jSerialComm-2.5.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fazecast/jSerialComm/2.5.0/b9e6222384a17cb10474b7f25768edf4b742ec16/jSerialComm-2.5.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.github.jiconfont:jiconfont-font_awesome:4.7.0.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-font_awesome/4.7.0.1/121d019023f631af04a1357e637dd05a2cc91f7b/jiconfont-font_awesome-4.7.0.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-font_awesome/4.7.0.1/278c25d163a2e2eec5b2e93669b73936365cea69/jiconfont-font_awesome-4.7.0.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-font_awesome/4.7.0.1/8c79f5ccd872310c6dfd4f89f7fe3f748fba94a4/jiconfont-font_awesome-4.7.0.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.github.jiconfont:jiconfont-javafx:1.0.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-javafx/1.0.0/233530f110ab0c3ff845aa52799c43645a11f1c8/jiconfont-javafx-1.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-javafx/1.0.0/8cf9282bff0c0cf56cab9ba18f74943019f9a979/jiconfont-javafx-1.0.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-javafx/1.0.0/687e090ac5e1b4d20a242995a80631043d4ee78d/jiconfont-javafx-1.0.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.github.jiconfont:jiconfont-swing:1.0.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-swing/1.0.1/14621054d72340b4a664cce4710ba974a3882a19/jiconfont-swing-1.0.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-swing/1.0.1/55df548424fa5159247b05f02eb712719ce75d5/jiconfont-swing-1.0.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-swing/1.0.1/cae76b75041118062159ffa0a7e2109cc7f88fad/jiconfont-swing-1.0.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.github.jiconfont:jiconfont:1.0.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont/1.0.0/e17d93321975b6735ebc283f6832fa0350614d3a/jiconfont-1.0.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont/1.0.0/8dc32616a97fe4b54380821a6aab9d8930262262/jiconfont-1.0.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont/1.0.0/e79316fba9458423ce944fba6da101262549464/jiconfont-1.0.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.github.wendykierp:JTransforms:3.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.wendykierp/JTransforms/3.1/f1f6d3c9c9bc04f848e38bdb77dea01e8764a01f/JTransforms-3.1.jar!/" />
|
||||
@@ -343,26 +399,26 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.2.0/a8cd7823aa1dcd2fd6677c0c5988fdde9d1fb0a3/error_prone_annotations-2.2.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.google.guava:failureaccess:1.0">
|
||||
<library name="Gradle: com.google.guava:failureaccess:1.0.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0/50bacb0bdbbeeda25142aff57e2214c402e1193c/failureaccess-1.0.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0.1/1dcf1de382a0bf95a3d8b0849546c88bac1292c9/failureaccess-1.0.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0/d4f5c095ed310f738d56b26bad666f342c1fd73d/failureaccess-1.0-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0.1/a9aadb2d577ff54ba3115ee7357cbe65bd2a470f/failureaccess-1.0.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0/1e90402881010c96ea5e4b7e6f56acb283e78db3/failureaccess-1.0-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0.1/1d064e61aad6c51cc77f9b59dc2cccc78e792f5a/failureaccess-1.0.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.google.guava:guava:27.0-jre">
|
||||
<library name="Gradle: com.google.guava:guava:27.0.1-jre">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0-jre/c6ad87d2575af8ac8ec38e28e75aefa882cc3a1f/guava-27.0-jre.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-jre/bd41a290787b5301e63929676d792c507bbc00ae/guava-27.0.1-jre.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0-jre/607bfb3210bdc9c60315b033710e35428bc400bb/guava-27.0-jre-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-jre/7515204e130bb73fea9f6df11e58559e6be2dcf0/guava-27.0.1-jre-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0-jre/d6484e2ee11ad928ccf61cf3e4ce9cedc2eead7e/guava-27.0-jre-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/27.0.1-jre/cb5c1119df8d41a428013289b193eba3ccaf5f60/guava-27.0.1-jre-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava">
|
||||
@@ -394,26 +450,26 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.jidesoft/jide-oss/3.6.18/b9cadcf869c0bdd032756228315252516856ad56/jide-oss-3.6.18-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.miglayout:miglayout-core:5.0">
|
||||
<library name="Gradle: com.miglayout:miglayout-core:5.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-core/5.0/956daa1576bccb24e727ab75540b078171c7c432/miglayout-core-5.0.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-core/5.2/c1234205bde8fa72a420ecc5f63b9f269edccbfb/miglayout-core-5.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-core/5.0/c346dd69f6de7ae6ab2e93d42fa5def41c99aa1c/miglayout-core-5.0-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-core/5.2/43595a2a4d2da858dfe1163f0d0ce769b7fbb11/miglayout-core-5.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-core/5.0/c457b92ed51255fdb5dfab4e89c7aec06674cd7c/miglayout-core-5.0-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-core/5.2/4b3f0bfa2312173be31f87caa3cc58b617eea1df/miglayout-core-5.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: com.miglayout:miglayout-swing:5.0">
|
||||
<library name="Gradle: com.miglayout:miglayout-swing:5.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-swing/5.0/7b4c4d92002459e68e8f115b9ae8a774b2660490/miglayout-swing-5.0.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-swing/5.2/53e2fc5586a120797e3af0ff4cf9adb3b403ab14/miglayout-swing-5.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-swing/5.0/7f87b7818189138a1e9691b861d06f20f121820d/miglayout-swing-5.0-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-swing/5.2/9ca06755a629747815fa91b3cd480bfb40fbb0e1/miglayout-swing-5.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-swing/5.0/d3e06bcc8aabd8cdd1f40989e12ced6413fae68c/miglayout-swing-5.0-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.miglayout/miglayout-swing/5.2/df798d1660b3106a0304efa9e04dfd1a1a2e6025/miglayout-swing-5.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: commons-io:commons-io:1.3.2">
|
||||
@@ -427,6 +483,28 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/1.3.2/b41a5f39fa90007a6c247175933555ba5f166d70/commons-io-1.3.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: eu.hansolo:charts:1.0.5">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/eu.hansolo/charts/1.0.5/2bb9d233ac3e5f678920ff04effd57c0ba21d860/charts-1.0.5.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/eu.hansolo/charts/1.0.5/d65ed2f94c0d5579cf87eea01c5d14fa8196d235/charts-1.0.5-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/eu.hansolo/charts/1.0.5/aa26011a2c525ddf86cb92ed23558b18c0174fa4/charts-1.0.5-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: io.github.dsheirer:radio-reference-api:15.0.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.0.1/8af16506c26025517f0bc971ba35485e96130c9a/radio-reference-api-15.0.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.0.1/219528429b3ed73f285a8c2c4ded40d8eae61fc0/radio-reference-api-15.0.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.0.1/b699a9821030b54fbb7f472f97fcc634afb114fb/radio-reference-api-15.0.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: javax.usb:usb-api:1.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.usb/usb-api/1.0.2/f0ae0d322a99811cf79d508d37f3dc2d82753070/usb-api-1.0.2.jar!/" />
|
||||
@@ -449,31 +527,15 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/a6c32b40bf3d76eca54e3c601e5d1470c86fcdfa/junit-4.12-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: net.coderazzi:tablefilter-swing:5.3.1">
|
||||
<library name="Gradle: net.coderazzi:tablefilter-swing:5.4.0">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.coderazzi/tablefilter-swing/5.3.1/998bed231fba3ca6ab2f182aa576e5cb1df3339c/tablefilter-swing-5.3.1.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.coderazzi/tablefilter-swing/5.4.0/ba7415b409712bf1988b2f03eda7730deba26c10/tablefilter-swing-5.4.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.coderazzi/tablefilter-swing/5.3.1/90c361ce16cd898f1629906fa1df6d74a678704c/tablefilter-swing-5.3.1-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.coderazzi/tablefilter-swing/5.4.0/3bc5c263fc43180f4137a41bd0848851830761ff/tablefilter-swing-5.4.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.coderazzi/tablefilter-swing/5.3.1/9e46adc554cfdf19029afa46ee81044ea5602b44/tablefilter-swing-5.3.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.apache.axis:axis-jaxrpc:1.4">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.axis/axis-jaxrpc/1.4/b393f1f0c0d95b68c86d0b1ab2e687bb71f3c075/axis-jaxrpc-1.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
<library name="Gradle: org.apache.axis:axis:1.4">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.axis/axis/1.4/94a9ce681a42d0352b3ad22659f67835e560d107/axis-1.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.axis/axis/1.4/442ab1fe3e66b6d5c916ae3f203fbfa618515f1c/axis-1.4-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.coderazzi/tablefilter-swing/5.4.0/298121afccdf36281e234aaf478f97ddbe8aecf3/tablefilter-swing-5.4.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.apache.commons:commons-lang3:3.8.1">
|
||||
@@ -498,26 +560,26 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.5/1d3f412d28c8bff0e8f048240b08bcd21cfa432e/commons-math3-3.5-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.apache.mina:mina-core:2.0.16">
|
||||
<library name="Gradle: org.apache.mina:mina-core:2.0.19">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-core/2.0.16/f720f17643eaa7b0fec07c1d7f6272972c02bba4/mina-core-2.0.16.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-core/2.0.19/b6ba5d8c6d1dd38a1151c590018e4e5edd6c4cfa/mina-core-2.0.19.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-core/2.0.16/9b8b385cba38847719659ce6fde466828c4ab623/mina-core-2.0.16-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-core/2.0.19/b50fe4a0802588c70fa9f23ae6dcefe54a688bca/mina-core-2.0.19-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-core/2.0.16/660fb813ca1c8d8a936f894324091400a5ac128a/mina-core-2.0.16-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-core/2.0.19/a070a03134f7c4f5c9ad79b094423d48f3e9551b/mina-core-2.0.19-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.apache.mina:mina-http:2.0.16">
|
||||
<library name="Gradle: org.apache.mina:mina-http:2.0.19">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-http/2.0.16/b2081b5255a3998a044c34b12797b915e1640122/mina-http-2.0.16.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-http/2.0.19/b42328dbaeed685376821fe704f70ad36f4de24/mina-http-2.0.19.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-http/2.0.16/38119b808477795cc161a2aaf3e5bb16920a9aae/mina-http-2.0.16-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-http/2.0.19/3d6ce6d25fc14d858486f50a96213872ca7ae467/mina-http-2.0.19-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-http/2.0.16/fadd15439d830c99ccd6e020a2e125f0a4224059/mina-http-2.0.16-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.mina/mina-http/2.0.19/ce16dff76ec41be9f9c21df8bb8c271a3bb709cb/mina-http-2.0.19-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.assertj:assertj-core:3.8.0">
|
||||
@@ -575,37 +637,158 @@
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/1dc37250fbc78e23a65a67fbbaf71d2e9cbc3c0b/hamcrest-core-1.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.kordamp.ikonli:ikonli-core:2.1.1">
|
||||
<library name="Gradle: org.openjfx:javafx-base:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-core/2.1.1/99b0142ea24785d14d51e329dd8050a9a55b8904/ikonli-core-2.1.1.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/7fb2e4a8528ec9e434a9ac9ee98b39af79e6dcb8/javafx-base-11.0.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-core/2.1.1/2931b2864da41ce42f1a5973b4239d25e02d0f37/ikonli-core-2.1.1-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/f0dfede8adbd1b28d1b5084bd2812ace69ebe673/javafx-base-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-core/2.1.1/ce342290407ae3c6ce7b1249978c9ddf45b92974/ikonli-core-2.1.1-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/c082cdf645f9833408dcd960bf68344c9384f569/javafx-base-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.kordamp.ikonli:ikonli-fontawesome-pack:2.1.1">
|
||||
<library name="Gradle: org.openjfx:javafx-base:linux:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-fontawesome-pack/2.1.1/5c48f3fbb11a21b6fe460650569085aa0b7e4ae1/ikonli-fontawesome-pack-2.1.1.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/8db178adb1085d455e5ef643a48c34959c8771c1/javafx-base-11.0.2-linux.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-fontawesome-pack/2.1.1/e8c0c8694953bdbcca312b8cc0e6dbc5c2e7ec8b/ikonli-fontawesome-pack-2.1.1-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/f0dfede8adbd1b28d1b5084bd2812ace69ebe673/javafx-base-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-fontawesome-pack/2.1.1/19de8abf192dcc83c0d0b7b507fa1fc72ce6bbb2/ikonli-fontawesome-pack-2.1.1-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/c082cdf645f9833408dcd960bf68344c9384f569/javafx-base-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.kordamp.ikonli:ikonli-javafx:2.1.1">
|
||||
<library name="Gradle: org.openjfx:javafx-base:mac:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-javafx/2.1.1/bf3e8f21146016f3fcee9ab48ab2228b7db4bc49/ikonli-javafx-2.1.1.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/b774922e5da6a56afee221d1a288d179a1342aa9/javafx-base-11.0.2-mac.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-javafx/2.1.1/7c45822547d7a8a53d80ebb8e4a5602db10f9e45/ikonli-javafx-2.1.1-javadoc.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/f0dfede8adbd1b28d1b5084bd2812ace69ebe673/javafx-base-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.kordamp.ikonli/ikonli-javafx/2.1.1/a9c7c4aaad34e274df03701b84e064d9169ddd1d/ikonli-javafx-2.1.1-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/c082cdf645f9833408dcd960bf68344c9384f569/javafx-base-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-base:win:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/1852e57b8cf9a9b6488c33605bccd5d06ff210e1/javafx-base-11.0.2-win.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/f0dfede8adbd1b28d1b5084bd2812ace69ebe673/javafx-base-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/c082cdf645f9833408dcd960bf68344c9384f569/javafx-base-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-controls:linux:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/5f6929050a744aab39ebfc1e8e5dd03bcd2ad47b/javafx-controls-11.0.2-linux.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/baa088249ee757119688e78d9c3a256928189902/javafx-controls-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/1535a8b57f01a712405a739a0d69e09ab99dbb6b/javafx-controls-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-controls:mac:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/996371d2868035845572cdb19179dca2bfd1139f/javafx-controls-11.0.2-mac.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/baa088249ee757119688e78d9c3a256928189902/javafx-controls-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/1535a8b57f01a712405a739a0d69e09ab99dbb6b/javafx-controls-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-controls:win:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/6c763707769c18adce406904c771c2ad1fcc370b/javafx-controls-11.0.2-win.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/baa088249ee757119688e78d9c3a256928189902/javafx-controls-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/1535a8b57f01a712405a739a0d69e09ab99dbb6b/javafx-controls-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-graphics:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/e522eb4ea422eceeee207b1c266ba3db19b2343a/javafx-graphics-11.0.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/b7ae1857e54ed7d8ca0e30e01d12fae2b75ba400/javafx-graphics-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/15eadef01f327a851a6d3af0ac89d56292076b13/javafx-graphics-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-graphics:linux:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/ef4c34f0ca77cd99100b76c2ccf1dce383572bb1/javafx-graphics-11.0.2-linux.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/b7ae1857e54ed7d8ca0e30e01d12fae2b75ba400/javafx-graphics-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/15eadef01f327a851a6d3af0ac89d56292076b13/javafx-graphics-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-graphics:mac:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/c1d99ae5bc48e7173f4514a941d27d8f8c972c71/javafx-graphics-11.0.2-mac.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/b7ae1857e54ed7d8ca0e30e01d12fae2b75ba400/javafx-graphics-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/15eadef01f327a851a6d3af0ac89d56292076b13/javafx-graphics-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-graphics:win:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/20459ea2cf714942bcbeb78a7f70ba3531dc1a44/javafx-graphics-11.0.2-win.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/b7ae1857e54ed7d8ca0e30e01d12fae2b75ba400/javafx-graphics-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/15eadef01f327a851a6d3af0ac89d56292076b13/javafx-graphics-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-swing:linux:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/cb57db3e5bd1e2b62b17c8a504858bfc26fa54b1/javafx-swing-11.0.2-linux.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/336999871b9a7e88179a76e2251c73f47e3d5cb6/javafx-swing-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/bcc8c0a402ff01a89a50888c9dc26824a9f3f2cc/javafx-swing-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-swing:mac:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/47efd914c1ebdeda8789a779c8341266340c743/javafx-swing-11.0.2-mac.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/336999871b9a7e88179a76e2251c73f47e3d5cb6/javafx-swing-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/bcc8c0a402ff01a89a50888c9dc26824a9f3f2cc/javafx-swing-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.openjfx:javafx-swing:win:11.0.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/9b5728e61148142fae8c47f6295c84d7a8d6c89/javafx-swing-11.0.2-win.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/336999871b9a7e88179a76e2251c73f47e3d5cb6/javafx-swing-11.0.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/bcc8c0a402ff01a89a50888c9dc26824a9f3f2cc/javafx-swing-11.0.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
<library name="Gradle: org.slf4j:slf4j-api:1.7.25">
|
||||
|
||||
@@ -1,34 +1,67 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.alias;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import net.coderazzi.filters.gui.AutoChoices;
|
||||
import net.coderazzi.filters.gui.TableFilterHeader;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasController extends JPanel
|
||||
implements ActionListener, ListSelectionListener
|
||||
public class AliasController extends JPanel implements ActionListener, ListSelectionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String TABLE_PREFERENCE_KEY = "alias.controller";
|
||||
private AliasModel mAliasModel;
|
||||
private JTable mAliasTable;
|
||||
private TableFilterHeader mTableFilterHeader;
|
||||
private AliasEditor mAliasEditor;
|
||||
private MultipleAliasEditor mMultipleAliasEditor;
|
||||
private JideSplitPane mSplitPane;
|
||||
private UserPreferences mUserPreferences;
|
||||
|
||||
private static final String NEW_ALIAS = "New";
|
||||
private static final String COPY_ALIAS = "Copy";
|
||||
@@ -39,15 +72,16 @@ public class AliasController extends JPanel
|
||||
private JButton mDeleteButton = new JButton(DELETE_ALIAS);
|
||||
|
||||
private IconCellRenderer mIconCellRenderer;
|
||||
private JTableColumnWidthMonitor mColumnWidthMonitor;
|
||||
|
||||
public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager)
|
||||
public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager,
|
||||
UserPreferences userPreferences)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
|
||||
mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager);
|
||||
mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager);
|
||||
|
||||
mIconCellRenderer = new IconCellRenderer(iconManager);
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
init();
|
||||
|
||||
@@ -56,9 +90,7 @@ public class AliasController extends JPanel
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0",
|
||||
"[grow,fill]",
|
||||
"[grow,fill]"));
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
||||
|
||||
//System Configuration View and Editor
|
||||
mAliasTable = new JTable(mAliasModel);
|
||||
@@ -66,11 +98,11 @@ public class AliasController extends JPanel
|
||||
mAliasTable.getSelectionModel().addListSelectionListener(this);
|
||||
mAliasTable.setAutoCreateRowSorter(true);
|
||||
|
||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR)
|
||||
.setCellRenderer(new ColorCellRenderer());
|
||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR).setCellRenderer(new ColorCellRenderer());
|
||||
|
||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON)
|
||||
.setCellRenderer(mIconCellRenderer);
|
||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON).setCellRenderer(mIconCellRenderer);
|
||||
|
||||
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mAliasTable, TABLE_PREFERENCE_KEY);
|
||||
|
||||
mTableFilterHeader = new TableFilterHeader(mAliasTable, AutoChoices.ENABLED);
|
||||
mTableFilterHeader.setFilterOnUpdates(true);
|
||||
@@ -79,8 +111,8 @@ public class AliasController extends JPanel
|
||||
|
||||
JPanel buttonsPanel = new JPanel();
|
||||
|
||||
buttonsPanel.setLayout(
|
||||
new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]"));
|
||||
buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0",
|
||||
"[grow,fill][grow,fill][grow,fill]", "[]"));
|
||||
|
||||
mNewButton.addActionListener(this);
|
||||
mNewButton.setToolTipText("Adds a new alias");
|
||||
@@ -98,8 +130,8 @@ public class AliasController extends JPanel
|
||||
|
||||
JPanel listAndButtonsPanel = new JPanel();
|
||||
|
||||
listAndButtonsPanel.setLayout(
|
||||
new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
|
||||
listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]",
|
||||
"[grow,fill][]"));
|
||||
|
||||
listAndButtonsPanel.add(tableScroller, "wrap");
|
||||
listAndButtonsPanel.add(buttonsPanel);
|
||||
@@ -212,18 +244,11 @@ public class AliasController extends JPanel
|
||||
*/
|
||||
private void addAlias(Alias alias)
|
||||
{
|
||||
//HACK: when inserting a row to the model, the JTable gets
|
||||
//notified and attempts to tell the coderazzi table filter
|
||||
//adaptive choices filter to refresh before the table filter is
|
||||
//notified of the row additions, causing an index out of bounds
|
||||
//exception. We turn off adaptive choices temporarily, add the
|
||||
//channel, and turn on adaptive choices again.
|
||||
mTableFilterHeader.setAdaptiveChoices(false);
|
||||
//Remove any column headers so that the newly added alias shows correctly.
|
||||
mTableFilterHeader.resetFilter();
|
||||
|
||||
int index = mAliasModel.addAlias(alias);
|
||||
|
||||
mTableFilterHeader.setAdaptiveChoices(true);
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
int translatedIndex = mAliasTable.convertRowIndexToView(index);
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.alias;
|
||||
|
||||
@@ -37,30 +39,34 @@ import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncIDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200IDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.MINEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327IDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordableEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupIDEditor;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackIDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.MINEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.priority.PriorityEditor;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.record.RecordEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteIDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupIDEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueIDEditor;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackIDEditor;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.priority.PriorityEditor;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
import io.github.dsheirer.alias.id.radio.RadioEditor;
|
||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||
import io.github.dsheirer.alias.id.radio.RadioRangeEditor;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.record.RecordEditor;
|
||||
import io.github.dsheirer.alias.id.status.StatusID;
|
||||
import io.github.dsheirer.alias.id.status.StatusIDEditor;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupEditor;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRangeEditor;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueIDEditor;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
@@ -102,6 +108,15 @@ public class AliasFactory
|
||||
Priority copyPriority = new Priority();
|
||||
copyPriority.setPriority(originalPriority.getPriority());
|
||||
return copyPriority;
|
||||
case RADIO_ID:
|
||||
Radio originalRadio = (Radio)id;
|
||||
Radio copyRadio = new Radio(originalRadio.getProtocol(), originalRadio.getValue());
|
||||
return copyRadio;
|
||||
case RADIO_ID_RANGE:
|
||||
RadioRange originalRadioRange = (RadioRange)id;
|
||||
RadioRange copyRadioRange = new RadioRange(originalRadioRange.getProtocol(), originalRadioRange.getMinRadio(),
|
||||
originalRadioRange.getMaxRadio());
|
||||
return copyRadioRange;
|
||||
case RECORD:
|
||||
return new Record();
|
||||
case SITE:
|
||||
@@ -213,6 +228,10 @@ public class AliasFactory
|
||||
return new MINEditor(aliasID);
|
||||
case PRIORITY:
|
||||
return new PriorityEditor(aliasID);
|
||||
case RADIO_ID:
|
||||
return new RadioEditor(aliasID);
|
||||
case RADIO_ID_RANGE:
|
||||
return new RadioRangeEditor(aliasID);
|
||||
case RECORD:
|
||||
return new RecordEditor(aliasID);
|
||||
case SITE:
|
||||
@@ -286,6 +305,10 @@ public class AliasFactory
|
||||
return new NonRecordable();
|
||||
case PRIORITY:
|
||||
return new Priority();
|
||||
case RADIO_ID:
|
||||
return new Radio();
|
||||
case RADIO_ID_RANGE:
|
||||
return new RadioRange();
|
||||
case RECORD:
|
||||
return new Record();
|
||||
case TALKGROUP:
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.alias;
|
||||
|
||||
@@ -24,6 +26,8 @@ import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||
import io.github.dsheirer.alias.id.status.StatusID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
@@ -32,6 +36,7 @@ import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.esn.ESNIdentifier;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroup;
|
||||
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.identifier.status.UnitStatusIdentifier;
|
||||
import io.github.dsheirer.identifier.status.UserStatusIdentifier;
|
||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||
@@ -48,10 +53,15 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* List of aliases that share the same alias list name and provides convenient methods for looking up alias
|
||||
* objects that match an identifier.
|
||||
*/
|
||||
public class AliasList implements Listener<AliasEvent>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AliasList.class);
|
||||
private Map<Protocol,TalkgroupAliasList> mTalkgroupProtocolMap = new HashMap<>();
|
||||
private Map<Protocol,RadioAliasList> mRadioProtocolMap = new HashMap<>();
|
||||
private Map<String,Alias> mESNMap = new HashMap<>();
|
||||
private Map<Integer,Alias> mStatusMap = new HashMap<>();
|
||||
private boolean mHasAliasActions = false;
|
||||
@@ -124,6 +134,32 @@ public class AliasList implements Listener<AliasEvent>
|
||||
|
||||
talkgroupRangeAliasList.add(talkgroupRange, alias);
|
||||
break;
|
||||
case RADIO_ID:
|
||||
Radio radio = (Radio)id;
|
||||
|
||||
RadioAliasList radioAliasList = mRadioProtocolMap.get(radio.getProtocol());
|
||||
|
||||
if(radioAliasList == null)
|
||||
{
|
||||
radioAliasList = new RadioAliasList();
|
||||
mRadioProtocolMap.put(radio.getProtocol(), radioAliasList);
|
||||
}
|
||||
|
||||
radioAliasList.add(radio, alias);
|
||||
break;
|
||||
case RADIO_ID_RANGE:
|
||||
RadioRange radioRange = (RadioRange)id;
|
||||
|
||||
RadioAliasList radioRangeAliasList = mRadioProtocolMap.get(radioRange.getProtocol());
|
||||
|
||||
if(radioRangeAliasList == null)
|
||||
{
|
||||
radioRangeAliasList = new RadioAliasList();
|
||||
mRadioProtocolMap.put(radioRange.getProtocol(), radioRangeAliasList);
|
||||
}
|
||||
|
||||
radioRangeAliasList.add(radioRange, alias);
|
||||
break;
|
||||
case ESN:
|
||||
String esn = ((Esn) id).getEsn();
|
||||
|
||||
@@ -253,14 +289,7 @@ public class AliasList implements Listener<AliasEvent>
|
||||
|
||||
if(talkgroupAliasList != null)
|
||||
{
|
||||
Alias alias = talkgroupAliasList.getAlias(talkgroup);
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
return aliases;
|
||||
}
|
||||
return toList(talkgroupAliasList.getAlias(talkgroup));
|
||||
}
|
||||
break;
|
||||
case PATCH_GROUP:
|
||||
@@ -293,53 +322,52 @@ public class AliasList implements Listener<AliasEvent>
|
||||
return aliases;
|
||||
}
|
||||
break;
|
||||
case RADIO:
|
||||
RadioIdentifier radio = (RadioIdentifier)identifier;
|
||||
|
||||
RadioAliasList radioAliasList = mRadioProtocolMap.get(identifier.getProtocol());
|
||||
|
||||
if(radioAliasList != null)
|
||||
{
|
||||
return toList(radioAliasList.getAlias(radio));
|
||||
}
|
||||
break;
|
||||
case ESN:
|
||||
if(identifier instanceof ESNIdentifier)
|
||||
{
|
||||
Alias alias = getESNAlias(((ESNIdentifier)identifier).getValue());
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
return aliases;
|
||||
}
|
||||
return toList(getESNAlias(((ESNIdentifier)identifier).getValue()));
|
||||
}
|
||||
break;
|
||||
case UNIT_STATUS:
|
||||
if(identifier instanceof UnitStatusIdentifier)
|
||||
{
|
||||
int status = ((UnitStatusIdentifier)identifier).getValue();
|
||||
|
||||
Alias alias = mStatusMap.get(status);
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
return aliases;
|
||||
}
|
||||
return toList(mStatusMap.get(status));
|
||||
}
|
||||
break;
|
||||
case USER_STATUS:
|
||||
if(identifier instanceof UserStatusIdentifier)
|
||||
{
|
||||
int status = ((UserStatusIdentifier)identifier).getValue();
|
||||
|
||||
Alias alias = mStatusMap.get(status);
|
||||
|
||||
if(alias != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
return aliases;
|
||||
}
|
||||
return toList(mStatusMap.get(status));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<Alias> toList(Alias alias)
|
||||
{
|
||||
if(alias != null)
|
||||
{
|
||||
List<Alias> aliases = new ArrayList<>();
|
||||
aliases.add(alias);
|
||||
return aliases;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -505,11 +533,32 @@ public class AliasList implements Listener<AliasEvent>
|
||||
|
||||
public void add(Talkgroup talkgroup, Alias alias)
|
||||
{
|
||||
//Detect talkgroup collisions
|
||||
if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()))
|
||||
{
|
||||
Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue());
|
||||
|
||||
mLog.warn("Alias [" + alias.getName() + "] talkgroup [" + talkgroup.getValue() +
|
||||
"] has the same talkgroup value as alias [" + existing.getName() +
|
||||
"] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]");
|
||||
}
|
||||
|
||||
mTalkgroupAliasMap.put(talkgroup.getValue(), alias);
|
||||
}
|
||||
|
||||
public void add(TalkgroupRange talkgroupRange, Alias alias)
|
||||
{
|
||||
//Log warning if the new talkgroup range overlaps with any existing ranges
|
||||
for(Map.Entry<TalkgroupRange,Alias> entry: mTalkgroupRangeAliasMap.entrySet())
|
||||
{
|
||||
if(talkgroupRange.overlaps(entry.getKey()))
|
||||
{
|
||||
mLog.warn("Alias [" + alias.getName() + "] with talkgroup range [" + talkgroupRange.toString() +
|
||||
"] overlaps with alias [" + entry.getValue().getName() +
|
||||
"] with talkgroup range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
mTalkgroupRangeAliasMap.put(talkgroupRange, alias);
|
||||
}
|
||||
|
||||
@@ -532,4 +581,87 @@ public class AliasList implements Listener<AliasEvent>
|
||||
AliasList.remove(alias, mTalkgroupRangeAliasMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listing of radio IDs and ranges for a specific protocol
|
||||
*/
|
||||
public class RadioAliasList
|
||||
{
|
||||
private Map<Integer,Alias> mRadioAliasMap = new TreeMap<>();
|
||||
private Map<RadioRange, Alias> mRadioRangeAliasMap = new HashMap<>();
|
||||
|
||||
public RadioAliasList()
|
||||
{
|
||||
}
|
||||
|
||||
public Alias getAlias(RadioIdentifier identifier)
|
||||
{
|
||||
int value = identifier.getValue();
|
||||
|
||||
if(mRadioAliasMap.containsKey(value))
|
||||
{
|
||||
return mRadioAliasMap.get(value);
|
||||
}
|
||||
|
||||
for(RadioRange radioRange: mRadioRangeAliasMap.keySet())
|
||||
{
|
||||
if(radioRange.contains(value))
|
||||
{
|
||||
return mRadioRangeAliasMap.get(radioRange);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void add(Radio radio, Alias alias)
|
||||
{
|
||||
//Detect collisions
|
||||
if(mRadioAliasMap.containsKey(radio.getValue()))
|
||||
{
|
||||
Alias existing = mRadioAliasMap.get(radio.getValue());
|
||||
|
||||
mLog.warn("Alias [" + alias.getName() + "] radio ID [" + radio.getValue() +
|
||||
"] has the same value as alias [" + existing.getName() +
|
||||
"] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]");
|
||||
}
|
||||
|
||||
mRadioAliasMap.put(radio.getValue(), alias);
|
||||
}
|
||||
|
||||
public void add(RadioRange radioRange, Alias alias)
|
||||
{
|
||||
//Log warning if the new range overlaps with any existing ranges
|
||||
for(Map.Entry<RadioRange,Alias> entry: mRadioRangeAliasMap.entrySet())
|
||||
{
|
||||
if(radioRange.overlaps(entry.getKey()))
|
||||
{
|
||||
mLog.warn("Alias [" + alias.getName() + "] with radio ID range [" + radioRange.toString() +
|
||||
"] overlaps with alias [" + entry.getValue().getName() +
|
||||
"] with range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
mRadioRangeAliasMap.put(radioRange, alias);
|
||||
}
|
||||
|
||||
public void remove(Radio radio)
|
||||
{
|
||||
mRadioAliasMap.remove(radio.getValue());
|
||||
}
|
||||
|
||||
public void remove(RadioRange radioRange)
|
||||
{
|
||||
mRadioRangeAliasMap.remove(radioRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the alias from all internal maps
|
||||
*/
|
||||
public void remove(Alias alias)
|
||||
{
|
||||
AliasList.remove(alias, mRadioAliasMap);
|
||||
AliasList.remove(alias, mRadioRangeAliasMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.alias.id;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@@ -25,18 +29,20 @@ import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.alias.id.esn.Esn;
|
||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||
import io.github.dsheirer.alias.id.priority.Priority;
|
||||
import io.github.dsheirer.alias.id.radio.Radio;
|
||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||
import io.github.dsheirer.alias.id.record.Record;
|
||||
import io.github.dsheirer.alias.id.status.StatusID;
|
||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||
@JsonSubTypes({
|
||||
@@ -48,6 +54,8 @@ import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||
@JsonSubTypes.Type(value = Min.class, name = "min"),
|
||||
@JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"),
|
||||
@JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"),
|
||||
@JsonSubTypes.Type(value = Radio.class, name = "radio"),
|
||||
@JsonSubTypes.Type(value = RadioRange.class, name = "radioRange"),
|
||||
@JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"),
|
||||
@JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"),
|
||||
@JsonSubTypes.Type(value = Priority.class, name = "priority"),
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.alias.id;
|
||||
|
||||
@@ -28,6 +30,8 @@ public enum AliasIDType
|
||||
LTR_NET_UID("LTR-Net UID"),
|
||||
MIN("Passport MIN"),
|
||||
PRIORITY("Audio Priority"),
|
||||
RADIO_ID("Radio ID"),
|
||||
RADIO_ID_RANGE("Radio ID Range"),
|
||||
RECORD("Record"),
|
||||
SITE("Site"),
|
||||
STATUS("Status"),
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.alias.id;
|
||||
|
||||
@@ -124,6 +126,8 @@ public class AliasIdentifierEditor extends Editor<Alias>
|
||||
|
||||
menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP));
|
||||
menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP_RANGE));
|
||||
menu.add(new AddAliasIdentifierItem(AliasIDType.RADIO_ID));
|
||||
menu.add(new AddAliasIdentifierItem(AliasIDType.RADIO_ID_RANGE));
|
||||
menu.add(new AddAliasIdentifierItem(AliasIDType.ESN));
|
||||
// menu.add(new AddAliasIdentifierItem(AliasIDType.LOJACK));
|
||||
menu.add(new AddAliasIdentifierItem(AliasIDType.STATUS));
|
||||
|
||||
118
src/main/java/io/github/dsheirer/alias/id/radio/Radio.java
Normal file
118
src/main/java/io/github/dsheirer/alias/id/radio/Radio.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.alias.id.radio;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormat;
|
||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Integer radio identifier with protocol.
|
||||
*/
|
||||
public class Radio extends AliasID
|
||||
{
|
||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||
private int mValue;
|
||||
|
||||
public Radio()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
public Radio(Protocol protocol, int value)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(Protocol protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
if(mProtocol == null || mProtocol == Protocol.UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(mProtocol);
|
||||
return talkgroupFormat.getMinimumValidValue() <= mValue && mValue <= talkgroupFormat.getMaximumValidValue();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Radio ID:").append(TalkgroupFormatter.format(mProtocol, mValue));
|
||||
sb.append(" Protocol:").append((mProtocol));
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(id instanceof Radio)
|
||||
{
|
||||
Radio tgid = (Radio)id;
|
||||
return (getProtocol() == tgid.getProtocol()) && (getValue() == tgid.getValue());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.RADIO_ID;
|
||||
}
|
||||
}
|
||||
235
src/main/java/io/github/dsheirer/alias/id/radio/RadioEditor.java
Normal file
235
src/main/java/io/github/dsheirer/alias/id/radio/RadioEditor.java
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.alias.id.radio;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class RadioEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RadioEditor.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 ";
|
||||
private MaskFormatter mMaskFormatter = new MaskFormatter();
|
||||
private JComboBox<Protocol> mComboProtocol;
|
||||
private JFormattedTextField mRadioIdField;
|
||||
|
||||
|
||||
public RadioEditor(AliasID aliasID)
|
||||
{
|
||||
initGUI();
|
||||
setItem(aliasID);
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
||||
|
||||
add(new JLabel("Protocol:"));
|
||||
|
||||
mComboProtocol = new JComboBox<>();
|
||||
|
||||
DefaultComboBoxModel<Protocol> model = new DefaultComboBoxModel<>();
|
||||
|
||||
for(Protocol protocol : Protocol.RADIO_ID_PROTOCOLS)
|
||||
{
|
||||
model.addElement(protocol);
|
||||
}
|
||||
|
||||
mComboProtocol.setModel(model);
|
||||
mComboProtocol.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
Protocol protocol = (Protocol)mComboProtocol.getSelectedItem();
|
||||
updateEditor(protocol);
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(mComboProtocol);
|
||||
|
||||
add(new JLabel("Value:"));
|
||||
|
||||
mRadioIdField = new JFormattedTextField(mMaskFormatter);
|
||||
mRadioIdField.getDocument().addDocumentListener(this);
|
||||
add(mRadioIdField, "growx,push");
|
||||
|
||||
JLabel help = new JLabel("Help ...");
|
||||
help.setForeground(Color.BLUE.brighter());
|
||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
help.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
RadioFormat radioFormat = RadioFormat.get(getCurrentProtocol());
|
||||
JOptionPane.showMessageDialog(RadioEditor.this,
|
||||
radioFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
});
|
||||
add(help, "align left");
|
||||
}
|
||||
|
||||
private void updateEditor(Protocol protocol)
|
||||
{
|
||||
RadioFormat mask = RadioFormat.get(protocol);
|
||||
int currentValue = 0;
|
||||
Radio radio = getRadio();
|
||||
|
||||
if(radio != null)
|
||||
{
|
||||
currentValue = radio.getValue();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mRadioIdField.setValue(null);
|
||||
mRadioIdField.setToolTipText(mask.getValidRangeHelpText());
|
||||
mMaskFormatter.setMask(mask.getMask());
|
||||
if(mask.getMask().contains("*"))
|
||||
{
|
||||
mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS);
|
||||
}
|
||||
mRadioIdField.setValue(RadioFormatter.format(protocol, currentValue));
|
||||
}
|
||||
catch(ParseException pe)
|
||||
{
|
||||
mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private Protocol getCurrentProtocol()
|
||||
{
|
||||
return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Current talkgroup alias id
|
||||
*/
|
||||
public Radio getRadio()
|
||||
{
|
||||
if(getItem() instanceof Radio)
|
||||
{
|
||||
return (Radio)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(AliasID aliasID)
|
||||
{
|
||||
super.setItem(aliasID);
|
||||
|
||||
Radio radio = getRadio();
|
||||
|
||||
if(radio != null)
|
||||
{
|
||||
Protocol currentProtocol = getCurrentProtocol();
|
||||
if(currentProtocol == radio.getProtocol())
|
||||
{
|
||||
updateEditor(radio.getProtocol());
|
||||
}
|
||||
mComboProtocol.getModel().setSelectedItem(radio.getProtocol());
|
||||
|
||||
String formatted = RadioFormatter.format(radio.getProtocol(), radio.getValue());
|
||||
mRadioIdField.setValue(formatted);
|
||||
}
|
||||
else
|
||||
{
|
||||
mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN);
|
||||
mRadioIdField.setValue(RadioFormatter.format(getCurrentProtocol(), 0));
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
Radio radio = getRadio();
|
||||
|
||||
if(radio != null)
|
||||
{
|
||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
||||
|
||||
if(protocol == null)
|
||||
{
|
||||
protocol = Protocol.UNKNOWN;
|
||||
}
|
||||
|
||||
int value = -1;
|
||||
|
||||
try
|
||||
{
|
||||
value = RadioFormatter.parse(protocol, mRadioIdField.getText());
|
||||
}
|
||||
catch(ParseException pe)
|
||||
{
|
||||
//ignore ... value is still -1 and outside valid value range
|
||||
}
|
||||
|
||||
RadioFormat mask = RadioFormat.get(protocol);
|
||||
|
||||
//Check for valid value within range ... notify user but allow value to persist
|
||||
if(value < mask.getMinimumValidValue() || value > mask.getMaximumValidValue())
|
||||
{
|
||||
String message = "Invalid value [" + mRadioIdField.getText() + "]. " + protocol.name() +
|
||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
||||
|
||||
JOptionPane.showMessageDialog(RadioEditor.this, message, "Invalid Radio ID Value",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
radio.setValue(value);
|
||||
}
|
||||
|
||||
radio.setProtocol(protocol);
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
}
|
||||
117
src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java
Normal file
117
src/main/java/io/github/dsheirer/alias/id/radio/RadioFormat.java
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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.alias.id.radio;
|
||||
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Radio ID protocol identifier entry masks and min/max valid ranges and valid range descriptions
|
||||
*
|
||||
* Note: the getMask() value is intended for use with a MaskFormatter attached to a JFormattedTextField. The
|
||||
* protocol masks that have a dash (-) use the # (any digit) character and the protocol masks that do not have
|
||||
* a formatting dash use an asterisk (*). Within the editor(s) that use this enumeration, the editor must use
|
||||
* a setValidCharacters() on the mask formatter whenever it detects the mask containing one or more asterisks.
|
||||
*/
|
||||
public enum RadioFormat
|
||||
{
|
||||
APCO25("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
||||
"<html>APCO25 valid range is 0 to 16,777,215"),
|
||||
UNKNOWN("********", 1, 0xFFFFFF, "1 to 16,777,215",
|
||||
"Unknown protocol valid value range is 1-16,777,215");
|
||||
|
||||
private String mMask;
|
||||
private int mMinimumValue;
|
||||
private int mMaximumValue;
|
||||
private String mValidRangeDescription;
|
||||
private String mValidRangeHelpText;
|
||||
|
||||
RadioFormat(String mask, int minimumValue, int maximumValue, String validRangeDescription, String validRangeHelpText)
|
||||
{
|
||||
mMask = mask;
|
||||
mMinimumValue = minimumValue;
|
||||
mMaximumValue = maximumValue;
|
||||
mValidRangeDescription = validRangeDescription;
|
||||
mValidRangeHelpText = validRangeHelpText;
|
||||
}
|
||||
|
||||
public String getMask()
|
||||
{
|
||||
return mMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum valid value for the protocol
|
||||
*/
|
||||
public int getMinimumValidValue()
|
||||
{
|
||||
return mMinimumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum valid value for the protocol
|
||||
*/
|
||||
public int getMaximumValidValue()
|
||||
{
|
||||
return mMaximumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the value is valid for the protocol
|
||||
*/
|
||||
public boolean isValid(int value)
|
||||
{
|
||||
return getMinimumValidValue() <= value && value <= getMaximumValidValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Short description of the valid value range
|
||||
*/
|
||||
public String getValidRangeDescription()
|
||||
{
|
||||
return mValidRangeDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Help text for valid value ranges
|
||||
*/
|
||||
public String getValidRangeHelpText()
|
||||
{
|
||||
return mValidRangeHelpText;
|
||||
}
|
||||
|
||||
public static RadioFormat get(Protocol protocol)
|
||||
{
|
||||
if(protocol == null)
|
||||
{
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
switch(protocol)
|
||||
{
|
||||
case APCO25:
|
||||
return APCO25;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.alias.id.radio;
|
||||
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
||||
import io.github.dsheirer.preference.identifier.talkgroup.IntegerFormatter;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Protocol-specific formatting of radio id values and parsing of formatted radio id values.
|
||||
*/
|
||||
public class RadioFormatter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RadioFormatter.class);
|
||||
private static Map<Protocol,IntegerFormatter> mFormatterMap = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter());
|
||||
mFormatterMap.put(Protocol.UNKNOWN, new IntegerFormatter());
|
||||
}
|
||||
|
||||
public RadioFormatter()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the formatted value into an integer using a protocol specific formatter
|
||||
*/
|
||||
public static int parse(Protocol protocol, String value) throws ParseException
|
||||
{
|
||||
IntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the integer value using a protocol-specific formatter
|
||||
*/
|
||||
public static String format(Protocol protocol, int value)
|
||||
{
|
||||
IntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||
|
||||
if(formatter == null)
|
||||
{
|
||||
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||
}
|
||||
|
||||
return formatter.format(value);
|
||||
}
|
||||
}
|
||||
166
src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java
Normal file
166
src/main/java/io/github/dsheirer/alias/id/radio/RadioRange.java
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.alias.id.radio;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* Integer radio identifier range of values with protocol.
|
||||
*/
|
||||
public class RadioRange extends AliasID
|
||||
{
|
||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||
private int mMinRadio;
|
||||
private int mMaxRadio;
|
||||
|
||||
public RadioRange()
|
||||
{
|
||||
//No arg JAXB constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a radio range of from - to radio values (inclusive) for the specified protocol
|
||||
* @param protocol for the radio range
|
||||
* @param minRadio starting or minimum radio value
|
||||
* @param maxRadio ending or maximum radio value
|
||||
*/
|
||||
public RadioRange(Protocol protocol, int minRadio, int maxRadio)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
mMinRadio = minRadio;
|
||||
mMaxRadio = maxRadio;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
||||
public int getMinRadio()
|
||||
{
|
||||
return mMinRadio;
|
||||
}
|
||||
|
||||
public void setMinRadio(int minRadio)
|
||||
{
|
||||
mMinRadio = minRadio;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
||||
public int getMaxRadio()
|
||||
{
|
||||
return mMaxRadio;
|
||||
}
|
||||
|
||||
public void setMaxRadio(int maxRadio)
|
||||
{
|
||||
mMaxRadio = maxRadio;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||
public Protocol getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(Protocol protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
if(mProtocol == null || mProtocol == Protocol.UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RadioFormat radioFormat = RadioFormat.get(mProtocol);
|
||||
return radioFormat.getMinimumValidValue() <= mMinRadio &&
|
||||
mMinRadio <= radioFormat.getMaximumValidValue() &&
|
||||
radioFormat.getMinimumValidValue() <= mMaxRadio &&
|
||||
mMaxRadio <= radioFormat.getMaximumValidValue();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Radio ID Range:").append(RadioFormatter.format(mProtocol, mMinRadio));
|
||||
sb.append(" to ").append(RadioFormatter.format(mProtocol, mMaxRadio));
|
||||
sb.append(" Protocol:").append(mProtocol != null ? mProtocol : "unspecified");
|
||||
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append(" **NOT VALID**");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(AliasID id)
|
||||
{
|
||||
if(id instanceof RadioRange)
|
||||
{
|
||||
RadioRange tgid = (RadioRange)id;
|
||||
return (getProtocol() == tgid.getProtocol()) &&
|
||||
(getMinRadio() == tgid.getMinRadio()) &&
|
||||
(getMaxRadio() == tgid.getMaxRadio());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this radio range contains/includes the value
|
||||
* @param radioValue
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(int radioValue)
|
||||
{
|
||||
return getMinRadio() <= radioValue && radioValue <= getMaxRadio();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
||||
* @param radioRange to check for overlap
|
||||
* @return true if the ranges overlap
|
||||
*/
|
||||
public boolean overlaps(RadioRange radioRange)
|
||||
{
|
||||
return contains(radioRange.getMinRadio()) ||
|
||||
contains(radioRange.getMaxRadio()) ||
|
||||
(getMinRadio() < radioRange.getMinRadio() &&
|
||||
radioRange.getMaxRadio() < getMaxRadio()) ||
|
||||
(radioRange.getMinRadio() < getMinRadio() &&
|
||||
getMaxRadio() < radioRange.getMaxRadio());
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
{
|
||||
return AliasIDType.RADIO_ID_RANGE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.alias.id.radio;
|
||||
|
||||
import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.text.MaskFormatter;
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.ParseException;
|
||||
|
||||
public class RadioRangeEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RadioRangeEditor.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 ";
|
||||
private MaskFormatter mMaskFormatter = new MaskFormatter();
|
||||
private JComboBox<Protocol> mComboProtocol;
|
||||
private JFormattedTextField mMinRadioField;
|
||||
private JFormattedTextField mMaxRadioField;
|
||||
|
||||
public RadioRangeEditor(AliasID aliasID)
|
||||
{
|
||||
initGUI();
|
||||
|
||||
setItem(aliasID);
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
||||
|
||||
add(new JLabel("Protocol:"));
|
||||
|
||||
mComboProtocol = new JComboBox<Protocol>();
|
||||
|
||||
DefaultComboBoxModel<Protocol> model = new DefaultComboBoxModel<>();
|
||||
|
||||
for(Protocol protocol : Protocol.RADIO_ID_PROTOCOLS)
|
||||
{
|
||||
model.addElement(protocol);
|
||||
}
|
||||
|
||||
mComboProtocol.setModel(model);
|
||||
mComboProtocol.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
Protocol protocol = (Protocol)mComboProtocol.getSelectedItem();
|
||||
updateEditor(protocol);
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(mComboProtocol);
|
||||
|
||||
add(new JLabel("Min:"));
|
||||
|
||||
mMinRadioField = new JFormattedTextField(mMaskFormatter);
|
||||
mMinRadioField.getDocument().addDocumentListener(this);
|
||||
add(mMinRadioField, "growx,push");
|
||||
|
||||
add(new JLabel("Max:"));
|
||||
|
||||
mMaxRadioField = new JFormattedTextField(mMaskFormatter);
|
||||
mMaxRadioField.getDocument().addDocumentListener(this);
|
||||
add(mMaxRadioField, "growx,push");
|
||||
|
||||
JLabel help = new JLabel("Help ...");
|
||||
help.setForeground(Color.BLUE.brighter());
|
||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
help.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
RadioFormat radioFormat = RadioFormat.get(getCurrentProtocol());
|
||||
JOptionPane.showMessageDialog(RadioRangeEditor.this,
|
||||
radioFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
});
|
||||
add(help, "align left");
|
||||
}
|
||||
|
||||
private void updateEditor(Protocol protocol)
|
||||
{
|
||||
RadioFormat mask = RadioFormat.get(protocol);
|
||||
int currentMinValue = 0;
|
||||
int currentMaxValue = 0;
|
||||
RadioRange radioRange = getRadioRange();
|
||||
|
||||
if(radioRange != null)
|
||||
{
|
||||
currentMinValue = radioRange.getMinRadio();
|
||||
currentMaxValue = radioRange.getMaxRadio();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mMinRadioField.setValue(null);
|
||||
mMaxRadioField.setValue(null);
|
||||
|
||||
mMinRadioField.setToolTipText(mask.getValidRangeHelpText());
|
||||
mMaxRadioField.setToolTipText(mask.getValidRangeHelpText());
|
||||
mMaskFormatter.setMask(mask.getMask());
|
||||
if(mask.getMask().contains("*"))
|
||||
{
|
||||
mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS);
|
||||
}
|
||||
|
||||
mMinRadioField.setValue(RadioFormatter.format(protocol, currentMinValue));
|
||||
mMaxRadioField.setValue(RadioFormatter.format(protocol, currentMaxValue));
|
||||
}
|
||||
catch(ParseException pe)
|
||||
{
|
||||
mLog.error("Error applying radio editor mask to mask formatter [" + mask.getMask() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private Protocol getCurrentProtocol()
|
||||
{
|
||||
return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public RadioRange getRadioRange()
|
||||
{
|
||||
if(getItem() instanceof RadioRange)
|
||||
{
|
||||
return (RadioRange)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(AliasID aliasID)
|
||||
{
|
||||
super.setItem(aliasID);
|
||||
|
||||
RadioRange radioRange = getRadioRange();
|
||||
|
||||
updateEditor(radioRange.getProtocol());
|
||||
|
||||
if(radioRange != null)
|
||||
{
|
||||
mComboProtocol.getModel().setSelectedItem(radioRange.getProtocol());
|
||||
String minFormatted = RadioFormatter.format(radioRange.getProtocol(), radioRange.getMinRadio());
|
||||
mMinRadioField.setValue(minFormatted);
|
||||
String maxFormatted = RadioFormatter.format(radioRange.getProtocol(), radioRange.getMaxRadio());
|
||||
mMaxRadioField.setValue(maxFormatted);
|
||||
}
|
||||
else
|
||||
{
|
||||
mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN);
|
||||
mMinRadioField.setValue(RadioFormatter.format(getCurrentProtocol(), 0));
|
||||
mMaxRadioField.setValue(RadioFormatter.format(getCurrentProtocol(), 0));
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
RadioRange radioRange = getRadioRange();
|
||||
|
||||
if(radioRange != null)
|
||||
{
|
||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
||||
|
||||
if(protocol == null)
|
||||
{
|
||||
protocol = Protocol.UNKNOWN;
|
||||
}
|
||||
|
||||
radioRange.setProtocol(protocol);
|
||||
|
||||
int minValue = -1;
|
||||
int maxValue = -1;
|
||||
|
||||
try
|
||||
{
|
||||
minValue = RadioFormatter.parse(protocol, mMinRadioField.getText());
|
||||
}
|
||||
catch(ParseException pe)
|
||||
{
|
||||
//ignore ... value is still -1 and outside valid value range
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
maxValue = RadioFormatter.parse(protocol, mMaxRadioField.getText());
|
||||
}
|
||||
catch(ParseException pe)
|
||||
{
|
||||
//ignore ... value is still -1 and outside valid value range
|
||||
}
|
||||
|
||||
RadioFormat mask = RadioFormat.get(protocol);
|
||||
|
||||
//Check for valid value within range ... notify user but allow value to persist
|
||||
if(minValue < mask.getMinimumValidValue() || minValue > mask.getMaximumValidValue())
|
||||
{
|
||||
String message = "Invalid minimum value [" + mMinRadioField.getText() + "]. " + protocol.name() +
|
||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
||||
|
||||
JOptionPane.showMessageDialog(RadioRangeEditor.this, message,
|
||||
"Invalid Radio Value", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
radioRange.setMinRadio(minValue);
|
||||
}
|
||||
|
||||
//Check for valid value within range ... notify user but allow value to persist
|
||||
if(maxValue < mask.getMinimumValidValue() || maxValue > mask.getMaximumValidValue())
|
||||
{
|
||||
String message = "Invalid maximum value [" + mMaxRadioField.getText() + "]. " + protocol.name() +
|
||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
||||
|
||||
JOptionPane.showMessageDialog(RadioRangeEditor.this, message,
|
||||
"Invalid Radio Value", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
radioRange.setMaxRadio(maxValue);
|
||||
}
|
||||
|
||||
radioRange.setProtocol(protocol);
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
}
|
||||
@@ -193,6 +193,11 @@ public class TalkgroupEditor extends DocumentListenerEditor<AliasID>
|
||||
{
|
||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
||||
|
||||
if(protocol == null)
|
||||
{
|
||||
protocol = Protocol.UNKNOWN;
|
||||
}
|
||||
|
||||
int value = -1;
|
||||
|
||||
try
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.alias.id.talkgroup;
|
||||
@@ -32,8 +34,8 @@ import io.github.dsheirer.protocol.Protocol;
|
||||
*/
|
||||
public enum TalkgroupFormat
|
||||
{
|
||||
APCO25("********", 1, 0xFFFFFF, "1 to 16,777,215",
|
||||
"<html>APCO25 valid range is 1 to 65,535(talkgroup)<br>or 1 to 16,777,215 (radio ID)"),
|
||||
APCO25("********", 0, 0xFFFFFF, "0 to 65,535",
|
||||
"<html>APCO25 talkgroup valid range is 0 to 65,535"),
|
||||
FLEETSYNC("###-####", 1, 0x7FFFFF, "001-0001 to 127-8192",
|
||||
"<html>Fleetsync valid ranges are 1-127(prefix)<br>and 1-8192(ident) (ie. 001-0001 to 127-8192)"),
|
||||
LTR("##-###", 257, 5375, "01-001 to 20-255",
|
||||
@@ -83,6 +85,14 @@ public enum TalkgroupFormat
|
||||
return mMaximumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the value is valid for the protocol
|
||||
*/
|
||||
public boolean isValid(int value)
|
||||
{
|
||||
return getMinimumValidValue() <= value && value <= getMaximumValidValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Short description of the valid value range
|
||||
*/
|
||||
@@ -99,9 +109,14 @@ public enum TalkgroupFormat
|
||||
return mValidRangeHelpText;
|
||||
}
|
||||
|
||||
public static TalkgroupFormat get(Protocol prococol)
|
||||
public static TalkgroupFormat get(Protocol protocol)
|
||||
{
|
||||
switch(prococol)
|
||||
if(protocol == null)
|
||||
{
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
switch(protocol)
|
||||
{
|
||||
case APCO25:
|
||||
return APCO25;
|
||||
|
||||
@@ -140,6 +140,21 @@ public class TalkgroupRange extends AliasID
|
||||
return getMinTalkgroup() <= talkgroupValue && talkgroupValue <= getMaxTalkgroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
||||
* @param talkgroupRange to check for overlap
|
||||
* @return true if the ranges overlap
|
||||
*/
|
||||
public boolean overlaps(TalkgroupRange talkgroupRange)
|
||||
{
|
||||
return contains(talkgroupRange.getMinTalkgroup()) ||
|
||||
contains(talkgroupRange.getMaxTalkgroup()) ||
|
||||
(getMinTalkgroup() < talkgroupRange.getMinTalkgroup() &&
|
||||
talkgroupRange.getMaxTalkgroup() < getMaxTalkgroup()) ||
|
||||
(talkgroupRange.getMinTalkgroup() < getMinTalkgroup() &&
|
||||
getMaxTalkgroup() < talkgroupRange.getMaxTalkgroup());
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public AliasIDType getType()
|
||||
|
||||
@@ -174,6 +174,8 @@ public class TalkgroupRangeEditor extends DocumentListenerEditor<AliasID>
|
||||
|
||||
TalkgroupRange talkgroupRange = getTalkgroupRange();
|
||||
|
||||
updateEditor(talkgroupRange.getProtocol());
|
||||
|
||||
if(talkgroupRange != null)
|
||||
{
|
||||
mComboProtocol.getModel().setSelectedItem(talkgroupRange.getProtocol());
|
||||
@@ -202,6 +204,12 @@ public class TalkgroupRangeEditor extends DocumentListenerEditor<AliasID>
|
||||
if(talkgroupRange != null)
|
||||
{
|
||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
||||
|
||||
if(protocol == null)
|
||||
{
|
||||
protocol = Protocol.UNKNOWN;
|
||||
}
|
||||
|
||||
talkgroupRange.setProtocol(protocol);
|
||||
|
||||
int minValue = -1;
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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;
|
||||
|
||||
import io.github.dsheirer.audio.squelch.SquelchState;
|
||||
import io.github.dsheirer.audio.squelch.SquelchStateEvent;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter2;
|
||||
@@ -136,7 +139,7 @@ public class AudioModule extends AbstractAudioModule implements IReusableBufferL
|
||||
|
||||
|
||||
@Override
|
||||
public Listener<SquelchState> getSquelchStateListener()
|
||||
public Listener<SquelchStateEvent> getSquelchStateListener()
|
||||
{
|
||||
return mSquelchStateListener;
|
||||
}
|
||||
@@ -169,7 +172,7 @@ public class AudioModule extends AbstractAudioModule implements IReusableBufferL
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener getReusableBufferListener()
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
{
|
||||
//Redirect received reusable buffers to the receive(buffer) method
|
||||
return this;
|
||||
@@ -178,12 +181,12 @@ public class AudioModule extends AbstractAudioModule implements IReusableBufferL
|
||||
/**
|
||||
* Wrapper for squelch state listener
|
||||
*/
|
||||
public class SquelchStateListener implements Listener<SquelchState>
|
||||
public class SquelchStateListener implements Listener<SquelchStateEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(SquelchState state)
|
||||
public void receive(SquelchStateEvent event)
|
||||
{
|
||||
if(state == SquelchState.SQUELCH && hasAudioPacketListener())
|
||||
if(event.getSquelchState() == SquelchState.SQUELCH && hasAudioPacketListener())
|
||||
{
|
||||
ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer();
|
||||
endAudioPacket.resetAttributes();
|
||||
@@ -192,7 +195,7 @@ public class AudioModule extends AbstractAudioModule implements IReusableBufferL
|
||||
getAudioPacketListener().receive(endAudioPacket);
|
||||
}
|
||||
|
||||
mSquelchState = state;
|
||||
mSquelchState = event.getSquelchState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast;
|
||||
|
||||
import io.github.dsheirer.audio.convert.ISilenceGenerator;
|
||||
@@ -373,9 +376,9 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
||||
broadcastAudio(mSilenceGenerator.generate(PROCESSOR_RUN_INTERVAL_MS));
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error while processing audio streaming queue", e);
|
||||
mLog.error("Error while processing audio streaming queue", t);
|
||||
}
|
||||
|
||||
mProcessing.set(false);
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast;
|
||||
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
@@ -40,6 +43,7 @@ import io.github.dsheirer.audio.convert.MP3SilenceGenerator;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.record.AudioRecorder;
|
||||
import io.github.dsheirer.record.mp3.MP3Recorder;
|
||||
import org.slf4j.Logger;
|
||||
@@ -147,7 +151,8 @@ public class BroadcastFactory
|
||||
* @param broadcastModel model for broadcastAudio configurations
|
||||
* @return an editor for the specified broadcastAudio configuration
|
||||
*/
|
||||
public static Editor<BroadcastConfiguration> getEditor(BroadcastConfiguration configuration,
|
||||
public static Editor<BroadcastConfiguration> getEditor(UserPreferences userPreferences,
|
||||
BroadcastConfiguration configuration,
|
||||
BroadcastModel broadcastModel,
|
||||
AliasModel aliasModel,
|
||||
IconManager iconManager)
|
||||
@@ -157,7 +162,7 @@ public class BroadcastFactory
|
||||
switch(configuration.getBroadcastServerType())
|
||||
{
|
||||
case BROADCASTIFY:
|
||||
editor = new BroadcastifyConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
||||
editor = new BroadcastifyConfigurationEditor(userPreferences, broadcastModel, aliasModel, iconManager);
|
||||
break;
|
||||
case ICECAST_TCP:
|
||||
editor = new IcecastTCPConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast;
|
||||
|
||||
@@ -586,12 +588,20 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Reusa
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
if(columnIndex == COLUMN_SERVER_ICON)
|
||||
switch(columnIndex)
|
||||
{
|
||||
return ImageIcon.class;
|
||||
case COLUMN_BROADCASTER_STATUS:
|
||||
return BroadcastState.class;
|
||||
case COLUMN_BROADCASTER_AGED_OFF_COUNT:
|
||||
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
||||
case COLUMN_BROADCASTER_STREAMED_COUNT:
|
||||
return Integer.class;
|
||||
case COLUMN_SERVER_ICON:
|
||||
return ImageIcon.class;
|
||||
case COLUMN_STREAM_NAME:
|
||||
default:
|
||||
return String.class;
|
||||
}
|
||||
|
||||
return String.class;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
@@ -23,6 +26,7 @@ import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -51,6 +55,7 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
|
||||
private BroadcastModel mBroadcastModel;
|
||||
private AliasModel mAliasModel;
|
||||
private IconManager mIconManager;
|
||||
private UserPreferences mUserPreferences;
|
||||
|
||||
private BroadcastStatusPanel mBroadcastStatusPanel;
|
||||
private JideSplitPane mSplitPane;
|
||||
@@ -60,12 +65,14 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
|
||||
private JButton mCopyButton = new JButton(COPY_BROADCAST_CONFIGURATION);
|
||||
private JButton mDeleteButton = new JButton(DELETE_BROADCAST_CONFIGURATION);
|
||||
|
||||
public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager)
|
||||
public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager,
|
||||
UserPreferences userPreferences)
|
||||
{
|
||||
mBroadcastModel = broadcastModel;
|
||||
mAliasModel = aliasModel;
|
||||
mIconManager = iconManager;
|
||||
mEditor = mEmptyEditor;
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
init();
|
||||
}
|
||||
@@ -74,7 +81,8 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
|
||||
|
||||
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
|
||||
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel, mUserPreferences,
|
||||
"broadcast.panel.broadcast.status.panel");
|
||||
|
||||
mBroadcastStatusPanel.getTable().setAutoCreateRowSorter(true);
|
||||
mBroadcastStatusPanel.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@@ -210,8 +218,8 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
|
||||
|
||||
if (selectedConfiguration != null)
|
||||
{
|
||||
mEditor = BroadcastFactory.getEditor(selectedConfiguration, mBroadcastModel, mAliasModel,
|
||||
mIconManager);
|
||||
mEditor = BroadcastFactory.getEditor(mUserPreferences, selectedConfiguration, mBroadcastModel,
|
||||
mAliasModel, mIconManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*******************************************************************************
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
* 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
|
||||
@@ -14,10 +15,12 @@
|
||||
*
|
||||
* 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.broadcast;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
@@ -32,12 +35,17 @@ import java.awt.Component;
|
||||
public class BroadcastStatusPanel extends JPanel
|
||||
{
|
||||
private JTable mTable;
|
||||
private JTableColumnWidthMonitor mColumnWidthMonitor;
|
||||
private JScrollPane mScrollPane;
|
||||
private BroadcastModel mBroadcastModel;
|
||||
private UserPreferences mUserPreferences;
|
||||
private String mPreferenceKey;
|
||||
|
||||
public BroadcastStatusPanel(BroadcastModel broadcastModel)
|
||||
public BroadcastStatusPanel(BroadcastModel broadcastModel, UserPreferences userPreferences, String preferenceKey)
|
||||
{
|
||||
mBroadcastModel = broadcastModel;
|
||||
mUserPreferences = userPreferences;
|
||||
mPreferenceKey = preferenceKey;
|
||||
|
||||
init();
|
||||
}
|
||||
@@ -57,6 +65,7 @@ public class BroadcastStatusPanel extends JPanel
|
||||
renderer.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
mTable.getColumnModel().getColumn(BroadcastModel.COLUMN_BROADCASTER_STATUS).setCellRenderer(new StatusCellRenderer());
|
||||
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, mPreferenceKey);
|
||||
|
||||
mScrollPane = new JScrollPane(mTable);
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast;
|
||||
|
||||
@@ -200,7 +202,16 @@ public class StreamManager implements Listener<ReusableAudioPacket>
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
|
||||
sb.append(sNextRecordingNumber.incrementAndGet()).append("_");
|
||||
|
||||
int recordingNumber = sNextRecordingNumber.incrementAndGet();
|
||||
|
||||
//Check for integer overflow and readjust negative value to 0
|
||||
if(recordingNumber < 0)
|
||||
{
|
||||
sNextRecordingNumber.set(0);
|
||||
recordingNumber = sNextRecordingNumber.incrementAndGet();
|
||||
}
|
||||
sb.append(recordingNumber).append("_");
|
||||
sb.append(TimeStamp.getLongTimeStamp("_"));
|
||||
sb.append(mBroadcastFormat.getFileExtension());
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast.broadcastify;
|
||||
|
||||
import com.radioreference.api.soap2.UserFeedBroadcast;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastConfigurationEditor;
|
||||
@@ -26,6 +28,8 @@ import io.github.dsheirer.audio.broadcast.BroadcastEvent;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
@@ -64,10 +68,13 @@ public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEdito
|
||||
private JCheckBox mEnabled;
|
||||
private JButton mSaveButton;
|
||||
private JButton mResetButton;
|
||||
private UserPreferences mUserPreferences;
|
||||
|
||||
public BroadcastifyConfigurationEditor(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager)
|
||||
public BroadcastifyConfigurationEditor(UserPreferences userPreferences, BroadcastModel broadcastModel,
|
||||
AliasModel aliasModel, IconManager iconManager)
|
||||
{
|
||||
super(broadcastModel, aliasModel, iconManager);
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
init();
|
||||
}
|
||||
@@ -91,7 +98,7 @@ public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEdito
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
final UserFeedSelectionDialog dialog = new UserFeedSelectionDialog(BroadcastifyConfigurationEditor.this);
|
||||
final UserFeedSelectionDialog dialog = new UserFeedSelectionDialog(mUserPreferences,BroadcastifyConfigurationEditor.this);
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@@ -391,7 +398,7 @@ public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEdito
|
||||
@Override
|
||||
public void receive(UserFeedBroadcast userFeedBroadcast)
|
||||
{
|
||||
mName.setText(userFeedBroadcast.getDescr());
|
||||
mName.setText(userFeedBroadcast.getDescription());
|
||||
mServer.setText(userFeedBroadcast.getHostname());
|
||||
mPort.setText(userFeedBroadcast.getPort());
|
||||
mMountPoint.setText(userFeedBroadcast.getMount());
|
||||
|
||||
@@ -1,78 +1,155 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast.broadcastify;
|
||||
|
||||
import com.radioreference.api.soap2.UserFeedBroadcast;
|
||||
import io.github.dsheirer.radioreference.RadioReferenceService;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.rrapi.RadioReferenceException;
|
||||
import io.github.dsheirer.rrapi.RadioReferenceService;
|
||||
import io.github.dsheirer.rrapi.type.AuthorizationInformation;
|
||||
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
|
||||
import io.github.dsheirer.rrapi.type.UserInfo;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListCellRenderer;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.xml.rpc.ServiceException;
|
||||
import java.awt.*;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.net.UnknownHostException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.List;
|
||||
|
||||
public class UserFeedSelectionDialog extends JDialog implements DocumentListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( UserFeedSelectionDialog.class );
|
||||
|
||||
public static final String SDRTRUNK_APP_KEY = "88969092";
|
||||
|
||||
private Listener<UserFeedBroadcast> mListener;
|
||||
private UserPreferences mUserPreferences;
|
||||
private JTextField mUserNameText;
|
||||
private JTextField mPasswordText;
|
||||
private JCheckBox mSaveCredentialsCheckBox;
|
||||
private JButton mClearStoredCredentialsButton;
|
||||
private JButton mLookupButton;
|
||||
private JList mUserFeedBroadcastList;
|
||||
private JButton mOKButton;
|
||||
|
||||
public UserFeedSelectionDialog(Listener<UserFeedBroadcast> listener)
|
||||
public UserFeedSelectionDialog(UserPreferences userPreferences, Listener<UserFeedBroadcast> listener)
|
||||
{
|
||||
mUserPreferences = userPreferences;
|
||||
mListener = listener;
|
||||
|
||||
//Register to receive radio reference preferences updates
|
||||
MyEventBus.getEventBus().register(this);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new MigLayout("","[align right,grow,fill][align left,grow,fill][]","[][][][][grow][]"));
|
||||
panel.setLayout(new MigLayout("","[align right,grow,fill][align left,grow,fill][]","[][][][][][grow][]"));
|
||||
|
||||
panel.add(new JLabel("Please enter your credentials"), "align left,span");
|
||||
|
||||
panel.add(new JLabel("User Name:"));
|
||||
mUserNameText = new JTextField();
|
||||
String storedUserName = mUserPreferences.getRadioReferencePreference().getUserName();
|
||||
if(storedUserName != null)
|
||||
{
|
||||
mUserNameText.setText(storedUserName);
|
||||
}
|
||||
mUserNameText.getDocument().addDocumentListener(this);
|
||||
panel.add(mUserNameText, "wrap");
|
||||
|
||||
panel.add(new JLabel("Password:"));
|
||||
mPasswordText = new JTextField();
|
||||
String storedPassword = mUserPreferences.getRadioReferencePreference().getPassword();
|
||||
if(storedPassword != null)
|
||||
{
|
||||
mPasswordText.setText(storedPassword);
|
||||
}
|
||||
mPasswordText.getDocument().addDocumentListener(this);
|
||||
panel.add(mPasswordText, "wrap");
|
||||
|
||||
mSaveCredentialsCheckBox = new JCheckBox("Store Credentials");
|
||||
mSaveCredentialsCheckBox.setSelected(mUserPreferences.getRadioReferencePreference().isStoreCredentials());
|
||||
mSaveCredentialsCheckBox.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
//If user unchecks box and there are stored credentials, prompt to clear them
|
||||
if(!mSaveCredentialsCheckBox.isSelected() && mUserPreferences.getRadioReferencePreference().hasStoredCredentials())
|
||||
{
|
||||
int choice = JOptionPane.showOptionDialog(UserFeedSelectionDialog.this,
|
||||
"Would you like to remove previously stored credentials from this computer?",
|
||||
"Clear Stored Credentials?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
|
||||
null, null, 0);
|
||||
|
||||
if(choice == JOptionPane.YES_OPTION)
|
||||
{
|
||||
mUserPreferences.getRadioReferencePreference().removeStoreCredentials();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
panel.add(mSaveCredentialsCheckBox);
|
||||
|
||||
mClearStoredCredentialsButton = new JButton("Remove");
|
||||
mClearStoredCredentialsButton.setToolTipText("Clear stored credentials");
|
||||
mClearStoredCredentialsButton.setEnabled(mUserPreferences.getRadioReferencePreference().hasStoredCredentials());
|
||||
mClearStoredCredentialsButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
mUserPreferences.getRadioReferencePreference().removeStoreCredentials();
|
||||
}
|
||||
});
|
||||
panel.add(mClearStoredCredentialsButton, "wrap");
|
||||
|
||||
mLookupButton = new JButton("Get Feeds");
|
||||
mLookupButton.setEnabled(false);
|
||||
mLookupButton.setEnabled(storedPassword != null && storedPassword != null);
|
||||
mLookupButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
@@ -87,45 +164,25 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
|
||||
{
|
||||
try
|
||||
{
|
||||
RadioReferenceService service =
|
||||
new RadioReferenceService(mUserNameText.getText(), mPasswordText.getText());
|
||||
AuthorizationInformation authorizationInformation = new AuthorizationInformation(SDRTRUNK_APP_KEY,
|
||||
mUserNameText.getText(), mPasswordText.getText());
|
||||
|
||||
UserFeedBroadcast[] feeds = service.getUserFeedBroadcasts();
|
||||
mLog.debug("User:" + authorizationInformation.getUserName() + " Pass:" + authorizationInformation.getPassword() + " Key:" + authorizationInformation.getApplicationKey());
|
||||
|
||||
mUserFeedBroadcastList.setListData(feeds);
|
||||
RadioReferenceService service = new RadioReferenceService(authorizationInformation);
|
||||
UserInfo userInfo = service.getUserInfo();
|
||||
mLog.info("RR User:" + userInfo.getUserName() + " Expiration Date:" + userInfo.getExpirationDate());
|
||||
List<UserFeedBroadcast> feeds = service.getUserFeeds();
|
||||
|
||||
mUserFeedBroadcastList.setListData(feeds.toArray(new UserFeedBroadcast[feeds.size()]));
|
||||
}
|
||||
catch(ServiceException se)
|
||||
catch(RadioReferenceException rre)
|
||||
{
|
||||
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
|
||||
"Unable to create radio reference web service client. See log file.",
|
||||
"Error", JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
mLog.error("Error creating the radio reference web service client", se);
|
||||
}
|
||||
catch(RemoteException re)
|
||||
{
|
||||
Throwable cause = re.getCause();
|
||||
|
||||
if(cause == null)
|
||||
{
|
||||
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
|
||||
re.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else if(cause instanceof UnknownHostException)
|
||||
{
|
||||
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
|
||||
"Broadcastify server is unavailable", "No Server",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
|
||||
"An unknown error has occurred. Please see log file.", "" +
|
||||
"Unknown Error", JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
mLog.error("Error retrieving user broadcast feed configurations from radio reference " +
|
||||
"service: ", re);
|
||||
}
|
||||
mLog.error("Error creating the radio reference web service client", rre);
|
||||
}
|
||||
|
||||
mLookupButton.setEnabled(true);
|
||||
@@ -160,6 +217,7 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
|
||||
{
|
||||
if(mListener != null)
|
||||
{
|
||||
storeCredentials();
|
||||
mListener.receive((UserFeedBroadcast)mUserFeedBroadcastList.getSelectedValue());
|
||||
}
|
||||
|
||||
@@ -182,11 +240,37 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
|
||||
setTitle("Broadcastify Feed Lookup");
|
||||
setLayout(new BorderLayout());
|
||||
add(panel, BorderLayout.CENTER);
|
||||
setSize(300,250);
|
||||
setSize(400,450);
|
||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores login credentials in the user preferences
|
||||
*/
|
||||
private void storeCredentials()
|
||||
{
|
||||
boolean store = mSaveCredentialsCheckBox.isSelected();
|
||||
mUserPreferences.getRadioReferencePreference().setStoreCredentials(store);
|
||||
|
||||
if(store)
|
||||
{
|
||||
String userName = mUserNameText.getText();
|
||||
|
||||
if(userName != null)
|
||||
{
|
||||
mUserPreferences.getRadioReferencePreference().setUserName(userName);
|
||||
}
|
||||
|
||||
String password = mPasswordText.getText();
|
||||
|
||||
if(password != null)
|
||||
{
|
||||
mUserPreferences.getRadioReferencePreference().setPassword(password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateButtons()
|
||||
{
|
||||
String username = mUserNameText.getText();
|
||||
@@ -240,8 +324,20 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
|
||||
setForeground(list.getForeground());
|
||||
}
|
||||
|
||||
setText(value.getDescr());
|
||||
setText(value.getDescription());
|
||||
|
||||
return this; }
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.RADIO_REFERENCE)
|
||||
{
|
||||
//Update the store-credentials checkbox only, so we don't change the username or password currently displayed
|
||||
mSaveCredentialsCheckBox.setSelected(mUserPreferences.getRadioReferencePreference().isStoreCredentials());
|
||||
mClearStoredCredentialsButton.setEnabled(mUserPreferences.getRadioReferencePreference().hasStoredCredentials());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast.icecast;
|
||||
|
||||
@@ -31,43 +33,26 @@ import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.properties.SystemProperties;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.apache.mina.core.RuntimeIoException;
|
||||
import org.apache.mina.core.future.ConnectFuture;
|
||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.http.HttpClientCodec;
|
||||
import org.apache.mina.http.HttpRequestImpl;
|
||||
import org.apache.mina.http.api.HttpMethod;
|
||||
import org.apache.mina.http.api.HttpRequest;
|
||||
import org.apache.mina.http.api.HttpVersion;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.channels.UnresolvedAddressException;
|
||||
import java.util.HashMap;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdater
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(IcecastBroadcastMetadataUpdater.class);
|
||||
private final static String UTF8 = "UTF-8";
|
||||
|
||||
private HttpClient mHttpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
|
||||
private IcecastConfiguration mIcecastConfiguration;
|
||||
private AliasModel mAliasModel;
|
||||
private NioSocketConnector mSocketConnector;
|
||||
private AtomicBoolean mUpdating = new AtomicBoolean();
|
||||
private Queue<String> mMetadataQueue = new LinkedTransferQueue<>();
|
||||
private Map<String,String> mHTTPHeaders;
|
||||
private boolean mStackTraceLoggingSuppressed;
|
||||
private boolean mConnectionLoggingSuppressed = false;
|
||||
|
||||
/**
|
||||
* Icecast song metadata updater. Each metadata update is processed in the order received and the dispatch of
|
||||
@@ -81,41 +66,6 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
mAliasModel = aliasModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket connector - lazy constructor.
|
||||
*/
|
||||
private NioSocketConnector getSocketConnector()
|
||||
{
|
||||
if(mSocketConnector == null)
|
||||
{
|
||||
mSocketConnector = new NioSocketConnector();
|
||||
// mSocketConnector.getFilterChain().addLast("logger", new LoggingFilter(IcecastBroadcastMetadataUpdater.class));
|
||||
mSocketConnector.getFilterChain().addLast("http_client_codec", new HttpClientCodec());
|
||||
|
||||
//Each metadata update session is single-use, so we'll shut it down upon success or failure
|
||||
mSocketConnector.setHandler(new IoHandlerAdapter()
|
||||
{
|
||||
@Override
|
||||
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
|
||||
{
|
||||
//Single-use session - close it after we receive an error
|
||||
session.closeNow();
|
||||
|
||||
mLog.error("Metadata update failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(IoSession session, Object message) throws Exception
|
||||
{
|
||||
//Single-use session - close it after we receive a response
|
||||
session.closeNow();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mSocketConnector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a song metadata update to the remote server. Additional metadata updates received while the updater
|
||||
* is in the process of sending an existing metadata update will be queued and processed in received order.
|
||||
@@ -126,66 +76,87 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
*/
|
||||
public void update(IdentifierCollection identifierCollection)
|
||||
{
|
||||
mMetadataQueue.offer(getSong(identifierCollection));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!mUpdating.get() && !mMetadataQueue.isEmpty())
|
||||
try
|
||||
{
|
||||
Runnable runnable = new Runnable()
|
||||
sb.append("http://");
|
||||
sb.append(mIcecastConfiguration.getHost());
|
||||
sb.append(":");
|
||||
sb.append(mIcecastConfiguration.getPort());
|
||||
sb.append("/admin/metadata?mode=updinfo&mount=");
|
||||
sb.append(URLEncoder.encode(mIcecastConfiguration.getMountPoint(), UTF8));
|
||||
sb.append("&charset=UTF%2d8");
|
||||
sb.append("&song=").append(URLEncoder.encode(getSong(identifierCollection), UTF8));
|
||||
}
|
||||
catch(UnsupportedEncodingException uee)
|
||||
{
|
||||
mLog.error("Error encoding metadata information to UTF-8", uee);
|
||||
sb = null;
|
||||
}
|
||||
|
||||
if(sb != null)
|
||||
{
|
||||
final String metadataUpdateURL = sb.toString();
|
||||
URI uri = URI.create(metadataUpdateURL);
|
||||
|
||||
ThreadPool.SCHEDULED.submit(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(!mMetadataQueue.isEmpty() && mUpdating.compareAndSet(false, true))
|
||||
try
|
||||
{
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(uri)
|
||||
.header(IcecastHeader.AUTHORIZATION.getValue(), mIcecastConfiguration.getBase64EncodedCredentials())
|
||||
.header(IcecastHeader.USER_AGENT.getValue(), SystemProperties.getInstance().getApplicationName())
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = null;
|
||||
|
||||
try
|
||||
{
|
||||
ConnectFuture connectFuture = getSocketConnector()
|
||||
.connect(new InetSocketAddress(mIcecastConfiguration.getHost(),
|
||||
mIcecastConfiguration.getPort()));
|
||||
connectFuture.awaitUninterruptibly();
|
||||
IoSession session = connectFuture.getSession();
|
||||
|
||||
if(session != null)
|
||||
response = mHttpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
if(!mConnectionLoggingSuppressed)
|
||||
{
|
||||
String song = mMetadataQueue.poll();
|
||||
mLog.error("IO Error submitting Icecast metadata update [" +
|
||||
(metadataUpdateURL != null ? metadataUpdateURL : "no url"), ioe);
|
||||
mConnectionLoggingSuppressed = true;
|
||||
}
|
||||
}
|
||||
catch(InterruptedException ie)
|
||||
{
|
||||
mLog.error("Interrupted Exception Error", ie);
|
||||
}
|
||||
|
||||
while(song != null)
|
||||
if(response != null)
|
||||
{
|
||||
if(response.statusCode() == 200)
|
||||
{
|
||||
mConnectionLoggingSuppressed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!mConnectionLoggingSuppressed)
|
||||
{
|
||||
HttpRequest updateRequest = createUpdateRequest(song);
|
||||
|
||||
if(updateRequest != null)
|
||||
{
|
||||
session.write(updateRequest);
|
||||
}
|
||||
|
||||
//Fetch next metadata update to send
|
||||
song = mMetadataQueue.poll();
|
||||
mLog.info("Error submitting Icecast 2 Metadata update to URL [" + metadataUpdateURL +
|
||||
"] HTTP Response Code [" + response.statusCode() + "] Body [" + response.body() + "]");
|
||||
mConnectionLoggingSuppressed = true;
|
||||
}
|
||||
|
||||
session.closeNow();
|
||||
}
|
||||
}
|
||||
catch(UnresolvedAddressException | RuntimeIoException ure)
|
||||
{
|
||||
//Do nothing - the server is temporarily unavailable
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Throwable throwableCause = e.getCause();
|
||||
|
||||
if(!mStackTraceLoggingSuppressed)
|
||||
{
|
||||
mLog.error("Error sending metadata update. Future errors will be suppressed", e);
|
||||
mStackTraceLoggingSuppressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
mUpdating.set(false);
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("There was an error submitting an Icecast metadata update", t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,13 +178,22 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO);
|
||||
}
|
||||
|
||||
if(to == null)
|
||||
{
|
||||
List<Identifier> toIdentifiers = identifierCollection.getIdentifiers(Role.TO);
|
||||
if(!toIdentifiers.isEmpty())
|
||||
{
|
||||
to = toIdentifiers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(to != null)
|
||||
{
|
||||
sb.append("TO:").append(to);
|
||||
|
||||
List<Alias> aliases = aliasList.getAliases(to);
|
||||
|
||||
if(!aliases.isEmpty())
|
||||
if(aliases != null && !aliases.isEmpty())
|
||||
{
|
||||
sb.append(" ").append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
}
|
||||
@@ -223,7 +203,17 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
sb.append("TO:UNKNOWN");
|
||||
}
|
||||
|
||||
Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM);
|
||||
Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.RADIO, Role.FROM);
|
||||
|
||||
if(from == null)
|
||||
{
|
||||
List<Identifier> fromIdentifiers = identifierCollection.getIdentifiers(Role.FROM);
|
||||
|
||||
if(!fromIdentifiers.isEmpty())
|
||||
{
|
||||
from = fromIdentifiers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(from != null)
|
||||
{
|
||||
@@ -231,7 +221,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
|
||||
List<Alias> aliases = aliasList.getAliases(from);
|
||||
|
||||
if(!aliases.isEmpty())
|
||||
if(aliases != null && !aliases.isEmpty())
|
||||
{
|
||||
sb.append(" ").append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
}
|
||||
@@ -248,46 +238,4 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HTTP GET request to update the metadata on the remote server
|
||||
*/
|
||||
private HttpRequest createUpdateRequest(String song)
|
||||
{
|
||||
try
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("mode=updinfo");
|
||||
sb.append("&mount=").append(URLEncoder.encode(mIcecastConfiguration.getMountPoint(), UTF8));
|
||||
sb.append("&charset=UTF%2d8");
|
||||
sb.append("&song=").append(URLEncoder.encode(song, UTF8));
|
||||
|
||||
HttpRequestImpl request = new HttpRequestImpl(HttpVersion.HTTP_1_1, HttpMethod.GET, "/admin/metadata",
|
||||
sb.toString(), getHTTPHeaders());
|
||||
|
||||
return request;
|
||||
}
|
||||
catch(UnsupportedEncodingException e)
|
||||
{
|
||||
//This should never happen
|
||||
mLog.error("UTF-8 encoding is not supported - can't update song metadata");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP headers to use for a metadata update request.
|
||||
*/
|
||||
private Map<String,String> getHTTPHeaders()
|
||||
{
|
||||
if(mHTTPHeaders == null)
|
||||
{
|
||||
mHTTPHeaders = new HashMap<>();
|
||||
mHTTPHeaders.put(IcecastHeader.USER_AGENT.getValue(), SystemProperties.getInstance().getApplicationName());
|
||||
mHTTPHeaders.put(IcecastHeader.AUTHORIZATION.getValue(), mIcecastConfiguration.getBase64EncodedCredentials());
|
||||
}
|
||||
|
||||
return mHTTPHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast.icecast;
|
||||
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
@@ -30,11 +33,9 @@ import org.apache.mina.core.service.IoHandlerAdapter;
|
||||
import org.apache.mina.core.session.IoSession;
|
||||
import org.apache.mina.filter.codec.ProtocolDecoderException;
|
||||
import org.apache.mina.http.HttpClientCodec;
|
||||
import org.apache.mina.http.HttpException;
|
||||
import org.apache.mina.http.HttpRequestImpl;
|
||||
import org.apache.mina.http.api.DefaultHttpResponse;
|
||||
import org.apache.mina.http.api.HttpMethod;
|
||||
import org.apache.mina.http.api.HttpStatus;
|
||||
import org.apache.mina.http.api.HttpVersion;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||
import org.slf4j.Logger;
|
||||
@@ -51,6 +52,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(IcecastHTTPAudioBroadcaster.class);
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||
private static final String HTTP_1_0_OK_HEX_DUMP = "48 54 54 50 2F 31 2E 30 20 32 30 30 20 4F 4B";
|
||||
|
||||
private NioSocketConnector mSocketConnector;
|
||||
private IoSession mStreamingSession = null;
|
||||
@@ -113,7 +115,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
mSocketConnector.setConnectTimeoutCheckInterval(10000);
|
||||
|
||||
// mSocketConnector.getFilterChain().addLast("logger",
|
||||
// new LoggingFilter(IcecastTCPAudioBroadcaster.class));
|
||||
// new LoggingFilter(IcecastHTTPAudioBroadcaster.class));
|
||||
|
||||
mSocketConnector.getFilterChain().addLast("codec", new HttpClientCodec());
|
||||
mSocketConnector.setHandler(new IcecastHTTPIOHandler());
|
||||
@@ -146,12 +148,12 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
}
|
||||
else if(throwableCause != null)
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
mLog.debug("Failed to connect", rie);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
mLog.debug("Failed to connect - no exception is available");
|
||||
}
|
||||
|
||||
@@ -266,25 +268,30 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
||||
{
|
||||
if(throwable instanceof ProtocolDecoderException)
|
||||
{
|
||||
Throwable cause = ((ProtocolDecoderException) throwable).getCause();
|
||||
Throwable cause = throwable.getCause();
|
||||
|
||||
if(cause instanceof HttpException &&
|
||||
((HttpException) cause).getStatusCode() == HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED.code())
|
||||
/**
|
||||
* Warning: Hack. Icecast 2.4.2+ doesn't like to send HTTP headers along with their HTTP/1.0 OK
|
||||
* message which causes Apache Mina to think that it hasn't received the full HTTP response. So, we
|
||||
* intercept the out of bounds exception and then inspect the message hex dump to see if it's an
|
||||
* OK response and then play like everything is well and good.
|
||||
*/
|
||||
if(cause instanceof ArrayIndexOutOfBoundsException &&
|
||||
((ProtocolDecoderException)throwable).getHexdump().startsWith(HTTP_1_0_OK_HEX_DUMP))
|
||||
{
|
||||
//Ignore - Icecast 2.4 sometimes doesn't send any headers with their HTTP responses. Mina expects a
|
||||
//content-length for HTTP responses.
|
||||
setBroadcastState(BroadcastState.CONNECTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("HTTP protocol decoder error", throwable);
|
||||
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Broadcast error", throwable);
|
||||
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast.shoutcast.v1;
|
||||
|
||||
@@ -203,6 +205,15 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
|
||||
to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO);
|
||||
}
|
||||
|
||||
if(to == null)
|
||||
{
|
||||
List<Identifier> toIdentifiers = identifierCollection.getIdentifiers(Role.TO);
|
||||
if(!toIdentifiers.isEmpty())
|
||||
{
|
||||
to = toIdentifiers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(to != null)
|
||||
{
|
||||
sb.append("TO:").append(to);
|
||||
@@ -219,7 +230,17 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
|
||||
sb.append("TO:UNKNOWN");
|
||||
}
|
||||
|
||||
Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM);
|
||||
Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.RADIO, Role.FROM);
|
||||
|
||||
if(from == null)
|
||||
{
|
||||
List<Identifier> fromIdentifiers = identifierCollection.getIdentifiers(Role.FROM);
|
||||
|
||||
if(!fromIdentifiers.isEmpty())
|
||||
{
|
||||
from = fromIdentifiers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(from != null)
|
||||
{
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.broadcast.shoutcast.v2;
|
||||
|
||||
@@ -278,6 +280,8 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
|
||||
if(identifierCollection != null)
|
||||
{
|
||||
StringBuilder sbTitle2 = new StringBuilder();
|
||||
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
|
||||
Identifier to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.PATCH_GROUP, Role.TO);
|
||||
@@ -287,39 +291,61 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
||||
to = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.TO);
|
||||
}
|
||||
|
||||
if(to == null)
|
||||
{
|
||||
List<Identifier> toIdentifiers = identifierCollection.getIdentifiers(Role.TO);
|
||||
|
||||
if(!toIdentifiers.isEmpty())
|
||||
{
|
||||
to = toIdentifiers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(to != null)
|
||||
{
|
||||
sb.append("TO:").append(to);
|
||||
sbTitle2.append("TO:").append(to);
|
||||
|
||||
List<Alias> aliases = aliasList.getAliases(to);
|
||||
|
||||
if(!aliases.isEmpty())
|
||||
{
|
||||
sb.append(" ").append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
sbTitle2.append(" ").append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("TO:UNKNOWN");
|
||||
sbTitle2.append("TO:UNKNOWN");
|
||||
}
|
||||
|
||||
Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.TALKGROUP, Role.FROM);
|
||||
Identifier from = identifierCollection.getIdentifier(IdentifierClass.USER, Form.RADIO, Role.FROM);
|
||||
|
||||
if(from == null)
|
||||
{
|
||||
List<Identifier> fromIdentifiers = identifierCollection.getIdentifiers(Role.FROM);
|
||||
|
||||
if(!fromIdentifiers.isEmpty())
|
||||
{
|
||||
from = fromIdentifiers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(from != null)
|
||||
{
|
||||
sb.append(" FROM:").append(from);
|
||||
sbTitle2.append(" FROM:").append(from);
|
||||
|
||||
List<Alias> aliases = aliasList.getAliases(from);
|
||||
|
||||
if(!aliases.isEmpty())
|
||||
{
|
||||
sb.append(" ").append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
sbTitle2.append(" ").append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(" FROM:UNKNOWN");
|
||||
sbTitle2.append(" FROM:UNKNOWN");
|
||||
}
|
||||
|
||||
sb.append(UltravoxMetadata.TITLE_2.asXML(sbTitle2.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class AmbeAudioModule extends JmbeAudioModule
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AmbeAudioModule.class);
|
||||
private static final String AMBE_CODEC = "AMBE 3600 x 2450";
|
||||
private static boolean sLibraryStatusLogged = false;
|
||||
|
||||
public AmbeAudioModule(UserPreferences userPreferences)
|
||||
{
|
||||
super(userPreferences);
|
||||
|
||||
if(!sLibraryStatusLogged)
|
||||
{
|
||||
if(getAudioCodec() != null)
|
||||
{
|
||||
mLog.info("AMBE CODEC successfully loaded - P25-2/DMR/NXDN audio will be available");
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn("AMBE CODEC not loaded - P25-2/DMR/NXDN audio will NOT be available");
|
||||
}
|
||||
|
||||
sLibraryStatusLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCodecName()
|
||||
{
|
||||
return AMBE_CODEC;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import io.github.dsheirer.identifier.encryption.EncryptionKeyIdentifier;
|
||||
|
||||
/**
|
||||
* P25 Encryption Parameters
|
||||
*/
|
||||
public interface IEncryptionSyncParameters
|
||||
{
|
||||
/**
|
||||
* Encryption key identifier
|
||||
*/
|
||||
EncryptionKeyIdentifier getEncryptionKey();
|
||||
|
||||
/**
|
||||
* Message Indicator
|
||||
* @return key generator fill values
|
||||
*/
|
||||
String getMessageIndicator();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class ImbeAudioModule extends JmbeAudioModule
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(ImbeAudioModule.class);
|
||||
private static final String IMBE_CODEC = "IMBE";
|
||||
private static boolean sLibraryStatusLogged = false;
|
||||
|
||||
public ImbeAudioModule(UserPreferences userPreferences)
|
||||
{
|
||||
super(userPreferences);
|
||||
|
||||
if(!sLibraryStatusLogged)
|
||||
{
|
||||
if(getAudioCodec() != null)
|
||||
{
|
||||
mLog.info("JMBE audio conversion library IMBE CODEC successfully loaded - P25-1 audio will be available");
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.warn("JMBE audio conversion library, IMBE CODEC not loaded - P25-1 audio will NOT be available");
|
||||
}
|
||||
|
||||
sLibraryStatusLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCodecName()
|
||||
{
|
||||
return IMBE_CODEC;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.audio.AbstractAudioModule;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.IMessageListener;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
|
||||
import jmbe.iface.IAudioCodec;
|
||||
import jmbe.iface.IAudioCodecLibrary;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class JmbeAudioModule extends AbstractAudioModule implements Listener<IMessage>, IMessageListener
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(JmbeAudioModule.class);
|
||||
private static final String JMBE_AUDIO_LIBRARY = "JMBE";
|
||||
private static List<String> mLibraryLoadStatusLogged = new ArrayList<>();
|
||||
private IAudioCodec mAudioCodec;
|
||||
private UserPreferences mUserPreferences;
|
||||
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("JmbeAudioModule");
|
||||
|
||||
public JmbeAudioModule(UserPreferences userPreferences)
|
||||
{
|
||||
mUserPreferences = userPreferences;
|
||||
MyEventBus.getEventBus().register(this);
|
||||
loadConverter();
|
||||
}
|
||||
|
||||
protected IAudioCodec getAudioCodec()
|
||||
{
|
||||
return mAudioCodec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that the JMBE audio library has been loaded and a suitable audio codec is usable (ie non-null)
|
||||
*/
|
||||
protected boolean hasAudioCodec()
|
||||
{
|
||||
return getAudioCodec() != null;
|
||||
}
|
||||
|
||||
protected ReusableAudioPacketQueue getAudioPacketQueue()
|
||||
{
|
||||
return mAudioPacketQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<IMessage> getMessageListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives notifications that the JMBE library preference has been updated via the Guava event bus
|
||||
*
|
||||
* @param preferenceType that was updated
|
||||
*/
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.JMBE_LIBRARY)
|
||||
{
|
||||
mLibraryLoadStatusLogged.clear();
|
||||
loadConverter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the CODEC to use from the JMBE library
|
||||
*/
|
||||
protected abstract String getCodecName();
|
||||
|
||||
/**
|
||||
* Loads audio frame processing chain. Constructs an imbe targetdataline
|
||||
* to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion
|
||||
* stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper.
|
||||
*/
|
||||
protected void loadConverter()
|
||||
{
|
||||
IAudioCodec audioConverter = null;
|
||||
|
||||
Path path = mUserPreferences.getJmbeLibraryPreference().getPathJmbeLibrary();
|
||||
|
||||
if(path != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.info("Loading JMBE library from [" + path.toString() + "]");
|
||||
}
|
||||
|
||||
URLClassLoader childClassLoader = new URLClassLoader(new URL[]{path.toUri().toURL()},
|
||||
this.getClass().getClassLoader());
|
||||
|
||||
Class classToLoad = Class.forName("jmbe.JMBEAudioLibrary", true, childClassLoader);
|
||||
|
||||
Object instance = classToLoad.getDeclaredConstructor().newInstance();
|
||||
|
||||
if(instance instanceof IAudioCodecLibrary)
|
||||
{
|
||||
IAudioCodecLibrary library = (IAudioCodecLibrary)instance;
|
||||
|
||||
if((library.getMajorVersion() == 1 && library.getMinorVersion() >= 0 &&
|
||||
library.getBuildVersion() >= 0) || library.getMajorVersion() >= 1)
|
||||
{
|
||||
audioConverter = library.getAudioConverter(getCodecName());
|
||||
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.info("JMBE audio conversion library loaded: " + library.getVersion());
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.warn("JMBE library version 1.0.0 or higher is required - found: " + library.getVersion());
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.info("JMBE audio conversion library NOT FOUND");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY + getCodecName()))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - " + iae.getMessage());
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY + getCodecName());
|
||||
}
|
||||
}
|
||||
catch(NoSuchMethodException nsme)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - no such method exception");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(MalformedURLException mue)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library from path [" + path + "]");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(ClassNotFoundException e1)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - class not found");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(InvocationTargetException ite)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - invocation target exception", ite);
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(InstantiationException e1)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - instantiation exception", e1);
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
catch(IllegalAccessException e1)
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.error("Couldn't load JMBE audio conversion library - security restrictions");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!mLibraryLoadStatusLogged.contains(JMBE_AUDIO_LIBRARY))
|
||||
{
|
||||
mLog.warn("JMBE audio library path is NOT SET in your User Preferences.");
|
||||
mLibraryLoadStatusLogged.add(JMBE_AUDIO_LIBRARY);
|
||||
}
|
||||
}
|
||||
|
||||
if(audioConverter != null)
|
||||
{
|
||||
mAudioCodec = audioConverter;
|
||||
}
|
||||
else
|
||||
{
|
||||
mAudioCodec = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mAudioCodec = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||
import io.github.dsheirer.identifier.encryption.EncryptionKey;
|
||||
import io.github.dsheirer.module.decode.p25.audio.VoiceFrame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* MBE Call Sequence containing one or more voice frames with optional encryption parameters and optional from and to
|
||||
* radio identifiers.
|
||||
*/
|
||||
@JsonRootName("mbe_call")
|
||||
@JsonPropertyOrder({"protocol", "call_type", "from", "to", "encrypted", "frames"})
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class MBECallSequence
|
||||
{
|
||||
private String mProtocol;
|
||||
private String mFromIdentifier;
|
||||
private String mToIdentifier;
|
||||
private String mCallType;
|
||||
private String mSystem;
|
||||
private String mSite;
|
||||
private boolean mEncrypted;
|
||||
private List<VoiceFrame> mVoiceFrames = new ArrayList<>();
|
||||
private IEncryptionSyncParameters mTemporaryEncryptionSyncParameters;
|
||||
|
||||
/**
|
||||
* Constructs a call sequence
|
||||
*/
|
||||
public MBECallSequence(String protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
}
|
||||
|
||||
public MBECallSequence()
|
||||
{
|
||||
//no-arg constructor for faster jackson deserialization
|
||||
}
|
||||
|
||||
/**
|
||||
* Protocol/format for the voice frames
|
||||
*/
|
||||
@JsonProperty("protocol")
|
||||
public String getProtocol()
|
||||
{
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol)
|
||||
{
|
||||
mProtocol = protocol;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates if this sequences contains any audio frames
|
||||
*/
|
||||
@JsonIgnore
|
||||
public boolean hasAudio()
|
||||
{
|
||||
return mVoiceFrames.size() > 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this call sequence is encrypted
|
||||
*/
|
||||
@JsonProperty("encrypted")
|
||||
public boolean isEncrypted()
|
||||
{
|
||||
return mEncrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encrypted status of this call sequence
|
||||
*
|
||||
* @param encrypted status
|
||||
*/
|
||||
public void setEncrypted(boolean encrypted)
|
||||
{
|
||||
mEncrypted = encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets encryption sync parameters to use with the next sequence of voice frames. This is usually obtained from the
|
||||
* HDU (phase1) or the PTT (phase2)
|
||||
*
|
||||
* @param encryptionSyncParameters to apply to the next set of voice frames
|
||||
*/
|
||||
public void setEncryptionSyncParameters(IEncryptionSyncParameters encryptionSyncParameters)
|
||||
{
|
||||
mTemporaryEncryptionSyncParameters = encryptionSyncParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the from radio identifier
|
||||
*
|
||||
* @param from id
|
||||
*/
|
||||
public void setFromIdentifier(String from)
|
||||
{
|
||||
if(from != null && !from.isEmpty() && !from.contentEquals("0"))
|
||||
{
|
||||
mFromIdentifier = from;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier that originated the call
|
||||
*
|
||||
* @return from id
|
||||
*/
|
||||
@JsonProperty("from")
|
||||
public String getFromIdentifier()
|
||||
{
|
||||
return mFromIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the to radio identifier
|
||||
*
|
||||
* @param to id
|
||||
*/
|
||||
public void setToIdentifier(String to)
|
||||
{
|
||||
if(to != null && !to.isEmpty() && !to.contentEquals("0"))
|
||||
{
|
||||
mToIdentifier = to;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Radio identifier that received the call
|
||||
*
|
||||
* @return to id
|
||||
*/
|
||||
@JsonProperty("to")
|
||||
public String getToIdentifier()
|
||||
{
|
||||
return mToIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call type
|
||||
*
|
||||
* @param type from GROUP, INDIVIDUAL, or TELEPHONE INTERCONNECT
|
||||
*/
|
||||
public void setCallType(String type)
|
||||
{
|
||||
mCallType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the call type
|
||||
*
|
||||
* @return call type
|
||||
*/
|
||||
@JsonProperty("call_type")
|
||||
public String getCallType()
|
||||
{
|
||||
return mCallType;
|
||||
}
|
||||
|
||||
/**
|
||||
* System name defined by the user
|
||||
* @return
|
||||
*/
|
||||
@JsonProperty("system")
|
||||
public String getSystem()
|
||||
{
|
||||
return mSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the system name
|
||||
* @param system
|
||||
*/
|
||||
public void setSystem(String system)
|
||||
{
|
||||
mSystem = system;
|
||||
}
|
||||
|
||||
/**
|
||||
* Site name defined by the user
|
||||
* @return
|
||||
*/
|
||||
@JsonProperty("site")
|
||||
public String getSite()
|
||||
{
|
||||
return mSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the site name
|
||||
* @param site
|
||||
*/
|
||||
public void setSite(String site)
|
||||
{
|
||||
mSite = site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordered list of voice frames for the call sequence
|
||||
*
|
||||
* @return list of unencrypted or encrypted audio frames
|
||||
*/
|
||||
@JsonProperty("frames")
|
||||
public List<VoiceFrame> getVoiceFrames()
|
||||
{
|
||||
return mVoiceFrames;
|
||||
}
|
||||
|
||||
public void setVoiceFrames(List<VoiceFrame> voiceFrames)
|
||||
{
|
||||
mVoiceFrames = voiceFrames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an unencrypted audio frame to this call sequence
|
||||
*
|
||||
* @param timestamp of the audio frame
|
||||
* @param frame of hexadecimal values representing the transmitted audio frame and ecc bits
|
||||
*/
|
||||
public void addVoiceFrame(long timestamp, String frame)
|
||||
{
|
||||
if(mTemporaryEncryptionSyncParameters != null)
|
||||
{
|
||||
addEncryptedVoiceFrame(timestamp, frame, mTemporaryEncryptionSyncParameters);
|
||||
mTemporaryEncryptionSyncParameters = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
mVoiceFrames.add(new VoiceFrame(timestamp, frame));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an encrypted audio frame to this call sequence
|
||||
*
|
||||
* @param timestamp of the audio frame
|
||||
* @param frame of hexadecimal values representing the transmitted audio frame and ecc bits
|
||||
* @param parameters identifying the encryption used for the frame
|
||||
*/
|
||||
public void addEncryptedVoiceFrame(long timestamp, String frame, IEncryptionSyncParameters parameters)
|
||||
{
|
||||
EncryptionKey encryption = parameters.getEncryptionKey().getValue();
|
||||
addEncryptedVoiceFrame(timestamp, frame, encryption.getAlgorithm(), encryption.getKey(), parameters.getMessageIndicator());
|
||||
setEncrypted(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an encrypted audio frame to this call sequence
|
||||
*
|
||||
* @param timestamp of the audio frame
|
||||
* @param frame of hexadecimal values representing the transmitted audio frame and ecc bits
|
||||
* @param algorithm identifier for encryption
|
||||
* @param keyid identifying which encryption key was used by the radio which may have multiple keys
|
||||
* @param messageIndicator to seed the key generator
|
||||
*/
|
||||
public void addEncryptedVoiceFrame(long timestamp, String frame, int algorithm, int keyid, String messageIndicator)
|
||||
{
|
||||
mVoiceFrames.add(new VoiceFrame(timestamp, frame, algorithm, keyid, messageIndicator));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.github.dsheirer.audio.convert.thumbdv.ThumbDv;
|
||||
import io.github.dsheirer.audio.convert.thumbdv.message.response.AmbeResponse;
|
||||
import io.github.dsheirer.module.decode.p25.audio.VoiceFrame;
|
||||
import io.github.dsheirer.record.wave.AudioPacketWaveRecorder;
|
||||
import io.github.dsheirer.record.wave.WaveMetadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Utility for converting MBE call sequences (*.mbe) to PCM wave audio format
|
||||
*/
|
||||
public class MBECallSequenceConverter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MBECallSequenceConverter.class);
|
||||
|
||||
public static void convert(Path input, Path output) throws IOException
|
||||
{
|
||||
InputStream inputStream = Files.newInputStream(input);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
MBECallSequence sequence = mapper.readValue(inputStream, MBECallSequence.class);
|
||||
convert(sequence, output);
|
||||
}
|
||||
|
||||
public static void convert(MBECallSequence callSequence, Path outputPath)
|
||||
{
|
||||
if(callSequence == null || callSequence.isEncrypted())
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot decode null or encrypted call sequence");
|
||||
}
|
||||
|
||||
if(callSequence != null && !callSequence.isEncrypted())
|
||||
{
|
||||
ThumbDv.AudioProtocol protocol = ThumbDv.AudioProtocol.P25_PHASE2;
|
||||
|
||||
AudioPacketWaveRecorder recorder = new AudioPacketWaveRecorder(outputPath);
|
||||
recorder.start();
|
||||
|
||||
long delayMillis = 0;
|
||||
|
||||
try(ThumbDv thumbDv = new ThumbDv(protocol, recorder))
|
||||
{
|
||||
thumbDv.start();
|
||||
for(VoiceFrame voiceFrame: callSequence.getVoiceFrames())
|
||||
{
|
||||
mLog.debug("Frame [" + voiceFrame.getFrame() + "] + Hex [" + AmbeResponse.toHex(voiceFrame.getFrameBytes()) + "]");
|
||||
thumbDv.decode(voiceFrame.getFrameBytes());
|
||||
delayMillis += 30;
|
||||
}
|
||||
|
||||
if(delayMillis > 0)
|
||||
{
|
||||
delayMillis += 1000;
|
||||
try
|
||||
{
|
||||
Thread.sleep(delayMillis);
|
||||
}
|
||||
catch(InterruptedException ie)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("Error", ioe);
|
||||
}
|
||||
|
||||
recorder.stop(Paths.get(outputPath.toString().replace(".tmp", ".wav")), new WaveMetadata());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// String mbe = "/home/denny/SDRTrunk/recordings/20190331085324_154250000_3_TS0_65084_6591007.mbe";
|
||||
// String mbe = "/home/denny/SDRTrunk/recordings/20190331085324_154250000_2_TS1_65035.mbe";
|
||||
//
|
||||
// Path input = Paths.get(mbe);
|
||||
// Path output = Paths.get(mbe.replace(".mbe", ".tmp"));
|
||||
//
|
||||
// mLog.info("Converting: " + mbe);
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// MBECallSequenceConverter.convert(input, output);
|
||||
// }
|
||||
// catch(IOException ioe)
|
||||
// {
|
||||
// mLog.error("Error", ioe);
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.base.Joiner;
|
||||
import io.github.dsheirer.module.decode.p25.audio.VoiceFrame;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reader for MBE call sequence recordings
|
||||
*/
|
||||
public class MBECallSequenceReader
|
||||
{
|
||||
public static List<String> getAudioFrames(Path path) throws IOException
|
||||
{
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Object obj = mapper.readValue(path.toFile(), MBECallSequence.class);
|
||||
|
||||
if(obj instanceof MBECallSequence)
|
||||
{
|
||||
MBECallSequence sequence = (MBECallSequence)obj;
|
||||
|
||||
List<String> audioFrames = new ArrayList<>();
|
||||
|
||||
for(VoiceFrame voiceFrame: sequence.getVoiceFrames())
|
||||
{
|
||||
audioFrames.add(voiceFrame.getFrame());
|
||||
}
|
||||
|
||||
return audioFrames;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Path path = Path.of("/home/denny/SDRTrunk/recordings/20190706063149_154250000_7_TS1_65084_6591001.mbe");
|
||||
|
||||
try
|
||||
{
|
||||
List<String> frames = MBECallSequenceReader.getAudioFrames(path);
|
||||
|
||||
Files.writeString(Path.of("/home/denny/SDRTrunk/recordings/mbe_frames.txt"), Joiner.on("\",\n\"").join(frames));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.codec.mbe;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.IMessageListener;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.preference.TimestampFormat;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Records MBE audio frame call sequences and metadata to a JSON format recording file
|
||||
*/
|
||||
public abstract class MBECallSequenceRecorder extends Module implements IMessageListener, Listener<IMessage>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MBECallSequenceRecorder.class);
|
||||
|
||||
protected static final String CALL_TYPE_GROUP = "GROUP";
|
||||
protected static final String CALL_TYPE_INDIVIDUAL = "INDIVIDUAL";
|
||||
protected static final String CALL_TYPE_TELEPHONE_INTERCONNECT = "TELEPHONE INTERCONNECT";
|
||||
protected UserPreferences mUserPreferences;
|
||||
protected long mChannelFrequency;
|
||||
protected String mSystem;
|
||||
protected String mSite;
|
||||
private int mCallNumber = 1;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param userPreferences to obtain recording directory
|
||||
* @param channelFrequency for the channel to record
|
||||
*/
|
||||
public MBECallSequenceRecorder(UserPreferences userPreferences, long channelFrequency, String system, String site)
|
||||
{
|
||||
mUserPreferences = userPreferences;
|
||||
mChannelFrequency = channelFrequency;
|
||||
mSystem = system;
|
||||
mSite = site;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an MBE call sequence recording to the recording directory
|
||||
* @param sequence to write
|
||||
*/
|
||||
protected void writeCallSequence(MBECallSequence sequence)
|
||||
{
|
||||
writeCallSequence(sequence, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an MBE call sequence recording to the recording directory.
|
||||
*
|
||||
* @param optionalChannelTag to include in the filename
|
||||
* @param sequence containing voice frames
|
||||
*/
|
||||
protected void writeCallSequence(MBECallSequence sequence, String optionalChannelTag)
|
||||
{
|
||||
if(sequence != null && sequence.hasAudio())
|
||||
{
|
||||
sequence.setSystem(mSystem);
|
||||
sequence.setSite(mSite);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(TimestampFormat.TIMESTAMP_COMPACT.getFormatter().format(new Date(System.currentTimeMillis())));
|
||||
sb.append("_").append(mChannelFrequency);
|
||||
sb.append("_").append(mCallNumber++);
|
||||
|
||||
if(mCallNumber < 1)
|
||||
{
|
||||
mCallNumber = 1;
|
||||
}
|
||||
|
||||
if(optionalChannelTag != null)
|
||||
{
|
||||
sb.append("_").append(optionalChannelTag);
|
||||
}
|
||||
|
||||
if(sequence.getToIdentifier() != null)
|
||||
{
|
||||
sb.append("_").append(sequence.getToIdentifier().replace(":", ""));
|
||||
}
|
||||
if(sequence.getFromIdentifier() != null)
|
||||
{
|
||||
sb.append("_").append(sequence.getFromIdentifier().replace(":", ""));
|
||||
}
|
||||
|
||||
if(sequence.isEncrypted())
|
||||
{
|
||||
sb.append("_encrypted");
|
||||
}
|
||||
|
||||
sb.append(".mbe");
|
||||
|
||||
Path recordingDirectory = mUserPreferences.getDirectoryPreference().getDirectoryRecording();
|
||||
Path filePath = recordingDirectory.resolve(sb.toString());
|
||||
|
||||
try
|
||||
{
|
||||
OutputStream outputStream = Files.newOutputStream(filePath);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(outputStream, sequence);
|
||||
outputStream.close();
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("Couldn't write MBE call sequence to path [" + filePath.toString() + "]", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of IMessageListener interface
|
||||
*/
|
||||
@Override
|
||||
public Listener<IMessage> getMessageListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
|
||||
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.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
|
||||
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<ReusableAudioPacket> mAudioPacketListener;
|
||||
private ReusableAudioPacketQueue mReusableAudioPacketQueue = new ReusableAudioPacketQueue("ThumbDv");
|
||||
private boolean mStarted;
|
||||
|
||||
public ThumbDv(AudioProtocol audioProtocol, Listener<ReusableAudioPacket> listener)
|
||||
{
|
||||
mAudioProtocol = audioProtocol;
|
||||
mAudioPacketListener = 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 && mAudioPacketListener != null)
|
||||
{
|
||||
float[] samples = ((DecodeSpeechResponse)message).getSamples();
|
||||
ReusableAudioPacket audioPacket = mReusableAudioPacketQueue.getBuffer(samples.length);
|
||||
audioPacket.loadAudioFrom(samples);
|
||||
mAudioPacketListener.receive(audioPacket);
|
||||
}
|
||||
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<ReusableAudioPacket> listener = reusableAudioPacket -> {
|
||||
mLog.info("Got an audio packet!");
|
||||
reusableAudioPacket.decrementUserCount();
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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();
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,6 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
|
||||
private JLabel mIdentifierLabel = new JLabel("-----");
|
||||
private Identifier mIdentifier;
|
||||
private List<Alias> mAliases = Collections.EMPTY_LIST;
|
||||
|
||||
private boolean mConfigured = false;
|
||||
private AliasModel mAliasModel;
|
||||
|
||||
public AudioChannelPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager,
|
||||
@@ -191,31 +189,70 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the from and to labels. Note: this does not happen on the swing
|
||||
* event thread. Only invoke from the swing thread.
|
||||
* Resets the from and to labels.
|
||||
*/
|
||||
private void resetLabels()
|
||||
{
|
||||
boolean updated = mIdentifier != null;
|
||||
mIdentifier = null;
|
||||
mAliases = Collections.EMPTY_LIST;
|
||||
updateLabels();
|
||||
mConfigured = false;
|
||||
|
||||
if(updated)
|
||||
{
|
||||
updateLabels();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates the string to the specified length using an ellipses at the end
|
||||
* @param value to optionally truncate
|
||||
* @param length of the final formatted string
|
||||
* @return formatted string or null
|
||||
*/
|
||||
private static String truncate(String value, int length)
|
||||
private void updateIdentifiers(IdentifierCollection identifierCollection)
|
||||
{
|
||||
if(value != null && value.length() > length)
|
||||
if(identifierCollection == null || identifierCollection.isEmpty())
|
||||
{
|
||||
return value.substring(0, length - 3) + "...";
|
||||
resetLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
return value;
|
||||
List<Identifier> toIds = identifierCollection.getIdentifiers(IdentifierClass.USER, Role.TO);
|
||||
|
||||
if(toIds.isEmpty())
|
||||
{
|
||||
resetLabels();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean updated = false;
|
||||
|
||||
if(toIds.size() == 1)
|
||||
{
|
||||
Identifier currentIdentifier = mIdentifier;
|
||||
|
||||
if(currentIdentifier == null || currentIdentifier != toIds.get(0))
|
||||
{
|
||||
mIdentifier = toIds.get(0);
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
|
||||
if(aliasList != null)
|
||||
{
|
||||
mAliases = aliasList.getAliases(mIdentifier);
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mIdentifier = toIds.get(0);
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
|
||||
if(aliasList != null)
|
||||
{
|
||||
mAliases = aliasList.getAliases(mIdentifier);
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if(updated)
|
||||
{
|
||||
updateLabels();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +286,8 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
|
||||
}
|
||||
|
||||
final ImageIcon icon = iconName != null ? mIconManager.getIcon(iconName, 18) : null;
|
||||
final String identifierText = truncate(identifier, 33);
|
||||
|
||||
final String identifierText = identifier;
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@@ -258,7 +296,6 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
|
||||
{
|
||||
mIdentifierLabel.setText(identifierText);
|
||||
mIconLabel.setIcon(icon);
|
||||
mConfigured = true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -272,23 +309,10 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
|
||||
@Override
|
||||
public void receive(final IdentifierCollection identifierCollection)
|
||||
{
|
||||
if(identifierCollection != null && (identifierCollection.isUpdated() || !mConfigured))
|
||||
{
|
||||
List<Identifier> toIds = identifierCollection.getIdentifiers(IdentifierClass.USER, Role.TO);
|
||||
mIdentifier = !toIds.isEmpty() ? toIds.get(0) : null;
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
|
||||
if(aliasList != null)
|
||||
{
|
||||
mAliases = aliasList.getAliases(mIdentifier);
|
||||
}
|
||||
|
||||
updateLabels();
|
||||
}
|
||||
updateIdentifiers(identifierCollection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void settingChanged(Setting setting)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* 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
|
||||
@@ -39,7 +39,8 @@ public class AudioChannelsPanel extends JPanel
|
||||
public AudioChannelsPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager,
|
||||
IAudioController controller, AliasModel aliasModel)
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[][sg abc,grow,fill][][sg abc,grow,fill]", "[grow,fill]"));
|
||||
setLayout(new MigLayout("insets 0 0 0 0",
|
||||
"[][sizegroup abc,grow,fill][][sizegroup abc,grow,fill]", "[grow,fill]"));
|
||||
|
||||
setBackground(Color.BLACK);
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.playback;
|
||||
|
||||
@@ -54,6 +56,11 @@ public abstract class AudioOutput implements Listener<ReusableAudioPacket>, Line
|
||||
private LinkedTransferQueue<ReusableAudioPacket> mBuffer = new LinkedTransferQueue<>();
|
||||
private int mBufferStartThreshold;
|
||||
private int mBufferStopThreshold;
|
||||
private static IdentifierCollection EMPTY_IDENTIFIER_COLLECTION = new IdentifierCollection(0);
|
||||
static
|
||||
{
|
||||
EMPTY_IDENTIFIER_COLLECTION.setUpdated(true);
|
||||
}
|
||||
|
||||
private Listener<IdentifierCollection> mIdentifierCollectionListener;
|
||||
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
|
||||
@@ -380,12 +387,11 @@ public abstract class AudioOutput implements Listener<ReusableAudioPacket>, Line
|
||||
*/
|
||||
private void checkStop()
|
||||
{
|
||||
if(mCanProcessAudio &&
|
||||
mOutput.isRunning() &&
|
||||
mOutput.available() >= mBufferStopThreshold)
|
||||
if(mCanProcessAudio && mOutput.isRunning() && mOutput.available() >= mBufferStopThreshold)
|
||||
{
|
||||
mOutput.drain();
|
||||
mOutput.stop();
|
||||
broadcast(EMPTY_IDENTIFIER_COLLECTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.squelch;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
public interface ISquelchStateListener
|
||||
{
|
||||
public Listener<SquelchState> getSquelchStateListener();
|
||||
Listener<SquelchStateEvent> getSquelchStateListener();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.squelch;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
public interface ISquelchStateProvider
|
||||
{
|
||||
public void setSquelchStateListener( Listener<SquelchState> listener );
|
||||
public void removeSquelchStateListener();
|
||||
void setSquelchStateListener(Listener<SquelchStateEvent> listener);
|
||||
|
||||
void removeSquelchStateListener();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.squelch;
|
||||
|
||||
/**
|
||||
* Squelch state change event
|
||||
*/
|
||||
public class SquelchStateEvent
|
||||
{
|
||||
private SquelchState mSquelchState;
|
||||
private int mTimeslot;
|
||||
|
||||
public SquelchStateEvent(SquelchState squelchState, int timeslot)
|
||||
{
|
||||
mSquelchState = squelchState;
|
||||
mTimeslot = timeslot;
|
||||
}
|
||||
|
||||
public SquelchState getSquelchState()
|
||||
{
|
||||
return mSquelchState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeslot associated with the squelch state
|
||||
*/
|
||||
public int getTimeslot()
|
||||
{
|
||||
return mTimeslot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a squelch state event with a default timeslot of 0
|
||||
* @param squelchState for the event
|
||||
* @return event
|
||||
*/
|
||||
public static SquelchStateEvent create(SquelchState squelchState)
|
||||
{
|
||||
return new SquelchStateEvent(squelchState, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a squelch state event for the specified timeslot
|
||||
* @param squelchState for the event
|
||||
* @param timeslot that the event applies to
|
||||
* @return event
|
||||
*/
|
||||
public static SquelchStateEvent create(SquelchState squelchState, int timeslot)
|
||||
{
|
||||
return new SquelchStateEvent(squelchState, timeslot);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Squelch Event - State:").append(mSquelchState).append(" Timeslot:").append(mTimeslot);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.bits;
|
||||
|
||||
import io.github.dsheirer.edac.CRC;
|
||||
@@ -285,19 +289,28 @@ public class BinaryMessage extends BitSet
|
||||
|
||||
public String toHexString()
|
||||
{
|
||||
int pointer = 0;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
while(pointer < size())
|
||||
for(int x = 0; x < size(); x += 4)
|
||||
{
|
||||
sb.append(getHex(pointer, pointer + 3, 1));
|
||||
pointer += 4;
|
||||
sb.append(getNibbleAsHex(x));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the parent method which returns a BitSet so that we can return a BinaryMessage
|
||||
* @param from
|
||||
* @param to
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public BinaryMessage get(int from, int to)
|
||||
{
|
||||
return new BinaryMessage(super.get(from, to), (to - from));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this bitset as an array of integer ones and zeros
|
||||
*/
|
||||
@@ -545,22 +558,23 @@ public class BinaryMessage extends BitSet
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value contained between index and index + 7 bit positions
|
||||
* Returns the byte value contained between index and index + 7 bit positions. If the length of this message is
|
||||
* shorter than index + 7, then the least significant bits are set to zero in the returned value.
|
||||
*
|
||||
* @param index specifying the start of the byte value
|
||||
* @param startIndex specifying the start of the byte value
|
||||
* @return byte value contained at index <> index + 7 bit positions
|
||||
*/
|
||||
public byte getByte(int index)
|
||||
public byte getByte(int startIndex)
|
||||
{
|
||||
Validate.isTrue((index + 7) <= size());
|
||||
|
||||
int value = 0;
|
||||
|
||||
for(int x = 0; x < 8; x++)
|
||||
{
|
||||
value = value << 1;
|
||||
value <<= 1;
|
||||
|
||||
if(get(index + x))
|
||||
int index = startIndex + x;
|
||||
|
||||
if(index <= size() && get(index))
|
||||
{
|
||||
value++;
|
||||
}
|
||||
@@ -569,6 +583,50 @@ public class BinaryMessage extends BitSet
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the message to a byte array.
|
||||
*
|
||||
* Note: BitSet.toByteArray() outputs the bytes in big-endian (inverted) order. This method retains the correct
|
||||
* endianness where bit 0 is the most significant bit of the first byte.
|
||||
*/
|
||||
public byte[] getBytes()
|
||||
{
|
||||
byte[] bytes = new byte[(int)Math.ceil((double)size() / 8.0)];
|
||||
|
||||
for(int x = 0; x < bytes.length; x++)
|
||||
{
|
||||
bytes[x] = getByte(x * 8);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 4-bit nibble value contained between index and index + 3 bit positions. If the length of this
|
||||
* message is shorter than index + 3, then the least significant bits are set to zero in the returned value.
|
||||
*
|
||||
* @param startIndex specifying the start of the byte value
|
||||
* @return nibble value contained at index <> index + 3 bit positions
|
||||
*/
|
||||
public int getNibble(int startIndex)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
for(int x = 0; x < 4; x++)
|
||||
{
|
||||
value <<= 1;
|
||||
|
||||
int index = startIndex + x;
|
||||
|
||||
if(index <= size() && get(index))
|
||||
{
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the byte value at index position through index + 7 position.
|
||||
*
|
||||
@@ -627,6 +685,69 @@ public class BinaryMessage extends BitSet
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value represented by the bit array
|
||||
*
|
||||
* @param bits - an array of bit positions that will be treated as if they
|
||||
* were contiguous bits, with index 0 being the MSB and index
|
||||
* length - 1 being the LSB
|
||||
* @param offset to apply to each of the bits indices
|
||||
* @return - integer value of the bit array
|
||||
*/
|
||||
public long getLong(int[] bits, int offset)
|
||||
{
|
||||
if(bits.length > 64)
|
||||
{
|
||||
throw new IllegalArgumentException("Overflow - must be 64 bits "
|
||||
+ "or less to fit into a primitive long value");
|
||||
}
|
||||
|
||||
long value = 0;
|
||||
|
||||
for(int index : bits)
|
||||
{
|
||||
value = Long.rotateLeft(value, 1);
|
||||
|
||||
if(get(index + offset))
|
||||
{
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bit values between start and end (inclusive) bit indices. If the overall length of the bit sequence
|
||||
* is not a multiple of 8 bits, the value is zero padded with least significant bits to make it a multiple of 8.
|
||||
* @param start index
|
||||
* @param end index
|
||||
* @return hexadecimal representation of the bit sequence
|
||||
*/
|
||||
public String getHex(int start, int end)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(int x = start; x <= end; x += 4)
|
||||
{
|
||||
sb.append(getNibbleAsHex(x));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the byte value that starts at the specified index as hexadecimal. If the length of the message is less
|
||||
* than the start index plus 7 bits, then the value represents those bits as high-order bits with zero padding in
|
||||
* the least significant bits to make up the 8 bit value.
|
||||
* @param startIndex of the byte.
|
||||
* @return hexadecimal representation of the byte value
|
||||
*/
|
||||
public String getNibbleAsHex(int startIndex)
|
||||
{
|
||||
int value = getNibble(startIndex);
|
||||
return Integer.toHexString(value).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts up to 63 bits from the bit array into an integer and then
|
||||
@@ -658,29 +779,6 @@ public class BinaryMessage extends BitSet
|
||||
}
|
||||
}
|
||||
|
||||
public String getHex(int msb, int lsb, int digitDisplayCount)
|
||||
{
|
||||
int length = lsb - msb;
|
||||
|
||||
if(length <= 32)
|
||||
{
|
||||
int value = getInt(msb, lsb);
|
||||
|
||||
return String.format("%0" + digitDisplayCount + "X", value);
|
||||
}
|
||||
else if(length <= 64)
|
||||
{
|
||||
long value = getLong(msb, lsb);
|
||||
|
||||
return String.format("%0" + digitDisplayCount + "X", value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("BitSetBuffer.getHex() "
|
||||
+ "maximum array length is 64 bits");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the int value represented by the bit range. This method will
|
||||
* parse the bits in big endian or little endian format. The start value
|
||||
@@ -877,6 +975,23 @@ public class BinaryMessage extends BitSet
|
||||
return checksumIndexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a binary message loaded with the byte array
|
||||
* @param bytes to load
|
||||
* @return loaded binary message
|
||||
*/
|
||||
public static BinaryMessage from(byte[] bytes)
|
||||
{
|
||||
BinaryMessage message = new BinaryMessage(bytes.length * 8);
|
||||
|
||||
for(int x = 0; x < bytes.length; x++)
|
||||
{
|
||||
message.setByte(x * 8, bytes[x]);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a bitsetbuffer loaded from a string of zeros and ones
|
||||
*
|
||||
@@ -998,4 +1113,20 @@ public class BinaryMessage extends BitSet
|
||||
|
||||
this.xor(mask);
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
BinaryMessage message = new BinaryMessage(10);
|
||||
message.set(1);
|
||||
message.set(4);
|
||||
message.set(5);
|
||||
message.set(8);
|
||||
message.set(9);
|
||||
|
||||
System.out.println("Hex: " + message.toHexString() + " Size:" + message.size());
|
||||
for(int x = 0; x < 10; x++)
|
||||
{
|
||||
System.out.println(x + ": " + message.getHex(x, x + 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
/*
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.bits;
|
||||
|
||||
import java.util.BitSet;
|
||||
@@ -48,6 +51,12 @@ public class CorrectedBinaryMessage extends BinaryMessage
|
||||
super(data);
|
||||
}
|
||||
|
||||
public CorrectedBinaryMessage(BinaryMessage message)
|
||||
{
|
||||
this(message.size());
|
||||
this.xor(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCorrectedBitCount()
|
||||
{
|
||||
@@ -72,4 +81,16 @@ public class CorrectedBinaryMessage extends BinaryMessage
|
||||
mCorrectedBitCount += additionalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new binary message containing the bits from (inclusive) to end (exclusive).
|
||||
*
|
||||
* @param start bit
|
||||
* @param end bit
|
||||
* @return message
|
||||
*/
|
||||
public CorrectedBinaryMessage getSubMessage(int start, int end)
|
||||
{
|
||||
BitSet subset = this.get(start, end);
|
||||
return new CorrectedBinaryMessage(subset, end - start);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* 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
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
package io.github.dsheirer.channel;
|
||||
|
||||
import io.github.dsheirer.module.decode.p25.message.IFrequencyBand;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.IFrequencyBand;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.channel.metadata;
|
||||
@@ -61,14 +63,31 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
||||
private List<Alias> mFromIdentifierAliases;
|
||||
private Identifier mToIdentifier;
|
||||
private List<Alias> mToIdentifierAliases;
|
||||
private Integer mTimeslot;
|
||||
|
||||
private IChannelMetadataUpdateListener mIChannelMetadataUpdateListener;
|
||||
private AliasModel mAliasModel;
|
||||
private AliasList mAliasList;
|
||||
|
||||
public ChannelMetadata(AliasModel aliasModel)
|
||||
public ChannelMetadata(AliasModel aliasModel, Integer timeslot)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
mTimeslot = timeslot;
|
||||
}
|
||||
|
||||
public ChannelMetadata(AliasModel aliasModel)
|
||||
{
|
||||
this(aliasModel, null);
|
||||
}
|
||||
|
||||
public Integer getTimeslot()
|
||||
{
|
||||
return mTimeslot;
|
||||
}
|
||||
|
||||
public boolean hasTimeslot()
|
||||
{
|
||||
return mTimeslot != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -242,6 +261,7 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
||||
@Override
|
||||
public void receive(IdentifierUpdateNotification update)
|
||||
{
|
||||
// mLog.debug("Received update: " + update + " class:" + update.getIdentifier().getClass());
|
||||
Identifier identifier = update.getIdentifier();
|
||||
|
||||
switch(identifier.getIdentifierClass())
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.channel.metadata;
|
||||
|
||||
@@ -24,6 +26,7 @@ import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.controller.channel.Channel;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -31,6 +34,7 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.awt.EventQueue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -77,7 +81,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements IChannel
|
||||
}
|
||||
|
||||
|
||||
public void add(ChannelMetadata channelMetadata, Channel channel)
|
||||
public void add(Collection<ChannelMetadata> channelMetadatas, Channel channel)
|
||||
{
|
||||
//Execute on the swing thread to avoid threading issues
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
@@ -85,11 +89,14 @@ public class ChannelMetadataModel extends AbstractTableModel implements IChannel
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mChannelMetadata.add(channelMetadata);
|
||||
mMetadataChannelMap.put(channelMetadata, channel);
|
||||
int index = mChannelMetadata.indexOf(channelMetadata);
|
||||
fireTableRowsInserted(index, index);
|
||||
channelMetadata.setUpdateEventListener(ChannelMetadataModel.this);
|
||||
for(ChannelMetadata channelMetadata: channelMetadatas)
|
||||
{
|
||||
mChannelMetadata.add(channelMetadata);
|
||||
mMetadataChannelMap.put(channelMetadata, channel);
|
||||
int index = mChannelMetadata.indexOf(channelMetadata);
|
||||
fireTableRowsInserted(index, index);
|
||||
channelMetadata.setUpdateEventListener(ChannelMetadataModel.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -161,6 +168,8 @@ public class ChannelMetadataModel extends AbstractTableModel implements IChannel
|
||||
case COLUMN_USER_FROM_ALIAS:
|
||||
case COLUMN_USER_TO_ALIAS:
|
||||
return Alias.class;
|
||||
case COLUMN_DECODER_LOGICAL_CHANNEL_NAME:
|
||||
return String.class;
|
||||
default:
|
||||
return Identifier.class;
|
||||
}
|
||||
@@ -180,7 +189,31 @@ public class ChannelMetadataModel extends AbstractTableModel implements IChannel
|
||||
case COLUMN_DECODER_TYPE:
|
||||
return channelMetadata.getDecoderTypeConfigurationIdentifier();
|
||||
case COLUMN_DECODER_LOGICAL_CHANNEL_NAME:
|
||||
return channelMetadata.getDecoderLogicalChannelNameIdentifier();
|
||||
DecoderLogicalChannelNameIdentifier id = channelMetadata.getDecoderLogicalChannelNameIdentifier();
|
||||
|
||||
if(channelMetadata.hasTimeslot())
|
||||
{
|
||||
if(id == null)
|
||||
{
|
||||
return "TS:" + channelMetadata.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
String value = id.getValue().replace(" TS0", "").replace(" TS1", "");
|
||||
return value + " TS:" + channelMetadata.getTimeslot();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(id == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return id.getValue();
|
||||
}
|
||||
}
|
||||
case COLUMN_CONFIGURATION_FREQUENCY:
|
||||
return channelMetadata.getFrequencyConfigurationIdentifier();
|
||||
case COLUMN_CONFIGURATION_CHANNEL:
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.channel.metadata;
|
||||
|
||||
@@ -33,6 +35,7 @@ import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
@@ -58,6 +61,7 @@ import java.util.Map;
|
||||
|
||||
public class ChannelMetadataPanel extends JPanel implements ListSelectionListener
|
||||
{
|
||||
private static final String TABLE_PREFERENCE_KEY = "channel.metadata.panel";
|
||||
private ChannelModel mChannelModel;
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
private IconManager mIconManager;
|
||||
@@ -66,6 +70,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
||||
private Broadcaster<ProcessingChain> mSelectedProcessingChainBroadcaster = new Broadcaster<>();
|
||||
private Map<State,Color> mBackgroundColors = new HashMap<>();
|
||||
private Map<State,Color> mForegroundColors = new HashMap<>();
|
||||
private JTableColumnWidthMonitor mTableColumnMonitor;
|
||||
|
||||
/**
|
||||
* Table view for currently decoding channel metadata
|
||||
@@ -108,6 +113,9 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_CONFIGURATION_FREQUENCY)
|
||||
.setCellRenderer(new FrequencyCellRenderer());
|
||||
|
||||
//Add a table column width monitor to store/restore column widths
|
||||
mTableColumnMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(mTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
@@ -122,7 +130,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
||||
private void setColors()
|
||||
{
|
||||
mBackgroundColors.put(State.ACTIVE, Color.CYAN);
|
||||
mForegroundColors.put(State.ACTIVE, Color.YELLOW);
|
||||
mForegroundColors.put(State.ACTIVE, Color.BLUE);
|
||||
mBackgroundColors.put(State.CALL, Color.BLUE);
|
||||
mForegroundColors.put(State.CALL, Color.YELLOW);
|
||||
mBackgroundColors.put(State.CONTROL, Color.ORANGE);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*******************************************************************************
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||
* 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
|
||||
@@ -14,8 +15,8 @@
|
||||
*
|
||||
* 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.channel.metadata;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
@@ -33,10 +34,9 @@ import net.miginfocom.swing.MigLayout;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.Color;
|
||||
|
||||
public class ChannelMetadataViewer extends JPanel
|
||||
public class NowPlayingPanel extends JPanel
|
||||
{
|
||||
private ChannelMetadataPanel mChannelMetadataPanel;
|
||||
|
||||
private ChannelDetailPanel mChannelDetailPanel;
|
||||
private DecodeEventPanel mDecodeEventPanel;
|
||||
private MessageActivityPanel mMessageActivityPanel;
|
||||
@@ -45,12 +45,12 @@ public class ChannelMetadataViewer extends JPanel
|
||||
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
|
||||
* messages, events, and spectral view.
|
||||
*/
|
||||
public ChannelMetadataViewer(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager,
|
||||
IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences)
|
||||
public NowPlayingPanel(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager,
|
||||
IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences)
|
||||
{
|
||||
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
|
||||
mDecodeEventPanel = new DecodeEventPanel(iconManager, userPreferences, aliasModel);
|
||||
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
|
||||
mMessageActivityPanel = new MessageActivityPanel(userPreferences);
|
||||
mChannelMetadataPanel = new ChannelMetadataPanel(channelModel, channelProcessingManager, iconManager, userPreferences);
|
||||
|
||||
init();
|
||||
@@ -64,7 +64,6 @@ public class ChannelMetadataViewer extends JPanel
|
||||
tabbedPane.addTab("Details", mChannelDetailPanel);
|
||||
tabbedPane.addTab("Events", mDecodeEventPanel);
|
||||
tabbedPane.addTab("Messages", mMessageActivityPanel);
|
||||
// tabbedPane.addTab("Spectrum", mChannelSpectrumPanel);
|
||||
tabbedPane.setFont(this.getFont());
|
||||
tabbedPane.setForeground(Color.BLACK);
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.channel.state;
|
||||
|
||||
import io.github.dsheirer.audio.squelch.ISquelchStateProvider;
|
||||
import io.github.dsheirer.channel.metadata.ChannelMetadata;
|
||||
import io.github.dsheirer.controller.channel.Channel;
|
||||
import io.github.dsheirer.controller.channel.ChannelEvent;
|
||||
import io.github.dsheirer.controller.channel.IChannelEventProvider;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateProvider;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEventProvider;
|
||||
import io.github.dsheirer.sample.IOverflowListener;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.heartbeat.Heartbeat;
|
||||
import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public abstract class AbstractChannelState extends Module implements IChannelEventProvider, IDecodeEventProvider,
|
||||
IDecoderStateEventProvider, ISourceEventProvider, IHeartbeatListener, ISquelchStateProvider,
|
||||
IdentifierUpdateProvider, IOverflowListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AbstractChannelState.class);
|
||||
|
||||
protected Listener<ChannelEvent> mChannelEventListener;
|
||||
protected Listener<IDecodeEvent> mDecodeEventListener;
|
||||
protected Listener<DecoderStateEvent> mDecoderStateListener;
|
||||
protected Listener<SourceEvent> mExternalSourceEventListener;
|
||||
protected Channel mChannel;
|
||||
protected boolean mSourceOverflow = false;
|
||||
private HeartbeatReceiver mHeartbeatReceiver = new HeartbeatReceiver();
|
||||
|
||||
public AbstractChannelState(Channel channel)
|
||||
{
|
||||
mChannel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked each time that a heartbeat is received so that sub-class implementations can check current timers and
|
||||
* adjust channel state as necessary. The heartbeat arrives on a periodic basis independent of any decoded
|
||||
* messages so that channel state is not entirely dependent on a continuous decoded message stream.
|
||||
*/
|
||||
protected abstract void checkState();
|
||||
|
||||
public abstract Collection<ChannelMetadata> getChannelMetadata();
|
||||
|
||||
public abstract void updateChannelStateIdentifiers(IdentifierUpdateNotification notification);
|
||||
|
||||
/**
|
||||
* Receiver inner class that implements the IHeartbeatListener interface to receive heartbeat messages.
|
||||
*/
|
||||
@Override
|
||||
public Listener<Heartbeat> getHeartbeatListener()
|
||||
{
|
||||
return mHeartbeatReceiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked if the source buffer provider goes into overflow state. Since this is an external state,
|
||||
* we use the mSourceOverflow variable to override the internal state reported to external listeners.
|
||||
*
|
||||
* @param overflow true to indicate an overflow state
|
||||
*/
|
||||
@Override
|
||||
public void sourceOverflow(boolean overflow)
|
||||
{
|
||||
mSourceOverflow = overflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this channel's sample buffer is in overflow state, meaning that the inbound sample
|
||||
* stream is not being processed fast enough and samples are being thrown away until the processing can
|
||||
* catch up.
|
||||
*
|
||||
* @return true if the channel is in overflow state.
|
||||
*/
|
||||
public boolean isOverflow()
|
||||
{
|
||||
return mSourceOverflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChannelEventListener(Listener<ChannelEvent> listener)
|
||||
{
|
||||
mChannelEventListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChannelEventListener()
|
||||
{
|
||||
mChannelEventListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDecodeEventListener(Listener<IDecodeEvent> listener)
|
||||
{
|
||||
mDecodeEventListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDecodeEventListener(Listener<IDecodeEvent> listener)
|
||||
{
|
||||
mDecodeEventListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void setDecoderStateListener(Listener<DecoderStateEvent> listener)
|
||||
{
|
||||
mDecoderStateListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeDecoderStateListener()
|
||||
{
|
||||
mDecoderStateListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive source events from the channel state
|
||||
*/
|
||||
@Override
|
||||
public void setSourceEventListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mExternalSourceEventListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* De-Registers a listener from receiving source events from the channel state
|
||||
*/
|
||||
@Override
|
||||
public void removeSourceEventListener()
|
||||
{
|
||||
mExternalSourceEventListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes periodic heartbeats received from the processing chain to perform state monitoring and cleanup
|
||||
* functions.
|
||||
*
|
||||
* Monitors decoder state events to automatically transition the channel state to IDLE (standard channel) or to
|
||||
* TEARDOWN (traffic channel) when decoding stops or the monitored channel returns to a no signal state.
|
||||
*
|
||||
* Provides a FADE transition state to allow for momentary decoding dropouts and to allow the user access to call
|
||||
* details for a fade period upon call end.
|
||||
*/
|
||||
public class HeartbeatReceiver implements Listener<Heartbeat>
|
||||
{
|
||||
@Override
|
||||
public void receive(Heartbeat heartbeat)
|
||||
{
|
||||
checkState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * 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.channel.state;
|
||||
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateListener;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateProvider;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.IMessageListener;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.event.ActivitySummaryProvider;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEventProvider;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class AbstractDecoderState extends Module implements ActivitySummaryProvider, Listener<IMessage>,
|
||||
IDecodeEventProvider, IDecoderStateEventListener, IDecoderStateEventProvider, IMessageListener,
|
||||
IdentifierUpdateProvider, IdentifierUpdateListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AbstractDecoderState.class);
|
||||
protected String DIVIDER1 = "======================================================\n";
|
||||
protected String DIVIDER2 = "------------------------------------------------------\n";
|
||||
/* This has to be a broadcaster in order for references to persist */
|
||||
protected Broadcaster<IDecodeEvent> mDecodeEventBroadcaster = new Broadcaster<>();
|
||||
protected Listener<DecoderStateEvent> mDecoderStateListener;
|
||||
private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener();
|
||||
|
||||
public abstract DecoderType getDecoderType();
|
||||
|
||||
/**
|
||||
* Provides subclass reference to the decode event broadcaster
|
||||
*/
|
||||
protected Broadcaster<IDecodeEvent> getDecodeEventBroadcaster()
|
||||
{
|
||||
return mDecodeEventBroadcaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<IMessage> getMessageListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IDecoderStateEventListener interface to receive state
|
||||
* reset events.
|
||||
*/
|
||||
public abstract void receiveDecoderStateEvent(DecoderStateEvent event);
|
||||
|
||||
/**
|
||||
* Activity Summary - textual summary of activity observed by the channel state.
|
||||
*/
|
||||
public abstract String getActivitySummary();
|
||||
|
||||
/**
|
||||
* Broadcasts a decode event to any registered listeners
|
||||
*/
|
||||
protected void broadcast(IDecodeEvent event)
|
||||
{
|
||||
mDecodeEventBroadcaster.broadcast(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a call event listener
|
||||
*/
|
||||
@Override
|
||||
public void addDecodeEventListener(Listener<IDecodeEvent> listener)
|
||||
{
|
||||
mDecodeEventBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the call event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeDecodeEventListener(Listener<IDecodeEvent> listener)
|
||||
{
|
||||
mDecodeEventBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<DecoderStateEvent> getDecoderStateListener()
|
||||
{
|
||||
return mDecoderStateEventListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a channel state event to any registered listeners
|
||||
*/
|
||||
protected void broadcast(DecoderStateEvent event)
|
||||
{
|
||||
if(mDecoderStateListener != null)
|
||||
{
|
||||
mDecoderStateListener.receive(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void setDecoderStateListener(Listener<DecoderStateEvent> listener)
|
||||
{
|
||||
mDecoderStateListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeDecoderStateListener()
|
||||
{
|
||||
mDecoderStateListener = null;
|
||||
}
|
||||
|
||||
private class DecoderStateEventListener implements Listener<DecoderStateEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(DecoderStateEvent event)
|
||||
{
|
||||
receiveDecoderStateEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.
|
||||
* * ******************************************************************************
|
||||
* * 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.channel.state;
|
||||
|
||||
@@ -48,11 +50,6 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivitySummary()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user