mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 17:17:29 -05:00
Compare commits
126 Commits
v0.3.0-bet
...
v0.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e321bdaa3 | ||
|
|
f191d28c98 | ||
|
|
afc3d299b6 | ||
|
|
0471d90b54 | ||
|
|
8f68967b6c | ||
|
|
91b950c13d | ||
|
|
581ff9f449 | ||
|
|
67c3cb9639 | ||
|
|
cebd2da89f | ||
|
|
73b6ef164f | ||
|
|
b2c7ac8c66 | ||
|
|
108691dc3c | ||
|
|
455aa97286 | ||
|
|
d608834214 | ||
|
|
41660e1881 | ||
|
|
110ccb158c | ||
|
|
64ccacf04a | ||
|
|
f00b9555fe | ||
|
|
85442a9648 | ||
|
|
c67a0b329d | ||
|
|
8035d64fcd | ||
|
|
df2a061a08 | ||
|
|
e430319937 | ||
|
|
724f769771 | ||
|
|
c196cfebed | ||
|
|
e80b5866d4 | ||
|
|
c8994d5593 | ||
|
|
99ef57c90f | ||
|
|
be62ae0459 | ||
|
|
b9be6f43e0 | ||
|
|
d5ad3f807d | ||
|
|
d85ebd018d | ||
|
|
ab4e31ae00 | ||
|
|
5efd89632e | ||
|
|
1dc3863489 | ||
|
|
441286ba13 | ||
|
|
e4323df347 | ||
|
|
a872e0d664 | ||
|
|
fbc92cfe31 | ||
|
|
1a432197ac | ||
|
|
fbc6e508bd | ||
|
|
f9b6e67b00 | ||
|
|
a628800a71 | ||
|
|
25f71a76c4 | ||
|
|
5fe8ccd478 | ||
|
|
069f951a86 | ||
|
|
dab6df7836 | ||
|
|
89da46b63c | ||
|
|
5f2ce2a9f2 | ||
|
|
7a0b377971 | ||
|
|
5dbe0ecce3 | ||
|
|
6647c581a6 | ||
|
|
a2ab5bfa95 | ||
|
|
ed486440b8 | ||
|
|
13464e01fc | ||
|
|
ea46a8b0e8 | ||
|
|
d510c34356 | ||
|
|
ae8a44caed | ||
|
|
1aa85a86ca | ||
|
|
17539d4d55 | ||
|
|
1db1d28390 | ||
|
|
37a941e904 | ||
|
|
57f6655824 | ||
|
|
4cd8f8209f | ||
|
|
61bfef5798 | ||
|
|
ef172a04c6 | ||
|
|
a877afcceb | ||
|
|
980ad72d4d | ||
|
|
9b746cc032 | ||
|
|
f51ae0b5da | ||
|
|
561078e9bf | ||
|
|
e052b01f9b | ||
|
|
09cf4fcfd7 | ||
|
|
5e731d0e9f | ||
|
|
670a02b0ba | ||
|
|
9ef156b061 | ||
|
|
d9cf1b19f3 | ||
|
|
a4a339b505 | ||
|
|
119370c46e | ||
|
|
4eb66e6045 | ||
|
|
8827ecd0e9 | ||
|
|
8660002f93 | ||
|
|
2058fcbc1d | ||
|
|
9515dfa380 | ||
|
|
9e9a604c6a | ||
|
|
234e313b7a | ||
|
|
e1e7169029 | ||
|
|
f3605db0ed | ||
|
|
c0a6d578c7 | ||
|
|
79b4b30ec7 | ||
|
|
17c7466c6b | ||
|
|
8162d6ffd6 | ||
|
|
cd60251337 | ||
|
|
5647879b37 | ||
|
|
6916d95cdf | ||
|
|
a0c32f6fe0 | ||
|
|
c3d60dc174 | ||
|
|
e55a2226a2 | ||
|
|
9f01678b00 | ||
|
|
46425e9cae | ||
|
|
3726401563 | ||
|
|
a3e50acfe3 | ||
|
|
cf0018e6ba | ||
|
|
e8055dcab0 | ||
|
|
fce3d5c4f3 | ||
|
|
9cc0d367a8 | ||
|
|
2ca9e5d5ce | ||
|
|
861203c3a0 | ||
|
|
7c930deefe | ||
|
|
0e0af932eb | ||
|
|
6ba5e91fcb | ||
|
|
34dd2161ef | ||
|
|
2543072e08 | ||
|
|
281be147f8 | ||
|
|
12ecca4555 | ||
|
|
4c27d04120 | ||
|
|
ece202315f | ||
|
|
6afe5dcec9 | ||
|
|
8db47c25bc | ||
|
|
6a340050c6 | ||
|
|
183eb2f7c7 | ||
|
|
6bc92e8b94 | ||
|
|
767b302ed7 | ||
|
|
952d930b5b | ||
|
|
26087c465f | ||
|
|
74c15d530b |
2
.idea/dictionaries/denny.xml
generated
2
.idea/dictionaries/denny.xml
generated
@@ -7,6 +7,7 @@
|
|||||||
<w>fleetsync</w>
|
<w>fleetsync</w>
|
||||||
<w>icecast</w>
|
<w>icecast</w>
|
||||||
<w>ident</w>
|
<w>ident</w>
|
||||||
|
<w>overflowable</w>
|
||||||
<w>polyphase</w>
|
<w>polyphase</w>
|
||||||
<w>remez</w>
|
<w>remez</w>
|
||||||
<w>samplerate</w>
|
<w>samplerate</w>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
<w>talkgroup</w>
|
<w>talkgroup</w>
|
||||||
<w>tgid</w>
|
<w>tgid</w>
|
||||||
<w>ultravox</w>
|
<w>ultravox</w>
|
||||||
|
<w>wacn</w>
|
||||||
<w>xtea</w>
|
<w>xtea</w>
|
||||||
</words>
|
</words>
|
||||||
</dictionary>
|
</dictionary>
|
||||||
|
|||||||
36
.idea/libraries/imports.xml
generated
36
.idea/libraries/imports.xml
generated
@@ -27,39 +27,9 @@
|
|||||||
<root url="jar://$PROJECT_DIR$/imports/commons-codec-1.9.jar!/" />
|
<root url="jar://$PROJECT_DIR$/imports/commons-codec-1.9.jar!/" />
|
||||||
<root url="jar://$PROJECT_DIR$/imports/commons-logging-1.2.jar!/" />
|
<root url="jar://$PROJECT_DIR$/imports/commons-logging-1.2.jar!/" />
|
||||||
<root url="jar://$PROJECT_DIR$/imports/slf4j-api-1.7.21.jar!/" />
|
<root url="jar://$PROJECT_DIR$/imports/slf4j-api-1.7.21.jar!/" />
|
||||||
|
<root url="jar://$PROJECT_DIR$/imports/libusb4java-1.2.0-linux-aarch64.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC />
|
||||||
<root url="https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/index.html" />
|
<SOURCES />
|
||||||
</JAVADOC>
|
|
||||||
<SOURCES>
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/fluent-hc/src/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/fluent-hc/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/fluent-hc/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-cache/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-cache/src/main/java-deprecated" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-cache/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-osgi/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-osgi/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-win/src/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-win/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-win/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java-deprecated" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/main/java-deprecated" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/main/java-deprecated" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/test/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/examples" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java-deprecated" />
|
|
||||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/test/java" />
|
|
||||||
</SOURCES>
|
|
||||||
</library>
|
</library>
|
||||||
</component>
|
</component>
|
||||||
15
CHANGELOG
15
CHANGELOG
@@ -1,10 +1,18 @@
|
|||||||
|
0.3.2 20171105
|
||||||
|
- Resolves issue with tuner frequency adjustments that was causing channel processing
|
||||||
|
to lockup and show persistent 'overflow' state in each channel status
|
||||||
|
- Resolves issue with broadcastify 'mountpoint-in-use' error handling and updates
|
||||||
|
memory usage logging
|
||||||
|
|
||||||
0.3.0-beta1 20170129
|
0.3.1 20171009
|
||||||
|
- Resolves Java 9 incompatibility issues
|
||||||
|
|
||||||
|
0.3.0 20170922
|
||||||
|
- REQUIRES: Java 8 (or newer) for compatibility and eventual changeover to JavaFX
|
||||||
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
|
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
|
||||||
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
|
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
|
||||||
stream can be independently configured with a delay. Audio calls are directed
|
stream can be independently configured with a delay. Audio calls are directed
|
||||||
to one or more streams by attaching one or more stream identifiers to each alias.
|
to one or more streams by attaching one or more stream identifiers to each alias.
|
||||||
- Requires Java 8 (or newer) for compatibility and eventual changeover to JavaFX
|
|
||||||
- Extensive GUI updates for tuner, channel and alias management. Includes new
|
- Extensive GUI updates for tuner, channel and alias management. Includes new
|
||||||
'Now Playing' channel view and 'Detail' view.
|
'Now Playing' channel view and 'Detail' view.
|
||||||
- Updated alias list management of alias id values and wildcards. Weighting is now
|
- Updated alias list management of alias id values and wildcards. Weighting is now
|
||||||
@@ -17,7 +25,8 @@
|
|||||||
- Implemented bounded queues for both tuner and individual channel IQ sample
|
- Implemented bounded queues for both tuner and individual channel IQ sample
|
||||||
buffers. Bounded queues have ability to enter/exit overflow state when queue
|
buffers. Bounded queues have ability to enter/exit overflow state when queue
|
||||||
size reaches overflow threshold and reset when queue size falls below the reset
|
size reaches overflow threshold and reset when queue size falls below the reset
|
||||||
threshold.
|
threshold. Channel state will show OVERFLOW in red/yellow.
|
||||||
|
- Improves handling of P25 TDMA channels (NO Phase 2 decoder support yet)
|
||||||
- Updated airspy controller for airspy-mini specific sample rate handling.
|
- Updated airspy controller for airspy-mini specific sample rate handling.
|
||||||
- New developer remez filter designer tool and internal usage
|
- New developer remez filter designer tool and internal usage
|
||||||
|
|
||||||
|
|||||||
@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
|
|||||||
state the exclusion of warranty; and each file should have at least
|
state the exclusion of warranty; and each file should have at least
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
{one line to give the program's name and a brief idea of what it does.}
|
||||||
Copyright (C) <year> <name of author>
|
Copyright (C) {year} {name of author}
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|||||||
If the program does terminal interaction, make it output a short
|
If the program does terminal interaction, make it output a short
|
||||||
notice like this when it starts in an interactive mode:
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
<program> Copyright (C) <year> <name of author>
|
{project} Copyright (C) {year} {fullname}
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
15
README.md
15
README.md
@@ -2,18 +2,9 @@
|
|||||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
||||||
|
|
||||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
||||||
* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
* [User's Manual Version 0.3.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_Version0.3.0)
|
||||||
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
||||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||||
|
|
||||||

|

|
||||||
<script>
|
**Figure 1:** sdrtrunk **Version 0.3.0** Application Screenshot
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-67489234-1', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<!-- version properties -->
|
<!-- version properties -->
|
||||||
<property name="major" value="0"/>
|
<property name="major" value="0"/>
|
||||||
<property name="minor" value="3"/>
|
<property name="minor" value="3"/>
|
||||||
<property name="patch" value="0"/>
|
<property name="patch" value="2"/>
|
||||||
<property name="release" value="-beta2"/>
|
<property name="release" value=""/>
|
||||||
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
|
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}${release}"/>
|
||||||
|
|
||||||
<!-- Build properties -->
|
<!-- Build properties -->
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
<property name="source.configs" location="${repo}/config"/>
|
<property name="source.configs" location="${repo}/config"/>
|
||||||
<property name="source.images" location="${repo}/images"/>
|
<property name="source.images" location="${repo}/images"/>
|
||||||
<property name="source.libs" location="${repo}/imports"/>
|
<property name="source.libs" location="${repo}/imports"/>
|
||||||
<property name="source.license" location="${repo}/license"/>
|
|
||||||
<property name="source.scripts" location="${repo}/scripts"/>
|
<property name="source.scripts" location="${repo}/scripts"/>
|
||||||
|
|
||||||
<property name="output" location="${repo}/product"/>
|
<property name="output" location="${repo}/product"/>
|
||||||
@@ -38,10 +37,32 @@
|
|||||||
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
|
<echo message="${major}.${minor}.${patch}-${release}" file="${output.classes}/sdrtrunk-version"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="compile" depends="clean,init" description="Compile java classes" >
|
<target name="compile" depends="clean,init,compile-java-8,compile-java-9"
|
||||||
|
description="Compile java classes" />
|
||||||
|
|
||||||
|
<target name="compile-java-8" depends="check-java-version" if="java.8" description="Compiles code with Java 8 arguments">
|
||||||
|
<echo message="Compiling with Java 8 Options"/>
|
||||||
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false"/>
|
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<target name="compile-java-9" depends="check-java-version" if="java.9" description="Compiles code with Java 9 arguments">
|
||||||
|
<echo message="Compiling with Java 9 Options"/>
|
||||||
|
<javac srcdir="${source.code}" destdir="${output.classes}" classpathref="classpath" includeantruntime="false">
|
||||||
|
<compilerarg value="--add-modules"/>
|
||||||
|
<compilerarg value="java.xml.bind"/>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="check-java-version" description="Determines if the compiler is Java 8 or 9">
|
||||||
|
<condition property="java.8" value="true">
|
||||||
|
<contains string="${java.version}" substring="1.8"/>
|
||||||
|
</condition>
|
||||||
|
<condition property="java.9" value="true">
|
||||||
|
<equals arg1="${java.version}" arg2="9"/>
|
||||||
|
</condition>
|
||||||
|
<echo message="Java Version: ${java.version}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
<target name="SDRTrunk" depends="compile" description="Create SDRTrunk application">
|
<target name="SDRTrunk" depends="compile" description="Create SDRTrunk application">
|
||||||
<!-- Create the distribution directory -->
|
<!-- Create the distribution directory -->
|
||||||
<mkdir dir="${output.folder}"/>
|
<mkdir dir="${output.folder}"/>
|
||||||
@@ -57,8 +78,8 @@
|
|||||||
<exclude name="*.rules"/>
|
<exclude name="*.rules"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
|
|
||||||
<fileset dir="${source.license}">
|
<fileset dir="${repo}">
|
||||||
<include name="license.txt"/>
|
<include name="LICENSE"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</jar>
|
</jar>
|
||||||
|
|
||||||
@@ -85,8 +106,8 @@
|
|||||||
|
|
||||||
<!-- Copy license file -->
|
<!-- Copy license file -->
|
||||||
<copy todir="${output.folder}">
|
<copy todir="${output.folder}">
|
||||||
<fileset dir="${source.license}">
|
<fileset dir="${repo}">
|
||||||
<include name="license.txt"/>
|
<include name="LICENSE"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
|
<conversionRule conversionWord="memory_usage" converterClass="util.MemoryUsageLogger" />
|
||||||
|
|
||||||
<timestamp key="appstart" datePattern="HHmmss"/>
|
<timestamp key="appstart" datePattern="HHmmss"/>
|
||||||
|
|
||||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
<maxHistory>10</maxHistory>
|
<maxHistory>10</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg%n</pattern>
|
<pattern>%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg %memory_usage%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@
|
|||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg%n</pattern>
|
<pattern>%-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg %memory_usage%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
|
|||||||
BIN
imports/libusb4java-1.2.0-linux-aarch64.jar
Normal file
BIN
imports/libusb4java-1.2.0-linux-aarch64.jar
Normal file
Binary file not shown.
@@ -1,2 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk
|
|
||||||
|
java -version
|
||||||
|
|
||||||
|
# Test for java version 9
|
||||||
|
JAVA_9=$(java -version 2>&1 | grep -i version | sed 's/.* "\([9]\).*/\1/')
|
||||||
|
|
||||||
|
if [ -z ${JAVA_9+x} ]; then
|
||||||
|
echo Using Java 9 program options
|
||||||
|
# Java 9 uses the G1 garbage collector by default and requires that we specify the
|
||||||
|
# java.xml.bind package since it's been removed from the java se core library
|
||||||
|
java -cp "*:libs/*" --add-modules java.xml.bind gui.SDRTrunk;
|
||||||
|
else
|
||||||
|
echo Using Java 8 program options
|
||||||
|
java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk;
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1 +1,25 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
java -version
|
||||||
|
|
||||||
|
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||||
|
|
||||||
|
for /f "usebackq tokens=3" %%a in (`java -version 2^>^&1 ^| find "version"`) do (
|
||||||
|
rem set b to the unquoted version string parsed from the java version command
|
||||||
|
set b=%%~a
|
||||||
|
|
||||||
|
rem set c to the first character of the unquoted version string
|
||||||
|
set c=!b:~0,1!
|
||||||
|
|
||||||
|
if !c! equ 9 (
|
||||||
|
echo Using Java 9 options
|
||||||
|
java -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" --add-modules java.xml.bind gui.SDRTrunk
|
||||||
|
) else (
|
||||||
|
echo Using Java 8 options
|
||||||
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
|
java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
ENDLOCAL
|
||||||
|
|
||||||
|
@echo on
|
||||||
|
|||||||
@@ -185,27 +185,6 @@ public class Alias
|
|||||||
*/
|
*/
|
||||||
public void validate()
|
public void validate()
|
||||||
{
|
{
|
||||||
//Check for duplicate broadcast channels
|
|
||||||
List<BroadcastChannel> broadcastChannels = getBroadcastChannels();
|
|
||||||
|
|
||||||
if(broadcastChannels.size() > 0)
|
|
||||||
{
|
|
||||||
Set<String> channelNames = new TreeSet<>();
|
|
||||||
|
|
||||||
for(BroadcastChannel broadcastChannel: broadcastChannels)
|
|
||||||
{
|
|
||||||
String channelName = broadcastChannel.getChannelName();
|
|
||||||
|
|
||||||
if(channelName == null || channelName.isEmpty() || channelNames.contains(channelName))
|
|
||||||
{
|
|
||||||
removeAliasID(broadcastChannel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
channelNames.add(channelName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns the priority level of this alias, if defined, or the default priority
|
* Returns the priority level of this alias, if defined, or the default priority
|
||||||
@@ -336,9 +315,9 @@ public class Alias
|
|||||||
* List of broadcast channels specified for this alias.
|
* List of broadcast channels specified for this alias.
|
||||||
*/
|
*/
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
public List<BroadcastChannel> getBroadcastChannels()
|
public Set<BroadcastChannel> getBroadcastChannels()
|
||||||
{
|
{
|
||||||
List<BroadcastChannel> broadcastChannels = new ArrayList<>();
|
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
|
||||||
|
|
||||||
for(AliasID id: getId())
|
for(AliasID id: getId())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -631,11 +631,21 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this alias list has a non-null, non-empty name
|
||||||
|
*/
|
||||||
|
private boolean hasName()
|
||||||
|
{
|
||||||
|
return mName != null && !mName.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive alias change event notifications and modify this list accordingly
|
* Receive alias change event notifications and modify this list accordingly
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receive(AliasEvent event)
|
public void receive(AliasEvent event)
|
||||||
|
{
|
||||||
|
if(hasName())
|
||||||
{
|
{
|
||||||
Alias alias = event.getAlias();
|
Alias alias = event.getAlias();
|
||||||
|
|
||||||
@@ -664,3 +674,4 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,12 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package alias;
|
package alias;
|
||||||
|
|
||||||
|
import alias.AliasEvent.Event;
|
||||||
|
import alias.id.broadcast.BroadcastChannel;
|
||||||
|
import sample.Broadcaster;
|
||||||
|
import sample.Listener;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -24,13 +30,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import javax.swing.table.AbstractTableModel;
|
|
||||||
|
|
||||||
import alias.id.broadcast.BroadcastChannel;
|
|
||||||
import sample.Broadcaster;
|
|
||||||
import sample.Listener;
|
|
||||||
import alias.AliasEvent.Event;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alias Model
|
* Alias Model
|
||||||
*/
|
*/
|
||||||
@@ -72,16 +71,18 @@ public class AliasModel extends AbstractTableModel
|
|||||||
|
|
||||||
public AliasList getAliasList(String name)
|
public AliasList getAliasList(String name)
|
||||||
{
|
{
|
||||||
if( mAliasListMap.containsKey( name ) )
|
if(name != null && mAliasListMap.containsKey(name))
|
||||||
{
|
{
|
||||||
return mAliasListMap.get(name);
|
return mAliasListMap.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
AliasList aliasList = new AliasList(name);
|
AliasList aliasList = new AliasList(name);
|
||||||
|
|
||||||
|
if(name != null)
|
||||||
|
{
|
||||||
for(Alias alias : mAliases)
|
for(Alias alias : mAliases)
|
||||||
{
|
{
|
||||||
if( alias.getList().equalsIgnoreCase( name ) )
|
if(alias.hasList() && alias.getList().equalsIgnoreCase(name))
|
||||||
{
|
{
|
||||||
aliasList.addAlias(alias);
|
aliasList.addAlias(alias);
|
||||||
}
|
}
|
||||||
@@ -91,6 +92,7 @@ public class AliasModel extends AbstractTableModel
|
|||||||
|
|
||||||
//Register the new alias list to receive updates from this model
|
//Register the new alias list to receive updates from this model
|
||||||
addListener(aliasList);
|
addListener(aliasList);
|
||||||
|
}
|
||||||
|
|
||||||
return aliasList;
|
return aliasList;
|
||||||
}
|
}
|
||||||
@@ -213,6 +215,7 @@ public class AliasModel extends AbstractTableModel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames any broadcast channels that have the previous name.
|
* Renames any broadcast channels that have the previous name.
|
||||||
|
*
|
||||||
* @param previousName to rename
|
* @param previousName to rename
|
||||||
* @param newName to assign to the broadcast channel
|
* @param newName to assign to the broadcast channel
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -40,10 +40,11 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
{
|
{
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static ComboBoxModel<String> EMPTY_MODEL = new DefaultComboBoxModel<>();
|
private static ComboBoxModel<String> EMPTY_LIST_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
private static ComboBoxModel<String> EMPTY_GROUP_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
|
||||||
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_MODEL);
|
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_LIST_MODEL);
|
||||||
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_MODEL);
|
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_GROUP_MODEL);
|
||||||
private JComboBox<Icon> mIconCombo;
|
private JComboBox<Icon> mIconCombo;
|
||||||
private JTextField mName;
|
private JTextField mName;
|
||||||
private JButton mButtonColor;
|
private JButton mButtonColor;
|
||||||
@@ -110,7 +111,7 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
|
|
||||||
if(groups.isEmpty())
|
if(groups.isEmpty())
|
||||||
{
|
{
|
||||||
mGroupCombo.setModel(EMPTY_MODEL);
|
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -238,7 +239,7 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
|
|
||||||
if(listNames.isEmpty())
|
if(listNames.isEmpty())
|
||||||
{
|
{
|
||||||
mListCombo.setModel(EMPTY_MODEL);
|
mListCombo.setModel(EMPTY_LIST_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -253,7 +254,7 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
|
|
||||||
if(groupNames.isEmpty())
|
if(groupNames.isEmpty())
|
||||||
{
|
{
|
||||||
mGroupCombo.setModel(EMPTY_MODEL);
|
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -285,8 +286,8 @@ public class AliasNameEditor extends Editor<Alias>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mListCombo.setModel(EMPTY_MODEL);
|
mListCombo.setModel(EMPTY_LIST_MODEL);
|
||||||
mGroupCombo.setModel(EMPTY_MODEL);
|
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
||||||
mName.setText(null);
|
mName.setText(null);
|
||||||
|
|
||||||
mButtonColor.setBackground(getBackground());
|
mButtonColor.setBackground(getBackground());
|
||||||
|
|||||||
@@ -24,12 +24,20 @@ import alias.id.AliasIDType;
|
|||||||
import alias.id.broadcast.BroadcastChannel;
|
import alias.id.broadcast.BroadcastChannel;
|
||||||
import alias.id.priority.Priority;
|
import alias.id.priority.Priority;
|
||||||
import alias.id.talkgroup.TalkgroupID;
|
import alias.id.talkgroup.TalkgroupID;
|
||||||
|
import gui.SDRTrunk;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import playlist.version1.System;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
public class PatchGroupAlias extends Alias
|
public class PatchGroupAlias extends Alias
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(PatchGroupAlias.class);
|
||||||
|
|
||||||
private Alias mPatchGroupAlias;
|
private Alias mPatchGroupAlias;
|
||||||
private List<Alias> mPatchedAliases = new ArrayList<>();
|
private List<Alias> mPatchedAliases = new ArrayList<>();
|
||||||
private List<String> mPatchedTalkgroupIDs = new ArrayList<>();
|
private List<String> mPatchedTalkgroupIDs = new ArrayList<>();
|
||||||
@@ -167,6 +175,8 @@ public class PatchGroupAlias extends Alias
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
|
{
|
||||||
|
if(!(alias instanceof PatchGroupAlias))
|
||||||
{
|
{
|
||||||
for(AliasAction action: alias.getAction())
|
for(AliasAction action: alias.getAction())
|
||||||
{
|
{
|
||||||
@@ -176,6 +186,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return aliasActions;
|
return aliasActions;
|
||||||
}
|
}
|
||||||
@@ -185,7 +196,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
{
|
{
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasActions())
|
if(!(alias instanceof PatchGroupAlias) && alias.hasActions())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -194,21 +205,45 @@ public class PatchGroupAlias extends Alias
|
|||||||
return hasPatchGroupAlias() && getPatchGroupAlias().hasActions();
|
return hasPatchGroupAlias() && getPatchGroupAlias().hasActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the highest listenable audio priority defined among the aliases in the group, or returns 'Do Not Monitor'
|
||||||
|
* if that is the only priority defined. Otherwise, returns default (100) priority.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getCallPriority()
|
public int getCallPriority()
|
||||||
{
|
{
|
||||||
int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY);
|
boolean hasDoNotMonitor = false;
|
||||||
|
|
||||||
|
int highestPriority = (hasPatchGroupAlias() ? getPatchGroupAlias().getCallPriority() : Priority.DEFAULT_PRIORITY + 1);
|
||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasCallPriority() && alias.getCallPriority() < highestPriority)
|
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority() && alias.getCallPriority() < highestPriority)
|
||||||
|
{
|
||||||
|
if(alias.getCallPriority() == Priority.DO_NOT_MONITOR)
|
||||||
|
{
|
||||||
|
hasDoNotMonitor = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
highestPriority = alias.getCallPriority();
|
highestPriority = alias.getCallPriority();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(highestPriority <= Priority.DEFAULT_PRIORITY)
|
||||||
|
{
|
||||||
return highestPriority;
|
return highestPriority;
|
||||||
}
|
}
|
||||||
|
else if(hasDoNotMonitor)
|
||||||
|
{
|
||||||
|
return Priority.DO_NOT_MONITOR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Priority.DEFAULT_PRIORITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCallPriority()
|
public boolean hasCallPriority()
|
||||||
@@ -220,7 +255,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasCallPriority())
|
if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -239,7 +274,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.isRecordable())
|
if(!(alias instanceof PatchGroupAlias) && alias.isRecordable())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -258,7 +293,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.isStreamable())
|
if(!(alias instanceof PatchGroupAlias) && alias.isStreamable())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -268,9 +303,9 @@ public class PatchGroupAlias extends Alias
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BroadcastChannel> getBroadcastChannels()
|
public Set<BroadcastChannel> getBroadcastChannels()
|
||||||
{
|
{
|
||||||
List<BroadcastChannel> broadcastChannels = new ArrayList<>();
|
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
|
||||||
|
|
||||||
if(hasPatchGroupAlias())
|
if(hasPatchGroupAlias())
|
||||||
{
|
{
|
||||||
@@ -279,12 +314,9 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
|
if(!(alias instanceof PatchGroupAlias))
|
||||||
{
|
{
|
||||||
if(!broadcastChannels.contains(broadcastChannel))
|
broadcastChannels.addAll(alias.getBroadcastChannels());
|
||||||
{
|
|
||||||
broadcastChannels.add(broadcastChannel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +333,7 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
for(Alias alias: mPatchedAliases)
|
for(Alias alias: mPatchedAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasBroadcastChannel(channel))
|
if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -309,4 +341,41 @@ public class PatchGroupAlias extends Alias
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
Alias a1 = new Alias("Alias 1");
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||||
|
|
||||||
|
Alias a2 = new Alias("Alias 1");
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||||
|
|
||||||
|
Alias a3 = new Alias("Alias 1");
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||||
|
|
||||||
|
Alias a4 = new Alias("Alias 1");
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 1"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 2"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 3"));
|
||||||
|
a1.addAliasID(new BroadcastChannel("Channel 4"));
|
||||||
|
|
||||||
|
PatchGroupAlias pga = new PatchGroupAlias();
|
||||||
|
pga.setPatchGroupAlias(a1);
|
||||||
|
pga.addPatchedAlias(a2);
|
||||||
|
pga.addPatchedAlias(a3);
|
||||||
|
pga.addPatchedAlias(a4);
|
||||||
|
|
||||||
|
Set<BroadcastChannel> bc = pga.getBroadcastChannels();
|
||||||
|
|
||||||
|
mLog.debug("Channels:" + bc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package alias.id;
|
package alias.id;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class WildcardID implements Comparable<WildcardID>
|
public class WildcardID implements Comparable<WildcardID>
|
||||||
@@ -45,7 +47,7 @@ public class WildcardID implements Comparable<WildcardID>
|
|||||||
*/
|
*/
|
||||||
public WildcardID(String value)
|
public WildcardID(String value)
|
||||||
{
|
{
|
||||||
assert(value != null && value.contains(WILDCARD) && value.length() < 10);
|
Validate.isTrue(value != null && value.contains(WILDCARD) && value.length() < 10);
|
||||||
|
|
||||||
mValue = value;
|
mValue = value;
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,29 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if(this == o)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(!(o instanceof BroadcastChannel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BroadcastChannel that = (BroadcastChannel)o;
|
||||||
|
|
||||||
|
return getChannelName() != null ? getChannelName().equals(that.getChannelName()) : that.getChannelName() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return getChannelName() != null ? getChannelName().hashCode() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a named broadcast channel
|
* Creates a named broadcast channel
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ public class AudioChannelPanel extends JPanel
|
|||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
setLayout(new MigLayout("align center center, insets 0 0 0 0", "[][][align right]0[grow,fill]", ""));
|
setLayout(new MigLayout("align center center, insets 0 0 0 0",
|
||||||
|
"[][][align right]0[grow,fill]", ""));
|
||||||
setBackground(mBackgroundColor);
|
setBackground(mBackgroundColor);
|
||||||
|
|
||||||
mMutedLabel.setFont(mFont);
|
mMutedLabel.setFont(mFont);
|
||||||
@@ -109,7 +110,7 @@ public class AudioChannelPanel extends JPanel
|
|||||||
|
|
||||||
mTo.setFont(mFont);
|
mTo.setFont(mFont);
|
||||||
mTo.setForeground(mValueColor);
|
mTo.setForeground(mValueColor);
|
||||||
add(mTo);
|
add(mTo,"wmin 10lp");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -159,29 +160,39 @@ public class AudioChannelPanel extends JPanel
|
|||||||
* does not occur on the Swing event thread -- wrap any calls to this
|
* does not occur on the Swing event thread -- wrap any calls to this
|
||||||
* method with an event thread call.
|
* method with an event thread call.
|
||||||
*/
|
*/
|
||||||
private void updateLabel(JLabel valueLabel, String value, JLabel aliasLabel, Alias alias)
|
private void updateLabel(JLabel textLabel, String value, JLabel iconLabel, Alias alias)
|
||||||
{
|
{
|
||||||
if(value != null)
|
if(value != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(alias != null)
|
if(alias != null)
|
||||||
{
|
{
|
||||||
valueLabel.setText(alias.getName());
|
iconLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18));
|
||||||
aliasLabel.setIcon(mIconManager.getIcon(alias.getIconName(), 18));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
valueLabel.setText(value);
|
|
||||||
aliasLabel.setIcon(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
valueLabel.setText("-----");
|
|
||||||
aliasLabel.setIcon(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
String aliasName = alias.getName();
|
||||||
|
|
||||||
|
//Truncate label if length exceeds 33 characters - hack!
|
||||||
|
if(aliasName.length() > 33)
|
||||||
|
{
|
||||||
|
textLabel.setText(aliasName.substring(0,30) + "...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textLabel.setText(aliasName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iconLabel.setIcon(null);
|
||||||
|
textLabel.setText(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iconLabel.setIcon(null);
|
||||||
|
textLabel.setText("-----");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes audio metadata to update this panel's display values
|
* Processes audio metadata to update this panel's display values
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ public class AudioChannelsPanel extends JPanel
|
|||||||
|
|
||||||
public AudioChannelsPanel(IconManager iconManager, SettingsManager settingsManager, IAudioController controller)
|
public AudioChannelsPanel(IconManager iconManager, SettingsManager settingsManager, IAudioController controller)
|
||||||
{
|
{
|
||||||
// setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill][][grow,fill]", "[grow,fill]"));
|
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", "[][sg,grow,fill][][sg,grow,fill]", "[grow,fill]"));
|
|
||||||
|
|
||||||
setBackground(Color.BLACK);
|
setBackground(Color.BLACK);
|
||||||
|
|
||||||
@@ -44,7 +43,7 @@ public class AudioChannelsPanel extends JPanel
|
|||||||
|
|
||||||
for(int x = 0; x < outputs.size(); x++)
|
for(int x = 0; x < outputs.size(); x++)
|
||||||
{
|
{
|
||||||
add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x)), "growx");
|
add(new AudioChannelPanel(iconManager, settingsManager, outputs.get(x)));
|
||||||
|
|
||||||
if(x < outputs.size() - 1)
|
if(x < outputs.size() - 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
loadSettings();
|
loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the saved mixer configuration or a default configuration for audio playback.
|
||||||
|
*/
|
||||||
private void loadSettings()
|
private void loadSettings()
|
||||||
{
|
{
|
||||||
MixerChannelConfiguration configuration = null;
|
MixerChannelConfiguration configuration = null;
|
||||||
@@ -132,6 +135,9 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a default audio playback configuration with a mono audio playback channel.
|
||||||
|
*/
|
||||||
private MixerChannelConfiguration getDefaultConfiguration()
|
private MixerChannelConfiguration getDefaultConfiguration()
|
||||||
{
|
{
|
||||||
/* Use the system default mixer and mono channel as default startup */
|
/* Use the system default mixer and mono channel as default startup */
|
||||||
@@ -161,6 +167,9 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
mAudioOutputConnections.clear();
|
mAudioOutputConnections.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary ingest point for audio produced by all decoding channels, for distribution to audio playback devices.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void receive(AudioPacket packet)
|
public synchronized void receive(AudioPacket packet)
|
||||||
{
|
{
|
||||||
@@ -176,11 +185,11 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
|
|
||||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||||
{
|
{
|
||||||
if(connection.isInactive() &&
|
if(connection.isInactive() && mChannelConnectionMap.containsKey(connection.getChannelMetadataID()))
|
||||||
mChannelConnectionMap.containsKey(connection.getChannelMetadataID()))
|
|
||||||
{
|
{
|
||||||
mChannelConnectionMap.remove(connection.getChannelMetadataID());
|
mChannelConnectionMap.remove(connection.getChannelMetadataID());
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
|
mAvailableConnectionCount++;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,7 +201,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies the lowest priority channel connection
|
* Identifies the lowest priority channel connection where the a higher value indicates a lower priority.
|
||||||
*/
|
*/
|
||||||
private void updateLowestPriorityAssignment()
|
private void updateLowestPriorityAssignment()
|
||||||
{
|
{
|
||||||
@@ -201,19 +210,32 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||||
{
|
{
|
||||||
if(connection.isConnected() &&
|
if(connection.isConnected() &&
|
||||||
(mLowestPriorityConnection == null || mLowestPriorityConnection.getPriority() > connection.getPriority()))
|
(mLowestPriorityConnection == null || mLowestPriorityConnection.getPriority() < connection.getPriority()))
|
||||||
{
|
{
|
||||||
mLowestPriorityConnection = connection;
|
mLowestPriorityConnection = connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures audio playback to use the configuration specified in the entry argument.
|
||||||
|
*
|
||||||
|
* @param entry to use in configuring the audio playback setup.
|
||||||
|
* @throws AudioException if there is an error
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setMixerChannelConfiguration(MixerChannelConfiguration entry) throws AudioException
|
public void setMixerChannelConfiguration(MixerChannelConfiguration entry) throws AudioException
|
||||||
{
|
{
|
||||||
setMixerChannelConfiguration(entry, true);
|
setMixerChannelConfiguration(entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures audio playback to use the configuration specified in the entry argument.
|
||||||
|
*
|
||||||
|
* @param entry to use in configuring the audio playback setup.
|
||||||
|
* @param saveSettings to save the audio playback configuration settings in the properties file.
|
||||||
|
* @throws AudioException if there is an error
|
||||||
|
*/
|
||||||
public void setMixerChannelConfiguration(MixerChannelConfiguration entry, boolean saveSettings) throws AudioException
|
public void setMixerChannelConfiguration(MixerChannelConfiguration entry, boolean saveSettings) throws AudioException
|
||||||
{
|
{
|
||||||
if(entry != null && (entry.getMixerChannel() == MixerChannel.MONO || entry.getMixerChannel() == MixerChannel.STEREO))
|
if(entry != null && (entry.getMixerChannel() == MixerChannel.MONO || entry.getMixerChannel() == MixerChannel.STEREO))
|
||||||
@@ -266,9 +288,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all channel assignments and terminates all audio outputs in
|
* Clears all channel assignments and terminates all audio outputs in preparation for complete shutdown or change
|
||||||
* preparation for complete shutdown or change to another mixer/channel
|
* to another mixer/channel configuration
|
||||||
* configuration
|
|
||||||
*/
|
*/
|
||||||
private void disposeCurrentConfiguration()
|
private void disposeCurrentConfiguration()
|
||||||
{
|
{
|
||||||
@@ -288,12 +309,18 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
mLowestPriorityConnection = null;
|
mLowestPriorityConnection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current audio playback mixer channel configuration setting.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException
|
public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException
|
||||||
{
|
{
|
||||||
return mMixerChannelConfiguration;
|
return mMixerChannelConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of audio outputs available for the current mixer channel configuration
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<AudioOutput> getAudioOutputs()
|
public List<AudioOutput> getAudioOutputs()
|
||||||
{
|
{
|
||||||
@@ -311,12 +338,18 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an audio event listener to receive audio event notifications.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void addControllerListener(Listener<AudioEvent> listener)
|
public void addControllerListener(Listener<AudioEvent> listener)
|
||||||
{
|
{
|
||||||
mControllerBroadcaster.addListener(listener);
|
mControllerBroadcaster.addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an audio event listener from receiving audio event notifications.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void removeControllerListener(Listener<AudioEvent> listener)
|
public void removeControllerListener(Listener<AudioEvent> listener)
|
||||||
{
|
{
|
||||||
@@ -324,16 +357,15 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an audio output connection for the packet if one is
|
* Returns an audio output connection for the packet if one is available, or overrides an existing lower priority
|
||||||
* available, or overrides an existing lower priority connection.
|
* connection. Returns null if no connection is available for the audio packet.
|
||||||
* Returns null if no connection is available for the audio packet.
|
|
||||||
*
|
*
|
||||||
* @param packet from a source
|
* @param audioPacket from a decoding channel source
|
||||||
* @return an audio output connection or null
|
* @return an audio output connection or null
|
||||||
*/
|
*/
|
||||||
private AudioOutputConnection getConnection(AudioPacket packet)
|
private AudioOutputConnection getConnection(AudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
int channelMetadataID = packet.getMetadata().getMetadataID();
|
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
||||||
|
|
||||||
//Use an existing connection
|
//Use an existing connection
|
||||||
if(mChannelConnectionMap.containsKey(channelMetadataID))
|
if(mChannelConnectionMap.containsKey(channelMetadataID))
|
||||||
@@ -341,37 +373,35 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
return mChannelConnectionMap.get(channelMetadataID);
|
return mChannelConnectionMap.get(channelMetadataID);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Connect to and use an available connection
|
//Connect to an unused, available connection
|
||||||
if(mAvailableConnectionCount > 0)
|
if(mAvailableConnectionCount > 0)
|
||||||
{
|
{
|
||||||
for(AudioOutputConnection connection : mAudioOutputConnections)
|
for(AudioOutputConnection connection : mAudioOutputConnections)
|
||||||
{
|
{
|
||||||
if(connection.isDisconnected())
|
if(connection.isDisconnected())
|
||||||
{
|
{
|
||||||
connection.connect(channelMetadataID);
|
connection.connect(channelMetadataID, audioPacket.getMetadata().getAudioPriority());
|
||||||
mChannelConnectionMap.put(channelMetadataID, connection);
|
mChannelConnectionMap.put(channelMetadataID, connection);
|
||||||
|
mAvailableConnectionCount--;
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Preempt an existing connection and connect it to this higher priority source
|
//Preempt an existing lower priority connection and connect when this is a higher priority packet
|
||||||
else //Check for a channel priority override
|
else
|
||||||
{
|
{
|
||||||
int priority = packet.getMetadata().getAudioPriority();
|
int priority = audioPacket.getMetadata().getAudioPriority();
|
||||||
|
|
||||||
if(mLowestPriorityConnection != null &&
|
|
||||||
priority < mLowestPriorityConnection.getPriority())
|
|
||||||
{
|
|
||||||
AudioOutputConnection connection = mLowestPriorityConnection;
|
AudioOutputConnection connection = mLowestPriorityConnection;
|
||||||
|
|
||||||
|
if(connection != null && priority < connection.getPriority())
|
||||||
|
{
|
||||||
mChannelConnectionMap.remove(connection.getChannelMetadataID());
|
mChannelConnectionMap.remove(connection.getChannelMetadataID());
|
||||||
|
|
||||||
connection.connect(channelMetadataID);
|
connection.connect(channelMetadataID, priority);
|
||||||
|
|
||||||
mChannelConnectionMap.put(channelMetadataID, connection);
|
mChannelConnectionMap.put(channelMetadataID, connection);
|
||||||
|
|
||||||
updateLowestPriorityAssignment();
|
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -494,9 +524,11 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
* Connects this assignment to the indicated source so that audio
|
* Connects this assignment to the indicated source so that audio
|
||||||
* packets from this source can be sent to the audio output
|
* packets from this source can be sent to the audio output
|
||||||
*/
|
*/
|
||||||
public void connect(int source)
|
public void connect(int source, int priority)
|
||||||
{
|
{
|
||||||
mChannelMetadataID = source;
|
mChannelMetadataID = source;
|
||||||
|
mPriority = priority;
|
||||||
|
updateLowestPriorityAssignment();
|
||||||
|
|
||||||
mAudioOutput.updateTimestamp();
|
mAudioOutput.updateTimestamp();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ public class AudioPanel extends JPanel implements Listener<AudioEvent>
|
|||||||
{
|
{
|
||||||
setIcon(UNMUTED_ICON);
|
setIcon(UNMUTED_ICON);
|
||||||
setBorderPainted(false);
|
setBorderPainted(false);
|
||||||
|
getAccessibleContext().setAccessibleName("Mute");
|
||||||
|
|
||||||
addActionListener(new ActionListener()
|
addActionListener(new ActionListener()
|
||||||
{
|
{
|
||||||
@@ -402,6 +403,7 @@ public class AudioPanel extends JPanel implements Listener<AudioEvent>
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
setIcon(mMuted ? MUTED_ICON : UNMUTED_ICON);
|
setIcon(mMuted ? MUTED_ICON : UNMUTED_ICON);
|
||||||
|
getAccessibleContext().setAccessibleName(mMuted ? "Unmute" : "Mute");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public enum BroadcastState
|
|||||||
/**
|
/**
|
||||||
* Specified mount point is already in use
|
* Specified mount point is already in use
|
||||||
*/
|
*/
|
||||||
MOUNT_POINT_IN_USE("Mount Point In Use", true),
|
MOUNT_POINT_IN_USE("Mount Point In Use", false),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server is not known or reachable
|
* Server is not known or reachable
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
|
|||||||
session.write(updateRequest);
|
session.write(updateRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(UnresolvedAddressException uae)
|
||||||
|
{
|
||||||
|
//Do nothing - the server is temporarily unavailable
|
||||||
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
Throwable throwableCause = e.getCause();
|
Throwable throwableCause = e.getCause();
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
|
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mConnecting.set(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -176,8 +178,6 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
{
|
{
|
||||||
mStreamingSession.closeNow();
|
mStreamingSession.closeNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
mConnecting.set(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,6 +256,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
mSocketConnector = null;
|
mSocketConnector = null;
|
||||||
|
|
||||||
mConnecting.set(false);
|
mConnecting.set(false);
|
||||||
|
|
||||||
super.sessionClosed(session);
|
super.sessionClosed(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.apache.mina.core.RuntimeIoException;
|
|||||||
import org.apache.mina.core.future.ConnectFuture;
|
import org.apache.mina.core.future.ConnectFuture;
|
||||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||||
import org.apache.mina.core.session.IoSession;
|
import org.apache.mina.core.session.IoSession;
|
||||||
|
import org.apache.mina.core.write.WriteTimeoutException;
|
||||||
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -35,6 +36,7 @@ import util.ThreadPool;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.channels.UnresolvedAddressException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
@@ -43,7 +45,8 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
private final static Logger mLog = LoggerFactory.getLogger(IcecastTCPAudioBroadcaster.class);
|
private final static Logger mLog = LoggerFactory.getLogger(IcecastTCPAudioBroadcaster.class);
|
||||||
private final static String TERMINATOR = "\r\n";
|
private final static String TERMINATOR = "\r\n";
|
||||||
private final static String SEPARATOR = ":";
|
private final static String SEPARATOR = ":";
|
||||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
private static final long RECONNECT_INTERVAL_MILLISECONDS = 3000; //3 seconds
|
||||||
|
private static final long CONNECTION_ATTEMPT_TIMEOUT = 5000; //5 seconds
|
||||||
|
|
||||||
private NioSocketConnector mSocketConnector;
|
private NioSocketConnector mSocketConnector;
|
||||||
private IoSession mStreamingSession = null;
|
private IoSession mStreamingSession = null;
|
||||||
@@ -98,10 +101,11 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
if(mSocketConnector == null)
|
if(mSocketConnector == null)
|
||||||
{
|
{
|
||||||
mSocketConnector = new NioSocketConnector();
|
mSocketConnector = new NioSocketConnector();
|
||||||
mSocketConnector.setConnectTimeoutCheckInterval(10000);
|
mSocketConnector.setConnectTimeoutCheckInterval(CONNECTION_ATTEMPT_TIMEOUT);
|
||||||
|
|
||||||
// mSocketConnector.getFilterChain().addLast("logger",
|
// LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class);
|
||||||
// new LoggingFilter(IcecastTCPAudioBroadcaster.class));
|
// loggingFilter.setMessageSentLogLevel(LogLevel.NONE);
|
||||||
|
// mSocketConnector.getFilterChain().addLast("logger", loggingFilter);
|
||||||
|
|
||||||
mSocketConnector.getFilterChain().addLast("codec",
|
mSocketConnector.getFilterChain().addLast("codec",
|
||||||
new ProtocolCodecFilter(new IcecastCodecFactory()));
|
new ProtocolCodecFilter(new IcecastCodecFactory()));
|
||||||
@@ -122,9 +126,20 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
ConnectFuture future = mSocketConnector
|
ConnectFuture future = mSocketConnector
|
||||||
.connect(new InetSocketAddress(getBroadcastConfiguration().getHost(),
|
.connect(new InetSocketAddress(getBroadcastConfiguration().getHost(),
|
||||||
getBroadcastConfiguration().getPort()));
|
getBroadcastConfiguration().getPort()));
|
||||||
future.awaitUninterruptibly();
|
|
||||||
|
boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
mStreamingSession = future.getSession();
|
mStreamingSession = future.getSession();
|
||||||
}
|
}
|
||||||
|
catch(UnresolvedAddressException uae)
|
||||||
|
{
|
||||||
|
//What do you do with this? Nothing.
|
||||||
|
}
|
||||||
|
catch(InterruptedException ie)
|
||||||
|
{
|
||||||
|
//We couldn't connect within the timeout period -- disconnect so that we can try again later
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
catch(RuntimeIoException rie)
|
catch(RuntimeIoException rie)
|
||||||
{
|
{
|
||||||
Throwable throwableCause = rie.getCause();
|
Throwable throwableCause = rie.getCause();
|
||||||
@@ -136,16 +151,22 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
else if(throwableCause != null)
|
else if(throwableCause != null)
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
mLog.debug("Failed to connect", rie);
|
mLog.debug("[" + getStreamName() + "] failed to connect", rie);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
mLog.debug("Failed to connect - no exception is available");
|
mLog.debug("[" + getStreamName() + "] failed to connect - no exception is available");
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
catch(Throwable t)
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
mConnecting.set(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -161,12 +182,13 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if(connected())
|
if(connected() && mStreamingSession != null)
|
||||||
{
|
|
||||||
if(mStreamingSession != null)
|
|
||||||
{
|
{
|
||||||
mStreamingSession.closeNow();
|
mStreamingSession.closeNow();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,17 +245,18 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
@Override
|
@Override
|
||||||
public void sessionClosed(IoSession session) throws Exception
|
public void sessionClosed(IoSession session) throws Exception
|
||||||
{
|
{
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
|
|
||||||
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||||
if(!getBroadcastState().isErrorState())
|
if(!getBroadcastState().isErrorState())
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSocketConnector.dispose();
|
|
||||||
mStreamingSession = null;
|
mStreamingSession = null;
|
||||||
mSocketConnector = null;
|
|
||||||
|
|
||||||
mConnecting.set(false);
|
mConnecting.set(false);
|
||||||
|
|
||||||
super.sessionClosed(session);
|
super.sessionClosed(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,40 +271,41 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
{
|
{
|
||||||
String reason = ioe.getMessage();
|
String reason = ioe.getMessage();
|
||||||
|
|
||||||
if(reason.startsWith("Connection reset"))
|
if(connected())
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
else if(reason.startsWith("Connection reset"))
|
||||||
{
|
{
|
||||||
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
|
||||||
disconnect();
|
disconnect();
|
||||||
connect();
|
|
||||||
}
|
}
|
||||||
else if(reason.startsWith("Operation timed out"))
|
else if(reason.startsWith("Operation timed out"))
|
||||||
{
|
{
|
||||||
mLog.info("Streaming connection timed out - resetting connection");
|
|
||||||
disconnect();
|
disconnect();
|
||||||
connect();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
disconnect();
|
disconnect();
|
||||||
mLog.error("Unrecognized IO error: " + reason + ". Streaming halted.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(ioe instanceof WriteTimeoutException)
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
disconnect();
|
disconnect();
|
||||||
mLog.error("Unspecified IO error - streaming halted.");
|
mLog.error("[" + getStreamName() + "] Unspecified IO error - streaming halted.", cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mLog.error("Broadcast error", cause);
|
mLog.error("[" + getStreamName() + "] Broadcast error", cause);
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
mConnecting.set(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -301,11 +325,19 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.MOUNT_POINT_IN_USE);
|
setBroadcastState(BroadcastState.MOUNT_POINT_IN_USE);
|
||||||
}
|
}
|
||||||
|
else if(message.contains("Content-Type: text/html"))
|
||||||
|
{
|
||||||
|
//Disregard ... this normally follows the mount point in use error
|
||||||
|
}
|
||||||
else if(message.contains("Invalid Password") ||
|
else if(message.contains("Invalid Password") ||
|
||||||
message.contains("Authentication Required"))
|
message.contains("Authentication Required"))
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.INVALID_CREDENTIALS);
|
setBroadcastState(BroadcastState.INVALID_CREDENTIALS);
|
||||||
}
|
}
|
||||||
|
else if(message.contains("HTTP/1.1 501"))
|
||||||
|
{
|
||||||
|
disconnect(); //So that we can reconnect later
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mLog.error("Unrecognized server response:" + message);
|
mLog.error("Unrecognized server response:" + message);
|
||||||
@@ -315,7 +347,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mLog.error("Icecast TCP broadcaster - unrecognized message [ " + object.getClass() +
|
mLog.error("[" + getStreamName() + "]Icecast TCP broadcaster - unrecognized message [ " + object.getClass() +
|
||||||
"] received:" + object.toString());
|
"] received:" + object.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,6 +168,12 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
|||||||
|
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
catch(Throwable t)
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
mConnecting.set(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -183,12 +189,13 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if(connected())
|
if(connected() && mStreamingSession != null)
|
||||||
{
|
|
||||||
if(mStreamingSession != null)
|
|
||||||
{
|
{
|
||||||
mStreamingSession.closeNow();
|
mStreamingSession.closeNow();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,15 +231,15 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
|||||||
@Override
|
@Override
|
||||||
public void sessionClosed(IoSession session) throws Exception
|
public void sessionClosed(IoSession session) throws Exception
|
||||||
{
|
{
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
|
|
||||||
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||||
if(!getBroadcastState().isErrorState())
|
if(!getBroadcastState().isErrorState())
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSocketConnector.dispose();
|
|
||||||
mStreamingSession = null;
|
mStreamingSession = null;
|
||||||
mSocketConnector = null;
|
|
||||||
|
|
||||||
mConnecting.set(false);
|
mConnecting.set(false);
|
||||||
super.sessionClosed(session);
|
super.sessionClosed(session);
|
||||||
@@ -253,13 +260,11 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
|
|||||||
{
|
{
|
||||||
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
||||||
disconnect();
|
disconnect();
|
||||||
connect();
|
|
||||||
}
|
}
|
||||||
else if(reason.startsWith("Operation timed out"))
|
else if(reason.startsWith("Operation timed out"))
|
||||||
{
|
{
|
||||||
mLog.info("Streaming connection timed out - resetting connection");
|
mLog.info("Streaming connection timed out - resetting connection");
|
||||||
disconnect();
|
disconnect();
|
||||||
connect();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import properties.SystemProperties;
|
|||||||
import util.ThreadPool;
|
import util.ThreadPool;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -203,6 +204,12 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
|||||||
|
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
|
catch(Throwable t)
|
||||||
|
{
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
mConnecting.set(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -218,13 +225,13 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
|||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
if(connected())
|
if(connected() && mStreamingSession != null)
|
||||||
{
|
{
|
||||||
if(mStreamingSession != null)
|
|
||||||
{
|
|
||||||
mStreamingSession.write(new TerminateBroadcast());
|
|
||||||
mStreamingSession.closeNow();
|
mStreamingSession.closeNow();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,6 +251,8 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
|||||||
|
|
||||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?><metadata>");
|
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?><metadata>");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
sb.append(UltravoxMetadata.ALBUM_TITLE.asXML(getConfiguration().getName()));
|
sb.append(UltravoxMetadata.ALBUM_TITLE.asXML(getConfiguration().getName()));
|
||||||
|
|
||||||
sb.append(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML(SystemProperties.getInstance().getApplicationName()));
|
sb.append(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML(SystemProperties.getInstance().getApplicationName()));
|
||||||
@@ -282,6 +291,11 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
|||||||
{
|
{
|
||||||
sb.append(UltravoxMetadata.TITLE_2.asXML("Scanning ..."));
|
sb.append(UltravoxMetadata.TITLE_2.asXML("Scanning ..."));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch(UnsupportedEncodingException uee)
|
||||||
|
{
|
||||||
|
mLog.error("UTF-8 Encoding is not supported - shoutcast/ultravox metadata will not be updated");
|
||||||
|
}
|
||||||
|
|
||||||
sb.append("</metadata>");
|
sb.append("</metadata>");
|
||||||
|
|
||||||
@@ -339,15 +353,15 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
|||||||
@Override
|
@Override
|
||||||
public void sessionClosed(IoSession session) throws Exception
|
public void sessionClosed(IoSession session) throws Exception
|
||||||
{
|
{
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
|
|
||||||
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||||
if(!getBroadcastState().isErrorState())
|
if(!getBroadcastState().isErrorState())
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSocketConnector.dispose();
|
|
||||||
mStreamingSession = null;
|
mStreamingSession = null;
|
||||||
mSocketConnector = null;
|
|
||||||
|
|
||||||
mConnecting.set(false);
|
mConnecting.set(false);
|
||||||
super.sessionClosed(session);
|
super.sessionClosed(session);
|
||||||
@@ -368,13 +382,11 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
|
|||||||
{
|
{
|
||||||
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
||||||
disconnect();
|
disconnect();
|
||||||
connect();
|
|
||||||
}
|
}
|
||||||
else if(reason.startsWith("Operation timed out"))
|
else if(reason.startsWith("Operation timed out"))
|
||||||
{
|
{
|
||||||
mLog.info("Streaming connection timed out - resetting connection");
|
mLog.info("Streaming connection timed out - resetting connection");
|
||||||
disconnect();
|
disconnect();
|
||||||
connect();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package audio.broadcast.shoutcast.v2;
|
|||||||
import audio.broadcast.BroadcastConfiguration;
|
import audio.broadcast.BroadcastConfiguration;
|
||||||
import audio.broadcast.BroadcastFormat;
|
import audio.broadcast.BroadcastFormat;
|
||||||
import audio.broadcast.BroadcastServerType;
|
import audio.broadcast.BroadcastServerType;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAttribute;
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setStreamID(int streamID)
|
public void setStreamID(int streamID)
|
||||||
{
|
{
|
||||||
assert(1 <= streamID && streamID <= Integer.MAX_VALUE);
|
Validate.isTrue(1 <= streamID && streamID <= Integer.MAX_VALUE);
|
||||||
mStreamID = streamID;
|
mStreamID = streamID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,40 @@ public class AuthenticateBroadcast extends UltravoxMessage
|
|||||||
setPayload(sb.toString());
|
setPayload(sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the value using the XTEA block cipher algorithm. The byte array is sliced into 8-byte chunks and each
|
||||||
|
* chunk is encrypted into a 16 hexadecimal character string. All encrypted chunks are concatenated and returned
|
||||||
|
* as a single string. If the value byte array is not an even multiple of 8 characters, it will be padded with
|
||||||
|
* zero valued bytes to make it an even multiple of eight.
|
||||||
|
*
|
||||||
|
* @param xtea instance of the XTEA algorithm with a loaded encryption key
|
||||||
|
* @param value to encode
|
||||||
|
* @return value encrypted into one or more multiples of 16 hexadecimal characters
|
||||||
|
*/
|
||||||
|
private static String encrypt(XTEA xtea, byte[] value)
|
||||||
|
{
|
||||||
|
if(value.length % 8 != 0 || value.length == 0)
|
||||||
|
{
|
||||||
|
int newLength = ((value.length / 8) + 1) * 8;
|
||||||
|
value = Arrays.copyOf(value, newLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for(int x = 0; x < value.length; x += 8)
|
||||||
|
{
|
||||||
|
byte[] plaintext = Arrays.copyOfRange(value, x, x + 8);
|
||||||
|
byte[] encrypted = xtea.encrypt(plaintext);
|
||||||
|
|
||||||
|
for(byte b: encrypted)
|
||||||
|
{
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encrypts the value using the XTEA block cipher algorithm and returns the encrypted value as a 16 hex value
|
* Encrypts the value using the XTEA block cipher algorithm and returns the encrypted value as a 16 hex value
|
||||||
*
|
*
|
||||||
@@ -80,23 +114,6 @@ public class AuthenticateBroadcast extends UltravoxMessage
|
|||||||
*/
|
*/
|
||||||
private static String encrypt(XTEA xtea, String value)
|
private static String encrypt(XTEA xtea, String value)
|
||||||
{
|
{
|
||||||
byte[] valueBytes = value.getBytes();
|
return encrypt(xtea, value.getBytes());
|
||||||
|
|
||||||
if(valueBytes.length % 8 != 0 || valueBytes.length == 0)
|
|
||||||
{
|
|
||||||
int newLength = ((valueBytes.length / 8) + 1) * 8;
|
|
||||||
valueBytes = Arrays.copyOf(valueBytes, newLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] encrypted = xtea.encrypt(valueBytes);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
for(byte b: encrypted)
|
|
||||||
{
|
|
||||||
sb.append(String.format("%02x", b));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.ultravox;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -47,8 +49,8 @@ public class NegotiateMaxPayloadSize extends UltravoxMessage
|
|||||||
*/
|
*/
|
||||||
public void setMaximumPayloadSize(int desiredMaximum, int minimum)
|
public void setMaximumPayloadSize(int desiredMaximum, int minimum)
|
||||||
{
|
{
|
||||||
assert(desiredMaximum <= 16377);
|
Validate.isTrue(desiredMaximum <= 16377);
|
||||||
assert(minimum <= 16377);
|
Validate.isTrue(minimum <= 16377);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(desiredMaximum).append(":").append(minimum);
|
sb.append(desiredMaximum).append(":").append(minimum);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package audio.broadcast.shoutcast.v2.ultravox;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
import bits.BinaryMessage;
|
import bits.BinaryMessage;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@@ -154,7 +155,7 @@ public abstract class UltravoxMessage
|
|||||||
*/
|
*/
|
||||||
public void setSendQueuePriority(int priority)
|
public void setSendQueuePriority(int priority)
|
||||||
{
|
{
|
||||||
assert(0 <= priority && priority <= 7);
|
Validate.isTrue(0 <= priority && priority <= 7);
|
||||||
|
|
||||||
mMessage.setInt(priority, SEND_QUEUE_PRIORITY);
|
mMessage.setInt(priority, SEND_QUEUE_PRIORITY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.ultravox;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
public enum UltravoxMetadata
|
public enum UltravoxMetadata
|
||||||
{
|
{
|
||||||
ALBUM_TITLE("TALB"),
|
ALBUM_TITLE("TALB"),
|
||||||
@@ -58,11 +61,11 @@ public enum UltravoxMetadata
|
|||||||
* @param value to enclose in XML tags
|
* @param value to enclose in XML tags
|
||||||
* @return xml tag wrapped value
|
* @return xml tag wrapped value
|
||||||
*/
|
*/
|
||||||
public String asXML(String value)
|
public String asXML(String value) throws UnsupportedEncodingException
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("<").append(getXMLTag()).append(">");
|
sb.append("<").append(getXMLTag()).append(">");
|
||||||
sb.append(value);
|
sb.append(new String(value.getBytes(), "UTF-8"));
|
||||||
sb.append("</").append(getXMLTag()).append(">");
|
sb.append("</").append(getXMLTag()).append(">");
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.util.BitSet;
|
|||||||
import java.util.logging.LogManager;
|
import java.util.logging.LogManager;
|
||||||
|
|
||||||
import edac.CRC;
|
import edac.CRC;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
public class BinaryMessage extends BitSet
|
public class BinaryMessage extends BitSet
|
||||||
{
|
{
|
||||||
@@ -136,6 +137,17 @@ public class BinaryMessage extends BitSet
|
|||||||
mCRC = crc;
|
mCRC = crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the number of bit errors that were corrected in this message.
|
||||||
|
*
|
||||||
|
* @see CorrectedBinaryMessage subclass that supports setting this value
|
||||||
|
* @return number of corrected bits in this message
|
||||||
|
*/
|
||||||
|
public int getCorrectedBitCount()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current pointer index
|
* Current pointer index
|
||||||
*/
|
*/
|
||||||
@@ -456,7 +468,7 @@ public class BinaryMessage extends BitSet
|
|||||||
*/
|
*/
|
||||||
public byte getByte(int index)
|
public byte getByte(int index)
|
||||||
{
|
{
|
||||||
assert((index + 7) <= size());
|
Validate.isTrue((index + 7) <= size());
|
||||||
|
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
|
||||||
@@ -481,7 +493,7 @@ public class BinaryMessage extends BitSet
|
|||||||
*/
|
*/
|
||||||
public void setByte(int index, byte value)
|
public void setByte(int index, byte value)
|
||||||
{
|
{
|
||||||
assert((index + 8) <= size());
|
Validate.isTrue((index + 8) <= size());
|
||||||
|
|
||||||
int mask = 0x80;
|
int mask = 0x80;
|
||||||
|
|
||||||
|
|||||||
65
src/bits/CorrectedBinaryMessage.java
Normal file
65
src/bits/CorrectedBinaryMessage.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package bits;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
|
||||||
|
public class CorrectedBinaryMessage extends BinaryMessage
|
||||||
|
{
|
||||||
|
private int mCorrectedBitCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass of binary message class to allow capturing a corrected bits metric.
|
||||||
|
* @param size of message to create.
|
||||||
|
*/
|
||||||
|
public CorrectedBinaryMessage(int size)
|
||||||
|
{
|
||||||
|
super(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CorrectedBinaryMessage(int size, boolean[] bitsToPreload)
|
||||||
|
{
|
||||||
|
super(size, bitsToPreload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CorrectedBinaryMessage(BitSet bitset, int size)
|
||||||
|
{
|
||||||
|
super(bitset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CorrectedBinaryMessage(byte[] data)
|
||||||
|
{
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCorrectedBitCount()
|
||||||
|
{
|
||||||
|
return mCorrectedBitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of bits that were corrected in this message
|
||||||
|
* @param count of corrected bits
|
||||||
|
*/
|
||||||
|
public void setCorrectedBitCount(int count)
|
||||||
|
{
|
||||||
|
mCorrectedBitCount = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package bits;
|
package bits;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ public class MultiSyncPatternMatcher
|
|||||||
|
|
||||||
public MultiSyncPatternMatcher( int syncSize )
|
public MultiSyncPatternMatcher( int syncSize )
|
||||||
{
|
{
|
||||||
assert( syncSize < 64 );
|
Validate.isTrue(syncSize < 64);
|
||||||
|
|
||||||
//Setup a bit mask of all ones the length of the sync pattern
|
//Setup a bit mask of all ones the length of the sync pattern
|
||||||
mMask = (long)( ( Math.pow( 2, syncSize ) ) - 1 );
|
mMask = (long)( ( Math.pow( 2, syncSize ) ) - 1 );
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class ChannelDetailPanel extends JPanel implements Listener<ProcessingCha
|
|||||||
private JLabel mSystemLabel;
|
private JLabel mSystemLabel;
|
||||||
private JLabel mSiteLabel;
|
private JLabel mSiteLabel;
|
||||||
private JLabel mNameLabel;
|
private JLabel mNameLabel;
|
||||||
private JTextArea mDetailTextArea;
|
private JTextArea mDetailTextPane;
|
||||||
|
|
||||||
private ChannelProcessingManager mChannelProcessingManager;
|
private ChannelProcessingManager mChannelProcessingManager;
|
||||||
private ProcessingChain mProcessingChain;
|
private ProcessingChain mProcessingChain;
|
||||||
@@ -82,10 +82,10 @@ public class ChannelDetailPanel extends JPanel implements Listener<ProcessingCha
|
|||||||
|
|
||||||
add(buttonPanel, "wrap");
|
add(buttonPanel, "wrap");
|
||||||
|
|
||||||
mDetailTextArea = new JTextArea(EMPTY_DETAILS);
|
mDetailTextPane = new JTextArea(EMPTY_DETAILS);
|
||||||
DefaultCaret caret = (DefaultCaret)mDetailTextArea.getCaret();
|
DefaultCaret caret = (DefaultCaret)mDetailTextPane.getCaret();
|
||||||
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
||||||
add(new JScrollPane(mDetailTextArea));
|
add(new JScrollPane(mDetailTextPane));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,7 +126,7 @@ public class ChannelDetailPanel extends JPanel implements Listener<ProcessingCha
|
|||||||
mSystemLabel.setText(system);
|
mSystemLabel.setText(system);
|
||||||
mSiteLabel.setText(site);
|
mSiteLabel.setText(site);
|
||||||
mNameLabel.setText(name);
|
mNameLabel.setText(name);
|
||||||
mDetailTextArea.setText(details);
|
mDetailTextPane.setText(details);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
31
src/channel/heartbeat/Heartbeat.java
Normal file
31
src/channel/heartbeat/Heartbeat.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package channel.heartbeat;
|
||||||
|
|
||||||
|
public class Heartbeat
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Periodic pulse that is broadcast to subscribing modules within the demodulating chain so that
|
||||||
|
* monitoring of state and other attributes can occur on the primary demodulation and decoding
|
||||||
|
* thread.
|
||||||
|
*/
|
||||||
|
public Heartbeat()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/channel/heartbeat/IHeartbeatListener.java
Normal file
26
src/channel/heartbeat/IHeartbeatListener.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package channel.heartbeat;
|
||||||
|
|
||||||
|
import sample.Listener;
|
||||||
|
|
||||||
|
public interface IHeartbeatListener
|
||||||
|
{
|
||||||
|
public Listener<Heartbeat> getHeartbeatListener();
|
||||||
|
}
|
||||||
28
src/channel/heartbeat/IHeartbeatProvider.java
Normal file
28
src/channel/heartbeat/IHeartbeatProvider.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package channel.heartbeat;
|
||||||
|
|
||||||
|
import sample.Listener;
|
||||||
|
|
||||||
|
public interface IHeartbeatProvider
|
||||||
|
{
|
||||||
|
public void setHeartbeatListener(Listener<Heartbeat> listener);
|
||||||
|
|
||||||
|
public void removeHeartbeatListener();
|
||||||
|
}
|
||||||
@@ -19,8 +19,6 @@
|
|||||||
package channel.metadata;
|
package channel.metadata;
|
||||||
|
|
||||||
import alias.Alias;
|
import alias.Alias;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -30,8 +28,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class AttributeMonitor<T>
|
public class AttributeMonitor<T>
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(AttributeMonitor.class);
|
|
||||||
|
|
||||||
private Attribute mAttribute;
|
private Attribute mAttribute;
|
||||||
private Listener<AttributeChangeRequest> mListener;
|
private Listener<AttributeChangeRequest> mListener;
|
||||||
private boolean mHeuristicsEnabled = true;
|
private boolean mHeuristicsEnabled = true;
|
||||||
@@ -50,8 +46,8 @@ public class AttributeMonitor<T>
|
|||||||
* an internal mapping of values to occurrence counts is updated and a change is emitted once a value that is
|
* an internal mapping of values to occurrence counts is updated and a change is emitted once a value that is
|
||||||
* different from the current value, has a higher occurrence count than the current value. If heuristics are
|
* different from the current value, has a higher occurrence count than the current value. If heuristics are
|
||||||
* disabled, then a change is emitted at each change in the value.
|
* disabled, then a change is emitted at each change in the value.
|
||||||
* @param t
|
|
||||||
*
|
*
|
||||||
|
* @param t type
|
||||||
* @return true if the value was changed after processing
|
* @return true if the value was changed after processing
|
||||||
*/
|
*/
|
||||||
public boolean process(T t)
|
public boolean process(T t)
|
||||||
@@ -155,35 +151,4 @@ public class AttributeMonitor<T>
|
|||||||
{
|
{
|
||||||
return mCurrentValue != null;
|
return mCurrentValue != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
{
|
|
||||||
AttributeMonitor<String> monitor = new AttributeMonitor<>(Attribute.PRIMARY_ADDRESS_TO, new Listener<AttributeChangeRequest>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void receive(AttributeChangeRequest attributeChangeRequest)
|
|
||||||
{
|
|
||||||
mLog.debug("Attribute Changed - value:" + attributeChangeRequest.getValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
monitor.addIllegalValue("0000");
|
|
||||||
monitor.addIllegalValue("000000");
|
|
||||||
|
|
||||||
monitor.process("ABCD");
|
|
||||||
monitor.process("EFGH");
|
|
||||||
monitor.process("0000");
|
|
||||||
monitor.process("0000");
|
|
||||||
monitor.process("ABCD");
|
|
||||||
monitor.reset();
|
|
||||||
|
|
||||||
monitor.process("EFGH");
|
|
||||||
monitor.process("ABCD");
|
|
||||||
monitor.process("ABCD");
|
|
||||||
monitor.process("EFGH");
|
|
||||||
monitor.process("EFGH");
|
|
||||||
monitor.process("000000");
|
|
||||||
monitor.process("000000");
|
|
||||||
monitor.process("000000");
|
|
||||||
monitor.process("000000");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MutableMetadataChangeEvent>
|
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MutableMetadataChangeEvent>
|
||||||
{
|
{
|
||||||
private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" );
|
private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.00000" );
|
||||||
|
|
||||||
public static final int COLUMN_STATE = 0;
|
public static final int COLUMN_STATE = 0;
|
||||||
public static final int COLUMN_DECODER = 1;
|
public static final int COLUMN_DECODER = 1;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import controller.channel.ChannelUtils;
|
|||||||
import icon.IconManager;
|
import icon.IconManager;
|
||||||
import module.ProcessingChain;
|
import module.ProcessingChain;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import properties.SystemProperties;
|
import properties.SystemProperties;
|
||||||
@@ -140,7 +141,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
|||||||
|
|
||||||
public AliasedValueCellRenderer(Attribute attribute)
|
public AliasedValueCellRenderer(Attribute attribute)
|
||||||
{
|
{
|
||||||
assert(attribute != null);
|
Validate.isTrue(attribute != null);
|
||||||
mAttribute = attribute;
|
mAttribute = attribute;
|
||||||
setHorizontalAlignment(JLabel.CENTER);
|
setHorizontalAlignment(JLabel.CENTER);
|
||||||
}
|
}
|
||||||
@@ -210,6 +211,10 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
|||||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
|
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
|
||||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
|
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
|
||||||
break;
|
break;
|
||||||
|
case OVERFLOW:
|
||||||
|
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "overflow", Color.RED);
|
||||||
|
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "overflow", Color.YELLOW);
|
||||||
|
break;
|
||||||
case RESET:
|
case RESET:
|
||||||
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
|
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
|
||||||
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
|
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class ChannelMetadataViewer extends JPanel
|
|||||||
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
|
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
|
||||||
mCallEventPanel = new CallEventPanel(iconManager);
|
mCallEventPanel = new CallEventPanel(iconManager);
|
||||||
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
|
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
|
||||||
mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager);
|
// mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager);
|
||||||
mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager);
|
mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager);
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
public class Metadata
|
public class Metadata
|
||||||
{
|
{
|
||||||
@@ -61,7 +63,7 @@ public class Metadata
|
|||||||
private Integer mAudioPriority;
|
private Integer mAudioPriority;
|
||||||
protected Boolean mRecordable = false;
|
protected Boolean mRecordable = false;
|
||||||
protected Boolean mDoNotRecord;
|
protected Boolean mDoNotRecord;
|
||||||
private List<BroadcastChannel> mBroadcastChannels;
|
private Set<BroadcastChannel> mBroadcastChannels;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,7 +168,7 @@ public class Metadata
|
|||||||
/**
|
/**
|
||||||
* List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases.
|
* List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases.
|
||||||
*/
|
*/
|
||||||
public List<BroadcastChannel> getBroadcastChannels()
|
public Set<BroadcastChannel> getBroadcastChannels()
|
||||||
{
|
{
|
||||||
if(mBroadcastChannels == null)
|
if(mBroadcastChannels == null)
|
||||||
{
|
{
|
||||||
@@ -582,50 +584,26 @@ public class Metadata
|
|||||||
*/
|
*/
|
||||||
private void determineBroadcastChannels()
|
private void determineBroadcastChannels()
|
||||||
{
|
{
|
||||||
mBroadcastChannels = new ArrayList<>();
|
mBroadcastChannels = new TreeSet<>();
|
||||||
|
|
||||||
if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isStreamable())
|
if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isStreamable())
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel: mPrimaryAddressTo.getAlias().getBroadcastChannels())
|
mBroadcastChannels.addAll(mPrimaryAddressTo.getAlias().getBroadcastChannels());
|
||||||
{
|
|
||||||
if(!mBroadcastChannels.contains(broadcastChannel))
|
|
||||||
{
|
|
||||||
mBroadcastChannels.add(broadcastChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isStreamable())
|
if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isStreamable())
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel: mPrimaryAddressFrom.getAlias().getBroadcastChannels())
|
mBroadcastChannels.addAll(mPrimaryAddressFrom.getAlias().getBroadcastChannels());
|
||||||
{
|
|
||||||
if(!mBroadcastChannels.contains(broadcastChannel))
|
|
||||||
{
|
|
||||||
mBroadcastChannels.add(broadcastChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isStreamable())
|
if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isStreamable())
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel: mSecondaryAddressTo.getAlias().getBroadcastChannels())
|
mBroadcastChannels.addAll(mSecondaryAddressTo.getAlias().getBroadcastChannels());
|
||||||
{
|
|
||||||
if(!mBroadcastChannels.contains(broadcastChannel))
|
|
||||||
{
|
|
||||||
mBroadcastChannels.add(broadcastChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isStreamable())
|
if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isStreamable())
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel: mSecondaryAddressFrom.getAlias().getBroadcastChannels())
|
mBroadcastChannels.addAll(mSecondaryAddressFrom.getAlias().getBroadcastChannels());
|
||||||
{
|
|
||||||
if(!mBroadcastChannels.contains(broadcastChannel))
|
|
||||||
{
|
|
||||||
mBroadcastChannels.add(broadcastChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ package channel.state;
|
|||||||
|
|
||||||
import audio.squelch.ISquelchStateProvider;
|
import audio.squelch.ISquelchStateProvider;
|
||||||
import audio.squelch.SquelchState;
|
import audio.squelch.SquelchState;
|
||||||
|
import channel.heartbeat.Heartbeat;
|
||||||
|
import channel.heartbeat.IHeartbeatListener;
|
||||||
import channel.metadata.Attribute;
|
import channel.metadata.Attribute;
|
||||||
import channel.metadata.AttributeChangeRequest;
|
import channel.metadata.AttributeChangeRequest;
|
||||||
import channel.metadata.IAttributeChangeRequestListener;
|
import channel.metadata.IAttributeChangeRequestListener;
|
||||||
@@ -35,14 +37,17 @@ import module.decode.event.ICallEventProvider;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
|
import sample.real.IOverflowListener;
|
||||||
|
import source.tuner.frequency.FrequencyChangeEvent;
|
||||||
|
import source.tuner.frequency.IFrequencyChangeListener;
|
||||||
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import static source.tuner.frequency.FrequencyChangeEvent.Event.NOTIFICATION_FREQUENCY_CHANGE;
|
||||||
|
|
||||||
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
|
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
|
||||||
IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener
|
IDecoderStateEventProvider, IFrequencyChangeListener, IHeartbeatListener, ISquelchStateProvider,
|
||||||
|
IAttributeChangeRequestListener, IOverflowListener
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
|
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
|
||||||
|
|
||||||
@@ -55,14 +60,15 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
private Listener<DecoderStateEvent> mDecoderStateListener;
|
private Listener<DecoderStateEvent> mDecoderStateListener;
|
||||||
private Listener<SquelchState> mSquelchStateListener;
|
private Listener<SquelchState> mSquelchStateListener;
|
||||||
private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver();
|
private DecoderStateEventReceiver mDecoderStateEventReceiver = new DecoderStateEventReceiver();
|
||||||
private StateMonitor mStateMonitor = new StateMonitor();
|
private HeartbeatReceiver mHeartbeatReceiver = new HeartbeatReceiver();
|
||||||
private ScheduledFuture<?> mStateMonitorFuture;
|
|
||||||
private ChannelType mChannelType;
|
private ChannelType mChannelType;
|
||||||
private TrafficChannelManager mTrafficChannelEndListener;
|
private TrafficChannelManager mTrafficChannelEndListener;
|
||||||
private CallEvent mTrafficChannelCallEvent;
|
private CallEvent mTrafficChannelCallEvent;
|
||||||
|
private FrequencyChangeListener mFrequencyChangeListener;
|
||||||
|
|
||||||
private boolean mSquelchLocked = false;
|
private boolean mSquelchLocked = false;
|
||||||
private boolean mSelected = false;
|
private boolean mSelected = false;
|
||||||
|
private boolean mSourceOverflow = false;
|
||||||
|
|
||||||
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
|
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
|
||||||
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
|
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
|
||||||
@@ -71,30 +77,21 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Channel state tracks the overall state of all processing modules and decoders
|
* Channel state tracks the overall state of all processing modules and decoders configured for the channel and
|
||||||
* configured for the channel and provides squelch control and decoder state
|
* provides squelch control and decoder state reset events.
|
||||||
* reset events.
|
|
||||||
*
|
*
|
||||||
* Uses a state enumeration that defines allowable channel state transitions in
|
* Uses a state enumeration that defines allowable channel state transitions in order to track a call or data decode
|
||||||
* order to track a call or data decode event from start to finish. Uses a
|
* event from start to finish. Uses a timer to monitor for inactivity and to provide a FADE period that indicates
|
||||||
* timer to monitor for inactivity and to provide a FADE period that indicates
|
* to the user that the activity has stopped while continuing to provide details about the call, before the state is
|
||||||
* to the user that the activity has stopped while continuing to provide details
|
* reset to IDLE.
|
||||||
* about the call, before the state is reset to IDLE.
|
|
||||||
*
|
|
||||||
* Since this class is multi-threaded between decoder events and the internal
|
|
||||||
* timer that monitors for inactivity, this class uses the mState object for
|
|
||||||
* thread synchronization.
|
|
||||||
*
|
*
|
||||||
* State Descriptions:
|
* State Descriptions:
|
||||||
* IDLE Normal state. No voice or data call activity
|
* IDLE: Normal state. No voice or data call activity
|
||||||
* CALL/DATA/ENCRYPTED/CONTROL
|
* CALL/DATA/ENCRYPTED/CONTROL: Decoding states.
|
||||||
* Decoding states.
|
* FADE: The phase after a voice or data call when either an explicit call end has been received, or when no new
|
||||||
* FADE The phase after a voice or data call when either an explicit
|
* signalling updates have been received, and the fade timer has expired. This phase allows for gui updates to
|
||||||
* call end has been received, or when no new signalling updates
|
* signal to the user that the call is ended, while continuing to display the call details for the user
|
||||||
* have been received, and the fade timer has expired. This phase
|
* TEARDOWN: Indicates a traffic channel that will be torn down for reuse.
|
||||||
* allows for gui updates to signal to the user that the call is
|
|
||||||
* ended, while continuing to display the call details for the user
|
|
||||||
* TEARDOWN Indicates a traffic channel that will be torn down for reuse.
|
|
||||||
*/
|
*/
|
||||||
public ChannelState(ChannelType channelType)
|
public ChannelState(ChannelType channelType)
|
||||||
{
|
{
|
||||||
@@ -123,12 +120,11 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
|
|
||||||
mState = State.IDLE;
|
mState = State.IDLE;
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
||||||
|
mSourceOverflow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(ScheduledExecutorService executor)
|
public void start(ScheduledExecutorService executor)
|
||||||
{
|
|
||||||
if(mStateMonitorFuture == null && mStateMonitor != null)
|
|
||||||
{
|
{
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
||||||
|
|
||||||
@@ -136,16 +132,6 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
{
|
{
|
||||||
setState(State.CALL);
|
setState(State.CALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mStateMonitorFuture = executor.scheduleAtFixedRate(mStateMonitor, 0, 20, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
catch(RejectedExecutionException ree)
|
|
||||||
{
|
|
||||||
mLog.error("state monitor scheduled task rejected", ree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,16 +139,6 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
{
|
{
|
||||||
mMutableMetadata.resetAllAttributes();
|
mMutableMetadata.resetAllAttributes();
|
||||||
|
|
||||||
if(mStateMonitorFuture != null)
|
|
||||||
{
|
|
||||||
boolean success = mStateMonitorFuture.cancel(true);
|
|
||||||
|
|
||||||
if(!success)
|
|
||||||
{
|
|
||||||
mLog.error("Couldn't stop monitoring scheduled future");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mTrafficChannelEndListener = null;
|
mTrafficChannelEndListener = null;
|
||||||
|
|
||||||
if(mTrafficChannelCallEvent != null)
|
if(mTrafficChannelCallEvent != null)
|
||||||
@@ -173,8 +149,6 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
|
|
||||||
mTrafficChannelCallEvent = null;
|
mTrafficChannelCallEvent = null;
|
||||||
|
|
||||||
mStateMonitorFuture = null;
|
|
||||||
|
|
||||||
mSquelchLocked = false;
|
mSquelchLocked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +157,17 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
mCallEventListener = null;
|
mCallEventListener = null;
|
||||||
mDecoderStateListener = null;
|
mDecoderStateListener = null;
|
||||||
mSquelchStateListener = null;
|
mSquelchStateListener = null;
|
||||||
mStateMonitor = null;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||||
|
{
|
||||||
|
if(mFrequencyChangeListener == null)
|
||||||
|
{
|
||||||
|
mFrequencyChangeListener = new FrequencyChangeListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mFrequencyChangeListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStandardChannel()
|
private boolean isStandardChannel()
|
||||||
@@ -292,15 +276,12 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the channel state to the specified state, or updates the timeout
|
* Sets the channel state to the specified state, or updates the timeout values so that the state monitor will not
|
||||||
* values so that the state monitor will not change state. Broadcasts a
|
* change state. Broadcasts a squelch event when the state changes and the audio squelch state should change. Also
|
||||||
* squelch event when the state changes and the audio squelch state should
|
* broadcasts changed attribute and decoder state events so that external processes can maintain sync with this
|
||||||
* change. Also broadcasts changed attribute and decoder state events so
|
* channel state.
|
||||||
* that external processes can maintain sync with this channel state.
|
|
||||||
*/
|
*/
|
||||||
protected void setState(State state)
|
protected void setState(State state)
|
||||||
{
|
|
||||||
synchronized(mState)
|
|
||||||
{
|
{
|
||||||
if(state == mState)
|
if(state == mState)
|
||||||
{
|
{
|
||||||
@@ -309,13 +290,12 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(state != mState && mState.canChangeTo(state))
|
else if(mState.canChangeTo(state))
|
||||||
{
|
{
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
case CONTROL:
|
case CONTROL:
|
||||||
//Don't allow traffic channels to be control channels,
|
//Don't allow traffic channels to be control channels, otherwise they can't transition to teardown
|
||||||
//otherwise they can't transition to call tear down
|
|
||||||
if(isStandardChannel())
|
if(isStandardChannel())
|
||||||
{
|
{
|
||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
@@ -350,13 +330,32 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
|
||||||
|
mSourceOverflow ? State.OVERFLOW : mState));
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receiver inner class that implements the IHeartbeatListener interface to receive heartbeat messages.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Listener<Heartbeat> getHeartbeatListener()
|
||||||
{
|
{
|
||||||
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
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;
|
||||||
|
|
||||||
|
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
|
||||||
|
mSourceOverflow ? State.OVERFLOW : mState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -518,6 +517,23 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
broadcast(mTrafficChannelCallEvent);
|
broadcast(mTrafficChannelCallEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener to receive frequency change events and rebroadcast them to the decoder states
|
||||||
|
*/
|
||||||
|
public class FrequencyChangeListener implements Listener<FrequencyChangeEvent>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void receive(FrequencyChangeEvent frequencyChangeEvent)
|
||||||
|
{
|
||||||
|
if(frequencyChangeEvent.getEvent() == NOTIFICATION_FREQUENCY_CHANGE)
|
||||||
|
{
|
||||||
|
long frequency = frequencyChangeEvent.getValue().longValue();
|
||||||
|
|
||||||
|
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DecoderStateEvent receiver wrapper
|
* DecoderStateEvent receiver wrapper
|
||||||
*/
|
*/
|
||||||
@@ -577,30 +593,27 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monitors decoder state events to automatically transition the channel
|
* Processes periodic heartbeats received from the processing chain to perform state monitoring and cleanup
|
||||||
* state to IDLE (standard channel) or to TEARDOWN (traffic channel) when
|
* functions.
|
||||||
* decoding stops or the monitored channel returns to a no signal state.
|
|
||||||
*
|
*
|
||||||
* Provides a FADE transition state to allow for momentary decoding dropouts
|
* Monitors decoder state events to automatically transition the channel state to IDLE (standard channel) or to
|
||||||
* and to allow the user access to call details for a fade period upon
|
* TEARDOWN (traffic channel) when decoding stops or the monitored channel returns to a no signal state.
|
||||||
* call end.
|
*
|
||||||
|
* 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 StateMonitor implements Runnable
|
public class HeartbeatReceiver implements Listener<Heartbeat>
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void receive(Heartbeat heartbeat)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
synchronized(mState)
|
if(State.CALL_STATES.contains(mState) && mFadeTimeout <= System.currentTimeMillis())
|
||||||
{
|
|
||||||
if(State.CALL_STATES.contains(mState) &&
|
|
||||||
mFadeTimeout <= System.currentTimeMillis())
|
|
||||||
{
|
{
|
||||||
processFadeState();
|
processFadeState();
|
||||||
}
|
}
|
||||||
else if(mState == State.FADE &&
|
else if(mState == State.FADE && mEndTimeout <= System.currentTimeMillis())
|
||||||
mEndTimeout <= System.currentTimeMillis())
|
|
||||||
{
|
{
|
||||||
if(isTrafficChannel())
|
if(isTrafficChannel())
|
||||||
{
|
{
|
||||||
@@ -612,10 +625,9 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch(Throwable e)
|
||||||
catch(Exception e)
|
|
||||||
{
|
{
|
||||||
mLog.error("Exception thrown while state monitor is running " +
|
mLog.error("An error occurred while state monitor was running " +
|
||||||
"- state [" + getState() +
|
"- state [" + getState() +
|
||||||
"] current [" + System.currentTimeMillis() +
|
"] current [" + System.currentTimeMillis() +
|
||||||
"] mResetTimeout [" + mEndTimeout +
|
"] mResetTimeout [" + mEndTimeout +
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
|
|||||||
IAttributeChangeRequestProvider, ICallEventProvider, IDecoderStateEventListener,
|
IAttributeChangeRequestProvider, ICallEventProvider, IDecoderStateEventListener,
|
||||||
IDecoderStateEventProvider, IMessageListener
|
IDecoderStateEventProvider, IMessageListener
|
||||||
{
|
{
|
||||||
|
protected String DIVIDER1 = "======================================================\n";
|
||||||
|
protected String DIVIDER2 = "------------------------------------------------------\n";
|
||||||
|
|
||||||
/* This has to be a broadcaster in order for references to persist */
|
/* This has to be a broadcaster in order for references to persist */
|
||||||
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
|
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
|
||||||
@@ -51,6 +54,14 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
|
|||||||
return mAliasList != null;
|
return mAliasList != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides subclass reference to the call event broadcaster
|
||||||
|
*/
|
||||||
|
protected Broadcaster<CallEvent> getCallEventBroadcaster()
|
||||||
|
{
|
||||||
|
return mCallEventBroadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the decoder state to prepare for processing a different sample
|
* Reset the decoder state to prepare for processing a different sample
|
||||||
* source
|
* source
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo(State state)
|
public boolean canChangeTo(State state)
|
||||||
{
|
{
|
||||||
return state != TEARDOWN &&
|
return state != TEARDOWN && state != RESET && state != OVERFLOW;
|
||||||
state != RESET;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CALL("CALL")
|
CALL("CALL")
|
||||||
@@ -38,11 +37,7 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo(State state)
|
public boolean canChangeTo(State state)
|
||||||
{
|
{
|
||||||
return state == CONTROL ||
|
return state == CONTROL || state == DATA || state == ENCRYPTED || state == FADE || state == TEARDOWN;
|
||||||
state == DATA ||
|
|
||||||
state == ENCRYPTED ||
|
|
||||||
state == FADE ||
|
|
||||||
state == TEARDOWN;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DATA("DATA")
|
DATA("DATA")
|
||||||
@@ -50,11 +45,7 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo(State state)
|
public boolean canChangeTo(State state)
|
||||||
{
|
{
|
||||||
return state == CALL ||
|
return state == CALL || state == CONTROL || state == ENCRYPTED || state == FADE || state == TEARDOWN;
|
||||||
state == CONTROL ||
|
|
||||||
state == ENCRYPTED ||
|
|
||||||
state == FADE ||
|
|
||||||
state == TEARDOWN;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ENCRYPTED("ENCRYPTED")
|
ENCRYPTED("ENCRYPTED")
|
||||||
@@ -70,8 +61,7 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo(State state)
|
public boolean canChangeTo(State state)
|
||||||
{
|
{
|
||||||
return state == IDLE ||
|
return state == IDLE || state == FADE;
|
||||||
state == FADE;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FADE("FADE")
|
FADE("FADE")
|
||||||
@@ -79,8 +69,15 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo(State state)
|
public boolean canChangeTo(State state)
|
||||||
{
|
{
|
||||||
return state != FADE &&
|
return state != FADE && state != RESET && state != OVERFLOW;
|
||||||
state != RESET;
|
}
|
||||||
|
},
|
||||||
|
OVERFLOW("OVERFLOW")
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RESET("RESET")
|
RESET("RESET")
|
||||||
@@ -91,7 +88,6 @@ public enum State
|
|||||||
return state == IDLE;
|
return state == IDLE;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
TEARDOWN("TEARDOWN")
|
TEARDOWN("TEARDOWN")
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -63,13 +63,11 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
|
|||||||
private Listener<CallEvent> mCallEventListener;
|
private Listener<CallEvent> mCallEventListener;
|
||||||
|
|
||||||
private ChannelModel mChannelModel;
|
private ChannelModel mChannelModel;
|
||||||
private ChannelProcessingManager mChannelProcessingManager;
|
|
||||||
private DecodeConfiguration mDecodeConfiguration;
|
private DecodeConfiguration mDecodeConfiguration;
|
||||||
private RecordConfiguration mRecordConfiguration;
|
private RecordConfiguration mRecordConfiguration;
|
||||||
private String mSystem;
|
private String mSystem;
|
||||||
private String mSite;
|
private String mSite;
|
||||||
private String mAliasListName;
|
private String mAliasListName;
|
||||||
private CallEvent mPreviousDoNotMonitorCallEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monitors call events and allocates traffic decoder channels in response
|
* Monitors call events and allocates traffic decoder channels in response
|
||||||
@@ -77,30 +75,24 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
|
|||||||
* traffic channel allocations.
|
* traffic channel allocations.
|
||||||
*
|
*
|
||||||
* @param channelModel containing channels currently in use
|
* @param channelModel containing channels currently in use
|
||||||
* @param channelProcessingManager
|
|
||||||
* @param decodeConfiguration - decoder configuration to use for each
|
* @param decodeConfiguration - decoder configuration to use for each
|
||||||
* traffic channel allocation.
|
* traffic channel allocation.
|
||||||
* @param recordConfiguration - recording options for each traffic channel
|
* @param recordConfiguration - recording options for each traffic channel
|
||||||
* @param system label to use to describe the system
|
* @param system label to use to describe the system
|
||||||
* @param site label to use to describe the site
|
* @param site label to use to describe the site
|
||||||
* @param aliasListName designated for the channel
|
* @param aliasListName designated for the channel
|
||||||
* @param trafficChannelTimeout - millisecond call timer limit used when an
|
|
||||||
* end of call signalling event is not decoded.
|
|
||||||
* @param trafficChannelPoolSize - maximum number of allocated traffic channels
|
* @param trafficChannelPoolSize - maximum number of allocated traffic channels
|
||||||
* in the pool
|
* in the pool
|
||||||
*/
|
*/
|
||||||
public TrafficChannelManager(ChannelModel channelModel,
|
public TrafficChannelManager(ChannelModel channelModel,
|
||||||
ChannelProcessingManager channelProcessingManager,
|
|
||||||
DecodeConfiguration decodeConfiguration,
|
DecodeConfiguration decodeConfiguration,
|
||||||
RecordConfiguration recordConfiguration,
|
RecordConfiguration recordConfiguration,
|
||||||
String system,
|
String system,
|
||||||
String site,
|
String site,
|
||||||
String aliasListName,
|
String aliasListName,
|
||||||
long trafficChannelTimeout,
|
|
||||||
int trafficChannelPoolSize)
|
int trafficChannelPoolSize)
|
||||||
{
|
{
|
||||||
mChannelModel = channelModel;
|
mChannelModel = channelModel;
|
||||||
mChannelProcessingManager = channelProcessingManager;
|
|
||||||
mDecodeConfiguration = decodeConfiguration;
|
mDecodeConfiguration = decodeConfiguration;
|
||||||
mRecordConfiguration = recordConfiguration;
|
mRecordConfiguration = recordConfiguration;
|
||||||
mSystem = system;
|
mSystem = system;
|
||||||
@@ -123,7 +115,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
|
|||||||
|
|
||||||
mCallEventListener = null;
|
mCallEventListener = null;
|
||||||
mDecodeConfiguration = null;
|
mDecodeConfiguration = null;
|
||||||
mPreviousDoNotMonitorCallEvent = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,21 +191,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
|
|||||||
|
|
||||||
long frequency = callEvent.getFrequency();
|
long frequency = callEvent.getFrequency();
|
||||||
|
|
||||||
/* Check the from/to aliases for do not monitor priority */
|
if(frequency > 0)
|
||||||
if(isDoNotMonitor(callEvent))
|
|
||||||
{
|
|
||||||
callEvent.setCallEventType(CallEventType.CALL_DO_NOT_MONITOR);
|
|
||||||
|
|
||||||
if(isSameCallEvent(mPreviousDoNotMonitorCallEvent, callEvent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mPreviousDoNotMonitorCallEvent = callEvent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(frequency > 0)
|
|
||||||
{
|
{
|
||||||
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC,
|
Channel channel = getChannel(callEvent.getChannel(), new TunerChannel(Type.TRAFFIC,
|
||||||
frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
|
frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth()));
|
||||||
@@ -333,31 +310,6 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks for aliases that contain a do not follow/process priority.
|
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
* @return - true if there is an alias that has a do not process alias id
|
|
||||||
*/
|
|
||||||
private boolean isDoNotMonitor(CallEvent event)
|
|
||||||
{
|
|
||||||
Alias to = event.getToIDAlias();
|
|
||||||
|
|
||||||
if(to != null && to.hasCallPriority() && to.getCallPriority() == Priority.DO_NOT_MONITOR)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Alias from = event.getFromIDAlias();
|
|
||||||
|
|
||||||
if(from != null && from.hasCallPriority() && from.getCallPriority() == Priority.DO_NOT_MONITOR)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Listener<DecoderStateEvent> getDecoderStateListener()
|
public Listener<DecoderStateEvent> getDecoderStateListener()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package controller;
|
package controller;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import util.ThreadPool;
|
||||||
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -8,24 +12,23 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
public class NamingThreadFactory implements ThreadFactory
|
public class NamingThreadFactory implements ThreadFactory
|
||||||
{
|
{
|
||||||
static final AtomicInteger poolNumber = new AtomicInteger(1);
|
private final static Logger mLog = LoggerFactory.getLogger(ThreadPool.class);
|
||||||
|
|
||||||
final ThreadGroup mThreadGroup;
|
private static final AtomicInteger mPoolNumber = new AtomicInteger(1);
|
||||||
|
|
||||||
final AtomicInteger mThreadNumber = new AtomicInteger(1);
|
private final ThreadGroup mThreadGroup;
|
||||||
|
|
||||||
final String mNamePrefix;
|
private final AtomicInteger mThreadNumber = new AtomicInteger(1);
|
||||||
|
|
||||||
|
private final String mNamePrefix;
|
||||||
|
|
||||||
public NamingThreadFactory( String prefix )
|
public NamingThreadFactory( String prefix )
|
||||||
{
|
{
|
||||||
SecurityManager s = System.getSecurityManager();
|
SecurityManager s = System.getSecurityManager();
|
||||||
|
|
||||||
mThreadGroup = (s != null)? s.getThreadGroup() :
|
mThreadGroup = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
|
||||||
Thread.currentThread().getThreadGroup();
|
|
||||||
|
|
||||||
mNamePrefix = prefix + " pool-" +
|
mNamePrefix = prefix + " pool-" + mPoolNumber.getAndIncrement() + "-thread-";
|
||||||
poolNumber.getAndIncrement() +
|
|
||||||
"-thread-";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thread newThread( Runnable runnable )
|
public Thread newThread( Runnable runnable )
|
||||||
@@ -43,6 +46,15 @@ public class NamingThreadFactory implements ThreadFactory
|
|||||||
thread.setPriority( Thread.NORM_PRIORITY );
|
thread.setPriority( Thread.NORM_PRIORITY );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread t, Throwable e)
|
||||||
|
{
|
||||||
|
mLog.error("Error while executing runnable in scheduled thread pool [" + t.getName() + "]", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,15 +14,11 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package controller.channel;
|
package controller.channel;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAttribute;
|
import controller.config.Configuration;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
|
||||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
|
||||||
|
|
||||||
import module.decode.DecoderFactory;
|
import module.decode.DecoderFactory;
|
||||||
import module.decode.DecoderType;
|
import module.decode.DecoderType;
|
||||||
import module.decode.config.AuxDecodeConfiguration;
|
import module.decode.config.AuxDecodeConfiguration;
|
||||||
@@ -31,6 +27,7 @@ import module.log.EventLogType;
|
|||||||
import module.log.config.EventLogConfiguration;
|
import module.log.config.EventLogConfiguration;
|
||||||
import record.RecorderType;
|
import record.RecorderType;
|
||||||
import record.config.RecordConfiguration;
|
import record.config.RecordConfiguration;
|
||||||
|
import sample.Listener;
|
||||||
import source.SourceType;
|
import source.SourceType;
|
||||||
import source.config.SourceConfigFactory;
|
import source.config.SourceConfigFactory;
|
||||||
import source.config.SourceConfigRecording;
|
import source.config.SourceConfigRecording;
|
||||||
@@ -40,15 +37,24 @@ import source.tuner.TunerChannel;
|
|||||||
import source.tuner.TunerChannel.Type;
|
import source.tuner.TunerChannel.Type;
|
||||||
import source.tuner.frequency.FrequencyChangeEvent;
|
import source.tuner.frequency.FrequencyChangeEvent;
|
||||||
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
|
||||||
import controller.config.Configuration;
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||||
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
|
|
||||||
@XmlSeeAlso({Configuration.class})
|
@XmlSeeAlso({Configuration.class})
|
||||||
@XmlRootElement(name = "channel")
|
@XmlRootElement(name = "channel")
|
||||||
public class Channel extends Configuration implements IFrequencyChangeProcessor
|
public class Channel extends Configuration implements Listener<FrequencyChangeEvent>
|
||||||
{
|
{
|
||||||
// Standard channels are persisted and traffic channels are temporary
|
// Standard channels are persisted and traffic channels are temporary
|
||||||
public enum ChannelType { STANDARD, TRAFFIC };
|
public enum ChannelType
|
||||||
|
{
|
||||||
|
STANDARD, TRAFFIC
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
// Static unique channel identifier tracking
|
// Static unique channel identifier tracking
|
||||||
@@ -278,7 +284,7 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor
|
|||||||
* Note: this method is package private and is intended to be managed
|
* Note: this method is package private and is intended to be managed
|
||||||
* by a co-package central processing manager
|
* by a co-package central processing manager
|
||||||
*
|
*
|
||||||
* @see controller.ChannelProcessingManager
|
* @see controller.channel.ChannelProcessingManager
|
||||||
*/
|
*/
|
||||||
void setEnabled(boolean enabled)
|
void setEnabled(boolean enabled)
|
||||||
{
|
{
|
||||||
@@ -436,8 +442,7 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor
|
|||||||
{
|
{
|
||||||
TunerChannel tunerChannel = getTunerChannel();
|
TunerChannel tunerChannel = getTunerChannel();
|
||||||
|
|
||||||
return tunerChannel != null &&
|
return tunerChannel != null && tunerChannel.overlaps(minimum, maximum);
|
||||||
tunerChannel.overlaps( minimum, maximum );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -447,7 +452,7 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor
|
|||||||
* be used to visually show in the spectral display.
|
* be used to visually show in the spectral display.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void frequencyChanged( FrequencyChangeEvent event )
|
public void receive(FrequencyChangeEvent event)
|
||||||
{
|
{
|
||||||
if(event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE)
|
if(event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE)
|
||||||
{
|
{
|
||||||
@@ -455,9 +460,21 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current channel frequency correction value (when this channel is processing)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@XmlTransient
|
@XmlTransient
|
||||||
public int getChannelFrequencyCorrection()
|
public int getChannelFrequencyCorrection()
|
||||||
{
|
{
|
||||||
return mChannelFrequencyCorrection;
|
return mChannelFrequencyCorrection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets frequency correction value to 0
|
||||||
|
*/
|
||||||
|
public void resetFrequencyCorrection()
|
||||||
|
{
|
||||||
|
mChannelFrequencyCorrection = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
|
|
||||||
if(processingChain == null)
|
if(processingChain == null)
|
||||||
{
|
{
|
||||||
processingChain = new ProcessingChain(channel.getName(), channel.getChannelType());
|
processingChain = new ProcessingChain(channel.getChannelType());
|
||||||
|
|
||||||
/* Register global listeners */
|
/* Register global listeners */
|
||||||
for(Listener<AudioPacket> listener : mAudioPacketListeners)
|
for(Listener<AudioPacket> listener : mAudioPacketListeners)
|
||||||
@@ -244,6 +244,9 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
processingChain.addMessageListener(listener);
|
processingChain.addMessageListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Register channel to receive frequency correction events to show in the spectral display (hack!)
|
||||||
|
processingChain.addFrequencyChangeListener(channel);
|
||||||
|
|
||||||
/* Processing Modules */
|
/* Processing Modules */
|
||||||
List<Module> modules = DecoderFactory.getModules(mChannelModel, mChannelMapModel, this,
|
List<Module> modules = DecoderFactory.getModules(mChannelModel, mChannelMapModel, this,
|
||||||
mAliasModel, channel, processingChain.getChannelState().getMutableMetadata());
|
mAliasModel, channel, processingChain.getChannelState().getMutableMetadata());
|
||||||
@@ -347,6 +350,9 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
|
|
||||||
processingChain.removeRecordingModules();
|
processingChain.removeRecordingModules();
|
||||||
|
|
||||||
|
//Deregister channel from receive frequency correction events to show in the spectral display (hack!)
|
||||||
|
processingChain.removeFrequencyChangeListener(channel);
|
||||||
|
channel.resetFrequencyCorrection();
|
||||||
|
|
||||||
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_STOP));
|
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_STOP));
|
||||||
|
|
||||||
|
|||||||
@@ -17,34 +17,29 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package controller.channel;
|
package controller.channel;
|
||||||
|
|
||||||
|
import alias.AliasModel;
|
||||||
import gui.editor.Editor;
|
import gui.editor.Editor;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.ComboBoxModel;
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import alias.AliasModel;
|
|
||||||
|
|
||||||
public class NameConfigurationEditor extends Editor<Channel> implements DocumentListener
|
public class NameConfigurationEditor extends Editor<Channel> implements DocumentListener
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(NameConfigurationEditor.class);
|
private final static Logger mLog = LoggerFactory.getLogger(NameConfigurationEditor.class);
|
||||||
|
|
||||||
private static ComboBoxModel<String> EMPTY_MODEL = new DefaultComboBoxModel<>();
|
private static ComboBoxModel<String> EMPTY_SYSTEM_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
private static ComboBoxModel<String> EMPTY_SITE_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
private static ComboBoxModel<String> EMPTY_ALIAS_LIST_MODEL = new DefaultComboBoxModel<>();
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
private AliasModel mAliasModel;
|
||||||
private ChannelModel mChannelModel;
|
private ChannelModel mChannelModel;
|
||||||
@@ -73,7 +68,7 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
|
|||||||
add(new JLabel("Name:"));
|
add(new JLabel("Name:"));
|
||||||
add(mChannelName);
|
add(mChannelName);
|
||||||
|
|
||||||
mSystemNameCombo = new JComboBox<>( EMPTY_MODEL );
|
mSystemNameCombo = new JComboBox<>(EMPTY_SYSTEM_MODEL);
|
||||||
mSystemNameCombo.setEnabled(false);
|
mSystemNameCombo.setEnabled(false);
|
||||||
mSystemNameCombo.setEditable(true);
|
mSystemNameCombo.setEditable(true);
|
||||||
mSystemNameCombo.addActionListener(new ActionListener()
|
mSystemNameCombo.addActionListener(new ActionListener()
|
||||||
@@ -96,12 +91,13 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
|
|||||||
|
|
||||||
if(sites.isEmpty())
|
if(sites.isEmpty())
|
||||||
{
|
{
|
||||||
mSiteNameCombo.setModel( EMPTY_MODEL );
|
mSiteNameCombo.setModel(EMPTY_SITE_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
sites.toArray( new String[ sites.size() ] ) ) );;
|
sites.toArray(new String[sites.size()])));
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSiteNameCombo.setSelectedItem(getItem().getSite());
|
mSiteNameCombo.setSelectedItem(getItem().getSite());
|
||||||
@@ -114,7 +110,7 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
|
|||||||
add(new JLabel("System:"));
|
add(new JLabel("System:"));
|
||||||
add(mSystemNameCombo);
|
add(mSystemNameCombo);
|
||||||
|
|
||||||
mAliasListCombo = new JComboBox<String>();
|
mAliasListCombo = new JComboBox<String>(EMPTY_ALIAS_LIST_MODEL);
|
||||||
mAliasListCombo.setEnabled(false);
|
mAliasListCombo.setEnabled(false);
|
||||||
mAliasListCombo.setEditable(true);
|
mAliasListCombo.setEditable(true);
|
||||||
mAliasListCombo.addActionListener(new ActionListener()
|
mAliasListCombo.addActionListener(new ActionListener()
|
||||||
@@ -129,7 +125,7 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
|
|||||||
add(new JLabel("Alias List:"));
|
add(new JLabel("Alias List:"));
|
||||||
add(mAliasListCombo);
|
add(mAliasListCombo);
|
||||||
|
|
||||||
mSiteNameCombo = new JComboBox<>( EMPTY_MODEL );
|
mSiteNameCombo = new JComboBox<>(EMPTY_SITE_MODEL);
|
||||||
mSiteNameCombo.setEnabled(false);
|
mSiteNameCombo.setEnabled(false);
|
||||||
mSiteNameCombo.setEditable(true);
|
mSiteNameCombo.setEditable(true);
|
||||||
mSiteNameCombo.addActionListener(new ActionListener()
|
mSiteNameCombo.addActionListener(new ActionListener()
|
||||||
@@ -203,12 +199,13 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
|
|||||||
|
|
||||||
if(systems.isEmpty())
|
if(systems.isEmpty())
|
||||||
{
|
{
|
||||||
mSystemNameCombo.setModel( EMPTY_MODEL );
|
mSystemNameCombo.setModel(EMPTY_SYSTEM_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mSystemNameCombo.setModel(new DefaultComboBoxModel<String>(
|
mSystemNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
systems.toArray( new String[ systems.size() ] ) ) );;
|
systems.toArray(new String[systems.size()])));
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSystemNameCombo.setSelectedItem(channel.getSystem());
|
mSystemNameCombo.setSelectedItem(channel.getSystem());
|
||||||
@@ -217,12 +214,13 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
|
|||||||
|
|
||||||
if(sites.isEmpty())
|
if(sites.isEmpty())
|
||||||
{
|
{
|
||||||
mSiteNameCombo.setModel( EMPTY_MODEL );
|
mSiteNameCombo.setModel(EMPTY_SITE_MODEL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||||
sites.toArray( new String[ sites.size() ] ) ) );;
|
sites.toArray(new String[sites.size()])));
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(channel.getSite() != null)
|
if(channel.getSite() != null)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package dsp.filter;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
|
|
||||||
public class FloatFIRFilter implements RealSampleListener
|
public class FloatFIRFilter implements RealSampleListener
|
||||||
@@ -139,7 +140,7 @@ public class FloatFIRFilter implements RealSampleListener
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
mIndexMap = new int[ size ][ size ];
|
mIndexMap = new int[ size ][ size ];
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package dsp.filter;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
|
|
||||||
public class FloatHalfBandFilter implements RealSampleListener
|
public class FloatHalfBandFilter implements RealSampleListener
|
||||||
@@ -149,7 +150,7 @@ public class FloatHalfBandFilter implements RealSampleListener
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package dsp.filter;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
|
|
||||||
public class FloatHalfBandNoDecimateFilter implements RealSampleListener
|
public class FloatHalfBandNoDecimateFilter implements RealSampleListener
|
||||||
@@ -139,7 +140,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2015 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,22 +14,24 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.filter.cic;
|
package dsp.filter.cic;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import sample.Listener;
|
|
||||||
import sample.complex.ComplexBuffer;
|
|
||||||
import sample.complex.ComplexSampleListener;
|
|
||||||
import sample.complex.ComplexToComplexBufferAssembler;
|
|
||||||
import sample.decimator.ComplexDecimator;
|
|
||||||
import dsp.filter.FilterFactory;
|
import dsp.filter.FilterFactory;
|
||||||
import dsp.filter.Filters;
|
import dsp.filter.Filters;
|
||||||
import dsp.filter.Window.WindowType;
|
import dsp.filter.Window.WindowType;
|
||||||
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
||||||
import dsp.filter.halfband.complex.HalfBandFilter_CB_CB;
|
import dsp.filter.halfband.complex.HalfBandFilter_CB_CB;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import sample.Listener;
|
||||||
|
import sample.complex.ComplexBuffer;
|
||||||
|
import sample.complex.ComplexSampleListener;
|
||||||
|
import sample.complex.ComplexToComplexBufferAssembler;
|
||||||
|
import sample.decimator.ComplexDecimator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
||||||
{
|
{
|
||||||
@@ -67,7 +69,7 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
|||||||
public ComplexPrimeCICDecimate(int decimation, int order,
|
public ComplexPrimeCICDecimate(int decimation, int order,
|
||||||
int passFrequency, int attenuation, WindowType windowType)
|
int passFrequency, int attenuation, WindowType windowType)
|
||||||
{
|
{
|
||||||
assert( decimation <= 700 );
|
Validate.isTrue(decimation <= 700);
|
||||||
|
|
||||||
|
|
||||||
List<Integer> stageSizes = getPrimeFactors(decimation);
|
List<Integer> stageSizes = getPrimeFactors(decimation);
|
||||||
@@ -366,8 +368,11 @@ public class ComplexPrimeCICDecimate implements Listener<ComplexBuffer>
|
|||||||
attenuation,
|
attenuation,
|
||||||
windowType), 0.4f);
|
windowType), 0.4f);
|
||||||
|
|
||||||
mAssembler.setListener( mCleanupFilter );
|
//Bypassing the CIC cleanup filter for now
|
||||||
mCleanupFilter.setListener( mHalfBandFilter );
|
mAssembler.setListener(mHalfBandFilter);
|
||||||
|
|
||||||
|
// mAssembler.setListener( mCleanupFilter );
|
||||||
|
// mCleanupFilter.setListener( mHalfBandFilter );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose()
|
public void dispose()
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package dsp.filter.cic;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ public class RealPrimeCICDecimate
|
|||||||
public RealPrimeCICDecimate( int decimation, int order, int passFrequency,
|
public RealPrimeCICDecimate( int decimation, int order, int passFrequency,
|
||||||
int attenuation, WindowType windowType, int outputBufferSize )
|
int attenuation, WindowType windowType, int outputBufferSize )
|
||||||
{
|
{
|
||||||
assert( decimation <= 700 );
|
Validate.isTrue(decimation <= 700);
|
||||||
|
|
||||||
mOutputBufferSize = outputBufferSize;
|
mOutputBufferSize = outputBufferSize;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package dsp.filter.design;
|
package dsp.filter.design;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import dsp.filter.fir.FIRFilterSpecification;
|
import dsp.filter.fir.FIRFilterSpecification;
|
||||||
import dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
import dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class FilterViewer extends Application
|
public class FilterViewer extends Application
|
||||||
{
|
{
|
||||||
@@ -40,27 +39,27 @@ public class FilterViewer extends Application
|
|||||||
*/
|
*/
|
||||||
private float[] getFilter()
|
private float[] getFilter()
|
||||||
{
|
{
|
||||||
// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||||
// .sampleRate( 48000 )
|
.sampleRate(48000)
|
||||||
// .gridDensity( 16 )
|
.gridDensity(16)
|
||||||
// .passBandCutoff( 3200 )
|
.passBandCutoff(2500)
|
||||||
// .passBandAmplitude( 1.0 )
|
.passBandAmplitude(1.0)
|
||||||
// .passBandRipple( 0.02 )
|
.passBandRipple(0.01)
|
||||||
// .stopBandStart( 4000 )
|
.stopBandStart(4000)
|
||||||
|
.stopBandAmplitude(0.0)
|
||||||
|
.stopBandRipple(0.008)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
|
||||||
|
// .sampleRate(24000)
|
||||||
|
// .stopBandCutoff(800)
|
||||||
// .stopBandAmplitude(0.0)
|
// .stopBandAmplitude(0.0)
|
||||||
// .stopBandRipple(0.03)
|
// .stopBandRipple(0.03)
|
||||||
|
// .passBandStart(1000)
|
||||||
|
// .passBandAmplitude(1.0)
|
||||||
|
// .passBandRipple(0.08)
|
||||||
// .build();
|
// .build();
|
||||||
|
|
||||||
FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder()
|
|
||||||
.sampleRate( 24000 )
|
|
||||||
.stopBandCutoff( 800 )
|
|
||||||
.stopBandAmplitude( 0.0 )
|
|
||||||
.stopBandRipple( 0.03 )
|
|
||||||
.passBandStart( 1000 )
|
|
||||||
.passBandAmplitude( 1.0 )
|
|
||||||
.passBandRipple( 0.08 )
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
|
// FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder()
|
||||||
// .sampleRate( 48000 )
|
// .sampleRate( 48000 )
|
||||||
// .stopFrequency1( 200 )
|
// .stopFrequency1( 200 )
|
||||||
@@ -73,7 +72,8 @@ public class FilterViewer extends Application
|
|||||||
|
|
||||||
float[] taps = null;
|
float[] taps = null;
|
||||||
|
|
||||||
// taps = FilterFactory.getLowPass( 16000, 2400, 73, WindowType.HANNING );
|
// taps = FilterFactory.getLowPass(48000, 6750, 7500, 60,
|
||||||
|
// Window.WindowType.HAMMING, true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.filter.halfband.real;
|
package dsp.filter.halfband.real;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
public class HalfBandFilter
|
public class HalfBandFilter
|
||||||
{
|
{
|
||||||
private float[] mBuffer;
|
private float[] mBuffer;
|
||||||
@@ -122,7 +124,7 @@ public class HalfBandFilter
|
|||||||
private void generateIndexMap( int size )
|
private void generateIndexMap( int size )
|
||||||
{
|
{
|
||||||
//Ensure we have an odd size
|
//Ensure we have an odd size
|
||||||
assert( size % 2 == 1 );
|
Validate.isTrue(size % 2 == 1);
|
||||||
|
|
||||||
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
int mapWidth = ( ( size + 1 ) / 2 ) + 1;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dsp.filter.interpolator;
|
package dsp.filter.interpolator;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ public class RealInterpolator extends Interpolator
|
|||||||
public float filter( float[] samples, int offset, float mu )
|
public float filter( float[] samples, int offset, float mu )
|
||||||
{
|
{
|
||||||
/* Ensure we have enough samples in the array */
|
/* Ensure we have enough samples in the array */
|
||||||
assert( samples.length >= offset + 7 );
|
Validate.isTrue(samples.length >= offset + 7);
|
||||||
|
|
||||||
/* Identify the filter bank that corresponds to mu */
|
/* Identify the filter bank that corresponds to mu */
|
||||||
int index = (int)( NSTEPS * mu );
|
int index = (int)( NSTEPS * mu );
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.filter.polyphase;
|
package dsp.filter.polyphase;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ public class PolyphaseFIRInterpolatingFilter
|
|||||||
public PolyphaseFIRInterpolatingFilter( double[] coefficients, int interpolation )
|
public PolyphaseFIRInterpolatingFilter( double[] coefficients, int interpolation )
|
||||||
{
|
{
|
||||||
/* Ensure coefficients are an integral multiple of the interpolation factor */
|
/* Ensure coefficients are an integral multiple of the interpolation factor */
|
||||||
assert( coefficients.length % interpolation == 0 );
|
Validate.isTrue(coefficients.length % interpolation == 0);
|
||||||
|
|
||||||
mTaps = coefficients;
|
mTaps = coefficients;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package dsp.gain;
|
package dsp.gain;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ public class DirectGainControl implements GainController, RealSampleListener
|
|||||||
*/
|
*/
|
||||||
public DirectGainControl( float gain, float minimum, float maximum, float increment )
|
public DirectGainControl( float gain, float minimum, float maximum, float increment )
|
||||||
{
|
{
|
||||||
assert( minimum > 0 );
|
Validate.isTrue(minimum > 0);
|
||||||
|
|
||||||
mDefaultGain = gain;
|
mDefaultGain = gain;
|
||||||
mGain = gain;
|
mGain = gain;
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public class CRCLJ
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies 1 or 2 bit error positions that match the checksum error value.
|
* Identifies 1 bit error positions that match the checksum error value.
|
||||||
*/
|
*/
|
||||||
public static int[] findBitErrors( int checksumError )
|
public static int[] findBitErrors( int checksumError )
|
||||||
{
|
{
|
||||||
@@ -180,24 +180,6 @@ public class CRCLJ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Two bit errors */
|
|
||||||
for( int x = 0; x < 48; x++ )
|
|
||||||
{
|
|
||||||
for( int y = 0; y < 48; y++ )
|
|
||||||
{
|
|
||||||
if( x != y &&
|
|
||||||
( CHECKSUMS[ x ] ^ CHECKSUMS[ y ] ) == checksumError )
|
|
||||||
{
|
|
||||||
int[] errors = new int[ 2 ];
|
|
||||||
|
|
||||||
errors[ 0 ] = x;
|
|
||||||
errors[ 1 ] = y;
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
86
src/edac/trellis/DMRNode.java
Normal file
86
src/edac/trellis/DMRNode.java
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package edac.trellis;
|
||||||
|
|
||||||
|
public class DMRNode extends Node
|
||||||
|
{
|
||||||
|
//Hamming error values indicating the number of bits set in each indexed value, 0-15
|
||||||
|
public static final int[] HAMMING_ERROR_COUNT = new int[]{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmitted quad-bit value, derived from ETSI TS 102-361-1, Table B.7 Trellis encoder state transition table
|
||||||
|
*
|
||||||
|
* Note: the B.7 table contains constellation point ID numbers that have to be converted into actual bit values
|
||||||
|
* where the bits represent the dibit pair for the constellation point.
|
||||||
|
*/
|
||||||
|
public static final int[][] DMR_TRANSITION_MATRIX = new int[][]
|
||||||
|
{
|
||||||
|
{2,13,14,1,7,8,11,4},
|
||||||
|
{14,1,7,8,11,4,2,13},
|
||||||
|
{10,5,6,9,15,0,3,12},
|
||||||
|
{6,9,15,0,3,12,10,5},
|
||||||
|
{15,0,3,12,10,5,6,9},
|
||||||
|
{3,12,10,5,6,9,15,0},
|
||||||
|
{7,8,11,4,2,13,14,1},
|
||||||
|
{11,4,2,13,14,1,7,8}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trellis Coded Modulation (TCM) 3/4-rate trellis node for decoding DMR
|
||||||
|
*
|
||||||
|
* @param inputValue for the trellis time instant
|
||||||
|
* @param transmittedOutputValue the actual four-bit output value that was transmitted.
|
||||||
|
*/
|
||||||
|
public DMRNode(int inputValue, int transmittedOutputValue)
|
||||||
|
{
|
||||||
|
super(inputValue, transmittedOutputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the error between the preceeding node and this node as the Hamming distance between the expected
|
||||||
|
* four-bit output value versus the actual transmitted output value.
|
||||||
|
*
|
||||||
|
* @param precedingNode that precedes this node
|
||||||
|
* @return error value for this node relative to the preceeding node argument
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getError(Node precedingNode)
|
||||||
|
{
|
||||||
|
if(precedingNode != null)
|
||||||
|
{
|
||||||
|
int expectedOutputValue = getOutputValue(precedingNode);
|
||||||
|
int errorMask = expectedOutputValue ^ getTransmittedOutputValue();
|
||||||
|
return HAMMING_ERROR_COUNT[errorMask];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected output four-bit value when this node is preceeded by the argument node.
|
||||||
|
*
|
||||||
|
* @param precedingNode that precedes this node
|
||||||
|
* @return expected output value
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getOutputValue(Node precedingNode)
|
||||||
|
{
|
||||||
|
return DMR_TRANSITION_MATRIX[precedingNode.getInputValue()][getInputValue()];
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/edac/trellis/Node.java
Normal file
77
src/edac/trellis/Node.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package edac.trellis;
|
||||||
|
|
||||||
|
public abstract class Node
|
||||||
|
{
|
||||||
|
private int mInputValue;
|
||||||
|
private int mTransmittedOutputValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trellis Coded Modulation (TCM) node. A node represents a time instant on the trellis and contains an input
|
||||||
|
* value and the corresponding transmitted output value for the time instant. A node does not maintain the
|
||||||
|
* state at the time instant -- the state is determined by the input value of a preceeding node.
|
||||||
|
*
|
||||||
|
* @param inputValue for this node
|
||||||
|
* @param transmittedOutputValue for this node
|
||||||
|
*/
|
||||||
|
public Node(int inputValue, int transmittedOutputValue)
|
||||||
|
{
|
||||||
|
mInputValue = inputValue;
|
||||||
|
mTransmittedOutputValue = transmittedOutputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input value for this node.
|
||||||
|
*
|
||||||
|
* @return input value
|
||||||
|
*/
|
||||||
|
public int getInputValue()
|
||||||
|
{
|
||||||
|
return mInputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmitted output value for this node
|
||||||
|
* @return transmitted output value
|
||||||
|
*/
|
||||||
|
public int getTransmittedOutputValue()
|
||||||
|
{
|
||||||
|
return mTransmittedOutputValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the error value for this node (time instant) by determining the expected output value that would have
|
||||||
|
* been transmitted using the state from the preceding node argument and this node's input value and comparing that
|
||||||
|
* expected value to the actual transmitted output value. This error is normally represented as the Hamming
|
||||||
|
* distance between the two values.
|
||||||
|
*
|
||||||
|
* @param precedingNode that precedes this node
|
||||||
|
* @return error value for this node relative to the preceeding node
|
||||||
|
*/
|
||||||
|
public abstract int getError(Node precedingNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected output value when this node is preceeded by the node specified in the argument.
|
||||||
|
*
|
||||||
|
* @param precedingNode that precedes this node
|
||||||
|
* @return expected output value
|
||||||
|
*/
|
||||||
|
public abstract int getOutputValue(Node precedingNode);
|
||||||
|
}
|
||||||
131
src/edac/trellis/Path.java
Normal file
131
src/edac/trellis/Path.java
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package edac.trellis;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Path
|
||||||
|
{
|
||||||
|
private List<Node> mNodes = new ArrayList<>();
|
||||||
|
private int mError = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
|
||||||
|
* states across a trellis.
|
||||||
|
*
|
||||||
|
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
|
||||||
|
* @param startingNode to start the path. This node normally contains an input value of zero, but is implementation
|
||||||
|
* specific.
|
||||||
|
*/
|
||||||
|
public Path(Node startingNode)
|
||||||
|
{
|
||||||
|
add(startingNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trellis Coded Modulation (TCM) path. Represents a sequence of nodes that traverse various time instants and
|
||||||
|
* states across a trellis.
|
||||||
|
*
|
||||||
|
* Note: each node can be shared across multiple paths. This class does not assume that any nodes are unique.
|
||||||
|
*
|
||||||
|
* @param original path to create a copy from. Each of the nodes in the original will be added to this path and
|
||||||
|
* after construction both this path and the original will contain the exact same set of nodes.
|
||||||
|
*/
|
||||||
|
private Path(Path original)
|
||||||
|
{
|
||||||
|
for(Node node: original.getNodes())
|
||||||
|
{
|
||||||
|
add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cumulative error across all nodes contained within this path. Error value is updated as each new node is added
|
||||||
|
* to this path.
|
||||||
|
*
|
||||||
|
* @return cumulative error value
|
||||||
|
*/
|
||||||
|
public int getError()
|
||||||
|
{
|
||||||
|
return mError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length or number of nodes contained within this path. Note: this value includes the starting node and may
|
||||||
|
* also include a flushing node. The values contained in the starting and flushing nodes are normally not used
|
||||||
|
* when decoding a path.
|
||||||
|
*
|
||||||
|
* @return number of nodes in this path
|
||||||
|
*/
|
||||||
|
public int getLength()
|
||||||
|
{
|
||||||
|
return mNodes.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of nodes contained in this path.
|
||||||
|
* @return nodes
|
||||||
|
*/
|
||||||
|
public List<Node> getNodes()
|
||||||
|
{
|
||||||
|
return mNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the node to this path and updates the cumulative error value for this path by adding the node's error
|
||||||
|
* value to the running error total.
|
||||||
|
*
|
||||||
|
* @param node to add to this path
|
||||||
|
*/
|
||||||
|
public void add(Node node)
|
||||||
|
{
|
||||||
|
if(!mNodes.isEmpty())
|
||||||
|
{
|
||||||
|
mError += node.getError(getLastNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
mNodes.add(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last node in this path.
|
||||||
|
*
|
||||||
|
* @return last node
|
||||||
|
*/
|
||||||
|
public Node getLastNode()
|
||||||
|
{
|
||||||
|
if(!mNodes.isEmpty())
|
||||||
|
{
|
||||||
|
return mNodes.get(mNodes.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method to create an exact copy of this path.
|
||||||
|
*
|
||||||
|
* @return a new copy of this path.
|
||||||
|
*/
|
||||||
|
public Path copyOf()
|
||||||
|
{
|
||||||
|
return new Path(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
226
src/edac/trellis/ViterbiDecoder.java
Normal file
226
src/edac/trellis/ViterbiDecoder.java
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package edac.trellis;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class ViterbiDecoder
|
||||||
|
{
|
||||||
|
private int mInputBitLength;
|
||||||
|
private int mInputValueCount;
|
||||||
|
private int mOutputBitLength;
|
||||||
|
private int mOutputValueCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Viterbi decoder for trellis coded modulation (TCM) encoded binary sequences.
|
||||||
|
*
|
||||||
|
* @param inputBitLength for coding (e.g. 3/4 rate input bit length is 3)
|
||||||
|
* @param outputBitLength for coding (e.g. 3/4 rate output bit length is 4)
|
||||||
|
*/
|
||||||
|
public ViterbiDecoder(int inputBitLength, int outputBitLength)
|
||||||
|
{
|
||||||
|
mInputBitLength = inputBitLength;
|
||||||
|
mOutputBitLength = outputBitLength;
|
||||||
|
|
||||||
|
mInputValueCount = (int)Math.pow(2.0, mInputBitLength);
|
||||||
|
mOutputValueCount = (int)Math.pow(2.0, mOutputBitLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a node that represents an input value and the actual/transmitted output value.
|
||||||
|
*
|
||||||
|
* Note: this method must be implemented by a sub-class since the node requires a specific state transition
|
||||||
|
* table/matrix implementation.
|
||||||
|
*
|
||||||
|
* @param inputValue for the specific trellis node
|
||||||
|
* @param transmittedOutputValue the actual transmitted output value
|
||||||
|
* @return node for decoding
|
||||||
|
*/
|
||||||
|
protected abstract Node createNode(int inputValue, int transmittedOutputValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a flushing or final node that represents an actual/transmitted output value and a flushing input value.
|
||||||
|
*
|
||||||
|
* The final flushing input value is implementation-specific, therefore this will be implemented in any subclass
|
||||||
|
* of this decoder. A normal flushing input value is zero.
|
||||||
|
*
|
||||||
|
* @param transmittedOutputValue the actual transmitted output value
|
||||||
|
* @return flushing node
|
||||||
|
*/
|
||||||
|
protected abstract Node createFlushingNode(int transmittedOutputValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a starting node that represents a starting state and notional transmitted output value.
|
||||||
|
*
|
||||||
|
* The starting input value is implementation-specific, therefore this will be implemented in any subclass
|
||||||
|
* of this decoder. A normal starting input value is zero.
|
||||||
|
*
|
||||||
|
* @return starting node
|
||||||
|
*/
|
||||||
|
protected abstract Node createStartingNode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size in bits of the input and state values (e.g. 3/4 rate input size is 3)
|
||||||
|
*
|
||||||
|
* @return input bit size
|
||||||
|
*/
|
||||||
|
public int getInputBitLength()
|
||||||
|
{
|
||||||
|
return mInputBitLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of input or state values where the set of values is in the range of 0 <> count - 1
|
||||||
|
*
|
||||||
|
* @return input or state values count
|
||||||
|
*/
|
||||||
|
public int getInputValueCount()
|
||||||
|
{
|
||||||
|
return mInputValueCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size in bits of the output constellation or transmitted symbol value (e.g. 3/4 rate output size is 4)
|
||||||
|
*
|
||||||
|
* @return output size in bits
|
||||||
|
*/
|
||||||
|
public int getOutputBitLength()
|
||||||
|
{
|
||||||
|
return mOutputBitLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of input or state values where the set of values is in the range of 0 <> count - 1
|
||||||
|
*
|
||||||
|
* @return input or state values count
|
||||||
|
*/
|
||||||
|
public int getOutputValueCount()
|
||||||
|
{
|
||||||
|
return mOutputValueCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the TCM encoded transmitted output values and returns a path the represents the most likely transmitted
|
||||||
|
* sequence of nodes.
|
||||||
|
*
|
||||||
|
* @param transmittedOutputValues from the encoded message
|
||||||
|
* @return most likely path representing the transmitted values
|
||||||
|
*/
|
||||||
|
public Path decode(int[] transmittedOutputValues)
|
||||||
|
{
|
||||||
|
Collection<Path> survivingPaths = new ArrayList<>();
|
||||||
|
|
||||||
|
survivingPaths.add(new Path(createStartingNode()));
|
||||||
|
|
||||||
|
//Add all but the last transmitted value to the starting path, creating a set of survivors at each iteration.
|
||||||
|
for(int x = 0; x < transmittedOutputValues.length - 1; x++)
|
||||||
|
{
|
||||||
|
survivingPaths = add(survivingPaths, transmittedOutputValues[x]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Flush the survivors with the final transmitted value and return the lone surviving path
|
||||||
|
return flush(survivingPaths, transmittedOutputValues[transmittedOutputValues.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a set of new nodes representing all possible input values and the transmitted output value argument
|
||||||
|
* and iteratively adds the set of nodes to a copy of each of the surviving paths. A set of surviving paths is
|
||||||
|
* returned that represents each path that terminates at a distinct input/state value that has the least error
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* @param survivingPaths from the previous time instant to evaluate for the next time instant in the trellis
|
||||||
|
* using the transmitted output value.
|
||||||
|
*
|
||||||
|
* @param transmittedOutputValue that will be decoded for this time instant.
|
||||||
|
*
|
||||||
|
* @return paths that have survived (ie have the lowest error value) after adding nodes for the transmitted
|
||||||
|
* output value
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Collection<Path> add(Collection<Path> survivingPaths, int transmittedOutputValue)
|
||||||
|
{
|
||||||
|
Map<Integer,Path> survivingPathMap = new HashMap<>();
|
||||||
|
|
||||||
|
for(int x = 0; x < getInputValueCount(); x++)
|
||||||
|
{
|
||||||
|
//Create a single/unique node for each possible input value combined with the transmitted output value
|
||||||
|
Node node = createNode(x, transmittedOutputValue);
|
||||||
|
|
||||||
|
//Evaluate each surviving path by creating a copy and adding the node to it and see if it survives
|
||||||
|
for(Path path: survivingPaths)
|
||||||
|
{
|
||||||
|
Path pathToEvaluate = path.copyOf();
|
||||||
|
|
||||||
|
pathToEvaluate.add(node);
|
||||||
|
|
||||||
|
if(survivingPathMap.containsKey(node.getInputValue()))
|
||||||
|
{
|
||||||
|
Path survivingPath = survivingPathMap.get(node.getInputValue());
|
||||||
|
|
||||||
|
//Replace the current survivor path if this evaluate path has a lower error value
|
||||||
|
if(pathToEvaluate.getError() < survivingPath.getError())
|
||||||
|
{
|
||||||
|
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//No survivor yet -- set this evaluate path as the first survivor
|
||||||
|
survivingPathMap.put(node.getInputValue(), pathToEvaluate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return survivingPathMap.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes each of the surviving paths with a final/flushing node using the transmitted symbol. A final flushing
|
||||||
|
* input value or state is implementation-specific, but normally uses a value of zero. Since all paths will be
|
||||||
|
* evaluated against each other as terminating at the same state, only the path with the lowest error value will
|
||||||
|
* be returned from this method.
|
||||||
|
*
|
||||||
|
* @param survivingPaths to flush with the final flushing node
|
||||||
|
* @param transmittedOutputValue to use for the final flushing node
|
||||||
|
*
|
||||||
|
* @return the single survivor path that remains after adding the flushing node to each of the candidate argument
|
||||||
|
* survivor paths.
|
||||||
|
*/
|
||||||
|
public Path flush(Collection<Path> survivingPaths, int transmittedOutputValue)
|
||||||
|
{
|
||||||
|
Node flushingNode = createFlushingNode(transmittedOutputValue);
|
||||||
|
|
||||||
|
Path bestPath = null;
|
||||||
|
|
||||||
|
for(Path survivingPath: survivingPaths)
|
||||||
|
{
|
||||||
|
survivingPath.add(flushingNode);
|
||||||
|
|
||||||
|
if(bestPath == null || (survivingPath.getError() < bestPath.getError()))
|
||||||
|
{
|
||||||
|
bestPath = survivingPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/edac/trellis/ViterbiDecoder_3_4_DMR.java
Normal file
159
src/edac/trellis/ViterbiDecoder_3_4_DMR.java
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package edac.trellis;
|
||||||
|
|
||||||
|
import bits.BinaryMessage;
|
||||||
|
import bits.CorrectedBinaryMessage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ViterbiDecoder_3_4_DMR extends ViterbiDecoder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Viterbi decoder for Digital Mobile Radio (DMR) 3/4 rate Trellis Coded Modulation (TCM) encoded messages.
|
||||||
|
*/
|
||||||
|
public ViterbiDecoder_3_4_DMR()
|
||||||
|
{
|
||||||
|
super(3,4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a 3/4 rate trellis coded modulation (TCM) encoded DMR binary message containing 196 bits that have
|
||||||
|
* already been deinterleaved.
|
||||||
|
*
|
||||||
|
* @param encodedMessage to decode that has already been deinterleaved.
|
||||||
|
* @return decoded message
|
||||||
|
*/
|
||||||
|
public BinaryMessage decode(BinaryMessage encodedMessage)
|
||||||
|
{
|
||||||
|
int[] symbols = getSymbols(encodedMessage);
|
||||||
|
|
||||||
|
Path mostLikelyPath = decode(symbols);
|
||||||
|
|
||||||
|
return getMessage(mostLikelyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the decoded/corrected message from the surviving path argument.
|
||||||
|
*
|
||||||
|
* @param path to extract a message from
|
||||||
|
* @return corrected binary message.
|
||||||
|
*/
|
||||||
|
private static CorrectedBinaryMessage getMessage(Path path)
|
||||||
|
{
|
||||||
|
List<Node> mNodes = path.getNodes();
|
||||||
|
|
||||||
|
//Each node contains an input value excluding the starting(0) and final flushing(0) nodes.
|
||||||
|
CorrectedBinaryMessage message = new CorrectedBinaryMessage((mNodes.size() - 2) * 3);
|
||||||
|
|
||||||
|
if(mNodes.size() > 2)
|
||||||
|
{
|
||||||
|
for(int x = 1; x < mNodes.size() - 1; x++)
|
||||||
|
{
|
||||||
|
int inputValue = mNodes.get(x).getInputValue();
|
||||||
|
|
||||||
|
int messageOffset = 3 * (x - 1);
|
||||||
|
|
||||||
|
if((inputValue & 4) == 4)
|
||||||
|
{
|
||||||
|
message.set(messageOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((inputValue & 2) == 2)
|
||||||
|
{
|
||||||
|
message.set(messageOffset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((inputValue & 1) == 1)
|
||||||
|
{
|
||||||
|
message.set(messageOffset + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Transfer the corrected error count to the message
|
||||||
|
message.setCorrectedBitCount(path.getError());
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts (49) four-bit symbols (196 bits / 4) from the deinterleaved DMR binary message
|
||||||
|
*
|
||||||
|
* @param encodedMessage that deinterleaved and contains 196-bits of 3/4 TCM encoding
|
||||||
|
* @return symbols
|
||||||
|
*/
|
||||||
|
public int[] getSymbols(BinaryMessage encodedMessage)
|
||||||
|
{
|
||||||
|
//Ensure we have an integral number of transmitted symbols (nibbles) in the message
|
||||||
|
if(encodedMessage.size() % getOutputBitLength() != 0)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Encoded message must contain an integral number of 4-bit symbols -" +
|
||||||
|
" message size: " + encodedMessage.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] symbols = new int[encodedMessage.size() / getOutputBitLength()];
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
int pointer = 0;
|
||||||
|
|
||||||
|
while(index < encodedMessage.size())
|
||||||
|
{
|
||||||
|
int symbol = encodedMessage.getInt(index, index + (getOutputBitLength() - 1));
|
||||||
|
symbols[pointer++] = symbol;
|
||||||
|
index += getOutputBitLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DMR node
|
||||||
|
* @param inputValue for the specific trellis node
|
||||||
|
* @param transmittedOutputValue the actual transmitted output value
|
||||||
|
* @return created node
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Node createNode(int inputValue, int transmittedOutputValue)
|
||||||
|
{
|
||||||
|
return new DMRNode(inputValue, transmittedOutputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DMR flushing node. DMR uses an input value of zero to flush the final output value out of the
|
||||||
|
* encoder.
|
||||||
|
*
|
||||||
|
* @param transmittedOutputValue the actual transmitted output value
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Node createFlushingNode(int transmittedOutputValue)
|
||||||
|
{
|
||||||
|
return new DMRNode(0, transmittedOutputValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DMR starting node. DMR uses a starting input value of zero for the initial node.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Node createStartingNode()
|
||||||
|
{
|
||||||
|
return new DMRNode(0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -151,41 +151,4 @@ public class RadioReferenceService
|
|||||||
|
|
||||||
return mClient.getUserFeedBroadcasts(mUserAuthorization);
|
return mClient.getUserFeedBroadcasts(mUserAuthorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RadioReferenceService service = new RadioReferenceService("dsheirer", "1dodgeram");
|
|
||||||
|
|
||||||
Long expiration = service.getAccountExpirationDate();
|
|
||||||
|
|
||||||
mLog.debug("Expiration Date:" + (expiration != null ? new Date(expiration).toString() : "unknown"));
|
|
||||||
|
|
||||||
mLog.debug("Is Active: " + service.isAccountActive());
|
|
||||||
|
|
||||||
UserFeedBroadcast[] feeds = service.getUserFeedBroadcasts();
|
|
||||||
|
|
||||||
for(UserFeedBroadcast feed: feeds)
|
|
||||||
{
|
|
||||||
mLog.debug("Feed ID: " + feed.getFeedId() + " Desc:" + feed.getDescr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(AxisFault af)
|
|
||||||
{
|
|
||||||
mLog.debug("Hey, I caught an axis fault!");
|
|
||||||
mLog.debug("Reason:" + af.getFaultReason());
|
|
||||||
}
|
|
||||||
catch(RemoteException re)
|
|
||||||
{
|
|
||||||
re.printStackTrace();
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
mLog.debug("Finished!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import icon.IconManager;
|
|||||||
import map.MapService;
|
import map.MapService;
|
||||||
import module.log.EventLogManager;
|
import module.log.EventLogManager;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import playlist.PlaylistManager;
|
import playlist.PlaylistManager;
|
||||||
@@ -80,16 +81,16 @@ public class SDRTrunk implements Listener<TunerEvent>
|
|||||||
|
|
||||||
public SDRTrunk()
|
public SDRTrunk()
|
||||||
{
|
{
|
||||||
mLog.info("");
|
|
||||||
mLog.info("*******************************************************************");
|
mLog.info("*******************************************************************");
|
||||||
mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***");
|
mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***");
|
||||||
mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***");
|
mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***");
|
||||||
mLog.info("*******************************************************************");
|
mLog.info("*******************************************************************");
|
||||||
mLog.info("");
|
mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]");
|
||||||
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
|
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
|
||||||
mLog.info("Host Memory Total: " + Runtime.getRuntime().totalMemory());
|
mLog.info("Host OS Name: " + System.getProperty("os.name"));
|
||||||
mLog.info("Host Memory Max: " + Runtime.getRuntime().maxMemory());
|
mLog.info("Host OS Arch: " + System.getProperty("os.arch"));
|
||||||
mLog.info("Host Memory Free: " + Runtime.getRuntime().freeMemory());
|
mLog.info("Host OS Version: " + System.getProperty("os.version"));
|
||||||
|
mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory()));
|
||||||
|
|
||||||
//Setup the application home directory
|
//Setup the application home directory
|
||||||
Path home = getHomePath();
|
Path home = getHomePath();
|
||||||
|
|||||||
@@ -1,21 +1,38 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package instrument;
|
package instrument;
|
||||||
|
|
||||||
|
import controller.channel.Channel.ChannelType;
|
||||||
import instrument.tap.Tap;
|
import instrument.tap.Tap;
|
||||||
import instrument.tap.TapGroup;
|
import instrument.tap.TapGroup;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import module.Module;
|
import module.Module;
|
||||||
import module.ProcessingChain;
|
import module.ProcessingChain;
|
||||||
import source.Source;
|
import source.Source;
|
||||||
import controller.channel.Channel.ChannelType;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class InstrumentableProcessingChain extends ProcessingChain implements Instrumentable
|
public class InstrumentableProcessingChain extends ProcessingChain implements Instrumentable
|
||||||
{
|
{
|
||||||
public InstrumentableProcessingChain()
|
public InstrumentableProcessingChain()
|
||||||
{
|
{
|
||||||
super( "instrumented", ChannelType.STANDARD );
|
super(ChannelType.STANDARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSource(Source source) throws IllegalStateException
|
public void setSource(Source source) throws IllegalStateException
|
||||||
|
|||||||
@@ -24,16 +24,17 @@ import audio.IAudioPacketProvider;
|
|||||||
import audio.squelch.ISquelchStateListener;
|
import audio.squelch.ISquelchStateListener;
|
||||||
import audio.squelch.ISquelchStateProvider;
|
import audio.squelch.ISquelchStateProvider;
|
||||||
import audio.squelch.SquelchState;
|
import audio.squelch.SquelchState;
|
||||||
|
import channel.heartbeat.Heartbeat;
|
||||||
|
import channel.heartbeat.IHeartbeatListener;
|
||||||
|
import channel.heartbeat.IHeartbeatProvider;
|
||||||
import channel.metadata.AttributeChangeRequest;
|
import channel.metadata.AttributeChangeRequest;
|
||||||
import channel.metadata.IAttributeChangeRequestListener;
|
import channel.metadata.IAttributeChangeRequestListener;
|
||||||
import channel.metadata.IAttributeChangeRequestProvider;
|
import channel.metadata.IAttributeChangeRequestProvider;
|
||||||
import channel.state.ChannelState;
|
import channel.state.ChannelState;
|
||||||
import channel.state.DecoderState;
|
import channel.state.DecoderState;
|
||||||
import channel.state.DecoderStateEvent;
|
import channel.state.DecoderStateEvent;
|
||||||
import channel.state.DecoderStateEvent.Event;
|
|
||||||
import channel.state.IDecoderStateEventListener;
|
import channel.state.IDecoderStateEventListener;
|
||||||
import channel.state.IDecoderStateEventProvider;
|
import channel.state.IDecoderStateEventProvider;
|
||||||
import channel.state.State;
|
|
||||||
import controller.channel.Channel.ChannelType;
|
import controller.channel.Channel.ChannelType;
|
||||||
import controller.channel.ChannelEvent;
|
import controller.channel.ChannelEvent;
|
||||||
import controller.channel.IChannelEventListener;
|
import controller.channel.IChannelEventListener;
|
||||||
@@ -63,8 +64,6 @@ import sample.real.RealBuffer;
|
|||||||
import source.ComplexSource;
|
import source.ComplexSource;
|
||||||
import source.RealSource;
|
import source.RealSource;
|
||||||
import source.Source;
|
import source.Source;
|
||||||
import source.SourceException;
|
|
||||||
import source.tuner.TunerChannelSource;
|
|
||||||
import source.tuner.frequency.FrequencyChangeEvent;
|
import source.tuner.frequency.FrequencyChangeEvent;
|
||||||
import source.tuner.frequency.IFrequencyChangeListener;
|
import source.tuner.frequency.IFrequencyChangeListener;
|
||||||
import source.tuner.frequency.IFrequencyChangeProvider;
|
import source.tuner.frequency.IFrequencyChangeProvider;
|
||||||
@@ -103,12 +102,12 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
|
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<DecoderStateEvent> mDecoderStateEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<DecoderStateEvent> mDecoderStateEventBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<FrequencyChangeEvent> mFrequencyChangeEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<FrequencyChangeEvent> mFrequencyChangeEventBroadcaster = new Broadcaster<>();
|
||||||
|
private Broadcaster<Heartbeat> mHeartbeatBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<Message> mMessageBroadcaster = new Broadcaster<>();
|
private Broadcaster<Message> mMessageBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<RealBuffer> mFilteredRealBufferBroadcaster = new Broadcaster<>();
|
private Broadcaster<RealBuffer> mFilteredRealBufferBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<RealBuffer> mUnFilteredRealBufferBroadcaster = new Broadcaster<>();
|
private Broadcaster<RealBuffer> mUnFilteredRealBufferBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<SquelchState> mSquelchStateBroadcaster = new Broadcaster<>();
|
private Broadcaster<SquelchState> mSquelchStateBroadcaster = new Broadcaster<>();
|
||||||
|
|
||||||
private String mName;
|
|
||||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||||
|
|
||||||
protected Source mSource;
|
protected Source mSource;
|
||||||
@@ -120,13 +119,10 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
/**
|
/**
|
||||||
* Creates a processing chain for managing a set of modules
|
* Creates a processing chain for managing a set of modules
|
||||||
*
|
*
|
||||||
* @param name for thread pool naming each thread
|
|
||||||
* @param channelType
|
* @param channelType
|
||||||
*/
|
*/
|
||||||
public ProcessingChain(String name, ChannelType channelType)
|
public ProcessingChain(ChannelType channelType)
|
||||||
{
|
{
|
||||||
mName = name;
|
|
||||||
|
|
||||||
mChannelState = new ChannelState(channelType);
|
mChannelState = new ChannelState(channelType);
|
||||||
addModule(mChannelState);
|
addModule(mChannelState);
|
||||||
|
|
||||||
@@ -211,6 +207,8 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
mSource = source;
|
mSource = source;
|
||||||
|
|
||||||
|
addModule(mSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -322,7 +320,18 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
|
|
||||||
if(module instanceof IFrequencyChangeListener)
|
if(module instanceof IFrequencyChangeListener)
|
||||||
{
|
{
|
||||||
mFrequencyChangeEventBroadcaster.addListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
|
Listener<FrequencyChangeEvent> frequencyChangeEventListener =
|
||||||
|
((IFrequencyChangeListener)module).getFrequencyChangeListener();
|
||||||
|
|
||||||
|
if(frequencyChangeEventListener != null)
|
||||||
|
{
|
||||||
|
mFrequencyChangeEventBroadcaster.addListener(frequencyChangeEventListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(module instanceof IHeartbeatListener)
|
||||||
|
{
|
||||||
|
mHeartbeatBroadcaster.addListener(((IHeartbeatListener)module).getHeartbeatListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(module instanceof IMessageListener)
|
if(module instanceof IMessageListener)
|
||||||
@@ -387,6 +396,11 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
mFrequencyChangeEventBroadcaster.removeListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
|
mFrequencyChangeEventBroadcaster.removeListener(((IFrequencyChangeListener) module).getFrequencyChangeListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(module instanceof IHeartbeatListener)
|
||||||
|
{
|
||||||
|
mHeartbeatBroadcaster.removeListener(((IHeartbeatListener)module).getHeartbeatListener());
|
||||||
|
}
|
||||||
|
|
||||||
if(module instanceof IMessageListener)
|
if(module instanceof IMessageListener)
|
||||||
{
|
{
|
||||||
mMessageBroadcaster.removeListener(((IMessageListener) module).getMessageListener());
|
mMessageBroadcaster.removeListener(((IMessageListener) module).getMessageListener());
|
||||||
@@ -444,6 +458,11 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
((IFrequencyChangeProvider) module).setFrequencyChangeListener(mFrequencyChangeEventBroadcaster);
|
((IFrequencyChangeProvider) module).setFrequencyChangeListener(mFrequencyChangeEventBroadcaster);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(module instanceof IHeartbeatProvider)
|
||||||
|
{
|
||||||
|
((IHeartbeatProvider)module).setHeartbeatListener(mHeartbeatBroadcaster);
|
||||||
|
}
|
||||||
|
|
||||||
if(module instanceof IMessageProvider)
|
if(module instanceof IMessageProvider)
|
||||||
{
|
{
|
||||||
((IMessageProvider) module).setMessageListener(mMessageBroadcaster);
|
((IMessageProvider) module).setMessageListener(mMessageBroadcaster);
|
||||||
@@ -501,6 +520,11 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
((IFrequencyChangeProvider) module).setFrequencyChangeListener(null);
|
((IFrequencyChangeProvider) module).setFrequencyChangeListener(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(module instanceof IHeartbeatProvider)
|
||||||
|
{
|
||||||
|
((IHeartbeatProvider)module).removeHeartbeatListener();
|
||||||
|
}
|
||||||
|
|
||||||
if(module instanceof IMessageProvider)
|
if(module instanceof IMessageProvider)
|
||||||
{
|
{
|
||||||
((IMessageProvider) module).setMessageListener(null);
|
((IMessageProvider) module).setMessageListener(null);
|
||||||
@@ -538,6 +562,9 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
module.reset();
|
module.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Setup the channel state to monitor source overflow conditions
|
||||||
|
mSource.setOverflowListener(mChannelState);
|
||||||
|
|
||||||
/* Register with the source to receive sample data. Setup a
|
/* Register with the source to receive sample data. Setup a
|
||||||
* timer task to process the buffer queues 50 times a second
|
* timer task to process the buffer queues 50 times a second
|
||||||
* (every 20 ms) */
|
* (every 20 ms) */
|
||||||
@@ -554,25 +581,6 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
+ "sample type - cannot start processing chain");
|
+ "sample type - cannot start processing chain");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this is a tuner source, broadcast the frequency to all
|
|
||||||
* of the decoder state's and start the samples flowing */
|
|
||||||
if(mSource instanceof TunerChannelSource)
|
|
||||||
{
|
|
||||||
TunerChannelSource tcs = (TunerChannelSource) mSource;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
long frequency = tcs.getFrequency();
|
|
||||||
|
|
||||||
mDecoderStateEventBroadcaster.broadcast(
|
|
||||||
new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, State.IDLE, frequency));
|
|
||||||
}
|
|
||||||
catch(SourceException e)
|
|
||||||
{
|
|
||||||
mLog.error("Error getting frequency from tuner channel source", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start each of the modules */
|
/* Start each of the modules */
|
||||||
for(Module module : mModules)
|
for(Module module : mModules)
|
||||||
{
|
{
|
||||||
@@ -585,8 +593,6 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
mLog.error("Error starting module", e);
|
mLog.error("Error starting module", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mSource.start(ThreadPool.SCHEDULED);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -603,9 +609,17 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
{
|
{
|
||||||
if(mRunning.compareAndSet(true, false))
|
if(mRunning.compareAndSet(true, false))
|
||||||
{
|
{
|
||||||
|
/* Stop each of the modules */
|
||||||
|
for(Module module : mModules)
|
||||||
|
{
|
||||||
|
module.stop();
|
||||||
|
}
|
||||||
|
|
||||||
if(mSource != null)
|
if(mSource != null)
|
||||||
{
|
{
|
||||||
mSource.stop();
|
removeModule(mSource);
|
||||||
|
|
||||||
|
mSource.setOverflowListener(null);
|
||||||
|
|
||||||
switch(mSource.getSampleType())
|
switch(mSource.getSampleType())
|
||||||
{
|
{
|
||||||
@@ -625,11 +639,6 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
mSource = null;
|
mSource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop each of the modules */
|
|
||||||
for(Module module : mModules)
|
|
||||||
{
|
|
||||||
module.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,6 +765,24 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
mMessageBroadcaster.removeListener(listener);
|
mMessageBroadcaster.removeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the listener to receive frequency change events from the processing chain
|
||||||
|
* @param listener to receive events
|
||||||
|
*/
|
||||||
|
public void addFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||||
|
{
|
||||||
|
mFrequencyChangeEventBroadcaster.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the listener from receiving frequency change events
|
||||||
|
* @param listener to remove
|
||||||
|
*/
|
||||||
|
public void removeFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||||
|
{
|
||||||
|
mFrequencyChangeEventBroadcaster.removeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the listener to receive call events from all modules.
|
* Adds the listener to receive call events from all modules.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -103,35 +103,35 @@ public class DecoderFactory
|
|||||||
|
|
||||||
//Low-pass filter with ~60 dB attenuation and 89 taps
|
//Low-pass filter with ~60 dB attenuation and 89 taps
|
||||||
private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
private static final FIRFilterSpecification MPT1327_FILTER_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
||||||
.sampleRate( 48000 )
|
.sampleRate( 48000 ).gridDensity( 16 ).passBandCutoff( 3200 ).passBandAmplitude( 1.0 ).passBandRipple( 0.02 )
|
||||||
.gridDensity( 16 )
|
.stopBandStart( 4000 ).stopBandAmplitude( 0.0 ).stopBandRipple( 0.03 ).build();
|
||||||
.passBandCutoff( 3200 )
|
|
||||||
.passBandAmplitude( 1.0 )
|
private static final FIRFilterSpecification P25_C4FM_IQ_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
||||||
.passBandRipple( 0.02 )
|
.sampleRate(48000).gridDensity(16).passBandCutoff(6750).passBandAmplitude(1.0).passBandRipple(0.01)
|
||||||
.stopBandStart( 4000 )
|
.stopBandStart(7000).stopBandAmplitude(0.0).stopBandRipple(0.008).build();
|
||||||
.stopBandAmplitude( 0.0 )
|
|
||||||
.stopBandRipple( 0.03 )
|
private static final FIRFilterSpecification P25_C4FM_DEMOD_SPECIFICATION = FIRFilterSpecification.lowPassBuilder()
|
||||||
.build();
|
.sampleRate(48000).gridDensity(16).passBandCutoff(2500).passBandAmplitude(1.0).passBandRipple(0.01)
|
||||||
|
.stopBandStart(4000).stopBandAmplitude(0.0).stopBandRipple(0.008).build();
|
||||||
|
|
||||||
private static float[] MPT1327_LOWPASS_FILTER;
|
private static float[] MPT1327_LOWPASS_FILTER;
|
||||||
|
private static float[] P25_C4FM_IQ_FILTER;
|
||||||
|
private static float[] P25_C4FM_DEMOD_FILTER;
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MPT1327_LOWPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION);
|
MPT1327_LOWPASS_FILTER = FilterFactory.getTaps(MPT1327_FILTER_SPECIFICATION);
|
||||||
|
P25_C4FM_IQ_FILTER = FilterFactory.getTaps(P25_C4FM_IQ_SPECIFICATION);
|
||||||
|
P25_C4FM_DEMOD_FILTER = FilterFactory.getTaps(P25_C4FM_DEMOD_SPECIFICATION);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
mLog.error("Couldn't design MPT1327 bandpass filter");
|
mLog.error("Couldn't design startup filter(s)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final boolean NO_REMOVE_DC = false;
|
|
||||||
public static final boolean REMOVE_DC = true;
|
|
||||||
|
|
||||||
public static final int NO_FREQUENCY_CORRECTION = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of one primary decoder and any auxiliary decoders, as
|
* Returns a list of one primary decoder and any auxiliary decoders, as
|
||||||
* specified in the configurations.
|
* specified in the configurations.
|
||||||
@@ -222,15 +222,13 @@ public class DecoderFactory
|
|||||||
|
|
||||||
if(channelType == ChannelType.STANDARD)
|
if(channelType == ChannelType.STANDARD)
|
||||||
{
|
{
|
||||||
long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds
|
modules.add(new TrafficChannelManager(channelModel, decodeConfig,
|
||||||
|
|
||||||
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig,
|
|
||||||
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
|
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
|
||||||
(aliasList != null ? aliasList.getName() : null), timeout, mptConfig.getTrafficChannelPoolSize()));
|
(aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
modules.add(new FMDemodulatorModule(iqPass, iqStop));
|
||||||
modules.add(new DemodulatedAudioFilterModule(MPT1327_LOWPASS_FILTER, 1.0f));
|
modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
|
||||||
modules.add(new AudioModule(metadata));
|
modules.add(new AudioModule(metadata));
|
||||||
break;
|
break;
|
||||||
case PASSPORT:
|
case PASSPORT:
|
||||||
@@ -249,7 +247,8 @@ public class DecoderFactory
|
|||||||
switch(modulation)
|
switch(modulation)
|
||||||
{
|
{
|
||||||
case C4FM:
|
case C4FM:
|
||||||
modules.add(new FMDemodulatorModule(6750, 7500));
|
modules.add(new FMDemodulatorModule(P25_C4FM_IQ_FILTER));
|
||||||
|
modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f));
|
||||||
modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
|
modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection()));
|
||||||
modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
|
modules.add(new P25DecoderState(aliasList, channelType, Modulation.C4FM,
|
||||||
p25Config.getIgnoreDataCalls()));
|
p25Config.getIgnoreDataCalls()));
|
||||||
@@ -264,12 +263,9 @@ public class DecoderFactory
|
|||||||
|
|
||||||
if(channelType == ChannelType.STANDARD)
|
if(channelType == ChannelType.STANDARD)
|
||||||
{
|
{
|
||||||
//Set call timeout to 3 seconds for P25 traffic channels
|
modules.add(new TrafficChannelManager(channelModel, decodeConfig,
|
||||||
long timeout = 3000;
|
|
||||||
|
|
||||||
modules.add(new TrafficChannelManager(channelModel, channelProcessingManager, decodeConfig,
|
|
||||||
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
|
channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(),
|
||||||
(aliasList != null ? aliasList.getName() : null), timeout, p25Config.getTrafficChannelPoolSize()));
|
(aliasList != null ? aliasList.getName() : null), p25Config.getTrafficChannelPoolSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
modules.add(new P25AudioModule(metadata));
|
modules.add(new P25AudioModule(metadata));
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class LJ1200DecoderState extends DecoderState
|
|||||||
super(aliasList);
|
super(aliasList);
|
||||||
|
|
||||||
mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO,
|
mAddressAttribute = new AliasedStringAttributeMonitor(Attribute.SECONDARY_ADDRESS_TO,
|
||||||
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.LOJACK);
|
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -389,9 +389,9 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append("Activity Summary\n");
|
sb.append("Activity Summary - Decoder: MPT-1327\n");
|
||||||
sb.append("Decoder:\tMPT-1327\n");
|
sb.append(DIVIDER1);
|
||||||
sb.append("Site:\t");
|
sb.append("Site: ");
|
||||||
|
|
||||||
if(mSite != null)
|
if(mSite != null)
|
||||||
{
|
{
|
||||||
@@ -403,9 +403,7 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
|
|
||||||
if(siteAlias != null)
|
if(siteAlias != null)
|
||||||
{
|
{
|
||||||
sb.append(" ");
|
sb.append(" ").append(siteAlias.getName()).append("\n");
|
||||||
sb.append(siteAlias.getName());
|
|
||||||
sb.append("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -414,9 +412,7 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
sb.append("Unknown\n");
|
sb.append("Unknown\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\n\n");
|
sb.append(DIVIDER2).append("Talkgroups: ");
|
||||||
|
|
||||||
sb.append("Talkgroups\n\n");
|
|
||||||
|
|
||||||
if(mGroups.isEmpty())
|
if(mGroups.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -429,8 +425,7 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
|
|
||||||
for(String talkgroup : talkgroups)
|
for(String talkgroup : talkgroups)
|
||||||
{
|
{
|
||||||
sb.append(" ");
|
sb.append("\n ").append(talkgroup);
|
||||||
sb.append(talkgroup);
|
|
||||||
|
|
||||||
if(hasAliasList())
|
if(hasAliasList())
|
||||||
{
|
{
|
||||||
@@ -466,20 +461,19 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
|
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\n");
|
sb.append(DIVIDER2).append("All Idents: ");
|
||||||
|
|
||||||
sb.append("Individual Idents\n");
|
|
||||||
if(mIdents.isEmpty())
|
if(mIdents.isEmpty())
|
||||||
{
|
{
|
||||||
sb.append("None\n");
|
sb.append("None\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
Iterator<String> it = mIdents.iterator();
|
Iterator<String> it = mIdents.iterator();
|
||||||
|
|
||||||
while(it.hasNext())
|
while(it.hasNext())
|
||||||
|
|||||||
@@ -9,19 +9,19 @@ import instrument.tap.stream.FloatTap;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
import sample.real.RealBuffer;
|
import sample.real.RealBuffer;
|
||||||
import sample.real.RealSampleListener;
|
import sample.real.RealSampleListener;
|
||||||
import sample.real.RealSampleProvider;
|
import sample.real.RealSampleProvider;
|
||||||
|
import source.tuner.TunerChannelSource;
|
||||||
import source.tuner.frequency.FrequencyChangeEvent;
|
import source.tuner.frequency.FrequencyChangeEvent;
|
||||||
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||||
import source.tuner.frequency.IFrequencyChangeListener;
|
import source.tuner.frequency.IFrequencyChangeListener;
|
||||||
import dsp.gain.DirectGainControl;
|
import dsp.gain.DirectGainControl;
|
||||||
|
|
||||||
public class C4FMSymbolFilter implements Listener<RealBuffer>,
|
public class C4FMSymbolFilter implements Listener<RealBuffer>, IFrequencyChangeListener, RealSampleProvider, Instrumentable
|
||||||
IFrequencyChangeListener,
|
|
||||||
RealSampleProvider,
|
|
||||||
Instrumentable
|
|
||||||
{
|
{
|
||||||
private static final float TAPS[][] =
|
private static final float TAPS[][] =
|
||||||
{
|
{
|
||||||
@@ -198,8 +198,7 @@ public class C4FMSymbolFilter implements Listener<RealBuffer>,
|
|||||||
|
|
||||||
private RealSampleListener mListener;
|
private RealSampleListener mListener;
|
||||||
|
|
||||||
private DirectGainControl mGainController =
|
private DirectGainControl mGainController = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f );
|
||||||
new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f );
|
|
||||||
|
|
||||||
private int mFrequencyAdjustmentRequested = 0;
|
private int mFrequencyAdjustmentRequested = 0;
|
||||||
private int mFrequencyCorrection = 0;
|
private int mFrequencyCorrection = 0;
|
||||||
@@ -258,8 +257,7 @@ public class C4FMSymbolFilter implements Listener<RealBuffer>,
|
|||||||
correction = -mFrequencyCorrectionMaximum;
|
correction = -mFrequencyCorrectionMaximum;
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast( new FrequencyChangeEvent(
|
broadcast( new FrequencyChangeEvent(Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) );
|
||||||
Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) );
|
|
||||||
|
|
||||||
mFrequencyAdjustmentRequested = 0;
|
mFrequencyAdjustmentRequested = 0;
|
||||||
}
|
}
|
||||||
@@ -452,6 +450,10 @@ public class C4FMSymbolFilter implements Listener<RealBuffer>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts a frequency change event to the registered listener
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
public void broadcast( FrequencyChangeEvent event )
|
public void broadcast( FrequencyChangeEvent event )
|
||||||
{
|
{
|
||||||
if( mFrequencyChangeListener != null )
|
if( mFrequencyChangeListener != null )
|
||||||
@@ -460,6 +462,10 @@ public class C4FMSymbolFilter implements Listener<RealBuffer>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the listener for frequency change events.
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener )
|
public void setFrequencyChangeListener( Listener<FrequencyChangeEvent> listener )
|
||||||
{
|
{
|
||||||
mFrequencyChangeListener = listener;
|
mFrequencyChangeListener = listener;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import alias.AliasList;
|
|||||||
import instrument.Instrumentable;
|
import instrument.Instrumentable;
|
||||||
import module.decode.Decoder;
|
import module.decode.Decoder;
|
||||||
import module.decode.DecoderType;
|
import module.decode.DecoderType;
|
||||||
|
import source.tuner.frequency.IFrequencyChangeListener;
|
||||||
|
import source.tuner.frequency.IFrequencyChangeProvider;
|
||||||
|
|
||||||
public abstract class P25Decoder extends Decoder implements Instrumentable
|
public abstract class P25Decoder extends Decoder implements IFrequencyChangeListener, IFrequencyChangeProvider, Instrumentable
|
||||||
{
|
{
|
||||||
private P25MessageProcessor mMessageProcessor;
|
private P25MessageProcessor mMessageProcessor;
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ package module.decode.p25;
|
|||||||
|
|
||||||
import alias.Alias;
|
import alias.Alias;
|
||||||
import alias.AliasList;
|
import alias.AliasList;
|
||||||
import alias.PatchGroupAlias;
|
|
||||||
import alias.id.AliasIDType;
|
import alias.id.AliasIDType;
|
||||||
import alias.id.talkgroup.TalkgroupID;
|
|
||||||
import channel.metadata.AliasedStringAttributeMonitor;
|
import channel.metadata.AliasedStringAttributeMonitor;
|
||||||
import channel.metadata.Attribute;
|
import channel.metadata.Attribute;
|
||||||
import channel.metadata.AttributeChangeRequest;
|
import channel.metadata.AttributeChangeRequest;
|
||||||
@@ -150,6 +148,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
private static final DecimalFormat mFrequencyFormatter =
|
private static final DecimalFormat mFrequencyFormatter =
|
||||||
new DecimalFormat("0.000000");
|
new DecimalFormat("0.000000");
|
||||||
|
|
||||||
|
|
||||||
private module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast mNetworkStatus;
|
private module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast mNetworkStatus;
|
||||||
private NetworkStatusBroadcastExtended mNetworkStatusExtended;
|
private NetworkStatusBroadcastExtended mNetworkStatusExtended;
|
||||||
private ProtectionParameterBroadcast mProtectionParameterBroadcast;
|
private ProtectionParameterBroadcast mProtectionParameterBroadcast;
|
||||||
@@ -163,7 +162,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
private Map<Integer,IdentifierUpdate> mBands = new HashMap<>();
|
private Map<Integer,IdentifierUpdate> mBands = new HashMap<>();
|
||||||
private Map<String,Long> mRegistrations = new HashMap<>();
|
private Map<String,Long> mRegistrations = new HashMap<>();
|
||||||
private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>();
|
private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>();
|
||||||
private Map<String,List<String>> mPatchGroupMap = new HashMap<>();
|
|
||||||
|
|
||||||
private String mLastCommandEventID;
|
private String mLastCommandEventID;
|
||||||
private String mLastPageEventID;
|
private String mLastPageEventID;
|
||||||
@@ -188,6 +186,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
private P25CallEvent mCurrentCallEvent;
|
private P25CallEvent mCurrentCallEvent;
|
||||||
private List<String> mCallDetectTalkgroups = new ArrayList<>();
|
private List<String> mCallDetectTalkgroups = new ArrayList<>();
|
||||||
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
|
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
|
||||||
|
private PatchGroupManager mPatchGroupManager;
|
||||||
|
|
||||||
public P25DecoderState(AliasList aliasList,
|
public P25DecoderState(AliasList aliasList,
|
||||||
ChannelType channelType,
|
ChannelType channelType,
|
||||||
@@ -199,6 +198,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
mModulation = modulation;
|
mModulation = modulation;
|
||||||
mIgnoreDataCalls = ignoreDataCalls;
|
mIgnoreDataCalls = ignoreDataCalls;
|
||||||
|
|
||||||
|
mPatchGroupManager = new PatchGroupManager(aliasList, getCallEventBroadcaster());
|
||||||
mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2,
|
mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2,
|
||||||
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
|
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
|
||||||
mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
|
mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
|
||||||
@@ -315,6 +315,8 @@ public class P25DecoderState extends DecoderState
|
|||||||
*/
|
*/
|
||||||
public void registerCallEvent(P25CallEvent event)
|
public void registerCallEvent(P25CallEvent event)
|
||||||
{
|
{
|
||||||
|
String channel = event.getChannel();
|
||||||
|
|
||||||
if(mChannelCallMap.containsKey(event.getChannel()))
|
if(mChannelCallMap.containsKey(event.getChannel()))
|
||||||
{
|
{
|
||||||
P25CallEvent previousEvent = mChannelCallMap.remove(event.getChannel());
|
P25CallEvent previousEvent = mChannelCallMap.remove(event.getChannel());
|
||||||
@@ -419,7 +421,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL)
|
mCurrentCallEvent = new P25CallEvent.Builder(CallEventType.CALL)
|
||||||
.aliasList(getAliasList())
|
.aliasList(getAliasList())
|
||||||
.channel(mCurrentChannel)
|
.channel(mCurrentChannel)
|
||||||
.details((hdu.isEncrypted() ? "ENCRYPTED " : ""))
|
.details((hdu.isEncryptedAudio() ? "ENCRYPTED " : ""))
|
||||||
.frequency(mCurrentChannelFrequency)
|
.frequency(mCurrentChannelFrequency)
|
||||||
.to(to)
|
.to(to)
|
||||||
.build();
|
.build();
|
||||||
@@ -1105,7 +1107,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
|
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
|
||||||
.aliasList(getAliasList())
|
.aliasList(getAliasList())
|
||||||
.channel(gvcu.getChannelA())
|
.channel(gvcu.getChannelA())
|
||||||
.details((gvcu.isEncrypted() ? "ENCRYPTED" : ""))
|
.details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : ""))
|
||||||
.frequency(gvcu.getDownlinkFrequencyA())
|
.frequency(gvcu.getDownlinkFrequencyA())
|
||||||
.to(userA)
|
.to(userA)
|
||||||
.build());
|
.build());
|
||||||
@@ -1120,7 +1122,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
|
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
|
||||||
.aliasList(getAliasList())
|
.aliasList(getAliasList())
|
||||||
.channel(gvcu.getChannelB())
|
.channel(gvcu.getChannelB())
|
||||||
.details((gvcu.isEncrypted() ? "ENCRYPTED" : ""))
|
.details((gvcu.isEncryptedLinkControlWord() ? "ENCRYPTED" : ""))
|
||||||
.frequency(gvcu.getDownlinkFrequencyB())
|
.frequency(gvcu.getDownlinkFrequencyB())
|
||||||
.to(userB)
|
.to(userB)
|
||||||
.build());
|
.build());
|
||||||
@@ -1149,7 +1151,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
|
broadcast(new P25CallEvent.Builder(CallEventType.CALL_DETECT)
|
||||||
.aliasList(getAliasList())
|
.aliasList(getAliasList())
|
||||||
.channel(gvcue.getTransmitChannel())
|
.channel(gvcue.getTransmitChannel())
|
||||||
.details((gvcue.isEncrypted() ? "ENCRYPTED" : ""))
|
.details((gvcue.isEncryptedLinkControlWord() ? "ENCRYPTED" : ""))
|
||||||
.frequency(gvcue.getDownlinkFrequency())
|
.frequency(gvcue.getDownlinkFrequency())
|
||||||
.to(group)
|
.to(group)
|
||||||
.build());
|
.build());
|
||||||
@@ -1185,7 +1187,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
if(mCurrentCallEvent.getDetails() == null)
|
if(mCurrentCallEvent.getDetails() == null)
|
||||||
{
|
{
|
||||||
mCurrentCallEvent.setDetails(
|
mCurrentCallEvent.setDetails(
|
||||||
(gvcuser.isEncrypted() ? "ENCRYPTED " : "") +
|
(gvcuser.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") +
|
||||||
(gvcuser.isEmergency() ? "EMERGENCY " : ""));
|
(gvcuser.isEmergency() ? "EMERGENCY " : ""));
|
||||||
|
|
||||||
broadcast(mCurrentCallEvent);
|
broadcast(mCurrentCallEvent);
|
||||||
@@ -1397,7 +1399,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
if(mCurrentCallEvent.getDetails() == null)
|
if(mCurrentCallEvent.getDetails() == null)
|
||||||
{
|
{
|
||||||
mCurrentCallEvent.setDetails(
|
mCurrentCallEvent.setDetails(
|
||||||
(tivcu.isEncrypted() ? "ENCRYPTED " : "") +
|
(tivcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") +
|
||||||
(tivcu.isEmergency() ? "EMERGENCY " : ""));
|
(tivcu.isEmergency() ? "EMERGENCY " : ""));
|
||||||
|
|
||||||
broadcast(mCurrentCallEvent);
|
broadcast(mCurrentCallEvent);
|
||||||
@@ -1480,7 +1482,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
if(mCurrentCallEvent.getDetails() == null)
|
if(mCurrentCallEvent.getDetails() == null)
|
||||||
{
|
{
|
||||||
mCurrentCallEvent.setDetails(
|
mCurrentCallEvent.setDetails(
|
||||||
(uuvcu.isEncrypted() ? "ENCRYPTED " : "") +
|
(uuvcu.isEncryptedLinkControlWord() ? "ENCRYPTED " : "") +
|
||||||
(uuvcu.isEmergency() ? "EMERGENCY " : ""));
|
(uuvcu.isEmergency() ? "EMERGENCY " : ""));
|
||||||
|
|
||||||
broadcast(mCurrentCallEvent);
|
broadcast(mCurrentCallEvent);
|
||||||
@@ -1557,6 +1559,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
break;
|
break;
|
||||||
case IDENTIFIER_UPDATE_NON_VUHF:
|
case IDENTIFIER_UPDATE_NON_VUHF:
|
||||||
case IDENTIFIER_UPDATE_VHF_UHF_BANDS:
|
case IDENTIFIER_UPDATE_VHF_UHF_BANDS:
|
||||||
|
case IDENTIFIER_UPDATE_TDMA:
|
||||||
IdentifierUpdate iu = (IdentifierUpdate)tsbk;
|
IdentifierUpdate iu = (IdentifierUpdate)tsbk;
|
||||||
|
|
||||||
if(!mBands.containsKey(iu.getIdentifier()))
|
if(!mBands.containsKey(iu.getIdentifier()))
|
||||||
@@ -1710,6 +1713,8 @@ public class P25DecoderState extends DecoderState
|
|||||||
.build());
|
.build());
|
||||||
break;
|
break;
|
||||||
case SNDCP_RF_CONFIRMED_DATA:
|
case SNDCP_RF_CONFIRMED_DATA:
|
||||||
|
if(pduc instanceof SNDCPUserData)
|
||||||
|
{
|
||||||
SNDCPUserData sud = (SNDCPUserData)pduc;
|
SNDCPUserData sud = (SNDCPUserData)pduc;
|
||||||
|
|
||||||
StringBuilder sbFrom = new StringBuilder();
|
StringBuilder sbFrom = new StringBuilder();
|
||||||
@@ -1737,6 +1742,12 @@ public class P25DecoderState extends DecoderState
|
|||||||
.from(sbFrom.toString())
|
.from(sbFrom.toString())
|
||||||
.to(pduc.getLogicalLinkID())
|
.to(pduc.getLogicalLinkID())
|
||||||
.build());
|
.build());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.error("Error - expected SNDCPUserData instance but class was: " + pduc.getClass() +
|
||||||
|
" and PDU Type:" + pduc.getPDUType().name());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SNDCP_RF_UNCONFIRMED_DATA:
|
case SNDCP_RF_UNCONFIRMED_DATA:
|
||||||
break;
|
break;
|
||||||
@@ -2719,6 +2730,9 @@ public class P25DecoderState extends DecoderState
|
|||||||
switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode())
|
switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode())
|
||||||
{
|
{
|
||||||
case PATCH_GROUP_CHANNEL_GRANT:
|
case PATCH_GROUP_CHANNEL_GRANT:
|
||||||
|
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
|
||||||
|
mPatchGroupManager.cleanupPatchGroups();
|
||||||
|
|
||||||
PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk;
|
PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk;
|
||||||
|
|
||||||
channel = pgvcg.getChannel();
|
channel = pgvcg.getChannel();
|
||||||
@@ -2732,6 +2746,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(pgvcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(pgvcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(pgvcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(pgvcg.getPriority());
|
details.append("PRI:").append(pgvcg.getPriority());
|
||||||
details.append(pgvcg.isEncryptedChannel() ? " ENCRYPTED" : "");
|
details.append(pgvcg.isEncryptedChannel() ? " ENCRYPTED" : "");
|
||||||
@@ -2750,10 +2765,15 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!pgvcg.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PATCH_GROUP_CHANNEL_GRANT_UPDATE:
|
case PATCH_GROUP_CHANNEL_GRANT_UPDATE:
|
||||||
|
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
|
||||||
|
mPatchGroupManager.cleanupPatchGroups();
|
||||||
|
|
||||||
PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk;
|
PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk;
|
||||||
|
|
||||||
channel = gvcgu.getChannel1();
|
channel = gvcgu.getChannel1();
|
||||||
@@ -2768,9 +2788,8 @@ public class P25DecoderState extends DecoderState
|
|||||||
P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL)
|
P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL)
|
||||||
.aliasList(getAliasList())
|
.aliasList(getAliasList())
|
||||||
.channel(channel)
|
.channel(channel)
|
||||||
.details((gvcgu.isEncrypted() ? "ENCRYPTED " : "") +
|
.details((gvcgu.isTDMAChannel1() ? "TDMA " : "") +
|
||||||
"PATCH UPDATE - GROUP 2:" + gvcgu.getPatchGroupAddress2() +
|
(gvcgu.isEncrypted() ? "ENCRYPTED " : ""))
|
||||||
" DN:" + gvcgu.getDownlinkFrequency2())
|
|
||||||
.frequency(gvcgu.getDownlinkFrequency1())
|
.frequency(gvcgu.getDownlinkFrequency1())
|
||||||
.to(to)
|
.to(to)
|
||||||
.build();
|
.build();
|
||||||
@@ -2779,40 +2798,46 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!gvcgu.isTDMAChannel1())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
|
|
||||||
|
String channel2 = gvcgu.getChannel2();
|
||||||
|
String to2 = gvcgu.getPatchGroupAddress2();
|
||||||
|
|
||||||
|
if(hasCallEvent(channel2, null, to2))
|
||||||
|
{
|
||||||
|
updateCallEvent(channel2, null, to2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
P25CallEvent event = new P25CallEvent.Builder(CallEventType.PATCH_GROUP_CALL)
|
||||||
|
.aliasList(getAliasList())
|
||||||
|
.channel(channel2)
|
||||||
|
.details((gvcgu.isTDMAChannel2() ? "TDMA " : "") +
|
||||||
|
(gvcgu.isEncrypted() ? "ENCRYPTED " : ""))
|
||||||
|
.frequency(gvcgu.getDownlinkFrequency2())
|
||||||
|
.to(to2)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
registerCallEvent(event);
|
||||||
|
broadcast(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!gvcgu.isTDMAChannel2())
|
||||||
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel2)));
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PATCH_GROUP_ADD:
|
case PATCH_GROUP_ADD:
|
||||||
PatchGroupAdd pga = (PatchGroupAdd)tsbk;
|
PatchGroupAdd pga = (PatchGroupAdd)tsbk;
|
||||||
|
mPatchGroupManager.updatePatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups());
|
||||||
if(addPatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups()))
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
sb.append("PATCH GROUP:").append(pga.getPatchGroupAddress());
|
|
||||||
sb.append(" ").append(pga.getPatchedTalkgroups());
|
|
||||||
|
|
||||||
broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_ADD)
|
|
||||||
.aliasList(getAliasList())
|
|
||||||
.details(sb.toString())
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case PATCH_GROUP_DELETE:
|
case PATCH_GROUP_DELETE:
|
||||||
PatchGroupDelete pgd = (PatchGroupDelete)tsbk;
|
PatchGroupDelete pgd = (PatchGroupDelete)tsbk;
|
||||||
|
mPatchGroupManager.removePatchGroup(pgd.getPatchGroupAddress());
|
||||||
if(removePatchGroup(pgd.getPatchGroupAddress(), pgd.getPatchedTalkgroups()))
|
|
||||||
{
|
|
||||||
StringBuilder sbpgd = new StringBuilder();
|
|
||||||
|
|
||||||
sbpgd.append("PATCH GROUP:").append(pgd.getPatchGroupAddress());
|
|
||||||
sbpgd.append(" ").append(pgd.getPatchedTalkgroups());
|
|
||||||
|
|
||||||
broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_DELETE)
|
|
||||||
.aliasList(getAliasList())
|
|
||||||
.details(sbpgd.toString())
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CCH_PLANNED_SHUTDOWN:
|
case CCH_PLANNED_SHUTDOWN:
|
||||||
if(!mControlChannelShutdownLogged)
|
if(!mControlChannelShutdownLogged)
|
||||||
@@ -2827,174 +2852,14 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new patch group or updates an existing patch group with the patched talkgroup entries.
|
|
||||||
*
|
|
||||||
* @param patchGroupID for the patch group
|
|
||||||
* @param talkgroupsToAdd to include in the patch group
|
|
||||||
* @return true if the patch group or the patch group map was updated
|
|
||||||
*/
|
|
||||||
private boolean addPatchGroup(String patchGroupID, List<String> talkgroupsToAdd)
|
|
||||||
{
|
|
||||||
boolean updated = false;
|
|
||||||
|
|
||||||
if(patchGroupID != null && !talkgroupsToAdd.isEmpty())
|
|
||||||
{
|
|
||||||
List<String> patchedTalkgroups = mPatchGroupMap.get(patchGroupID);
|
|
||||||
|
|
||||||
if(patchedTalkgroups == null)
|
|
||||||
{
|
|
||||||
patchedTalkgroups = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
for(String talkgroupToAdd : talkgroupsToAdd)
|
|
||||||
{
|
|
||||||
//Exclude the patch group ID if it is included in the patched talkgroup list so that we don't have
|
|
||||||
//an infinite loop situation
|
|
||||||
if(talkgroupToAdd.equals(patchGroupID))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!patchedTalkgroups.contains(talkgroupToAdd))
|
|
||||||
{
|
|
||||||
patchedTalkgroups.add(talkgroupToAdd);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(updated)
|
|
||||||
{
|
|
||||||
mPatchGroupMap.put(patchGroupID, patchedTalkgroups);
|
|
||||||
updatePatchAlias(patchGroupID, patchedTalkgroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the patched talkgroups from the patch group and removes the patch group from the patch group map if
|
|
||||||
* there are no more patched talkgroups in the patch group.
|
|
||||||
*
|
|
||||||
* @param patchGroupID for the patch group
|
|
||||||
* @param talkgroupsToRemove to remove from the patch group
|
|
||||||
* @return true if the patch group or the patch group map was updated
|
|
||||||
*/
|
|
||||||
private boolean removePatchGroup(String patchGroupID, List<String> talkgroupsToRemove)
|
|
||||||
{
|
|
||||||
boolean updated = false;
|
|
||||||
|
|
||||||
if(patchGroupID != null && !talkgroupsToRemove.isEmpty())
|
|
||||||
{
|
|
||||||
List<String> patchedTalkgroups = mPatchGroupMap.get(patchGroupID);
|
|
||||||
|
|
||||||
if(patchedTalkgroups != null)
|
|
||||||
{
|
|
||||||
for(String talkgroupToRemove : talkgroupsToRemove)
|
|
||||||
{
|
|
||||||
if(patchedTalkgroups.contains(talkgroupToRemove))
|
|
||||||
{
|
|
||||||
patchedTalkgroups.remove(talkgroupToRemove);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patchedTalkgroups.isEmpty())
|
|
||||||
{
|
|
||||||
mPatchGroupMap.remove(patchGroupID);
|
|
||||||
removePatchGroupAlias(patchGroupID);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updatePatchAlias(patchGroupID, patchedTalkgroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the
|
|
||||||
* patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and
|
|
||||||
* replaced with the aliases corresponding to the patched talkgroup aliases.
|
|
||||||
*
|
|
||||||
* @param patchGroupID for the patch group
|
|
||||||
* @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups
|
|
||||||
*/
|
|
||||||
private void updatePatchAlias(String patchGroupID, List<String> patchedTalkgroups)
|
|
||||||
{
|
|
||||||
PatchGroupAlias patchGroupAlias = null;
|
|
||||||
|
|
||||||
//Check for an existing alias for the patch talkgroup - do not include wildcard aliases
|
|
||||||
Alias existingAlias = getAliasList().getTalkgroupAlias(patchGroupID, false);
|
|
||||||
|
|
||||||
if(existingAlias instanceof PatchGroupAlias)
|
|
||||||
{
|
|
||||||
patchGroupAlias = (PatchGroupAlias)existingAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
patchGroupAlias = new PatchGroupAlias();
|
|
||||||
|
|
||||||
patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID));
|
|
||||||
|
|
||||||
if(existingAlias != null)
|
|
||||||
{
|
|
||||||
getAliasList().removeAlias(existingAlias);
|
|
||||||
|
|
||||||
patchGroupAlias.setPatchGroupAlias(existingAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAliasList().addAlias(patchGroupAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(patchGroupAlias != null)
|
|
||||||
{
|
|
||||||
patchGroupAlias.setPatchedTalkgroupIDs(patchedTalkgroups);
|
|
||||||
|
|
||||||
patchGroupAlias.clearPatchedAliases();
|
|
||||||
|
|
||||||
for(String patchedTalkgroup : patchedTalkgroups)
|
|
||||||
{
|
|
||||||
Alias patchedAlias = getAliasList().getTalkgroupAlias(patchedTalkgroup);
|
|
||||||
|
|
||||||
if(patchedAlias != null)
|
|
||||||
{
|
|
||||||
patchGroupAlias.addPatchedAlias(patchedAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the temporary patch group alias from the alias list
|
|
||||||
*/
|
|
||||||
private void removePatchGroupAlias(String patchGroupID)
|
|
||||||
{
|
|
||||||
Alias alias = getAliasList().getTalkgroupAlias(patchGroupID);
|
|
||||||
|
|
||||||
if(alias instanceof PatchGroupAlias)
|
|
||||||
{
|
|
||||||
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
|
|
||||||
|
|
||||||
getAliasList().removeAlias(patchGroupAlias);
|
|
||||||
|
|
||||||
//Replace our temporary patch group alias with the original alias for the patch group ID
|
|
||||||
if(patchGroupAlias.hasPatchGroupAlias())
|
|
||||||
{
|
|
||||||
getAliasList().addAlias(patchGroupAlias.getPatchGroupAlias());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a traffic channel allocation message
|
* Process a traffic channel allocation message
|
||||||
*/
|
*/
|
||||||
private void processTSBKChannelGrant(TSBKMessage message)
|
private void processTSBKChannelGrant(TSBKMessage message)
|
||||||
{
|
{
|
||||||
|
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
|
||||||
|
mPatchGroupManager.cleanupPatchGroups();
|
||||||
|
|
||||||
String channel = null;
|
String channel = null;
|
||||||
String from = null;
|
String from = null;
|
||||||
String to = null;
|
String to = null;
|
||||||
@@ -3015,6 +2880,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(gdcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(gdcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(gdcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(gdcg.getPriority()).append(" ");
|
details.append("PRI:").append(gdcg.getPriority()).append(" ");
|
||||||
details.append(gdcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(gdcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3033,7 +2899,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mIgnoreDataCalls)
|
if(!mIgnoreDataCalls && !gdcg.isTDMAChannel())
|
||||||
{
|
{
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
broadcast(new TrafficChannelAllocationEvent(this,
|
||||||
mChannelCallMap.get(channel)));
|
mChannelCallMap.get(channel)));
|
||||||
@@ -3053,6 +2919,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(gvcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(gvcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(gvcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(gvcg.getPriority()).append(" ");
|
details.append("PRI:").append(gvcg.getPriority()).append(" ");
|
||||||
details.append(gvcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(gvcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3071,12 +2938,13 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!gvcg.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GROUP_VOICE_CHANNEL_GRANT_UPDATE:
|
case GROUP_VOICE_CHANNEL_GRANT_UPDATE:
|
||||||
GroupVoiceChannelGrantUpdate gvcgu =
|
GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)message;
|
||||||
(GroupVoiceChannelGrantUpdate)message;
|
|
||||||
|
|
||||||
channel = gvcgu.getChannel1();
|
channel = gvcgu.getChannel1();
|
||||||
from = null;
|
from = null;
|
||||||
@@ -3089,6 +2957,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(gvcgu.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(gvcgu.isEmergency() ? "EMERGENCY " : "");
|
details.append(gvcgu.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(gvcgu.getPriority()).append(" ");
|
details.append("PRI:").append(gvcgu.getPriority()).append(" ");
|
||||||
details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3096,7 +2965,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL)
|
P25CallEvent event = new P25CallEvent.Builder(CallEventType.GROUP_CALL)
|
||||||
.aliasList(getAliasList())
|
.aliasList(getAliasList())
|
||||||
.channel(channel)
|
|
||||||
.channel(gvcgu.getChannel1())
|
.channel(gvcgu.getChannel1())
|
||||||
.details(details.toString())
|
.details(details.toString())
|
||||||
.frequency(gvcgu.getDownlinkFrequency1())
|
.frequency(gvcgu.getDownlinkFrequency1())
|
||||||
@@ -3108,8 +2976,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!gvcgu.isTDMAChannel1())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
|
|
||||||
if(gvcgu.hasChannelNumber2())
|
if(gvcgu.hasChannelNumber2())
|
||||||
{
|
{
|
||||||
@@ -3123,6 +2993,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(gvcgu.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(gvcgu.isEmergency() ? "EMERGENCY " : "");
|
details.append(gvcgu.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(gvcgu.getPriority()).append(" ");
|
details.append("PRI:").append(gvcgu.getPriority()).append(" ");
|
||||||
details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(gvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3140,8 +3011,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event2);
|
broadcast(event2);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!gvcgu.isTDMAChannel2())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:
|
case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:
|
||||||
@@ -3160,6 +3033,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(gvcgue.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(gvcgue.isEmergency() ? "EMERGENCY " : "");
|
details.append(gvcgue.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(gvcgue.getPriority()).append(" ");
|
details.append("PRI:").append(gvcgue.getPriority()).append(" ");
|
||||||
details.append(gvcgue.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(gvcgue.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3178,8 +3052,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!gvcgue.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case INDIVIDUAL_DATA_CHANNEL_GRANT:
|
case INDIVIDUAL_DATA_CHANNEL_GRANT:
|
||||||
IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message;
|
IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message;
|
||||||
@@ -3195,6 +3071,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(idcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(idcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(idcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(idcg.getPriority()).append(" ");
|
details.append("PRI:").append(idcg.getPriority()).append(" ");
|
||||||
details.append(idcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(idcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3213,10 +3090,9 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mIgnoreDataCalls)
|
if(!mIgnoreDataCalls && !idcg.isTDMAChannel())
|
||||||
{
|
{
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
mChannelCallMap.get(channel)));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SNDCP_DATA_CHANNEL_GRANT:
|
case SNDCP_DATA_CHANNEL_GRANT:
|
||||||
@@ -3233,6 +3109,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(sdcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(sdcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(sdcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append(sdcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(sdcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
details.append(sdcg.getSessionMode().name());
|
details.append(sdcg.getSessionMode().name());
|
||||||
@@ -3250,15 +3127,13 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!mIgnoreDataCalls)
|
if(!mIgnoreDataCalls && !sdcg.isTDMAChannel())
|
||||||
{
|
{
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
mChannelCallMap.get(channel)));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT:
|
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT:
|
||||||
TelephoneInterconnectVoiceChannelGrant tivcg =
|
TelephoneInterconnectVoiceChannelGrant tivcg = (TelephoneInterconnectVoiceChannelGrant)message;
|
||||||
(TelephoneInterconnectVoiceChannelGrant)message;
|
|
||||||
|
|
||||||
channel = tivcg.getChannel();
|
channel = tivcg.getChannel();
|
||||||
from = null;
|
from = null;
|
||||||
@@ -3273,6 +3148,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(tivcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(tivcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(tivcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(tivcg.getPriority()).append(" ");
|
details.append("PRI:").append(tivcg.getPriority()).append(" ");
|
||||||
details.append(tivcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(tivcg.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3293,15 +3169,15 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!tivcg.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE:
|
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE:
|
||||||
TelephoneInterconnectVoiceChannelGrantUpdate tivcgu =
|
TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = (TelephoneInterconnectVoiceChannelGrantUpdate)message;
|
||||||
(TelephoneInterconnectVoiceChannelGrantUpdate)message;
|
|
||||||
|
|
||||||
channel = tivcgu.getChannelIdentifier() + "-" +
|
channel = tivcgu.getChannelIdentifier() + "-" + tivcgu.getChannelNumber();
|
||||||
tivcgu.getChannelNumber();
|
|
||||||
from = null;
|
from = null;
|
||||||
|
|
||||||
/* Address is ambiguous and could mean either source or target,
|
/* Address is ambiguous and could mean either source or target,
|
||||||
@@ -3315,6 +3191,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(tivcgu.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(tivcgu.isEmergency() ? "EMERGENCY " : "");
|
details.append(tivcgu.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(tivcgu.getPriority()).append(" ");
|
details.append("PRI:").append(tivcgu.getPriority()).append(" ");
|
||||||
details.append(tivcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(tivcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3335,15 +3212,15 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!tivcgu.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT:
|
case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT:
|
||||||
UnitToUnitVoiceChannelGrant uuvcg =
|
UnitToUnitVoiceChannelGrant uuvcg = (UnitToUnitVoiceChannelGrant)message;
|
||||||
(UnitToUnitVoiceChannelGrant)message;
|
|
||||||
|
|
||||||
channel = uuvcg.getChannelIdentifier() + "-" +
|
channel = uuvcg.getChannelIdentifier() + "-" + uuvcg.getChannelNumber();
|
||||||
uuvcg.getChannelNumber();
|
|
||||||
from = uuvcg.getSourceAddress();
|
from = uuvcg.getSourceAddress();
|
||||||
to = uuvcg.getTargetAddress();
|
to = uuvcg.getTargetAddress();
|
||||||
|
|
||||||
@@ -3354,6 +3231,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(uuvcg.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(uuvcg.isEmergency() ? "EMERGENCY " : "");
|
details.append(uuvcg.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(uuvcg.getPriority()).append(" ");
|
details.append("PRI:").append(uuvcg.getPriority()).append(" ");
|
||||||
details.append(uuvcg.isEncryptedChannel() ? " ENCRYPTED " : "");
|
details.append(uuvcg.isEncryptedChannel() ? " ENCRYPTED " : "");
|
||||||
@@ -3373,15 +3251,15 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!uuvcg.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE:
|
case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE:
|
||||||
UnitToUnitVoiceChannelGrantUpdate uuvcgu =
|
UnitToUnitVoiceChannelGrantUpdate uuvcgu = (UnitToUnitVoiceChannelGrantUpdate)message;
|
||||||
(UnitToUnitVoiceChannelGrantUpdate)message;
|
|
||||||
|
|
||||||
channel = uuvcgu.getChannelIdentifier() + "-" +
|
channel = uuvcgu.getChannelIdentifier() + "-" + uuvcgu.getChannelNumber();
|
||||||
uuvcgu.getChannelNumber();
|
|
||||||
from = uuvcgu.getSourceAddress();
|
from = uuvcgu.getSourceAddress();
|
||||||
to = uuvcgu.getTargetAddress();
|
to = uuvcgu.getTargetAddress();
|
||||||
|
|
||||||
@@ -3392,6 +3270,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder details = new StringBuilder();
|
StringBuilder details = new StringBuilder();
|
||||||
|
details.append(uuvcgu.isTDMAChannel() ? "TDMA " : "");
|
||||||
details.append(uuvcgu.isEmergency() ? "EMERGENCY " : "");
|
details.append(uuvcgu.isEmergency() ? "EMERGENCY " : "");
|
||||||
details.append("PRI:").append(uuvcgu.getPriority()).append(" ");
|
details.append("PRI:").append(uuvcgu.getPriority()).append(" ");
|
||||||
details.append(uuvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
details.append(uuvcgu.isEncryptedChannel() ? "ENCRYPTED " : "");
|
||||||
@@ -3411,8 +3290,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast(event);
|
broadcast(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast(new TrafficChannelAllocationEvent(this,
|
if(!uuvcgu.isTDMAChannel())
|
||||||
mChannelCallMap.get(channel)));
|
{
|
||||||
|
broadcast(new TrafficChannelAllocationEvent(this, mChannelCallMap.get(channel)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -3522,29 +3403,25 @@ public class P25DecoderState extends DecoderState
|
|||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append("Activity Summary\n");
|
sb.append("Activity Summary - Decoder:P25 ").append(getModulation().getLabel()).append("\n");
|
||||||
sb.append("Decoder:\tP25\n");
|
sb.append(DIVIDER1);
|
||||||
sb.append("===================== THIS SITE ======================");
|
sb.append("SITE ");
|
||||||
|
|
||||||
if(mNetworkStatus != null)
|
if(mNetworkStatus != null)
|
||||||
{
|
{
|
||||||
sb.append("\nNAC:\t" + mNetworkStatus.getNAC());
|
sb.append("NAC:" + mNetworkStatus.getNAC());
|
||||||
sb.append("\nWACN-SYS:\t" + mNetworkStatus.getWACN());
|
sb.append("h WACN:" + mNetworkStatus.getWACN());
|
||||||
sb.append("-" + mNetworkStatus.getSystemID());
|
sb.append("h SYS:" + mNetworkStatus.getSystemID());
|
||||||
sb.append(" [");
|
sb.append("h [").append(mNetworkStatus.getNetworkCallsign()).append("]");
|
||||||
sb.append(mNetworkStatus.getNetworkCallsign());
|
sb.append(" LRA:" + mNetworkStatus.getLocationRegistrationArea());
|
||||||
sb.append("]");
|
|
||||||
sb.append("\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea());
|
|
||||||
}
|
}
|
||||||
else if(mNetworkStatusExtended != null)
|
else if(mNetworkStatusExtended != null)
|
||||||
{
|
{
|
||||||
sb.append("\nNAC:\t" + mNetworkStatusExtended.getNAC());
|
sb.append("NAC:" + mNetworkStatusExtended.getNAC());
|
||||||
sb.append("\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN());
|
sb.append("h WACN:" + mNetworkStatusExtended.getWACN());
|
||||||
sb.append("-" + mNetworkStatusExtended.getSystemID());
|
sb.append("h SYS:" + mNetworkStatusExtended.getSystemID());
|
||||||
sb.append(" [");
|
sb.append("h [").append(mNetworkStatusExtended.getNetworkCallsign()).append("]");
|
||||||
sb.append(mNetworkStatusExtended.getNetworkCallsign());
|
sb.append(" LRA:" + mNetworkStatusExtended.getLocationRegistrationArea());
|
||||||
sb.append("]");
|
|
||||||
sb.append("\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String site = null;
|
String site = null;
|
||||||
@@ -3555,11 +3432,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
else if(mRFSSStatusMessageExtended != null)
|
else if(mRFSSStatusMessageExtended != null)
|
||||||
{
|
{
|
||||||
site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" +
|
site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + mRFSSStatusMessageExtended.getSiteID();
|
||||||
mRFSSStatusMessageExtended.getSiteID();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\nRFSS-SITE:\t" + site);
|
sb.append("h RFSS-SITE:").append(site).append("h");
|
||||||
|
|
||||||
if(hasAliasList())
|
if(hasAliasList())
|
||||||
{
|
{
|
||||||
@@ -3571,121 +3447,120 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sb.append("\n").append(DIVIDER2);
|
||||||
|
|
||||||
if(mNetworkStatus != null)
|
if(mNetworkStatus != null)
|
||||||
{
|
{
|
||||||
sb.append("\nSERVICES:\t" + SystemService.toString(
|
sb.append("SERVICES: " + SystemService.toString(mNetworkStatus.getSystemServiceClass())).append("\n");
|
||||||
mNetworkStatus.getSystemServiceClass()));
|
sb.append(DIVIDER2);
|
||||||
sb.append("\nPCCH:\tDNLINK " + mFrequencyFormatter.format(
|
sb.append("PCCH DOWNLINK ")
|
||||||
(double)mNetworkStatus.getDownlinkFrequency() / 1E6d) +
|
.append(mFrequencyFormatter.format((double)mNetworkStatus.getDownlinkFrequency() / 1E6d))
|
||||||
" [" + mNetworkStatus.getIdentifier() + "-" +
|
.append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel())
|
||||||
mNetworkStatus.getChannel() + "]\n");
|
.append("] UPLINK ")
|
||||||
sb.append("\tUPLINK " + mFrequencyFormatter.format(
|
.append(mFrequencyFormatter.format((double)mNetworkStatus.getUplinkFrequency() / 1E6d))
|
||||||
(double)mNetworkStatus.getUplinkFrequency() / 1E6d) + " [" +
|
.append(" [").append(mNetworkStatus.getIdentifier()).append("-").append(mNetworkStatus.getChannel())
|
||||||
mNetworkStatus.getIdentifier() + "-" +
|
.append("]\n");
|
||||||
mNetworkStatus.getChannel() + "]\n");
|
|
||||||
}
|
}
|
||||||
else if(mNetworkStatusExtended != null)
|
else if(mNetworkStatusExtended != null)
|
||||||
{
|
{
|
||||||
sb.append("\nSERVICES:\t" + SystemService.toString(
|
sb.append("\nSERVICES:").append(SystemService.toString(mNetworkStatusExtended.getSystemServiceClass()))
|
||||||
mNetworkStatusExtended.getSystemServiceClass()));
|
.append("\n");
|
||||||
sb.append("\nPCCH:\tDNLINK " + mFrequencyFormatter.format(
|
sb.append(DIVIDER2);
|
||||||
(double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d) +
|
sb.append("PCCH DOWNLINK ")
|
||||||
" [" + mNetworkStatusExtended.getTransmitIdentifier() + "-" +
|
.append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d))
|
||||||
mNetworkStatusExtended.getTransmitChannel() + "]\n");
|
.append(" [").append(mNetworkStatusExtended.getTransmitIdentifier()).append("-")
|
||||||
sb.append("\tUPLINK " + mFrequencyFormatter.format(
|
.append(mNetworkStatusExtended.getTransmitChannel()).append("] UPLINK ")
|
||||||
(double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d) +
|
.append(mFrequencyFormatter.format((double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d))
|
||||||
" [" + mNetworkStatusExtended.getReceiveIdentifier() + "-" +
|
.append(" [").append(mNetworkStatusExtended.getReceiveIdentifier()).append("-")
|
||||||
mNetworkStatusExtended.getReceiveChannel() + "]");
|
.append(mNetworkStatusExtended.getReceiveChannel()).append("]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mSecondaryControlChannels.isEmpty())
|
if(mSecondaryControlChannels.isEmpty())
|
||||||
{
|
{
|
||||||
sb.append("\nSCCH:\tNONE");
|
sb.append("SCCH: NONE\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast
|
for(module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sec : mSecondaryControlChannels)
|
||||||
sec : mSecondaryControlChannels)
|
|
||||||
{
|
{
|
||||||
sb.append("\nSCCH:\tDNLINK " + mFrequencyFormatter.format(
|
sb.append("SCCH DOWNLINK ")
|
||||||
(double)sec.getDownlinkFrequency1() / 1E6d) +
|
.append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency1() / 1E6d))
|
||||||
" [" + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n");
|
.append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("] UPLINK ")
|
||||||
sb.append("\tUPLINK " + mFrequencyFormatter.format(
|
.append(mFrequencyFormatter.format((double)sec.getUplinkFrequency1() / 1E6d))
|
||||||
(double)sec.getUplinkFrequency1() / 1E6d) + " [" +
|
.append(" [").append(sec.getIdentifier1()).append("-").append(sec.getChannel1()).append("]");
|
||||||
sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n");
|
|
||||||
|
|
||||||
if(sec.hasChannel2())
|
if(sec.hasChannel2())
|
||||||
{
|
{
|
||||||
sb.append("\nSCCH:\tDNLINK " + mFrequencyFormatter.format(
|
sb.append(" SCCH 2 DOWNLINK ")
|
||||||
(double)sec.getDownlinkFrequency2() / 1E6d) +
|
.append(mFrequencyFormatter.format((double)sec.getDownlinkFrequency2() / 1E6d))
|
||||||
" [" + sec.getIdentifier2() + "-" + sec.getChannel2() + "]\n");
|
.append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2())
|
||||||
sb.append("\tUPLINK " + mFrequencyFormatter.format(
|
.append("] UPLINK ").append(mFrequencyFormatter.format((double)sec.getUplinkFrequency2() / 1E6d))
|
||||||
(double)sec.getUplinkFrequency2() / 1E6d) + " [" +
|
.append(" [").append(sec.getIdentifier2()).append("-").append(sec.getChannel2()).append("]");
|
||||||
sec.getIdentifier2() + "-" + sec.getChannel2() + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(mSNDCPDataChannel != null)
|
if(mSNDCPDataChannel != null)
|
||||||
{
|
{
|
||||||
sb.append("\nSNDCP:");
|
sb.append("SNDCP DOWNLINK ")
|
||||||
sb.append("\tDNLINK " + mFrequencyFormatter.format(
|
.append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D))
|
||||||
(double)mSNDCPDataChannel.getDownlinkFrequency() / 1E6D) +
|
.append(" [").append(mSNDCPDataChannel.getTransmitChannel()).append("]").append(" UPLINK ")
|
||||||
" [" + mSNDCPDataChannel.getTransmitChannel() + "]");
|
.append(mFrequencyFormatter.format((double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D))
|
||||||
sb.append("\tUPLINK " + mFrequencyFormatter.format(
|
.append(" [").append(mSNDCPDataChannel.getReceiveChannel()).append("]\n");
|
||||||
(double)mSNDCPDataChannel.getUplinkFrequency() / 1E6D) +
|
|
||||||
" [" + mSNDCPDataChannel.getReceiveChannel() + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mProtectionParameterBroadcast != null)
|
if(mProtectionParameterBroadcast != null)
|
||||||
{
|
{
|
||||||
sb.append("\nENCRYPTION:");
|
sb.append(DIVIDER2);
|
||||||
sb.append("\nTYPE:\t" + mProtectionParameterBroadcast
|
sb.append("ENCRYPTION TYPE:").append(mProtectionParameterBroadcast.getEncryptionType().name());
|
||||||
.getEncryptionType().name());
|
sb.append(" ALGORITHM:").append(mProtectionParameterBroadcast.getAlgorithmID());
|
||||||
sb.append("\nALGORITHM:\t" + mProtectionParameterBroadcast
|
sb.append(" KEY:").append(mProtectionParameterBroadcast.getKeyID());
|
||||||
.getAlgorithmID());
|
sb.append(" INBOUND IV:").append(mProtectionParameterBroadcast.getInboundInitializationVector());
|
||||||
sb.append("\nKEY:\t" + mProtectionParameterBroadcast.getKeyID());
|
sb.append(" OUTBOUND IV:").append(mProtectionParameterBroadcast.getOutboundInitializationVector());
|
||||||
sb.append("\nINBOUND IV:\t" + mProtectionParameterBroadcast
|
sb.append("\n");
|
||||||
.getInboundInitializationVector());
|
|
||||||
sb.append("\nOUTBOUND IV:\t" + mProtectionParameterBroadcast
|
|
||||||
.getOutboundInitializationVector());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Integer> identifiers = new ArrayList<>(mBands.keySet());
|
List<Integer> identifiers = new ArrayList<>(mBands.keySet());
|
||||||
|
|
||||||
Collections.sort(identifiers);
|
Collections.sort(identifiers);
|
||||||
|
|
||||||
sb.append("\nFREQUENCY BANDS:");
|
sb.append(DIVIDER2).append("FREQUENCY BANDS:\n");
|
||||||
for(Integer id : identifiers)
|
for(Integer id : identifiers)
|
||||||
{
|
{
|
||||||
IBandIdentifier band = mBands.get(id);
|
IBandIdentifier band = mBands.get(id);
|
||||||
|
sb.append(band.toString()).append("\n");
|
||||||
sb.append("\n\t" + id);
|
// sb.append(" ").append(id);
|
||||||
sb.append("- BASE: " + mFrequencyFormatter.format(
|
// sb.append(" - BASE: " + mFrequencyFormatter.format(
|
||||||
(double)band.getBaseFrequency() / 1E6d));
|
// (double)band.getBaseFrequency() / 1E6d));
|
||||||
sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format(
|
// sb.append(" CHANNEL SIZE: " + mFrequencyFormatter.format(
|
||||||
(double)band.getChannelSpacing() / 1E6d));
|
// (double)band.getChannelSpacing() / 1E6d));
|
||||||
sb.append(" UPLINK OFFSET: " + mFrequencyFormatter.format(
|
// sb.append(" UPLINK OFFSET: " + mFrequencyFormatter.format(
|
||||||
(double)band.getTransmitOffset() / 1E6D));
|
// (double)band.getTransmitOffset() / 1E6D));
|
||||||
|
// sb.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\n\n=================== NEIGHBORS ======================");
|
sb.append(DIVIDER2).append("NEIGHBOR SITES: ");
|
||||||
|
|
||||||
if(mNeighborMap.isEmpty())
|
if(mNeighborMap.isEmpty())
|
||||||
{
|
{
|
||||||
sb.append("\n\tNONE\n");
|
sb.append("NONE\n");
|
||||||
sb.append("\n----------------------------------------------------");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(IAdjacentSite neighbor : mNeighborMap.values())
|
for(IAdjacentSite neighbor : mNeighborMap.values())
|
||||||
{
|
{
|
||||||
sb.append("\nNAC:\t" + ((P25Message)neighbor).getNAC());
|
sb.append("\n");
|
||||||
sb.append("\nSYSTEM:\t" + neighbor.getSystemID());
|
sb.append("NAC:").append(((P25Message)neighbor).getNAC());
|
||||||
sb.append("\nLRA:\t" + neighbor.getLRA());
|
sb.append("h SYSTEM:" + neighbor.getSystemID());
|
||||||
|
sb.append("h LRA:" + neighbor.getLRA());
|
||||||
|
|
||||||
String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID();
|
String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID();
|
||||||
sb.append("\nRFSS-SITE:\t" + neighborID);
|
sb.append("h RFSS-SITE:" + neighborID);
|
||||||
|
|
||||||
|
sb.append("h ");
|
||||||
|
|
||||||
if(hasAliasList())
|
if(hasAliasList())
|
||||||
{
|
{
|
||||||
@@ -3693,18 +3568,16 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
if(siteAlias != null)
|
if(siteAlias != null)
|
||||||
{
|
{
|
||||||
sb.append(" " + siteAlias.getName());
|
sb.append(siteAlias.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\nPCCH:\tDNLINK " + mFrequencyFormatter.format(
|
sb.append("\n PCCH: DOWNLINK ")
|
||||||
(double)neighbor.getDownlinkFrequency() / 1E6d) +
|
.append(mFrequencyFormatter.format((double)neighbor.getDownlinkFrequency() / 1E6d))
|
||||||
" [" + neighbor.getDownlinkChannel() + "]");
|
.append(" [").append(neighbor.getDownlinkChannel()).append("] UPLINK:")
|
||||||
sb.append("\n\tUPLINK:" + mFrequencyFormatter.format(
|
.append(mFrequencyFormatter.format((double)neighbor.getUplinkFrequency() / 1E6d))
|
||||||
(double)neighbor.getUplinkFrequency() / 1E6d) +
|
.append(" [").append(neighbor.getDownlinkChannel()).append("] SERVICES: ")
|
||||||
" [" + neighbor.getDownlinkChannel() + "]\n");
|
.append(neighbor.getSystemServiceClass());
|
||||||
sb.append("\nSERVICES:\t" + neighbor.getSystemServiceClass());
|
|
||||||
sb.append("\n----------------------------------------------------");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -689,8 +689,7 @@ public class P25MessageFramer implements Listener<Dibit>
|
|||||||
private LSMDemodulator mDemodulator;
|
private LSMDemodulator mDemodulator;
|
||||||
private double mCorrection;
|
private double mCorrection;
|
||||||
|
|
||||||
public CostasPhaseErrorDetector( FrameSync frameSync,
|
public CostasPhaseErrorDetector( FrameSync frameSync, LSMDemodulator demodulator, double correction )
|
||||||
LSMDemodulator demodulator, double correction )
|
|
||||||
{
|
{
|
||||||
super( frameSync.getSync() );
|
super( frameSync.getSync() );
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014,2015 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package module.decode.p25;
|
package module.decode.p25;
|
||||||
|
|
||||||
@@ -41,18 +42,15 @@ import dsp.filter.FilterFactory;
|
|||||||
import dsp.filter.Window.WindowType;
|
import dsp.filter.Window.WindowType;
|
||||||
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
|
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
|
||||||
|
|
||||||
public class P25_C4FMDecoder extends P25Decoder
|
public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener
|
||||||
implements IFrequencyChangeListener, IFrequencyChangeProvider,IFilteredRealBufferListener
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class );
|
private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class );
|
||||||
|
|
||||||
/* Instrumentation Taps */
|
/* Instrumentation Taps */
|
||||||
private static final String INSTRUMENT_FILTER_OUTPUT = "Tap Point: Pre-Filter Output";
|
|
||||||
private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output";
|
private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output";
|
||||||
private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output";
|
private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output";
|
||||||
|
|
||||||
private List<TapGroup> mAvailableTaps;
|
private List<TapGroup> mAvailableTaps;
|
||||||
private RealFIRFilter_RB_RB mC4FMPreFilter;
|
|
||||||
private C4FMSymbolFilter mSymbolFilter;
|
private C4FMSymbolFilter mSymbolFilter;
|
||||||
private C4FMSlicer mC4FMSlicer;
|
private C4FMSlicer mC4FMSlicer;
|
||||||
private P25MessageFramer mMessageFramer;
|
private P25MessageFramer mMessageFramer;
|
||||||
@@ -74,14 +72,8 @@ public class P25_C4FMDecoder extends P25Decoder
|
|||||||
{
|
{
|
||||||
super( aliasList );
|
super( aliasList );
|
||||||
|
|
||||||
/* Filter demodulated sample buffers */
|
|
||||||
float[] filter = FilterFactory.getLowPass( 48000, 2500, 4000, 80, WindowType.HANNING, true );
|
|
||||||
|
|
||||||
mC4FMPreFilter = new RealFIRFilter_RB_RB( filter, 1.0f );
|
|
||||||
|
|
||||||
/* Shape gain and frequency offsets to optimize sample stream */
|
/* Shape gain and frequency offsets to optimize sample stream */
|
||||||
mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum );
|
mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum );
|
||||||
mC4FMPreFilter.setListener( mSymbolFilter );
|
|
||||||
|
|
||||||
/* Convert samples to symbols */
|
/* Convert samples to symbols */
|
||||||
mC4FMSlicer = new C4FMSlicer();
|
mC4FMSlicer = new C4FMSlicer();
|
||||||
@@ -100,9 +92,6 @@ public class P25_C4FMDecoder extends P25Decoder
|
|||||||
{
|
{
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
||||||
mC4FMPreFilter.dispose();
|
|
||||||
mC4FMPreFilter = null;
|
|
||||||
|
|
||||||
mSymbolFilter.dispose();
|
mSymbolFilter.dispose();
|
||||||
mSymbolFilter = null;
|
mSymbolFilter = null;
|
||||||
|
|
||||||
@@ -121,7 +110,7 @@ public class P25_C4FMDecoder extends P25Decoder
|
|||||||
@Override
|
@Override
|
||||||
public Listener<RealBuffer> getFilteredRealBufferListener()
|
public Listener<RealBuffer> getFilteredRealBufferListener()
|
||||||
{
|
{
|
||||||
return mC4FMPreFilter;
|
return mSymbolFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,8 +125,6 @@ public class P25_C4FMDecoder extends P25Decoder
|
|||||||
|
|
||||||
TapGroup group = new TapGroup( "P25 C4FM Decoder" );
|
TapGroup group = new TapGroup( "P25 C4FM Decoder" );
|
||||||
|
|
||||||
group.add( new FloatBufferTap( INSTRUMENT_FILTER_OUTPUT, 0, 1.0f ) );
|
|
||||||
|
|
||||||
group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) );
|
group.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) );
|
||||||
group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) );
|
group.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) );
|
||||||
|
|
||||||
@@ -165,11 +152,6 @@ public class P25_C4FMDecoder extends P25Decoder
|
|||||||
|
|
||||||
switch( tap.getName() )
|
switch( tap.getName() )
|
||||||
{
|
{
|
||||||
case INSTRUMENT_FILTER_OUTPUT:
|
|
||||||
FloatBufferTap filterOutput = (FloatBufferTap)tap;
|
|
||||||
mC4FMPreFilter.setListener( filterOutput );
|
|
||||||
filterOutput.setListener( mSymbolFilter );
|
|
||||||
break;
|
|
||||||
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
|
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
|
||||||
FloatTap symbolTap = (FloatTap)tap;
|
FloatTap symbolTap = (FloatTap)tap;
|
||||||
mSymbolFilter.setListener( symbolTap );
|
mSymbolFilter.setListener( symbolTap );
|
||||||
@@ -201,9 +183,6 @@ public class P25_C4FMDecoder extends P25Decoder
|
|||||||
|
|
||||||
switch( tap.getName() )
|
switch( tap.getName() )
|
||||||
{
|
{
|
||||||
case INSTRUMENT_FILTER_OUTPUT:
|
|
||||||
mC4FMPreFilter.setListener( mSymbolFilter );
|
|
||||||
break;
|
|
||||||
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
|
case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT:
|
||||||
mSymbolFilter.setListener( mC4FMSlicer );
|
mSymbolFilter.setListener( mC4FMSlicer );
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
|
|||||||
import dsp.gain.ComplexFeedForwardGainControl;
|
import dsp.gain.ComplexFeedForwardGainControl;
|
||||||
import dsp.psk.LSMDemodulator;
|
import dsp.psk.LSMDemodulator;
|
||||||
import dsp.psk.QPSKPolarSlicer;
|
import dsp.psk.QPSKPolarSlicer;
|
||||||
|
import source.tuner.frequency.FrequencyChangeEvent;
|
||||||
|
|
||||||
public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
|
public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
|
||||||
{
|
{
|
||||||
@@ -56,8 +57,7 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
|
|||||||
private List<TapGroup> mAvailableTaps;
|
private List<TapGroup> mAvailableTaps;
|
||||||
|
|
||||||
private ComplexFIRFilter_CB_CB mBasebandFilter;
|
private ComplexFIRFilter_CB_CB mBasebandFilter;
|
||||||
private ComplexBufferToStreamConverter mStreamConverter =
|
private ComplexBufferToStreamConverter mStreamConverter = new ComplexBufferToStreamConverter();
|
||||||
new ComplexBufferToStreamConverter();
|
|
||||||
private ComplexFeedForwardGainControl mAGC =
|
private ComplexFeedForwardGainControl mAGC =
|
||||||
new ComplexFeedForwardGainControl( 32 );
|
new ComplexFeedForwardGainControl( 32 );
|
||||||
private LSMDemodulator mLSMDemodulator = new LSMDemodulator();
|
private LSMDemodulator mLSMDemodulator = new LSMDemodulator();
|
||||||
@@ -108,6 +108,29 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
|
|||||||
mMessageFramer = null;
|
mMessageFramer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrequencyChangeListener(Listener<FrequencyChangeEvent> listener)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeFrequencyChangeListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Listener<FrequencyChangeEvent> getFrequencyChangeListener()
|
||||||
|
{
|
||||||
|
return new Listener<FrequencyChangeEvent>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void receive(FrequencyChangeEvent frequencyChangeEvent)
|
||||||
|
{
|
||||||
|
//Ignored
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Listener<ComplexBuffer> getComplexBufferListener()
|
public Listener<ComplexBuffer> getComplexBufferListener()
|
||||||
{
|
{
|
||||||
|
|||||||
102
src/module/decode/p25/PatchGroup.java
Normal file
102
src/module/decode/p25/PatchGroup.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package module.decode.p25;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PatchGroup
|
||||||
|
{
|
||||||
|
private long mExpireThreshold = 5000; //5 seconds
|
||||||
|
private long mLastUpdated;
|
||||||
|
private String mPatchGroupID;
|
||||||
|
private List<String> mPatchedGroups = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a patch group with the specified patch group ID
|
||||||
|
* @param patchGroupID that identifies this patch group
|
||||||
|
*/
|
||||||
|
public PatchGroup(String patchGroupID)
|
||||||
|
{
|
||||||
|
mPatchGroupID = patchGroupID;
|
||||||
|
mLastUpdated = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch group ID for this patch group
|
||||||
|
*/
|
||||||
|
public String getPatchGroupID()
|
||||||
|
{
|
||||||
|
return mPatchGroupID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the patched group to this patch group. Duplicate patched groups are ignored.
|
||||||
|
*
|
||||||
|
* Note: the patch group ID will not be added as a patched group, to avoid circular dependencies.
|
||||||
|
*
|
||||||
|
* @return true if the patched group was added to this patch group
|
||||||
|
*/
|
||||||
|
public boolean addPatchedGroup(String patchedGroup)
|
||||||
|
{
|
||||||
|
if(patchedGroup != null && !patchedGroup.equals(mPatchGroupID) && !mPatchedGroups.contains(patchedGroup))
|
||||||
|
{
|
||||||
|
mPatchedGroups.add(patchedGroup);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patched talkgroups that are part of this patch group.
|
||||||
|
*/
|
||||||
|
public List<String> getPatchedGroups()
|
||||||
|
{
|
||||||
|
return mPatchedGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the timestamp for this patch group.
|
||||||
|
*/
|
||||||
|
public void updateTimestamp()
|
||||||
|
{
|
||||||
|
mLastUpdated = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this patch group is expired, meaning that it hasn't been updated in the past (expire
|
||||||
|
* threshold) seconds.
|
||||||
|
*/
|
||||||
|
public boolean isExpired()
|
||||||
|
{
|
||||||
|
return System.currentTimeMillis() - mLastUpdated > mExpireThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the patch group expiration threshold (default = 5000 ms). A patch group is considered expired if
|
||||||
|
* no new updates have been received within the past (expire threshold) milliseconds
|
||||||
|
*
|
||||||
|
* @param expireThreshold in milliseconds
|
||||||
|
*/
|
||||||
|
public void setExpireThreshold(long expireThreshold)
|
||||||
|
{
|
||||||
|
mExpireThreshold = expireThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
245
src/module/decode/p25/PatchGroupManager.java
Normal file
245
src/module/decode/p25/PatchGroupManager.java
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package module.decode.p25;
|
||||||
|
|
||||||
|
import alias.Alias;
|
||||||
|
import alias.AliasList;
|
||||||
|
import alias.PatchGroupAlias;
|
||||||
|
import alias.id.talkgroup.TalkgroupID;
|
||||||
|
import module.decode.event.CallEvent;
|
||||||
|
import sample.Broadcaster;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PatchGroupManager
|
||||||
|
{
|
||||||
|
private AliasList mAliasList;
|
||||||
|
private List<PatchGroup> mPatchGroups = new ArrayList<>();
|
||||||
|
private Broadcaster<CallEvent> mCallEventBroadcaster;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages active P25 patch groups and auto-expires patch groups if not updated every 5 seconds.
|
||||||
|
*
|
||||||
|
* @param aliasList to keep updated with patch group changes
|
||||||
|
* @param broadcaster for rebroadcasting patch group related call events
|
||||||
|
*/
|
||||||
|
public PatchGroupManager(AliasList aliasList, Broadcaster<CallEvent> broadcaster)
|
||||||
|
{
|
||||||
|
mAliasList = aliasList;
|
||||||
|
mCallEventBroadcaster = broadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the patch group.
|
||||||
|
*
|
||||||
|
* @param patchGroupID of the patch group to remove
|
||||||
|
*/
|
||||||
|
public void removePatchGroup(String patchGroupID)
|
||||||
|
{
|
||||||
|
removePatchGroup(patchGroupID, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the patch group.
|
||||||
|
*
|
||||||
|
* @param patchGroupID of the patch group to remove
|
||||||
|
* @param expired true if the patch group is being removed because it expired, false if we receive a
|
||||||
|
* message commanding commanding a patch group delete.
|
||||||
|
*/
|
||||||
|
private void removePatchGroup(String patchGroupID, boolean expired)
|
||||||
|
{
|
||||||
|
PatchGroup patchGroup = getPatchGroup(patchGroupID);
|
||||||
|
|
||||||
|
if(patchGroup != null)
|
||||||
|
{
|
||||||
|
//Cleanup the alias list
|
||||||
|
if(mAliasList != null)
|
||||||
|
{
|
||||||
|
Alias alias = mAliasList.getTalkgroupAlias(patchGroupID);
|
||||||
|
|
||||||
|
if(alias instanceof PatchGroupAlias)
|
||||||
|
{
|
||||||
|
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
|
||||||
|
|
||||||
|
mAliasList.removeAlias(patchGroupAlias);
|
||||||
|
|
||||||
|
//Replace our temporary patch group alias with the original alias for the patch group ID
|
||||||
|
if(patchGroupAlias.hasPatchGroupAlias())
|
||||||
|
{
|
||||||
|
mAliasList.addAlias(patchGroupAlias.getPatchGroupAlias());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPatchGroups.remove(patchGroup);
|
||||||
|
|
||||||
|
if(mCallEventBroadcaster != null)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append((expired ? "EXPIRED " : "DELETED "));
|
||||||
|
sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID());
|
||||||
|
sb.append(" ").append(patchGroup.getPatchedGroups());
|
||||||
|
|
||||||
|
|
||||||
|
mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_DELETE)
|
||||||
|
.to(patchGroup.getPatchGroupID())
|
||||||
|
.details(sb.toString())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the
|
||||||
|
* patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and
|
||||||
|
* replaced with the aliases corresponding to the patched talkgroup aliases.
|
||||||
|
*
|
||||||
|
* @param patchGroupID for the patch group
|
||||||
|
* @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups
|
||||||
|
*/
|
||||||
|
public void updatePatchGroup(String patchGroupID, List<String> patchedTalkgroups)
|
||||||
|
{
|
||||||
|
boolean updated = false;
|
||||||
|
|
||||||
|
PatchGroup patchGroup = getPatchGroup(patchGroupID);
|
||||||
|
|
||||||
|
if(patchGroup == null)
|
||||||
|
{
|
||||||
|
//Remove any expired patch groups before we add a new one
|
||||||
|
cleanupPatchGroups();
|
||||||
|
patchGroup = new PatchGroup(patchGroupID);
|
||||||
|
mPatchGroups.add(patchGroup);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
patchGroup.updateTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String patchedGroup : patchedTalkgroups)
|
||||||
|
{
|
||||||
|
if(patchGroup.addPatchedGroup(patchedGroup))
|
||||||
|
{
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we make any changes to the patch group, update the patch group alias
|
||||||
|
if(updated)
|
||||||
|
{
|
||||||
|
PatchGroupAlias patchGroupAlias = null;
|
||||||
|
|
||||||
|
//Check for an existing alias for the patch talkgroup - do not include wildcard aliases
|
||||||
|
if(mAliasList != null)
|
||||||
|
{
|
||||||
|
Alias existingAlias = mAliasList.getTalkgroupAlias(patchGroupID, false);
|
||||||
|
|
||||||
|
if(existingAlias instanceof PatchGroupAlias)
|
||||||
|
{
|
||||||
|
patchGroupAlias = (PatchGroupAlias)existingAlias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
patchGroupAlias = new PatchGroupAlias();
|
||||||
|
|
||||||
|
patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID));
|
||||||
|
|
||||||
|
if(existingAlias != null)
|
||||||
|
{
|
||||||
|
mAliasList.removeAlias(existingAlias);
|
||||||
|
|
||||||
|
patchGroupAlias.setPatchGroupAlias(existingAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAliasList.addAlias(patchGroupAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(patchGroupAlias != null)
|
||||||
|
{
|
||||||
|
patchGroupAlias.setPatchedTalkgroupIDs(patchGroup.getPatchedGroups());
|
||||||
|
|
||||||
|
patchGroupAlias.clearPatchedAliases();
|
||||||
|
|
||||||
|
for(String patchedTalkgroup : patchGroup.getPatchedGroups())
|
||||||
|
{
|
||||||
|
Alias patchedAlias = mAliasList.getTalkgroupAlias(patchedTalkgroup);
|
||||||
|
|
||||||
|
if(patchedAlias != null)
|
||||||
|
{
|
||||||
|
patchGroupAlias.addPatchedAlias(patchedAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mCallEventBroadcaster != null)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID());
|
||||||
|
sb.append(" ").append(patchGroup.getPatchedGroups());
|
||||||
|
|
||||||
|
mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_ADD)
|
||||||
|
.aliasList(mAliasList)
|
||||||
|
.details(sb.toString())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the patch group matching the patch group ID or null
|
||||||
|
*/
|
||||||
|
private PatchGroup getPatchGroup(String patchGroupID)
|
||||||
|
{
|
||||||
|
for(PatchGroup patchGroup : mPatchGroups)
|
||||||
|
{
|
||||||
|
if(patchGroup.getPatchGroupID().equals(patchGroupID))
|
||||||
|
{
|
||||||
|
return patchGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the last updated timestamp on all patch groups and removes any patch groups that have not been
|
||||||
|
* updated in the past 5 seconds
|
||||||
|
*/
|
||||||
|
public void cleanupPatchGroups()
|
||||||
|
{
|
||||||
|
Iterator<PatchGroup> it = mPatchGroups.iterator();
|
||||||
|
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
PatchGroup patchGroup = it.next();
|
||||||
|
|
||||||
|
if(patchGroup.isExpired())
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
|
||||||
|
removePatchGroup(patchGroup.getPatchGroupID(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,9 @@ import jmbe.iface.AudioConverter;
|
|||||||
import message.IMessageListener;
|
import message.IMessageListener;
|
||||||
import message.Message;
|
import message.Message;
|
||||||
import module.Module;
|
import module.Module;
|
||||||
|
import module.decode.p25.message.hdu.HDUMessage;
|
||||||
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
|
import module.decode.p25.message.ldu.LDU2Message;
|
||||||
import module.decode.p25.message.ldu.LDUMessage;
|
import module.decode.p25.message.ldu.LDUMessage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -25,14 +28,18 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class);
|
private final static Logger mLog = LoggerFactory.getLogger(P25AudioModule.class);
|
||||||
|
|
||||||
private static final String IMBE_CODEC = "IMBE";
|
private static final String IMBE_CODEC = "IMBE";
|
||||||
|
|
||||||
private static boolean mLibraryLoadStatusLogged = false;
|
private static boolean mLibraryLoadStatusLogged = false;
|
||||||
|
|
||||||
private boolean mCanConvertAudio = false;
|
private boolean mCanConvertAudio = false;
|
||||||
|
private boolean mEncryptedCall = false;
|
||||||
|
private boolean mEncryptedCallStateEstablished = false;
|
||||||
|
|
||||||
private AudioConverter mAudioConverter;
|
private AudioConverter mAudioConverter;
|
||||||
private Listener<AudioPacket> mAudioPacketListener;
|
private Listener<AudioPacket> mAudioPacketListener;
|
||||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||||
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
|
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
|
||||||
private Metadata mMetadata;
|
private Metadata mMetadata;
|
||||||
|
private LDU1Message mCachedLDU1Message = null;
|
||||||
|
|
||||||
public P25AudioModule(Metadata metadata)
|
public P25AudioModule(Metadata metadata)
|
||||||
{
|
{
|
||||||
@@ -76,21 +83,60 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary processing method for p25 imbe audio frame messages. Each LDU
|
* Processes call header (HDU) and voice frame (LDU1/LDU2) messages to decode audio and to determine the
|
||||||
* audio message contains 9 imbe voice frames. Each frame is converted to
|
* encrypted audio status of a call event. Only the HDU and LDU2 messages convey encrypted call status. If an
|
||||||
* audio when the JMBE library is loaded, processed by auto-gain, and
|
* LDU1 message is received without a preceding HDU message, then the LDU1 message is cached until the first
|
||||||
* broadcast to the audio packet listener.
|
* LDU2 message is received and the encryption state can be determined. Both the LDU1 and the LDU2 message are
|
||||||
|
* then processed for audio if the call is unencrypted.
|
||||||
*/
|
*/
|
||||||
public void receive(Message message)
|
public void receive(Message message)
|
||||||
{
|
{
|
||||||
if(mCanConvertAudio && mAudioPacketListener != null)
|
if(mCanConvertAudio && mAudioPacketListener != null)
|
||||||
|
{
|
||||||
|
if(mEncryptedCallStateEstablished)
|
||||||
{
|
{
|
||||||
if(message instanceof LDUMessage)
|
if(message instanceof LDUMessage)
|
||||||
{
|
{
|
||||||
LDUMessage ldu = (LDUMessage) message;
|
processAudio((LDUMessage)message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(message instanceof HDUMessage)
|
||||||
|
{
|
||||||
|
mEncryptedCallStateEstablished = true;
|
||||||
|
mEncryptedCall = ((HDUMessage)message).isEncryptedAudio();
|
||||||
|
}
|
||||||
|
else if(message instanceof LDU1Message)
|
||||||
|
{
|
||||||
|
//When we receive an LDU1 message with first receiving the HDU message, cache the LDU1 Message
|
||||||
|
//until we can determine the encrypted call state from the next LDU2 message
|
||||||
|
mCachedLDU1Message = (LDU1Message)message;
|
||||||
|
}
|
||||||
|
else if(message instanceof LDU2Message)
|
||||||
|
{
|
||||||
|
mEncryptedCallStateEstablished = true;
|
||||||
|
LDU2Message ldu2 = (LDU2Message)message;
|
||||||
|
mEncryptedCall = ldu2.isEncryptedAudio();
|
||||||
|
|
||||||
/* Only process unencrypted audio frames */
|
if(mCachedLDU1Message != null)
|
||||||
if (!(ldu.isValid() && ldu.isEncrypted()))
|
{
|
||||||
|
processAudio(mCachedLDU1Message);
|
||||||
|
mCachedLDU1Message = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
processAudio(ldu2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an audio packet by decoding the IMBE audio frames and rebroadcasting them as PCM audio packets.
|
||||||
|
*/
|
||||||
|
private void processAudio(LDUMessage ldu)
|
||||||
|
{
|
||||||
|
if(!mEncryptedCall)
|
||||||
{
|
{
|
||||||
for(byte[] frame : ldu.getIMBEFrames())
|
for(byte[] frame : ldu.getIMBEFrames())
|
||||||
{
|
{
|
||||||
@@ -101,7 +147,9 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf()));
|
mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
//Encrypted audio processing not implemented
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,20 +244,26 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for squelch state listener. Internally, the P25 audio module
|
* Wrapper for squelch state to process end of call actions. At call end the encrypted call state established
|
||||||
* doesn't have a squelch state. If there are IMBE audio frames, we have
|
* flag is reset so that the encrypted audio state for the next call can be properly detected and we send an
|
||||||
* audio. We use this listener to signal to the recorder manager that the
|
* END audio packet so that downstream processors like the audio recorder can properly close out a call sequence.
|
||||||
* channel state is resetting and it should end the recording.
|
|
||||||
*/
|
*/
|
||||||
public class SquelchStateListener implements Listener<SquelchState>
|
public class SquelchStateListener implements Listener<SquelchState>
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void receive(SquelchState state)
|
public void receive(SquelchState state)
|
||||||
{
|
{
|
||||||
if (state == SquelchState.SQUELCH && mAudioPacketListener != null)
|
if(state == SquelchState.SQUELCH)
|
||||||
|
{
|
||||||
|
if(mAudioPacketListener != null)
|
||||||
{
|
{
|
||||||
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
|
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mEncryptedCallStateEstablished = false;
|
||||||
|
mEncryptedCall = false;
|
||||||
|
mCachedLDU1Message = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message;
|
package module.decode.p25.message;
|
||||||
|
|
||||||
public interface IBandIdentifier
|
public interface IBandIdentifier
|
||||||
@@ -29,4 +47,18 @@ public interface IBandIdentifier
|
|||||||
*/
|
*/
|
||||||
public abstract long getTransmitOffset();
|
public abstract long getTransmitOffset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downlink (tower to mobile) Frequency for the specified channel
|
||||||
|
*/
|
||||||
|
public abstract long getDownlinkFrequency(int channelNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uplink (mobile to tower) Frequency for the specified channel
|
||||||
|
*/
|
||||||
|
public abstract long getUplinkFrequency(int channelNumber);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this band is an FDMA (false) or TDMA (true) band
|
||||||
|
*/
|
||||||
|
public boolean isTDMA();
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,32 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message;
|
package module.decode.p25.message;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
|
|
||||||
import map.Plottable;
|
|
||||||
import message.Message;
|
|
||||||
import module.decode.p25.reference.DataUnitID;
|
|
||||||
import alias.Alias;
|
import alias.Alias;
|
||||||
import alias.AliasList;
|
import alias.AliasList;
|
||||||
import bits.BinaryMessage;
|
import bits.BinaryMessage;
|
||||||
import edac.CRC;
|
import edac.CRC;
|
||||||
|
import map.Plottable;
|
||||||
|
import message.Message;
|
||||||
|
import module.decode.p25.reference.DataUnitID;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
|
||||||
public class P25Message extends Message
|
public class P25Message extends Message
|
||||||
{
|
{
|
||||||
@@ -17,12 +35,10 @@ public class P25Message extends Message
|
|||||||
|
|
||||||
public static final int[] NAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
public static final int[] NAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||||
public static final int[] DUID = {12, 13, 14, 15};
|
public static final int[] DUID = {12, 13, 14, 15};
|
||||||
public static final int[] BCH = { 16,17,18,19,20,21,22,23,24,25,26,27,28,29,
|
public static final int[] BCH = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
|
||||||
30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
|
||||||
54,55,56,57,58,59,60,61,62,63 };
|
|
||||||
|
|
||||||
protected SimpleDateFormat mTimeDurationFormat =
|
protected SimpleDateFormat mTimeDurationFormat = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||||
new SimpleDateFormat( "HH:mm:ss.SSS");
|
|
||||||
|
|
||||||
protected BinaryMessage mMessage;
|
protected BinaryMessage mMessage;
|
||||||
protected DataUnitID mDUID;
|
protected DataUnitID mDUID;
|
||||||
@@ -30,9 +46,7 @@ public class P25Message extends Message
|
|||||||
|
|
||||||
protected CRC[] mCRC;
|
protected CRC[] mCRC;
|
||||||
|
|
||||||
public P25Message( BinaryMessage message,
|
public P25Message(BinaryMessage message, DataUnitID duid, AliasList aliasList)
|
||||||
DataUnitID duid,
|
|
||||||
AliasList aliasList )
|
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -171,44 +185,4 @@ public class P25Message extends Message
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the frequency of the uplink channel using the channel number
|
|
||||||
* and the IdentifierUpdate message.
|
|
||||||
*
|
|
||||||
* @param iden - Identifier Update message
|
|
||||||
* @param channel - channel number
|
|
||||||
* @return - frequency in Hertz
|
|
||||||
*/
|
|
||||||
protected static long calculateUplink( IBandIdentifier iden, int channel )
|
|
||||||
{
|
|
||||||
long downlink = calculateDownlink( iden, channel );
|
|
||||||
|
|
||||||
if( downlink > 0 && iden != null )
|
|
||||||
{
|
|
||||||
return downlink + iden.getTransmitOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the frequency of the downlink channel using the channel number
|
|
||||||
* and the IdentifierUpdate message.
|
|
||||||
*
|
|
||||||
* @param iden - Identifier Update message
|
|
||||||
* @param channel - channel number
|
|
||||||
* @return - frequency in Hertz
|
|
||||||
*/
|
|
||||||
protected static long calculateDownlink( IBandIdentifier iden, int channel )
|
|
||||||
{
|
|
||||||
if( iden != null )
|
|
||||||
{
|
|
||||||
return iden.getBaseFrequency() +
|
|
||||||
( channel * iden.getChannelSpacing() );
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ public class HDUMessage extends P25Message
|
|||||||
return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) );
|
return Encryption.fromValue( mMessage.getInt( ALGORITHM_ID ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEncrypted()
|
public boolean isEncryptedAudio()
|
||||||
{
|
{
|
||||||
return getEncryption() != Encryption.UNENCRYPTED;
|
return getEncryption() != Encryption.UNENCRYPTED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,22 @@
|
|||||||
package module.decode.p25.message.ldu;
|
package module.decode.p25.message.ldu;
|
||||||
|
|
||||||
import module.decode.p25.reference.DataUnitID;
|
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
|
||||||
import module.decode.p25.reference.Vendor;
|
|
||||||
import module.decode.p25.reference.VendorLinkControlOpcode;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import alias.AliasList;
|
import alias.AliasList;
|
||||||
import bits.BinaryMessage;
|
import bits.BinaryMessage;
|
||||||
import edac.CRC;
|
import edac.CRC;
|
||||||
import edac.Hamming10;
|
import edac.Hamming10;
|
||||||
import edac.ReedSolomon_63_47_17;
|
import edac.ReedSolomon_63_47_17;
|
||||||
|
import module.decode.p25.reference.DataUnitID;
|
||||||
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
import module.decode.p25.reference.Vendor;
|
||||||
|
import module.decode.p25.reference.VendorLinkControlOpcode;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class LDU1Message extends LDUMessage
|
public class LDU1Message extends LDUMessage
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(LDU1Message.class);
|
private final static Logger mLog = LoggerFactory.getLogger(LDU1Message.class);
|
||||||
|
|
||||||
public static final int ENCRYPTION_FLAG = 352;
|
public static final int ENCRYPTED_LINK_CONTROL_WORD_FLAG = 352;
|
||||||
public static final int IMPLICIT_VENDOR_FLAG = 353;
|
public static final int IMPLICIT_VENDOR_FLAG = 353;
|
||||||
public static final int[] OPCODE = {354, 355, 356, 357, 362, 363};
|
public static final int[] OPCODE = {354, 355, 356, 357, 362, 363};
|
||||||
public static final int[] VENDOR = {364, 365, 366, 367, 372, 373, 374, 375};
|
public static final int[] VENDOR = {364, 365, 366, 367, 372, 373, 374, 375};
|
||||||
@@ -81,10 +79,14 @@ public class LDU1Message extends LDUMessage
|
|||||||
mCRC = message.mCRC;
|
mCRC = message.mCRC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isEncrypted()
|
* Indicates if the contents of the Link Control Word are encrypted.
|
||||||
|
*
|
||||||
|
* @return true if the contents of the LCW are encrypted.
|
||||||
|
*/
|
||||||
|
public boolean isEncryptedLinkControlWord()
|
||||||
{
|
{
|
||||||
return mMessage.get( ENCRYPTION_FLAG );
|
return mMessage.get(ENCRYPTED_LINK_CONTROL_WORD_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCRC()
|
private void checkCRC()
|
||||||
@@ -197,7 +199,7 @@ public class LDU1Message extends LDUMessage
|
|||||||
|
|
||||||
sb.append(super.getMessageStub());
|
sb.append(super.getMessageStub());
|
||||||
|
|
||||||
if( isEncrypted() )
|
if(isEncryptedLinkControlWord())
|
||||||
{
|
{
|
||||||
sb.append("ENCRYPTED");
|
sb.append("ENCRYPTED");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,10 @@ public class LDU2Message extends LDUMessage
|
|||||||
checkCRC();
|
checkCRC();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isEncrypted()
|
* Indicates if the audio stream is encrypted
|
||||||
|
*/
|
||||||
|
public boolean isEncryptedAudio()
|
||||||
{
|
{
|
||||||
Encryption encryption = getEncryption();
|
Encryption encryption = getEncryption();
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ public abstract class LDUMessage extends P25Message
|
|||||||
super( message, duid, aliasList );
|
super( message, duid, aliasList );
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean isEncrypted();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage()
|
public String getMessage()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IAdjacentSite;
|
import module.decode.p25.message.IAdjacentSite;
|
||||||
@@ -7,19 +25,15 @@ import module.decode.p25.message.ldu.LDU1Message;
|
|||||||
import module.decode.p25.message.tsbk.osp.control.SystemService;
|
import module.decode.p25.message.tsbk.osp.control.SystemService;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
public class AdjacentSiteStatusBroadcast extends LDU1Message
|
public class AdjacentSiteStatusBroadcast extends LDU1Message implements IdentifierReceiver, IAdjacentSite
|
||||||
implements IdentifierReceiver, IAdjacentSite
|
|
||||||
{
|
{
|
||||||
public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375};
|
public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375};
|
||||||
public static final int[] SYSTEM_ID = { 384,385,386,387,536,537,538,539,540,
|
public static final int[] SYSTEM_ID = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547};
|
||||||
541,546,547 };
|
|
||||||
public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559};
|
public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559};
|
||||||
public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571};
|
public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571};
|
||||||
public static final int[] IDENTIFIER = {720, 721, 722, 723};
|
public static final int[] IDENTIFIER = {720, 721, 722, 723};
|
||||||
public static final int[] CHANNEL = { 724,725,730,731,732,733,734,735,740,
|
public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743};
|
||||||
741,742,743 };
|
public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755};
|
||||||
public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753,
|
|
||||||
754,755 };
|
|
||||||
|
|
||||||
private IBandIdentifier mIdentifierUpdate;
|
private IBandIdentifier mIdentifierUpdate;
|
||||||
|
|
||||||
@@ -138,11 +152,21 @@ public class AdjacentSiteStatusBroadcast extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequency()
|
public long getDownlinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mIdentifierUpdate, getChannelNumber() );
|
if(mIdentifierUpdate != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequency()
|
public long getUplinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateUplink( mIdentifierUpdate, getChannelNumber() );
|
if(mIdentifierUpdate != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdate.getUplinkFrequency(getChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IAdjacentSite;
|
import module.decode.p25.message.IAdjacentSite;
|
||||||
@@ -6,19 +24,15 @@ import module.decode.p25.message.IdentifierReceiver;
|
|||||||
import module.decode.p25.message.ldu.LDU1Message;
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message
|
public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver, IAdjacentSite
|
||||||
implements IdentifierReceiver, IAdjacentSite
|
|
||||||
{
|
{
|
||||||
public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375};
|
public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375};
|
||||||
public static final int[] TRANSMIT_IDENTIFIER = {376, 377, 382, 383};
|
public static final int[] TRANSMIT_IDENTIFIER = {376, 377, 382, 383};
|
||||||
public static final int[] TRANSMIT_CHANNEL = { 384,385,386,387,536,537,538,
|
public static final int[] TRANSMIT_CHANNEL = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547};
|
||||||
539,540,541,546,547 };
|
|
||||||
public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559};
|
public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559};
|
||||||
public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571};
|
public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571};
|
||||||
|
|
||||||
public static final int[] RECEIVE_IDENTIFIER = {720, 721, 722, 723};
|
public static final int[] RECEIVE_IDENTIFIER = {720, 721, 722, 723};
|
||||||
public static final int[] RECEIVE_CHANNEL = { 724,725,730,731,732,733,734,
|
public static final int[] RECEIVE_CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743};
|
||||||
735,740,741,742,743 };
|
|
||||||
|
|
||||||
private IBandIdentifier mTransmitBand;
|
private IBandIdentifier mTransmitBand;
|
||||||
private IBandIdentifier mReceiveBand;
|
private IBandIdentifier mReceiveBand;
|
||||||
@@ -123,12 +137,22 @@ public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequency()
|
public long getDownlinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mTransmitBand, getTransmitChannel() );
|
if(mTransmitBand != null)
|
||||||
|
{
|
||||||
|
return mTransmitBand.getDownlinkFrequency(getTransmitChannel());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequency()
|
public long getUplinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateUplink( mReceiveBand, getReceiveChannel() );
|
if(mReceiveBand != null)
|
||||||
|
{
|
||||||
|
return mReceiveBand.getUplinkFrequency(getReceiveChannel());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,11 +1,28 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IBandIdentifier;
|
import module.decode.p25.message.IBandIdentifier;
|
||||||
import module.decode.p25.message.ldu.LDU1Message;
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
public class ChannelIdentifierUpdate extends LDU1Message
|
public class ChannelIdentifierUpdate extends LDU1Message implements IBandIdentifier
|
||||||
implements IBandIdentifier
|
|
||||||
{
|
{
|
||||||
public static final int[] IDENTIFIER = {364, 365, 366, 367};
|
public static final int[] IDENTIFIER = {364, 365, 366, 367};
|
||||||
public static final int[] BANDWIDTH = {372, 373, 374, 375, 376, 377, 382, 383, 384};
|
public static final int[] BANDWIDTH = {372, 373, 374, 375, 376, 377, 382, 383, 384};
|
||||||
@@ -80,4 +97,22 @@ public class ChannelIdentifierUpdate extends LDU1Message
|
|||||||
{
|
{
|
||||||
return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l;
|
return -1 * mMessage.getLong(TRANSMIT_OFFSET) * 250000l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDownlinkFrequency(int channelNumber)
|
||||||
|
{
|
||||||
|
return getBaseFrequency() + (channelNumber * getChannelSpacing());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUplinkFrequency(int channelNumber)
|
||||||
|
{
|
||||||
|
return getDownlinkFrequency(channelNumber) + getTransmitOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTDMA()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import module.decode.p25.message.IBandIdentifier;
|
|||||||
import module.decode.p25.message.ldu.LDU1Message;
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
public class ChannelIdentifierUpdateExplicit extends LDU1Message
|
public class ChannelIdentifierUpdateExplicit extends LDU1Message implements IBandIdentifier
|
||||||
implements IBandIdentifier
|
|
||||||
{
|
{
|
||||||
public static final int[] IDENTIFIER = { 364,365,366,367 };
|
public static final int[] IDENTIFIER = { 364,365,366,367 };
|
||||||
public static final int[] BANDWIDTH = { 372,373,374,375 };
|
public static final int[] BANDWIDTH = { 372,373,374,375 };
|
||||||
@@ -80,4 +79,22 @@ public class ChannelIdentifierUpdateExplicit extends LDU1Message
|
|||||||
{
|
{
|
||||||
return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l;
|
return -1 * mMessage.getLong( TRANSMIT_OFFSET ) * 250000l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDownlinkFrequency(int channelNumber)
|
||||||
|
{
|
||||||
|
return getBaseFrequency() + ( channelNumber * getChannelSpacing() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUplinkFrequency(int channelNumber)
|
||||||
|
{
|
||||||
|
return getDownlinkFrequency(channelNumber) + getTransmitOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTDMA()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IBandIdentifier;
|
import module.decode.p25.message.IBandIdentifier;
|
||||||
@@ -5,25 +23,16 @@ import module.decode.p25.message.IdentifierReceiver;
|
|||||||
import module.decode.p25.message.ldu.LDU1Message;
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
public class GroupVoiceChannelUpdate extends LDU1Message implements IdentifierReceiver
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class GroupVoiceChannelUpdate extends LDU1Message
|
|
||||||
implements IdentifierReceiver
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
|
||||||
LoggerFactory.getLogger( GroupVoiceChannelUpdate.class );
|
|
||||||
|
|
||||||
public static final int[] IDENTIFIER_A = {364, 365, 366, 367};
|
public static final int[] IDENTIFIER_A = {364, 365, 366, 367};
|
||||||
public static final int[] CHANNEL_A = { 372,373,374,375,376,377,382,383,384,
|
public static final int[] CHANNEL_A = {372, 373, 374, 375, 376, 377, 382, 383, 384, 385, 386, 387};
|
||||||
385,386,387 };
|
public static final int[] GROUP_ADDRESS_A = {536, 537, 538, 539, 540, 541, 546, 547, 548, 549, 550, 551, 556, 557,
|
||||||
public static final int[] GROUP_ADDRESS_A = { 536,537,538,539,540,541,546,
|
558, 559};
|
||||||
547,548,549,550,551,556,557,558,559 };
|
|
||||||
public static final int[] IDENTIFIER_B = {560, 561, 566, 567};
|
public static final int[] IDENTIFIER_B = {560, 561, 566, 567};
|
||||||
public static final int[] CHANNEL_B = { 568,569,570,571,720,721,722,723,724,
|
public static final int[] CHANNEL_B = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731};
|
||||||
725,730,731 };
|
public static final int[] GROUP_ADDRESS_B = {732, 733, 734, 735, 740, 741, 742, 743, 744, 745, 750, 751, 752, 753,
|
||||||
public static final int[] GROUP_ADDRESS_B = { 732,733,734,735,740,741,742,
|
754, 755};
|
||||||
743,744,745,750,751,752,753,754,755 };
|
|
||||||
|
|
||||||
private IBandIdentifier mIdentifierUpdateA;
|
private IBandIdentifier mIdentifierUpdateA;
|
||||||
private IBandIdentifier mIdentifierUpdateB;
|
private IBandIdentifier mIdentifierUpdateB;
|
||||||
@@ -140,21 +149,41 @@ public class GroupVoiceChannelUpdate extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequencyA()
|
public long getDownlinkFrequencyA()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mIdentifierUpdateA, getChannelNumberA() );
|
if(mIdentifierUpdateA != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdateA.getDownlinkFrequency(getChannelNumberA());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequencyA()
|
public long getUplinkFrequencyA()
|
||||||
{
|
{
|
||||||
return calculateUplink( mIdentifierUpdateA, getChannelNumberA() );
|
if(mIdentifierUpdateA != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdateA.getUplinkFrequency(getChannelNumberA());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDownlinkFrequencyB()
|
public long getDownlinkFrequencyB()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mIdentifierUpdateB, getChannelNumberA() );
|
if(mIdentifierUpdateB != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdateB.getDownlinkFrequency(getChannelNumberB());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequencyB()
|
public long getUplinkFrequencyB()
|
||||||
{
|
{
|
||||||
return calculateUplink( mIdentifierUpdateB, getChannelNumberA() );
|
if(mIdentifierUpdateB != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdateB.getUplinkFrequency(getChannelNumberB());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IBandIdentifier;
|
import module.decode.p25.message.IBandIdentifier;
|
||||||
@@ -5,27 +23,17 @@ import module.decode.p25.message.IdentifierReceiver;
|
|||||||
import module.decode.p25.message.ldu.LDU1Message;
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
public class GroupVoiceChannelUpdateExplicit extends LDU1Message implements IdentifierReceiver
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class GroupVoiceChannelUpdateExplicit extends LDU1Message
|
|
||||||
implements IdentifierReceiver
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
|
||||||
LoggerFactory.getLogger( GroupVoiceChannelUpdateExplicit.class );
|
|
||||||
|
|
||||||
public static final int EMERGENCY_FLAG = 376;
|
public static final int EMERGENCY_FLAG = 376;
|
||||||
public static final int ENCRYPTED_CHANNEL_FLAG = 377;
|
public static final int ENCRYPTED_CHANNEL_FLAG = 377;
|
||||||
public static final int DUPLEX_MODE = 382;
|
public static final int DUPLEX_MODE = 382;
|
||||||
public static final int SESSION_MODE = 383;
|
public static final int SESSION_MODE = 383;
|
||||||
public static final int[] GROUP_ADDRESS = { 536,537,538,539,540,541,546,547,
|
public static final int[] GROUP_ADDRESS = {536, 537, 538, 539, 540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559};
|
||||||
548,549,550,551,556,557,558,559 };
|
|
||||||
public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567};
|
public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567};
|
||||||
public static final int[] TRANSMIT_CHANNEL = { 568,569,570,571,720,721,722,
|
public static final int[] TRANSMIT_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731};
|
||||||
723,724,725,730,731 };
|
|
||||||
public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735};
|
public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735};
|
||||||
public static final int[] RECEIVE_CHANNEL = { 740,741,742,743,744,745,750,
|
public static final int[] RECEIVE_CHANNEL = {740, 741, 742, 743, 744, 745, 750, 751, 752, 753, 754, 755};
|
||||||
751,752,753,754,755 };
|
|
||||||
|
|
||||||
private IBandIdentifier mTransmitIdentifierUpdate;
|
private IBandIdentifier mTransmitIdentifierUpdate;
|
||||||
private IBandIdentifier mReceiveIdentifierUpdate;
|
private IBandIdentifier mReceiveIdentifierUpdate;
|
||||||
@@ -143,13 +151,21 @@ public class GroupVoiceChannelUpdateExplicit extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequency()
|
public long getDownlinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mTransmitIdentifierUpdate,
|
if(mTransmitIdentifierUpdate != null)
|
||||||
getTransmitChannelNumber() );
|
{
|
||||||
|
return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequency()
|
public long getUplinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateUplink( mReceiveIdentifierUpdate,
|
if(mReceiveIdentifierUpdate != null)
|
||||||
getReceiveChannelNumber() );
|
{
|
||||||
|
return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IBandIdentifier;
|
import module.decode.p25.message.IBandIdentifier;
|
||||||
@@ -6,23 +24,14 @@ import module.decode.p25.message.ldu.LDU1Message;
|
|||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
import module.decode.p25.reference.Service;
|
import module.decode.p25.reference.Service;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
public class NetworkStatusBroadcast extends LDU1Message implements IdentifierReceiver
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class NetworkStatusBroadcast extends LDU1Message
|
|
||||||
implements IdentifierReceiver
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
public static final int[] NETWORK_ID = {376, 377, 382, 383, 384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546,
|
||||||
LoggerFactory.getLogger( NetworkStatusBroadcast.class );
|
547, 548, 549, 550, 551};
|
||||||
public static final int[] NETWORK_ID = { 376,377,382,383,384,385,386,387,
|
public static final int[] SYSTEM_ID = {556, 557, 558, 559, 560, 561, 566, 567, 568, 569, 570, 571};
|
||||||
536,537,538,539,540,541,546,547,548,549,550,551 };
|
|
||||||
public static final int[] SYSTEM_ID = { 556,557,558,559,560,561,566,567,568,
|
|
||||||
569,570,571 };
|
|
||||||
public static final int[] IDENTIFIER = {720, 721, 722, 723};
|
public static final int[] IDENTIFIER = {720, 721, 722, 723};
|
||||||
public static final int[] CHANNEL = { 724,725,730,731,732,733,734,735,740,
|
public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743};
|
||||||
741,742,743 };
|
public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755};
|
||||||
public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753,
|
|
||||||
754,755 };
|
|
||||||
|
|
||||||
private IBandIdentifier mIdentifierUpdate;
|
private IBandIdentifier mIdentifierUpdate;
|
||||||
|
|
||||||
@@ -100,11 +109,21 @@ public class NetworkStatusBroadcast extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequency()
|
public long getDownlinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mIdentifierUpdate, getChannelNumber() );
|
if(mIdentifierUpdate != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequency()
|
public long getUplinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateUplink( mIdentifierUpdate, getChannelNumber() );
|
if(mIdentifierUpdate != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdate.getUplinkFrequency(getChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,39 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IBandIdentifier;
|
import module.decode.p25.message.IBandIdentifier;
|
||||||
import module.decode.p25.message.IdentifierReceiver;
|
import module.decode.p25.message.IdentifierReceiver;
|
||||||
import module.decode.p25.message.ldu.LDU1Message;
|
import module.decode.p25.message.ldu.LDU1Message;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class NetworkStatusBroadcastExplicit extends LDU1Message
|
public class NetworkStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver
|
||||||
implements IdentifierReceiver
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
public static final int[] NETWORK_ID = {364, 365, 366, 367, 372, 373, 374, 375, 376, 377, 382, 383, 384, 385, 386,
|
||||||
LoggerFactory.getLogger( NetworkStatusBroadcastExplicit.class );
|
387, 536, 537, 538, 539};
|
||||||
public static final int[] NETWORK_ID = { 364,365,366,367,372,373,374,375,
|
public static final int[] SYSTEM_ID = {540, 541, 546, 547, 548, 549, 550, 551, 556, 557, 558, 559};
|
||||||
376,377,382,383,384,385,386,387,536,537,538,539 };
|
|
||||||
public static final int[] SYSTEM_ID = { 540,541,546,547,548,549,550,551,556,
|
|
||||||
557,558,559 };
|
|
||||||
public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567};
|
public static final int[] TRANSMIT_IDENTIFIER = {560, 561, 566, 567};
|
||||||
public static final int[] TRANSMIT_CHANNEL = { 568,569,570,571,720,721,722,
|
public static final int[] TRANSMIT_CHANNEL = {568, 569, 570, 571, 720, 721, 722, 723, 724, 725, 730, 731};
|
||||||
723,724,725,730,731 };
|
|
||||||
public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735};
|
public static final int[] RECEIVE_IDENTIFIER = {732, 733, 734, 735};
|
||||||
public static final int[] RECEIVE_CHANNEL = { 740,741,742,743,744,745,750,
|
public static final int[] RECEIVE_CHANNEL = {740, 741, 742, 743, 744, 745, 750, 751, 752, 753, 754, 755};
|
||||||
751,752,753,754,755 };
|
|
||||||
|
|
||||||
private IBandIdentifier mTransmitIdentifierUpdate;
|
private IBandIdentifier mTransmitIdentifierUpdate;
|
||||||
private IBandIdentifier mReceiveIdentifierUpdate;
|
private IBandIdentifier mReceiveIdentifierUpdate;
|
||||||
@@ -119,13 +130,21 @@ public class NetworkStatusBroadcastExplicit extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequency()
|
public long getDownlinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mTransmitIdentifierUpdate,
|
if(mTransmitIdentifierUpdate != null)
|
||||||
getTransmitChannelNumber() );
|
{
|
||||||
|
return mTransmitIdentifierUpdate.getDownlinkFrequency(getTransmitChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequency()
|
public long getUplinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateUplink( mReceiveIdentifierUpdate,
|
if(mReceiveIdentifierUpdate != null)
|
||||||
getReceiveChannelNumber() );
|
{
|
||||||
|
return mReceiveIdentifierUpdate.getUplinkFrequency(getReceiveChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,21 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
package module.decode.p25.message.ldu.lc;
|
package module.decode.p25.message.ldu.lc;
|
||||||
|
|
||||||
import module.decode.p25.message.IBandIdentifier;
|
import module.decode.p25.message.IBandIdentifier;
|
||||||
@@ -6,25 +24,15 @@ import module.decode.p25.message.ldu.LDU1Message;
|
|||||||
import module.decode.p25.message.tsbk.osp.control.SystemService;
|
import module.decode.p25.message.tsbk.osp.control.SystemService;
|
||||||
import module.decode.p25.reference.LinkControlOpcode;
|
import module.decode.p25.reference.LinkControlOpcode;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
public class RFSSStatusBroadcast extends LDU1Message implements IdentifierReceiver
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class RFSSStatusBroadcast extends LDU1Message
|
|
||||||
implements IdentifierReceiver
|
|
||||||
{
|
{
|
||||||
private final static Logger mLog =
|
|
||||||
LoggerFactory.getLogger( RFSSStatusBroadcast.class );
|
|
||||||
|
|
||||||
public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375};
|
public static final int[] LRA = {364, 365, 366, 367, 372, 373, 374, 375};
|
||||||
public static final int[] SYSTEM_ID = { 384,385,386,387,536,537,538,539,540,
|
public static final int[] SYSTEM_ID = {384, 385, 386, 387, 536, 537, 538, 539, 540, 541, 546, 547};
|
||||||
541,546,547 };
|
|
||||||
public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559};
|
public static final int[] RFSS_ID = {548, 549, 550, 551, 556, 557, 558, 559};
|
||||||
public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571};
|
public static final int[] SITE_ID = {560, 561, 566, 567, 568, 569, 570, 571};
|
||||||
public static final int[] IDENTIFIER = {720, 721, 722, 723};
|
public static final int[] IDENTIFIER = {720, 721, 722, 723};
|
||||||
public static final int[] CHANNEL = { 724,725,730,731,732,733,734,735,740,
|
public static final int[] CHANNEL = {724, 725, 730, 731, 732, 733, 734, 735, 740, 741, 742, 743};
|
||||||
741,742,743 };
|
public static final int[] SYSTEM_SERVICE_CLASS = {744, 745, 750, 751, 752, 753, 754, 755};
|
||||||
public static final int[] SYSTEM_SERVICE_CLASS = { 744,745,750,751,752,753,
|
|
||||||
754,755 };
|
|
||||||
|
|
||||||
private IBandIdentifier mIdentifierUpdate;
|
private IBandIdentifier mIdentifierUpdate;
|
||||||
|
|
||||||
@@ -119,12 +127,21 @@ public class RFSSStatusBroadcast extends LDU1Message
|
|||||||
|
|
||||||
public long getDownlinkFrequency()
|
public long getDownlinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateDownlink( mIdentifierUpdate, getChannelNumber() );
|
if(mIdentifierUpdate != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdate.getDownlinkFrequency(getChannelNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUplinkFrequency()
|
public long getUplinkFrequency()
|
||||||
{
|
{
|
||||||
return calculateUplink( mIdentifierUpdate, getChannelNumber() );
|
if(mIdentifierUpdate != null)
|
||||||
|
{
|
||||||
|
return mIdentifierUpdate.getUplinkFrequency(getChannelNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user