5 | *
6 | * Module: HackrfUsbException.java
7 | * Description: This Exception will be thrown if an Error with the USB
8 | * communication occurs.
9 | *
10 | * @author Dennis Mantz
11 | *
12 | * Copyright (C) 2014 Dennis Mantz
13 | * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
14 | *
15 | * This library is free software; you can redistribute it and/or
16 | * modify it under the terms of the GNU General Public
17 | * License as published by the Free Software Foundation; either
18 | * version 2 of the License, or (at your option) any later version.
19 | *
20 | * This library is distributed in the hope that it will be useful,
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 | * General Public License for more details.
24 | *
25 | * You should have received a copy of the GNU General Public
26 | * License along with this library; if not, write to the Free Software
27 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 | */
29 | public class HackrfUsbException extends Exception {
30 |
31 | private static final long serialVersionUID = 1L;
32 |
33 | public HackrfUsbException(String message) {
34 | super(message);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/examples/hackRF_Test/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
35 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mantz_it/hackrf_android/HackrfCallbackInterface.java:
--------------------------------------------------------------------------------
1 | package com.mantz_it.hackrf_android;
2 |
3 | /**
4 | *
HackRF USB Library for Android
5 | *
6 | * Module: HackrfCallbackInterface.java
7 | * Description: This Interface declares a callback method to return a
8 | * HackRF instance to the application after it was opened
9 | * by the initialization routine (asynchronous process
10 | * because it includes requesting the USB permissions)
11 | *
12 | * @author Dennis Mantz
13 | *
14 | * Copyright (C) 2014 Dennis Mantz
15 | * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
16 | *
17 | * This library is free software; you can redistribute it and/or
18 | * modify it under the terms of the GNU General Public
19 | * License as published by the Free Software Foundation; either
20 | * version 2 of the License, or (at your option) any later version.
21 | *
22 | * This library is distributed in the hope that it will be useful,
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 | * General Public License for more details.
26 | *
27 | * You should have received a copy of the GNU General Public
28 | * License along with this library; if not, write to the Free Software
29 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 | */
31 | public interface HackrfCallbackInterface {
32 |
33 | /**
34 | * Called by initHackrf() after the device is ready to be used.
35 | *
36 | * @param hackrf Instance of the HackRF that provides access to the device
37 | */
38 | public void onHackrfReady(Hackrf hackrf);
39 |
40 |
41 | /**
42 | * Called if there was an error when accessing the device.
43 | *
44 | * @param message Reason for the Error
45 | */
46 | public void onHackrfError(String message);
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/examples/hackRF_Test/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HackRF_Test
5 | Hello world!
6 | Settings
7 | Show Log
8 | Help
9 | Open
10 | Info
11 | RX
12 | TX
13 | Sample Rate
14 | Frequency
15 | Stop
16 | Amplifier
17 | Antenna Port Power
18 | Filename
19 | VGA Gain
20 | LNA Gain
21 |
22 | == HELP ==
24 | This is the example application delivered with the hackrf_android library. Its intention
25 | is to demonstrate the functionality of the library and to provide running example code.
26 | To use this App you need a HackRF connected to your device via an OTG USB cable. Press Open,
27 | give the App the permissions to open the USB device and hit Info to read out the Board ID,
28 | Version, Part ID and Serial Number from your device. RX will start receiving samples at the
29 | given sample rate and store them to a file on your SD card. If you leave the filename blank, all
30 | samples will go to /dev/null. With TX you can transmit a previously recorded file.
31 | For more information on how to use hackrf_android in your own application see
32 | http://tech.mantz-it.com/2014/10/hackrfandroid-using-hackrf-with-android.html
33 | Get the latest version (binaries and sources) from the Github repository:
34 | https://github.com/demantz/hackrf_android
37 | ]]>
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/examples/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/examples/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | HackRF Library for Android
2 | ==========================
3 |
4 | This repository is a ported version of Michael Ossmann's libhackrf
5 | ([https://github.com/mossmann/hackrf/tree/master/host/libhackrf]
6 | (https://github.com/mossmann/hackrf/tree/master/host/libhackrf))
7 | library to work with Android 3.1+.
8 |
9 |
10 |
11 | 
12 |
13 | (photo by Dennis Mantz)
14 |
15 | See [http://tech.mantz-it.com](http://tech.mantz-it.com) and @dennismantz for updates.
16 |
17 |
18 | Implemented Features
19 | --------------------
20 | * Open HackRF (including the USB permission request)
21 | * Reading Board ID from HackRF
22 | * Reading Version from HackRF
23 | * Reading Part ID and Serial Number from HackRF
24 | * Setting Sample Rate of HackRF
25 | * Setting Frequency of HackRF
26 | * Setting Baseband Filter Width of HackRF
27 | * Compute Baseband Filter Width for given Sample Rate
28 | * Setting VGA Gain (Rx/Tx) of HackRF
29 | * Setting LNA Gain of HackRF
30 | * Setting Amplifier of HackRF
31 | * Setting Antenna Port Power of HackRF
32 | * Setting Transceiver Mode of HackRF
33 | * Receiving from the HackRF using a BlockingQueue
34 | * Transmitting to the HackRF using a BlockingQueue
35 | * Get Transmission statistics
36 | * Example App that shows how to use the library
37 |
38 |
39 | Tested Devices
40 | --------------
41 |
42 | | Device | Does it work? | Comments | Tester |
43 | |:------------------:|:-------------:|:-----------------------------------------:|:----------------:|
44 | | Oneplus One | yes | | KR0SIV |
45 | | Nexus 7 2012 | yes | ~ 2 Msps, Filewriter is too slow. | demantz |
46 | | Nexus 7 2013 | yes | 15 Msps | @kx3companion |
47 | | Nexus 4 | needs ROM... | ...and Y cable. (http://goo.gl/rRyQVM) | - |
48 | | Nexus 5 | yes | 15 Msps | demantz |
49 | | Moto G | yes | ~ 2 Msps | @kx3companion |
50 | | Acer A500 | yes | ~ 5 Msps | @digiital |
51 | | Samsung S3 LTE | yes | 10 Msps, running CM 10.1.3 | dc1rdb |
52 | | Samsung S4 | yes | | @digiital |
53 | | Samsung S4 LTE | yes | 10 Msps | Jonyweb |
54 | | Samsung S4 Zoom | yes | | Ace Rimmer |
55 | | Samsung S5 | yes | | @simonroses |
56 | | Samsung Note 3 | yes | | @M3atShi3ld |
57 | | Galaxy Tab S 8.4 | yes | | Christophe Morel |
58 | | Galaxy Tab S 10.5 | yes | | Harald Pedersen |
59 | | HTC M8 | yes | | dmaynor |
60 | | Sony Xperia Pro | no | insufficient USB bus power | anttivs |
61 | | Sony Xperia Z2 | yes | | Harald Pedersen |
62 | | LG G2 | yes | | @michaelossmann |
63 | | LG G3 | yes | | @HashHacks |
64 | | Moto G 4G | yes | | @paul2dart |
65 | | Motorola Xoom M601 | yes | | Reg Blank |
66 | | Dragon Touch A1X | yes | | John Wright |
67 | | Moto X (1st gen) | yes | | sabas1080 |
68 |
69 |
70 | Known Issues
71 | ------------
72 | * USB connection is too slow for Sample Rates >15 Msps (testet on Nexus 7)
73 | * FileWriter in example app is too slow. Only works for ~ 2 Msps on old devices.
74 | * It seems that the HackRF sometimes gets to less power if using a to long or low
75 | quality USB cable.
76 |
77 |
78 | Installation / Usage
79 | --------------------
80 | Build the library and the example app by using the Android Studio projects:
81 | * hackrf_android/
82 | * hackrf_android/examples/
83 |
84 | If you want to use the library in your own app, just copy the
85 | hackrf_android/app/build/outputs/aar/app-debug.aar file into your project and
86 | include it as a library. See the example project to learn how to use the library.
87 |
88 | The hackrf_android.aar and the HackRF_Test.apk files are also in this repository
89 | so that they can be used without building them. But they won't be synched to the
90 | latest code base all the time.
91 |
92 | A short howto to get started can be found on this blog post:
93 | [http://tech.mantz-it.com/2014/10/hackrfandroid-using-hackrf-with-android.html]
94 | (http://tech.mantz-it.com/2014/10/hackrfandroid-using-hackrf-with-android.html)
95 |
96 | Use the example application to test the library on your device and trouble shoot
97 | any problems. It has the option to show the logcat output!
98 |
99 | License
100 | -------
101 | This library is free software; you can redistribute it and/or
102 | modify it under the terms of the GNU General Public
103 | License as published by the Free Software Foundation; either
104 | version 2 of the License, or (at your option) any later version.
105 | [http://www.gnu.org/licenses/gpl.html](http://www.gnu.org/licenses/gpl.html) GPL version 2 or higher
106 |
107 | principal author: Dennis Mantz
108 |
109 | principal author of libhackrf: Michael Ossmann
110 |
--------------------------------------------------------------------------------
/examples/hackRF_Test/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
27 |
28 |
35 |
36 |
43 |
44 |
51 |
52 |
60 |
61 |
71 |
72 |
80 |
81 |
91 |
92 |
100 |
101 |
109 |
110 |
117 |
118 |
127 |
128 |
135 |
136 |
145 |
146 |
153 |
154 |
161 |
162 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
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 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/examples/hackRF_Test/hackRF_Test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
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 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 | 51 Franklin Street, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Library General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C) 19yy
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License
307 | along with this program; if not, write to the Free Software
308 | Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA
309 |
310 |
311 | Also add information on how to contact you by electronic and paper mail.
312 |
313 | If the program is interactive, make it output a short notice like this
314 | when it starts in an interactive mode:
315 |
316 | Gnomovision version 69, Copyright (C) 19yy name of author
317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 | This is free software, and you are welcome to redistribute it
319 | under certain conditions; type `show c' for details.
320 |
321 | The hypothetical commands `show w' and `show c' should show the appropriate
322 | parts of the General Public License. Of course, the commands you use may
323 | be called something other than `show w' and `show c'; they could even be
324 | mouse-clicks or menu items--whatever suits your program.
325 |
326 | You should also get your employer (if you work as a programmer) or your
327 | school, if any, to sign a "copyright disclaimer" for the program, if
328 | necessary. Here is a sample; alter the names:
329 |
330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
332 |
333 | , 1 April 1989
334 | Ty Coon, President of Vice
335 |
336 | This General Public License does not permit incorporating your program into
337 | proprietary programs. If your program is a subroutine library, you may
338 | consider it more useful to permit linking proprietary applications with the
339 | library. If this is what you want to do, use the GNU Library General
340 | Public License instead of this License.
341 |
--------------------------------------------------------------------------------
/examples/hackRF_Test/src/main/java/com/mantz_it/hackrf_test/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.mantz_it.hackrf_test;
2 |
3 | import android.content.Intent;
4 | import android.content.pm.PackageManager;
5 | import android.content.pm.PackageManager.NameNotFoundException;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 | import android.os.Environment;
9 | import android.os.Handler;
10 | import android.support.v4.app.ActivityCompat;
11 | import android.support.v4.content.ContextCompat;
12 | import android.support.v4.content.FileProvider;
13 | import android.support.v7.app.AppCompatActivity;
14 | import android.text.Html;
15 | import android.text.method.ScrollingMovementMethod;
16 | import android.util.Log;
17 | import android.view.Menu;
18 | import android.view.MenuItem;
19 | import android.view.View;
20 | import android.widget.Button;
21 | import android.widget.CheckBox;
22 | import android.widget.EditText;
23 | import android.widget.SeekBar;
24 | import android.widget.TextView;
25 |
26 | import com.mantz_it.hackrf_android.Hackrf;
27 | import com.mantz_it.hackrf_android.HackrfCallbackInterface;
28 | import com.mantz_it.hackrf_android.HackrfUsbException;
29 |
30 | import java.io.BufferedInputStream;
31 | import java.io.BufferedOutputStream;
32 | import java.io.File;
33 | import java.io.FileInputStream;
34 | import java.io.FileOutputStream;
35 | import java.io.IOException;
36 | import java.util.concurrent.ArrayBlockingQueue;
37 | import java.util.concurrent.TimeUnit;
38 |
39 | /**
40 | *
Hackrf_Test
41 | *
42 | * Module: MainActivity.java
43 | * Description: This Android application shows the usage of the hackrf_android
44 | * library. It offers a simple user interface with buttons to open
45 | * the device, print the device info on the screen and start / stop
46 | * receiving / transmitting.
47 | *
48 | * @author Dennis Mantz
49 | *
50 | * Copyright (C) 2014 Dennis Mantz
51 | * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
52 | *
53 | * This library is free software; you can redistribute it and/or
54 | * modify it under the terms of the GNU General Public
55 | * License as published by the Free Software Foundation; either
56 | * version 2 of the License, or (at your option) any later version.
57 | *
58 | * This library is distributed in the hope that it will be useful,
59 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
60 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
61 | * General Public License for more details.
62 | *
63 | * You should have received a copy of the GNU General Public
64 | * License along with this library; if not, write to the Free Software
65 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
66 | */
67 | public class MainActivity extends AppCompatActivity implements Runnable, HackrfCallbackInterface{
68 |
69 | private static final int PERMISSION_REQUEST_WRITE_FILES = 100;
70 | private static final int PERMISSION_REQUEST_READ_FILES = 101;
71 |
72 | // References to the GUI elements:
73 | private Button bt_openHackRF = null;
74 | private Button bt_info = null;
75 | private Button bt_rx = null;
76 | private Button bt_tx = null;
77 | private Button bt_stop = null;
78 | private EditText et_sampRate = null;
79 | private EditText et_freq = null;
80 | private EditText et_filename = null;
81 | private SeekBar sb_vgaGain = null;
82 | private SeekBar sb_lnaGain = null;
83 | private CheckBox cb_amp = null;
84 | private CheckBox cb_antenna = null;
85 | private TextView tv_output = null;
86 |
87 | // Reference to the hackrf instance:
88 | private Hackrf hackrf = null;
89 |
90 | private int sampRate = 0;
91 | private long frequency = 0;
92 | private String filename = null;
93 | private int vgaGain = 0;
94 | private int lnaGain = 0;
95 | private boolean amp = false;
96 | private boolean antennaPower = false;
97 |
98 | // The handler is used to access GUI elements from other threads then the GUI thread
99 | private Handler handler = null;
100 |
101 | // This variable is used to select what the thread should do if it is started
102 | private int task = -1;
103 | private static final int PRINT_INFO = 0;
104 | private static final int RECEIVE = 1;
105 | private static final int TRANSMIT = 2;
106 |
107 | private boolean stopRequested = false; // Used to stop receive/transmit thread
108 |
109 | // Set this to true to rewind sample file every time the end is reached:
110 | private boolean repeatTransmitting = false;
111 |
112 | // Folder name for capture files:
113 | private static final String foldername = "Test_HackRF";
114 |
115 | // logcat process:
116 | Process logcat;
117 | File logfile;
118 |
119 | // This method is called on application startup by the Android System:
120 | @Override
121 | protected void onCreate(Bundle savedInstanceState) {
122 | super.onCreate(savedInstanceState);
123 | setContentView(R.layout.activity_main);
124 |
125 | // Start logging:
126 | try{
127 | logfile = new File(Environment.getExternalStorageDirectory() + "/" + foldername, "log.txt");
128 | logfile.getParentFile().mkdir(); // Create folder
129 | logcat = Runtime.getRuntime().exec("logcat -f " + logfile.getAbsolutePath());
130 | Log.i("MainActivity", "onCreate: log path: " + logfile.getAbsolutePath());
131 | } catch (Exception e) {
132 | Log.e("MainActivity", "onCreate: Failed to start logging!");
133 | }
134 |
135 | // Create a Handler instance to use in other threads:
136 | handler = new Handler();
137 |
138 | // Initialize the GUI references:
139 | bt_info = ((Button) this.findViewById(R.id.bt_info));
140 | bt_rx = ((Button) this.findViewById(R.id.bt_rx));
141 | bt_tx = ((Button) this.findViewById(R.id.bt_tx));
142 | bt_stop = ((Button) this.findViewById(R.id.bt_stop));
143 | bt_openHackRF = ((Button) this.findViewById(R.id.bt_openHackRF));
144 | et_sampRate = (EditText) this.findViewById(R.id.et_sampRate);
145 | et_freq = (EditText) this.findViewById(R.id.et_freq);
146 | et_filename = (EditText) this.findViewById(R.id.et_filename);
147 | sb_vgaGain = (SeekBar) this.findViewById(R.id.sb_vgaGain);
148 | sb_lnaGain = (SeekBar) this.findViewById(R.id.sb_lnaGain);
149 | cb_amp = (CheckBox) this.findViewById(R.id.cb_amp);
150 | cb_antenna = (CheckBox) this.findViewById(R.id.cb_antenna);
151 | tv_output = (TextView) findViewById(R.id.tv_output);
152 | tv_output.setMovementMethod(new ScrollingMovementMethod()); // make it scroll!
153 | this.toggleButtonsEnabledIfHackrfReady(false); // Disable all buttons except for 'Open HackRF'
154 |
155 | // Print Hello
156 | String version = "";
157 | try {
158 | version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
159 | } catch (NameNotFoundException e) {}
160 | this.tv_output.setText("Test_HackRF (version " + version + ") by Dennis Mantz\n");
161 |
162 | // Check for the WRITE_EXTERNAL_STORAGE permission (needed for logging):
163 | if (ContextCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE")
164 | != PackageManager.PERMISSION_GRANTED) {
165 | printOnScreen("Warning: Logfile cannot be written (no Storage Permission)!\n");
166 | }
167 | }
168 |
169 | @Override
170 | protected void onDestroy() {
171 | if(logcat != null)
172 | logcat.destroy();
173 | super.onDestroy();
174 | }
175 |
176 | @Override
177 | public boolean onCreateOptionsMenu(Menu menu) {
178 | // Inflate the menu; this adds items to the action bar if it is present.
179 | getMenuInflater().inflate(R.menu.main, menu);
180 | return true;
181 | }
182 |
183 | @Override
184 | public boolean onOptionsItemSelected(MenuItem item) {
185 | // Handle action bar item clicks here. The action bar will
186 | // automatically handle clicks on the Home/Up button, so long
187 | // as you specify a parent activity in AndroidManifest.xml.
188 | int id = item.getItemId();
189 | if (id == R.id.action_help) {
190 | this.tv_output.setText(Html.fromHtml(getResources().getString(R.string.helpText)));
191 | return true;
192 | }
193 | if (id == R.id.action_showLog) {
194 | Uri uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", logfile);
195 | Intent intent = new Intent(Intent.ACTION_VIEW);
196 | intent.setDataAndType(uri, "text/plain");
197 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
198 | intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
199 | this.startActivity(intent);
200 | return true;
201 | }
202 | return super.onOptionsItemSelected(item);
203 | }
204 |
205 | @Override
206 | public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
207 | switch (requestCode) {
208 | case PERMISSION_REQUEST_READ_FILES: {
209 | // If request is cancelled, the result arrays are empty.
210 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
211 | printOnScreen("Permission granted to read files. Start rx!\n");
212 | tx(null);
213 | }
214 | break;
215 | }
216 | case PERMISSION_REQUEST_WRITE_FILES: {
217 | // If request is cancelled, the result arrays are empty.
218 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
219 | printOnScreen("Permission granted to write files. Start rx!\n");
220 | rx(null);
221 | }
222 | break;
223 | }
224 | }
225 | }
226 |
227 |
228 | /**
229 | * Will append the message to the tv_output TextView. Can be called from
230 | * outside the GUI thread because it uses the handler reference to access
231 | * the TextView.
232 | *
233 | * @param msg Message to print on the screen
234 | */
235 | public void printOnScreen(final String msg)
236 | {
237 | handler.post(new Runnable() {
238 | public void run() {
239 | tv_output.append(msg);
240 | }
241 | });
242 | }
243 |
244 | /**
245 | * Will set all buttons to disabled except for the 'open hackrf' button. If
246 | * false is given, the behavior toggles. Can be called from
247 | * outside the GUI thread because it uses the handler reference to access
248 | * the TextView.
249 | *
250 | * @param enable if true: 'Open HackRf' will be enabled, all others disabled
251 | * if false: The other way round
252 | */
253 | public void toggleButtonsEnabledIfHackrfReady(final boolean enable)
254 | {
255 | handler.post(new Runnable() {
256 | public void run() {
257 | bt_info.setEnabled(enable);
258 | bt_rx.setEnabled(enable);
259 | bt_tx.setEnabled(enable);
260 | bt_stop.setEnabled(enable);
261 | bt_openHackRF.setEnabled(!enable);
262 | }
263 | });
264 | }
265 |
266 | /**
267 | * Will set 'Info', 'RX' and 'TX' to disabled and 'stop' to enabled (while
268 | * receiving/transmitting is running). If false is given, the behavior toggles.
269 | * Can be called from outside the GUI thread because it uses the handler
270 | * reference to access the TextView.
271 | *
272 | * @param enable if true: 'Stop' will be enabled, all others ('Info', 'RX' and 'TX') disabled
273 | * if false: The other way round
274 | */
275 | public void toggleButtonsEnabledIfTransceiving(final boolean enable)
276 | {
277 | handler.post(new Runnable() {
278 | public void run() {
279 | bt_info.setEnabled(!enable);
280 | bt_rx.setEnabled(!enable);
281 | bt_tx.setEnabled(!enable);
282 | bt_stop.setEnabled(enable);
283 | }
284 | });
285 | }
286 |
287 | /**
288 | * Will read the values from the GUI elements into the corresponding variables
289 | */
290 | public void readGuiElements()
291 | {
292 | sampRate = Integer.valueOf(et_sampRate.getText().toString());
293 | frequency = Long.valueOf(et_freq.getText().toString());
294 | filename = et_filename.getText().toString();
295 | vgaGain = sb_vgaGain.getProgress();
296 | lnaGain = sb_lnaGain.getProgress();
297 | amp = cb_amp.isChecked();
298 | antennaPower = cb_antenna.isChecked();
299 | }
300 |
301 | /**
302 | * Is called if the user presses the 'Open HackRF' Button. Will initialize the
303 | * HackRF device.
304 | *
305 | * @param view Reference to the calling View (in this case bt_openHackRF)
306 | */
307 | public void openHackrf(View view)
308 | {
309 | int queueSize = 15000000 * 2; // max. 15 Msps with 2 byte each ==> will buffer for 1 second
310 |
311 | // Initialize the HackRF (i.e. open the USB device, which requires the user to give permissions)
312 | if (!Hackrf.initHackrf(view.getContext(), this, queueSize))
313 | {
314 | tv_output.append("No HackRF could be found!\n");
315 | }
316 | // initHackrf() is asynchronous. this.onHackrfReady() will be called as soon as the device is ready.
317 | }
318 |
319 | /**
320 | * Is called if the user presses the 'Info' Button. Will start a Thread that
321 | * retrieves the BoardID, Version String, PartID and Serial number from the device
322 | * and then print the information on the screen.
323 | *
324 | * @param view Reference to the calling View (in this case bt_info)
325 | */
326 | public void info(View view)
327 | {
328 | if (hackrf != null)
329 | {
330 | this.task = PRINT_INFO;
331 | new Thread(this).start();
332 | }
333 | }
334 |
335 | /**
336 | * Is called if the user presses the 'RX' Button. Will start a Thread that
337 | * sets the HackRF into receiving mode and then save the received samples
338 | * to a file. Will run forever until user presses the 'Stop' button.
339 | *
340 | * @param view Reference to the calling View (in this case bt_rx)
341 | */
342 | public void rx(View view)
343 | {
344 | // Check for the WRITE_EXTERNAL_STORAGE permission:
345 | if (ContextCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE")
346 | != PackageManager.PERMISSION_GRANTED) {
347 | printOnScreen("Need to ask for permission to write files...\n");
348 | ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"},
349 | PERMISSION_REQUEST_WRITE_FILES);
350 | return; // wait for the permission response (handled in onRequestPermissionResult())
351 | }
352 |
353 | if (hackrf != null)
354 | {
355 | this.readGuiElements();
356 | this.task = RECEIVE;
357 | this.stopRequested = false;
358 | new Thread(this).start();
359 | toggleButtonsEnabledIfTransceiving(true);
360 | }
361 | }
362 |
363 | /**
364 | * Is called if the user presses the 'TX' Button. Will start a Thread that
365 | * sets the HackRF into transmitting mode and then read the samples
366 | * from a file and pass them to the HackRF. Will run forever until user
367 | * presses the 'Stop' button.
368 | *
369 | * @param view Reference to the calling View (in this case bt_tx)
370 | */
371 | public void tx(View view)
372 | {
373 | // Check for the READ_EXTERNAL_STORAGE permission:
374 | if (ContextCompat.checkSelfPermission(this, "android.permission.READ_EXTERNAL_STORAGE")
375 | != PackageManager.PERMISSION_GRANTED) {
376 | printOnScreen("Need to ask for permission to read files...\n");
377 | ActivityCompat.requestPermissions(this, new String[]{"android.permission.READ_EXTERNAL_STORAGE"},
378 | PERMISSION_REQUEST_WRITE_FILES);
379 | return; // wait for the permission response (handled in onRequestPermissionResult())
380 | }
381 |
382 | if (hackrf != null)
383 | {
384 | this.readGuiElements();
385 | this.task = TRANSMIT;
386 | this.stopRequested = false;
387 | new Thread(this).start();
388 | toggleButtonsEnabledIfTransceiving(true);
389 | }
390 | }
391 |
392 | /**
393 | * Is called if the user presses the 'Stop' Button. Will set the stopRequested
394 | * attribute to true, which will cause any running thread to shut down. It will
395 | * then set the transceiver mode of the HackRF to OFF.
396 | *
397 | * @param view Reference to the calling View (in this case bt_stop)
398 | */
399 | public void stop(View view)
400 | {
401 | this.stopRequested = true;
402 | toggleButtonsEnabledIfTransceiving(false);
403 |
404 | if(hackrf != null)
405 | {
406 | try {
407 | hackrf.stop();
408 | } catch (HackrfUsbException e) {
409 | printOnScreen("Error (USB)!\n");
410 | toggleButtonsEnabledIfHackrfReady(false);
411 | }
412 | }
413 | }
414 |
415 | /**
416 | * Is called by the hackrf_android library after the device is ready.
417 | * Was triggered by the initHackrf() call in openHackrf().
418 | * See also HackrfCallbackInterface.java
419 | *
420 | * @param hackrf Instance of the Hackrf class that represents the open device
421 | */
422 | @Override
423 | public void onHackrfReady(Hackrf hackrf) {
424 | tv_output.append("HackRF is ready!\n");
425 |
426 | this.hackrf = hackrf;
427 | this.toggleButtonsEnabledIfHackrfReady(true);
428 | this.toggleButtonsEnabledIfTransceiving(false);
429 | }
430 |
431 | /**
432 | * Is called by the hackrf_android library after a error occurred while opening
433 | * the device.
434 | * Was triggered by the initHackrf() call in openHackrf().
435 | * See also HackrfCallbackInterface.java
436 | *
437 | * @param message Short human readable error message
438 | */
439 | @Override
440 | public void onHackrfError(String message) {
441 | tv_output.append("Error while opening HackRF: " + message +"\n");
442 | this.toggleButtonsEnabledIfHackrfReady(false);
443 | }
444 |
445 | /**
446 | * Is called (in a separate Thread) after 'new Thread(this).start()' is
447 | * executed in info(), rx() and tx().
448 | * Will run either infoThread(), receiveThread() or transmitThread() depending
449 | * on how the task attribute is set.
450 | */
451 | @Override
452 | public void run() {
453 | switch(this.task)
454 | {
455 | case PRINT_INFO: infoThread(); break;
456 | case RECEIVE: receiveThread(); break;
457 | case TRANSMIT: transmitThread(); break;
458 | default:
459 | }
460 |
461 | }
462 |
463 | /**
464 | * Will run in a separate thread created in info(). Retrieves the BoardID,
465 | * Version String, PartID and Serial number from the device
466 | * and then print the information on the screen.
467 | */
468 | public void infoThread()
469 | {
470 | // Read out boardID, version, partID and serialNo:
471 | try
472 | {
473 | int boardID = hackrf.getBoardID();
474 | printOnScreen("Board ID: " + boardID + " (" + Hackrf.convertBoardIdToString(boardID) + ")\n" );
475 | printOnScreen("Version: " + hackrf.getVersionString() +"\n");
476 | int[] tmp = hackrf.getPartIdAndSerialNo();
477 | printOnScreen("Part ID: 0x" + Integer.toHexString(tmp[0]) +
478 | " 0x" + Integer.toHexString(tmp[1]) +"\n");
479 | printOnScreen("Serial No: 0x" + Integer.toHexString(tmp[2]) +
480 | " 0x" + Integer.toHexString(tmp[3]) +
481 | " 0x" + Integer.toHexString(tmp[4]) +
482 | " 0x" + Integer.toHexString(tmp[5]) +"\n\n");
483 | } catch (HackrfUsbException e) {
484 | printOnScreen("Error while reading Board Information!\n");
485 | this.toggleButtonsEnabledIfHackrfReady(false);
486 | }
487 | }
488 |
489 | /**
490 | * Will run in a separate thread created in rx(). Sets the HackRF into receiving
491 | * mode and then save the received samples to a file. Will run forever until user
492 | * presses the 'Stop' button.
493 | */
494 | public void receiveThread()
495 | {
496 | int basebandFilterWidth = Hackrf.computeBasebandFilterBandwidth((int)(0.75*sampRate));
497 | int i = 0;
498 | long lastTransceiverPacketCounter = 0;
499 | long lastTransceivingTime = 0;
500 |
501 | // vgaGain and lnaGain are still values from 0-100; scale them to the right range:
502 | vgaGain = (vgaGain * 62) / 100;
503 | lnaGain = (lnaGain * 40) / 100;
504 |
505 | try {
506 | // First set all parameters:
507 | printOnScreen("Setting Sample Rate to " + sampRate + " Sps ... ");
508 | hackrf.setSampleRate(sampRate, 1);
509 | printOnScreen("ok.\nSetting Frequency to " + frequency + " Hz ... ");
510 | hackrf.setFrequency(frequency);
511 | printOnScreen("ok.\nSetting Baseband Filter Bandwidth to " + basebandFilterWidth + " Hz ... ");
512 | hackrf.setBasebandFilterBandwidth(basebandFilterWidth);
513 | printOnScreen("ok.\nSetting RX VGA Gain to " + vgaGain + " ... ");
514 | hackrf.setRxVGAGain(vgaGain);
515 | printOnScreen("ok.\nSetting LNA Gain to " + lnaGain + " ... ");
516 | hackrf.setRxLNAGain(lnaGain);
517 | printOnScreen("ok.\nSetting Amplifier to " + amp + " ... ");
518 | hackrf.setAmp(amp);
519 | printOnScreen("ok.\nSetting Antenna Power to " + antennaPower + " ... ");
520 | hackrf.setAntennaPower(antennaPower);
521 | printOnScreen("ok.\n\n");
522 |
523 | // Check if external memory is available:
524 | if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
525 | {
526 | printOnScreen("External Media Storage not available.\n\n");
527 | return;
528 | }
529 |
530 | // Create a file ...
531 | // If no filename was given, write to /dev/null
532 | File file;
533 | if(filename.equals(""))
534 | file = new File("/dev/", "null");
535 | else
536 | file = new File(Environment.getExternalStorageDirectory() + "/" + foldername, filename);
537 | file.getParentFile().mkdir(); // Create folder if it does not exist
538 | printOnScreen("Saving samples to " + file.getAbsolutePath() + "\n");
539 |
540 | // ... and open it with a buffered output stream
541 | BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
542 |
543 | // Start Receiving:
544 | printOnScreen("Start Receiving... \n");
545 | ArrayBlockingQueue queue = hackrf.startRX();
546 |
547 | // Run until user hits the 'Stop' button
548 | while(!this.stopRequested)
549 | {
550 | i++; // only for statistics
551 |
552 | // Grab one packet from the top of the queue. Will block if queue is
553 | // empty and timeout after one second if the queue stays empty.
554 | byte[] receivedBytes = queue.poll(1000, TimeUnit.MILLISECONDS);
555 |
556 | /* HERE should be the DSP portion of the app. The receivedBytes
557 | * variable now contains a byte array of size hackrf.getPacketSize().
558 | * This is currently set to 16KB, but may change in the future.
559 | * The bytes are interleaved, 8-bit, signed IQ samples (in-phase
560 | * component first, followed by the quadrature component):
561 | *
562 | * [--------- first sample ----------] [-------- second sample --------]
563 | * I Q I Q ...
564 | * receivedBytes[0] receivedBytes[1] receivedBytes[2] ...
565 | *
566 | * Note: Make sure you read from the queue fast enough, because if it runs
567 | * full, the hackrf_android library will abort receiving and go back to
568 | * OFF mode.
569 | */
570 |
571 | // We just write the whole packet into the file:
572 | if(receivedBytes != null)
573 | {
574 | // On my Nexus 7 this is to slow for high sample rates. Nexus 5 works, though.
575 | bufferedOutputStream.write(receivedBytes);
576 |
577 | // IMPORTANT: After we used the receivedBytes buffer and don't need it any more,
578 | // we should return it to the buffer pool of the hackrf! This will save a lot of
579 | // allocation time and the garbage collector won't go off every second.
580 | hackrf.returnBufferToBufferPool(receivedBytes);
581 | }
582 | else
583 | {
584 | printOnScreen("Error: Queue is empty! (This happens most often because the queue ran full"
585 | + " which causes the Hackrf class to stop receiving. Writing the samples to a file"
586 | + " seems to be working to slowly... try a lower sample rate.)\n");
587 | break;
588 | }
589 |
590 | // print statistics
591 | if(i%1000 == 0)
592 | {
593 | long bytes = (hackrf.getTransceiverPacketCounter() - lastTransceiverPacketCounter) * hackrf.getPacketSize();
594 | double time = (hackrf.getTransceivingTime() - lastTransceivingTime)/1000.0;
595 | printOnScreen( String.format("Current Transfer Rate: %4.1f MB/s\n",(bytes/time)/1000000.0));
596 | lastTransceiverPacketCounter = hackrf.getTransceiverPacketCounter();
597 | lastTransceivingTime = hackrf.getTransceivingTime();
598 | }
599 | }
600 |
601 | // After loop ended: close the file and print more statistics:
602 | bufferedOutputStream.close();
603 | printOnScreen( String.format("Finished! (Average Transfer Rate: %4.1f MB/s\n",
604 | hackrf.getAverageTransceiveRate()/1000000.0));
605 | printOnScreen(String.format("Recorded %d packets (each %d Bytes) in %5.3f Seconds.\n\n",
606 | hackrf.getTransceiverPacketCounter(), hackrf.getPacketSize(),
607 | hackrf.getTransceivingTime()/1000.0));
608 | toggleButtonsEnabledIfTransceiving(false);
609 | } catch (HackrfUsbException e) {
610 | // This exception is thrown if a USB communication error occurres (e.g. you unplug / reset
611 | // the device while receiving)
612 | printOnScreen("error (USB)!\n");
613 | toggleButtonsEnabledIfHackrfReady(false);
614 | } catch (IOException e) {
615 | // This exception is thrown if the file could not be opened or write fails.
616 | printOnScreen("error (File IO: " + e.getMessage() + ")!\n");
617 | printOnScreen("Note: After granting storage permission, sometimes it is necessary to restart the app!\n");
618 | toggleButtonsEnabledIfTransceiving(false);
619 | } catch (InterruptedException e) {
620 | // This exception is thrown if queue.poll() is interrupted
621 | printOnScreen("error (Queue)!\n");
622 | toggleButtonsEnabledIfTransceiving(false);
623 | }
624 | }
625 |
626 | /**
627 | * Will run in a separate thread created in tx(). Sets the HackRF into transmitting
628 | * mode and then read the samples from a file and pass them to the HackRF. Will run
629 | * forever until user presses the 'Stop' button.
630 | */
631 | public void transmitThread()
632 | {
633 | int basebandFilterWidth = Hackrf.computeBasebandFilterBandwidth((int)(0.75*sampRate));
634 | int i = 0;
635 | long lastTransceiverPacketCounter = 0;
636 | long lastTransceivingTime = 0;
637 |
638 | // vgaGain is still a value from 0-100; scale it to the right range:
639 | vgaGain = (vgaGain * 47) / 100;
640 |
641 | try {
642 | printOnScreen("Setting Sample Rate to " + sampRate + " Sps ... ");
643 | hackrf.setSampleRate(sampRate, 1);
644 | printOnScreen("ok.\nSetting Frequency to " + frequency + " Hz ... ");
645 | hackrf.setFrequency(frequency);
646 | printOnScreen("ok.\nSetting Baseband Filter Bandwidth to " + basebandFilterWidth + " Hz ... ");
647 | hackrf.setBasebandFilterBandwidth(basebandFilterWidth);
648 | printOnScreen("ok.\nSetting TX VGA Gain to " + vgaGain + " ... ");
649 | hackrf.setTxVGAGain(vgaGain);
650 | printOnScreen("ok.\nSetting Amplifier to " + amp + " ... ");
651 | hackrf.setAmp(amp);
652 | printOnScreen("ok.\nSetting Antenna Power to " + antennaPower + " ... ");
653 | hackrf.setAntennaPower(antennaPower);
654 | printOnScreen("ok.\n\n");
655 |
656 | // Check if external memory is available:
657 | if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
658 | {
659 | printOnScreen("External Media Storage not available.\n\n");
660 | return;
661 | }
662 |
663 | // Open a file ...
664 | File file = new File(Environment.getExternalStorageDirectory() + "/" + foldername, filename);
665 | printOnScreen("Reading samples from " + file.getAbsolutePath() + "\n");
666 | if(!file.exists())
667 | {
668 | printOnScreen("Error: File does not exist!");
669 | return;
670 | }
671 |
672 | // ... and open it with a buffered input stream
673 | BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
674 |
675 | // Start Transmitting:
676 | printOnScreen("Start Transmitting... \n");
677 | ArrayBlockingQueue queue = hackrf.startTX();
678 |
679 | // Run until user hits the 'Stop' button
680 | while(!this.stopRequested)
681 | {
682 | i++; // only for statistics
683 |
684 | // IMPORTANT: We don't allocate the buffer for a packet ourself. We use the getBufferFromBufferPool()
685 | // method of the hackrf instance! This might give us an already allocated buffer from the buffer pool
686 | // and save a lot of time and memory! You will get a java.lang.OutOfMemoryError if you don't do that
687 | // trust me ;) If no buffer is available in the pool, this method will automatically allocate a buffer
688 | // of the correct size!
689 | byte[] packet = hackrf.getBufferFromBufferPool();
690 |
691 | // Read one packet from the file:
692 | if(bufferedInputStream.read(packet, 0, packet.length) != packet.length)
693 | {
694 | // If repeatTransmitting is set, we rewind. Otherwise we stop:
695 | if(this.repeatTransmitting)
696 | {
697 | printOnScreen("Reached End of File. Start over.\n");
698 | bufferedInputStream.close();
699 | new BufferedInputStream(new FileInputStream(file));
700 | }
701 | else
702 | {
703 | printOnScreen("Reached End of File. Stop.\n");
704 | break;
705 | }
706 | }
707 |
708 | // Put the packet into the queue:
709 | if(queue.offer(packet, 1000, TimeUnit.MILLISECONDS) == false)
710 | {
711 | printOnScreen("Error: Queue is full. Stop transmitting.\n");
712 | break;
713 | }
714 |
715 | // print statistics
716 | if(i%1000 == 0)
717 | {
718 | long bytes = (hackrf.getTransceiverPacketCounter() - lastTransceiverPacketCounter) * hackrf.getPacketSize();
719 | double time = (hackrf.getTransceivingTime() - lastTransceivingTime)/1000.0;
720 | printOnScreen( String.format("Current Transfer Rate: %4.1f MB/s\n",(bytes/time)/1000000.0));
721 | lastTransceiverPacketCounter = hackrf.getTransceiverPacketCounter();
722 | lastTransceivingTime = hackrf.getTransceivingTime();
723 | }
724 | }
725 |
726 | // After loop ended: close the file and print more statistics:
727 | bufferedInputStream.close();
728 | printOnScreen( String.format("Finished! (Average Transfer Rate: %4.1f MB/s)\n",
729 | hackrf.getAverageTransceiveRate()/1000000.0));
730 | printOnScreen(String.format("Transmitted %d packets (each %d Bytes) in %5.3f Seconds.\n\n",
731 | hackrf.getTransceiverPacketCounter(), hackrf.getPacketSize(),
732 | hackrf.getTransceivingTime()/1000.0));
733 | toggleButtonsEnabledIfTransceiving(false);
734 | } catch (HackrfUsbException e) {
735 | printOnScreen("Error (USB)!\n");
736 | toggleButtonsEnabledIfHackrfReady(false);
737 | } catch (IOException e) {
738 | printOnScreen("error (File IO: " + e.getMessage() + ")!\n");
739 | printOnScreen("Note: After granting storage permission, sometimes it is necessary to restart the app!\n");
740 | toggleButtonsEnabledIfTransceiving(false);
741 | } catch (InterruptedException e) {
742 | printOnScreen("Error (Queue interrupted)!\n");
743 | toggleButtonsEnabledIfTransceiving(false);
744 | }
745 | }
746 | }
747 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mantz_it/hackrf_android/Hackrf.java:
--------------------------------------------------------------------------------
1 | package com.mantz_it.hackrf_android;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 | import java.nio.ByteBuffer;
6 | import java.util.HashMap;
7 | import java.util.Iterator;
8 | import java.util.concurrent.ArrayBlockingQueue;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | import android.app.PendingIntent;
12 | import android.content.BroadcastReceiver;
13 | import android.content.Context;
14 | import androidx.core.content.ContextCompat;
15 | import android.content.Intent;
16 | import android.content.IntentFilter;
17 | import android.hardware.usb.UsbConstants;
18 | import android.hardware.usb.UsbDevice;
19 | import android.hardware.usb.UsbDeviceConnection;
20 | import android.hardware.usb.UsbEndpoint;
21 | import android.hardware.usb.UsbInterface;
22 | import android.hardware.usb.UsbManager;
23 | import android.hardware.usb.UsbRequest;
24 | import android.util.Log;
25 | import android.widget.Toast;
26 |
27 | /**
28 | *
HackRF USB Library for Android
29 | *
30 | * Module: Hackrf.java
31 | * Description: The Hackrf class represents the HackRF device and
32 | * acts as abstraction layer that manages the USB
33 | * communication between the device and the application.
34 | *
35 | * @author Dennis Mantz
36 | *
37 | * Copyright (C) 2014 Dennis Mantz
38 | * based on code of libhackrf [https://github.com/mossmann/hackrf/tree/master/host/libhackrf]:
39 | * Copyright (c) 2012, Jared Boone
40 | * Copyright (c) 2013, Benjamin Vernoux
41 | * Copyright (c) 2013, Michael Ossmann
42 | * All rights reserved.
43 | * Redistribution and use in source and binary forms, with or without modification,
44 | * are permitted provided that the following conditions are met:
45 | * - Redistributions of source code must retain the above copyright notice, this list
46 | * of conditions and the following disclaimer.
47 | * - Redistributions in binary form must reproduce the above copyright notice, this
48 | * list of conditions and the following disclaimer in the documentation and/or other
49 | * materials provided with the distribution.
50 | * - Neither the name of Great Scott Gadgets nor the names of its contributors may be
51 | * used to endorse or promote products derived from this software without specific
52 | * prior written permission.
53 | * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
54 | *
55 | * This library is free software; you can redistribute it and/or
56 | * modify it under the terms of the GNU General Public
57 | * License as published by the Free Software Foundation; either
58 | * version 2 of the License, or (at your option) any later version.
59 | *
60 | * This library is distributed in the hope that it will be useful,
61 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
62 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
63 | * General Public License for more details.
64 | *
65 | * You should have received a copy of the GNU General Public
66 | * License along with this library; if not, write to the Free Software
67 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
68 | */
69 | public class Hackrf implements Runnable{
70 |
71 | // Attributes to hold the USB related objects:
72 | private UsbManager usbManager = null;
73 | private UsbDevice usbDevice = null;
74 | private UsbInterface usbInterface = null;
75 | private UsbDeviceConnection usbConnection = null;
76 | private UsbEndpoint usbEndpointIN = null;
77 | private UsbEndpoint usbEndpointOUT = null;
78 |
79 | private int transceiverMode = HACKRF_TRANSCEIVER_MODE_OFF; // current mode of the HackRF
80 | private Thread usbThread = null; // hold the transceiver Thread if running
81 | private ArrayBlockingQueue queue = null; // queue that buffers samples to pass them
82 | // between hackrf_android and the application
83 | private ArrayBlockingQueue bufferPool = null; // queue that holds old buffers which can be
84 | // reused while receiving or transmitting samples
85 |
86 | // startTime (in ms since 1970) and packetCounter for statistics:
87 | private long transceiveStartTime = 0;
88 | private long transceivePacketCounter = 0;
89 |
90 | // Transceiver Modes:
91 | public static final int HACKRF_TRANSCEIVER_MODE_OFF = 0;
92 | public static final int HACKRF_TRANSCEIVER_MODE_RECEIVE = 1;
93 | public static final int HACKRF_TRANSCEIVER_MODE_TRANSMIT = 2;
94 |
95 | // USB Vendor Requests (from hackrf.c)
96 | private static final int HACKRF_VENDOR_REQUEST_SET_TRANSCEIVER_MODE = 1;
97 | private static final int HACKRF_VENDOR_REQUEST_MAX2837_WRITE = 2;
98 | private static final int HACKRF_VENDOR_REQUEST_MAX2837_READ = 3;
99 | private static final int HACKRF_VENDOR_REQUEST_SI5351C_WRITE = 4;
100 | private static final int HACKRF_VENDOR_REQUEST_SI5351C_READ = 5;
101 | private static final int HACKRF_VENDOR_REQUEST_SAMPLE_RATE_SET = 6;
102 | private static final int HACKRF_VENDOR_REQUEST_BASEBAND_FILTER_BANDWIDTH_SET = 7;
103 | private static final int HACKRF_VENDOR_REQUEST_RFFC5071_WRITE = 8;
104 | private static final int HACKRF_VENDOR_REQUEST_RFFC5071_READ = 9;
105 | private static final int HACKRF_VENDOR_REQUEST_SPIFLASH_ERASE = 10;
106 | private static final int HACKRF_VENDOR_REQUEST_SPIFLASH_WRITE = 11;
107 | private static final int HACKRF_VENDOR_REQUEST_SPIFLASH_READ = 12;
108 | private static final int HACKRF_VENDOR_REQUEST_BOARD_ID_READ = 14;
109 | private static final int HACKRF_VENDOR_REQUEST_VERSION_STRING_READ = 15;
110 | private static final int HACKRF_VENDOR_REQUEST_SET_FREQ = 16;
111 | private static final int HACKRF_VENDOR_REQUEST_AMP_ENABLE = 17;
112 | private static final int HACKRF_VENDOR_REQUEST_BOARD_PARTID_SERIALNO_READ = 18;
113 | private static final int HACKRF_VENDOR_REQUEST_SET_LNA_GAIN = 19;
114 | private static final int HACKRF_VENDOR_REQUEST_SET_VGA_GAIN = 20;
115 | private static final int HACKRF_VENDOR_REQUEST_SET_TXVGA_GAIN = 21;
116 | private static final int HACKRF_VENDOR_REQUEST_ANTENNA_ENABLE = 23;
117 | private static final int HACKRF_VENDOR_REQUEST_SET_FREQ_EXPLICIT = 24;
118 |
119 | // RF Filter Paths (from hackrf.c)
120 | public static final int RF_PATH_FILTER_BYPASS = 0;
121 | public static final int RF_PATH_FILTER_LOW_PASS = 1;
122 | public static final int RF_PATH_FILTER_HIGH_PASS = 2;
123 |
124 | // Some Constants:
125 | private static final String logTag = "hackrf_android";
126 | private static final String HACKRF_USB_PERMISSION = "com.mantz_it.hackrf_android.USB_PERMISSION";
127 | private static final int numUsbRequests = 4; // Number of parallel UsbRequests
128 | private static final int packetSize = 1024*256; // Buffer Size of each UsbRequest
129 |
130 | /**
131 | * Initializing the Hackrf Instance with a USB Device. This will try to request
132 | * the permissions to open the USB device and then create an instance of
133 | * the Hackrf class and pass it back via the callbackInterface
134 | *
135 | * @param context Application context. Used to retrieve System Services (USB)
136 | * @param callbackInterface This interface declares two methods that are called if the
137 | * device is ready or if there was an error
138 | * @param queueSize Size of the receive/transmit queue (will hold queueSize buffers with packetSize bytes each)
139 | * @return false if no Hackrf could be found
140 | */
141 | public static boolean initHackrf(Context context, final HackrfCallbackInterface callbackInterface, final int queueSize)
142 | {
143 | final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
144 | UsbDevice hackrfUsbDvice = null;
145 |
146 | if(usbManager == null) {
147 | Log.e(logTag, "initHackrf: Couldn't get an instance of UsbManager!");
148 | return false;
149 | }
150 |
151 | // Get a list of connected devices
152 | HashMap deviceList = usbManager.getDeviceList();
153 |
154 | if(deviceList == null) {
155 | Log.e(logTag, "initHackrf: Couldn't read the USB device list!");
156 | return false;
157 | }
158 |
159 | Log.i(logTag, "initHackrf: Found " + deviceList.size() + " USB devices.");
160 |
161 | // Iterate over the list. Use the first Device that matches a HackRF
162 | Iterator deviceIterator = deviceList.values().iterator();
163 | while(deviceIterator.hasNext()){
164 | UsbDevice device = deviceIterator.next();
165 |
166 | Log.d(logTag,"initHackrf: deviceList: vendor="+device.getVendorId() + " product="+device.getProductId());
167 |
168 | // HackRF One (Vendor ID: 7504 [0x1d50]; Product ID: 24713 [0x6089] )
169 | if ( device.getVendorId() == 7504 && device.getProductId() == 24713 )
170 | {
171 | Log.i(logTag,"initHackrf: Found HackRF One at " + device.getDeviceName());
172 | hackrfUsbDvice = device;
173 | }
174 |
175 | // rad1o (Vendor ID: 7504 [0x1d50]; Product ID: 52245 [0xCC15] )
176 | if ( device.getVendorId() == 7504 && device.getProductId() == 52245 )
177 | {
178 | Log.i(logTag,"initHackrf: Found rad1o at " + device.getDeviceName());
179 | hackrfUsbDvice = device;
180 | }
181 |
182 | // HackRF Jawbreaker (Vendor ID: 7504 [0x1d50]; Product ID: 24651 [0x604b])
183 | if ( device.getVendorId() == 7504 && device.getProductId() == 24651 )
184 | {
185 | Log.i(logTag,"initHackrf: Found HackRF Jawbreaker at " + device.getDeviceName());
186 | hackrfUsbDvice = device;
187 | }
188 | }
189 |
190 | // Check if we found a device:
191 | if (hackrfUsbDvice == null)
192 | {
193 | Log.e(logTag,"initHackrf: No HackRF Device found.");
194 | return false;
195 | }
196 |
197 | // Requesting Permissions:
198 | // First we define a broadcast receiver that handles the permission_granted intend:
199 | BroadcastReceiver permissionBroadcastReceiver = new BroadcastReceiver() {
200 | public void onReceive(Context context, Intent intent) {
201 | if (HACKRF_USB_PERMISSION.equals(intent.getAction())) {
202 | UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
203 | if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) && device != null) {
204 | // We have permissions to open the device! Lets init the hackrf instance and
205 | // return it to the calling application.
206 | Log.d(logTag,"initHackrf: Permission granted for device " + device.getDeviceName());
207 | try {
208 | Hackrf hackrf = new Hackrf(usbManager, device, queueSize);
209 | Toast.makeText(context, "HackRF at " + device.getDeviceName() + " is ready!",Toast.LENGTH_LONG).show();
210 | callbackInterface.onHackrfReady(hackrf);
211 | } catch (HackrfUsbException e) {
212 | Log.e(logTag, "initHackrf: Couldn't open device " + device.getDeviceName());
213 | Toast.makeText(context, "Couldn't open HackRF device",Toast.LENGTH_LONG).show();
214 | callbackInterface.onHackrfError("Couldn't open device " + device.getDeviceName());
215 | }
216 | }
217 | else if(device != null)
218 | {
219 | Log.e(logTag, "initHackrf: Permission denied for device " + device.getDeviceName());
220 | Toast.makeText(context, "Permission denied to open HackRF device",Toast.LENGTH_LONG).show();
221 | callbackInterface.onHackrfError("Permission denied for device " + device.getDeviceName());
222 | } else {
223 | Log.e(logTag, "initHackrf: Error with USB Permission Intent: " + intent.toString());
224 | Toast.makeText(context, "Error with USB Permission Intent",Toast.LENGTH_LONG).show();
225 | callbackInterface.onHackrfError("Error with USB Permission Intent: " + intent.toString());
226 | }
227 | }
228 |
229 | // unregister the Broadcast Receiver:
230 | context.unregisterReceiver(this);
231 | }
232 | };
233 |
234 | Intent innerIntent = new Intent(HACKRF_USB_PERMISSION);
235 | // setting the package name of the inner intent makes it explicit
236 | // From Android 14 it is required that mutable PendingIntents have explicit inner intents!
237 | innerIntent.setPackage(context.getPackageName());
238 | PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, innerIntent, PendingIntent.FLAG_MUTABLE);
239 | IntentFilter filter = new IntentFilter(HACKRF_USB_PERMISSION);
240 | ContextCompat.registerReceiver(context, permissionBroadcastReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED);
241 |
242 | // Fire the request:
243 | usbManager.requestPermission(hackrfUsbDvice, mPermissionIntent);
244 | Log.d(logTag,"Permission request for device " + hackrfUsbDvice.getDeviceName() + " was send. waiting...");
245 |
246 | return true;
247 | }
248 |
249 | /**
250 | * Initializing the Hackrf Instance with a USB Device.
251 | * Note: The application must have reclaimed permissions to
252 | * access the USB Device BEFOR calling this constructor.
253 | *
254 | * @param usbManager Instance of the USB Manager (System Service)
255 | * @param usbDevice Instance of an USB Device representing the HackRF
256 | * @param queueSize Size of the receive/transmit queue (will hold queueSize buffers with packetSize bytes each)
257 | * @throws HackrfUsbException
258 | */
259 | private Hackrf (UsbManager usbManager, UsbDevice usbDevice, int queueSize) throws HackrfUsbException
260 | {
261 | // Initialize the class attributes:
262 | this.usbManager = usbManager;
263 | this.usbDevice = usbDevice;
264 |
265 | // For detailed trouble shooting: Read out information of the device:
266 | Log.i(logTag,"constructor: create Hackrf instance from " + usbDevice.getDeviceName()
267 | + ". Vendor ID: " + usbDevice.getVendorId() + " Product ID: " + usbDevice.getProductId());
268 | Log.i(logTag,"constructor: device protocol: " + usbDevice.getDeviceProtocol());
269 | Log.i(logTag,"constructor: device class: " + usbDevice.getDeviceClass()
270 | + " subclass: " + usbDevice.getDeviceSubclass());
271 | Log.i(logTag,"constructor: interface count: " + usbDevice.getInterfaceCount());
272 |
273 | try {
274 | // Extract interface from the device:
275 | this.usbInterface = usbDevice.getInterface(0);
276 |
277 | // For detailed trouble shooting: Read out interface information of the device:
278 | Log.i(logTag,"constructor: [interface 0] interface protocol: " + usbInterface.getInterfaceProtocol()
279 | + " subclass: " + usbInterface.getInterfaceSubclass());
280 | Log.i(logTag,"constructor: [interface 0] interface class: " + usbInterface.getInterfaceClass());
281 | Log.i(logTag,"constructor: [interface 0] endpoint count: " + usbInterface.getEndpointCount());
282 |
283 | // Extract the endpoints from the device:
284 | this.usbEndpointIN = usbInterface.getEndpoint(0);
285 | this.usbEndpointOUT = usbInterface.getEndpoint(1);
286 |
287 | // For detailed trouble shooting: Read out endpoint information of the interface:
288 | Log.i(logTag,"constructor: [endpoint 0 (IN)] address: " + usbEndpointIN.getAddress()
289 | + " attributes: " + usbEndpointIN.getAttributes() + " direction: " + usbEndpointIN.getDirection()
290 | + " max_packet_size: " + usbEndpointIN.getMaxPacketSize());
291 | Log.i(logTag,"constructor: [endpoint 1 (OUT)] address: " + usbEndpointOUT.getAddress()
292 | + " attributes: " + usbEndpointOUT.getAttributes() + " direction: " + usbEndpointOUT.getDirection()
293 | + " max_packet_size: " + usbEndpointOUT.getMaxPacketSize());
294 |
295 | // Open the device:
296 | this.usbConnection = usbManager.openDevice(usbDevice);
297 |
298 | if(this.usbConnection == null) {
299 | Log.e(logTag, "constructor: Couldn't open HackRF USB Device: openDevice() returned null!");
300 | throw(new HackrfUsbException("Couldn't open HackRF USB Device! (device is gone)"));
301 | }
302 | } catch (Exception e) {
303 | Log.e(logTag, "constructor: Couldn't open HackRF USB Device: " + e.getMessage());
304 | throw(new HackrfUsbException("Error: Couldn't open HackRF USB Device!"));
305 | }
306 |
307 | // Create the queue that is used to transport samples to the application.
308 | this.queue = new ArrayBlockingQueue(queueSize);
309 |
310 | // Create another queue that will be used to collect old buffers for reusing them.
311 | // This will speed up things a lot!
312 | this.bufferPool = new ArrayBlockingQueue(queueSize);
313 | }
314 |
315 | /**
316 | * This returns the size of the packets that are used in receiving /
317 | * transmitting samples. Note that the size is measured in bytes and
318 | * a complex sample always consists of 2 bytes!
319 | *
320 | * @return Packet size in Bytes
321 | */
322 | public int getPacketSize()
323 | {
324 | //return this.usbEndpointIN.getMaxPacketSize(); <= gives 512 which is way too small
325 | return packetSize;
326 | }
327 |
328 | /**
329 | * Get a buffer (byte array with size getPacketSize()) that can be used to hold samples
330 | * for transmitting. Use this function to allocate your buffers which you will pass into the
331 | * queue while transmitting. It will reuse old buffers and save a lot of expensive memory
332 | * allocation and garbage collection time. If no old buffers are existing, it will allocate
333 | * a new one.
334 | *
335 | * @return allocated buffer of size getPacketSize()
336 | */
337 | public byte[] getBufferFromBufferPool()
338 | {
339 | byte[] buffer = this.bufferPool.poll();
340 |
341 | // Check if we got a buffer:
342 | if(buffer == null)
343 | buffer = new byte[getPacketSize()];
344 |
345 | return buffer;
346 | }
347 |
348 | /**
349 | * Returns a buffer that isn't used by the application any more to the buffer pool of this hackrf instance.
350 | * The buffer must be a byte array with size getPacketSize() (the one you got from the queue while receiving).
351 | * This will reuse old buffers while receiving and save a lot of expensive memory
352 | * allocation and garbage collection time.
353 | *
354 | * @param buffer a byte array of size getPacketSize() that is not used by the application any more.
355 | */
356 | public void returnBufferToBufferPool(byte[] buffer)
357 | {
358 | if (buffer.length == getPacketSize())
359 | {
360 | // Throw it into the pool (don't care if it's working or not):
361 | this.bufferPool.offer(buffer);
362 | }
363 | else
364 | Log.w(logTag, "returnBuffer: Got a buffer with wrong size. Ignore it!");
365 | }
366 |
367 | /**
368 | * This returns the number of packets (of size getPacketSize()) received/transmitted since start.
369 | *
370 | * @return Number of packets (of size getPacketSize()) received/transmitted since start
371 | */
372 | public long getTransceiverPacketCounter()
373 | {
374 | return this.transceivePacketCounter;
375 | }
376 |
377 | /**
378 | * This returns the time in milliseconds since receiving/transmitting was started.
379 | *
380 | * @return time in milliseconds since receiving/transmitting was started.
381 | */
382 | public long getTransceivingTime()
383 | {
384 | if(this.transceiveStartTime == 0)
385 | return 0;
386 | return System.currentTimeMillis() - this.transceiveStartTime;
387 | }
388 |
389 | /**
390 | * Returns the average rx/tx transfer rate in byte/seconds.
391 | *
392 | * @return average transfer rate in byte/seconds
393 | */
394 | public long getAverageTransceiveRate()
395 | {
396 | long transTime = this.getTransceivingTime()/1000; // Transfer Time in seconds
397 | if(transTime == 0)
398 | return 0;
399 | return this.getTransceiverPacketCounter() * this.getPacketSize() / transTime;
400 | }
401 |
402 | /**
403 | * Returns the current mode of receiving / transmitting
404 | *
405 | * @return HACKRF_TRANSCEIVER_MODE_OFF, *_RECEIVE, *_TRANSMIT
406 | */
407 | public int getTransceiverMode() {
408 | return transceiverMode;
409 | }
410 |
411 | /**
412 | * Converts a byte array into an integer using little endian byteorder.
413 | *
414 | * @param b byte array (length 4)
415 | * @param offset offset pointing to the first byte in the bytearray that should be used
416 | * @return integer
417 | */
418 | private int byteArrayToInt(byte[] b, int offset)
419 | {
420 | return b[offset+0] & 0xFF | (b[offset+1] & 0xFF) << 8 |
421 | (b[offset+2] & 0xFF) << 16 | (b[offset+3] & 0xFF) << 24;
422 | }
423 |
424 | /**
425 | * Converts a byte array into a long integer using little endian byteorder.
426 | *
427 | * @param b byte array (length 8)
428 | * @param offset offset pointing to the first byte in the bytearray that should be used
429 | * @return long integer
430 | */
431 | private long byteArrayToLong(byte[] b, int offset)
432 | {
433 | return b[offset+0] & 0xFF | (b[offset+1] & 0xFF) << 8 | (b[offset+2] & 0xFF) << 16 |
434 | (b[offset+3] & 0xFF) << 24 | (b[offset+4] & 0xFF) << 32 | (b[offset+5] & 0xFF) << 40 |
435 | (b[offset+6] & 0xFF) << 48 | (b[offset+7] & 0xFF) << 56;
436 | }
437 |
438 | /**
439 | * Converts an integer into a byte array using little endian byteorder.
440 | *
441 | * @param i integer
442 | * @return byte array (length 4)
443 | */
444 | private byte[] intToByteArray(int i)
445 | {
446 | byte[] b = new byte[4];
447 | b[0] = (byte) (i & 0xff);
448 | b[1] = (byte) ((i >> 8) & 0xff);
449 | b[2] = (byte) ((i >> 16) & 0xff);
450 | b[3] = (byte) ((i >> 24) & 0xff);
451 | return b;
452 | }
453 |
454 | /**
455 | * Converts a long integer into a byte array using little endian byteorder.
456 | *
457 | * @param i long integer
458 | * @return byte array (length 8)
459 | */
460 | private byte[] longToByteArray(long i)
461 | {
462 | byte[] b = new byte[8];
463 | b[0] = (byte) (i & 0xff);
464 | b[1] = (byte) ((i >> 8) & 0xff);
465 | b[2] = (byte) ((i >> 16) & 0xff);
466 | b[3] = (byte) ((i >> 24) & 0xff);
467 | b[4] = (byte) ((i >> 32) & 0xff);
468 | b[5] = (byte) ((i >> 40) & 0xff);
469 | b[6] = (byte) ((i >> 48) & 0xff);
470 | b[7] = (byte) ((i >> 56) & 0xff);
471 | return b;
472 | }
473 |
474 | /**
475 | * Executes a Request to the USB interface.
476 | *
477 | * Note: This function interacts with the USB Hardware and
478 | * should not be called from a GUI Thread!
479 | *
480 | * @param endpoint USB_DIR_IN or USB_DIR_OUT
481 | * @param request request type (HACKRF_VENDOR_REQUEST_**_READ)
482 | * @param value value to use in the controlTransfer call
483 | * @param index index to use in the controlTransfer call
484 | * @param buffer buffer to use in the controlTransfer call
485 | * @return count of received bytes. Negative on error
486 | * @throws HackrfUsbException
487 | */
488 | private int sendUsbRequest(int endpoint, int request, int value, int index, byte[] buffer) throws HackrfUsbException
489 | {
490 | int len = 0;
491 |
492 | // Determine the length of the buffer:
493 | if(buffer != null)
494 | len = buffer.length;
495 |
496 | // Claim the usb interface
497 | if( !this.usbConnection.claimInterface(this.usbInterface, true))
498 | {
499 | Log.e(logTag, "Couldn't claim HackRF USB Interface!");
500 | throw(new HackrfUsbException("Couldn't claim HackRF USB Interface!"));
501 | }
502 |
503 | // Send Board ID Read request
504 | len = this.usbConnection.controlTransfer(
505 | endpoint | UsbConstants.USB_TYPE_VENDOR, // Request Type
506 | request, // Request
507 | value, // Value (unused)
508 | index, // Index (unused)
509 | buffer, // Buffer
510 | len, // Length
511 | 0 // Timeout
512 | );
513 |
514 | // Release usb interface
515 | this.usbConnection.releaseInterface(this.usbInterface);
516 |
517 | return len;
518 | }
519 |
520 | /**
521 | * Returns the Board ID of the HackRF.
522 | *
523 | * Note: This function interacts with the USB Hardware and
524 | * should not be called from a GUI Thread!
525 | *
526 | * @return HackRF Board ID
527 | * @throws HackrfUsbException
528 | */
529 | public byte getBoardID() throws HackrfUsbException
530 | {
531 | byte[] buffer = new byte[1];
532 |
533 | if (this.sendUsbRequest(UsbConstants.USB_DIR_IN, HACKRF_VENDOR_REQUEST_BOARD_ID_READ, 0, 0, buffer) != 1)
534 | {
535 | Log.e(logTag, "getBoardID: USB Transfer failed!");
536 | throw(new HackrfUsbException("USB Transfer failed!"));
537 | }
538 |
539 | return buffer[0];
540 | }
541 |
542 | /**
543 | * Converts the Board ID into a human readable String (e.g. #2 => "HackRF One")
544 | *
545 | * @param boardID boardID to convert
546 | * @return Board ID interpretation as String
547 | * @throws HackrfUsbException
548 | */
549 | public static String convertBoardIdToString(int boardID)
550 | {
551 | switch(boardID)
552 | {
553 | case 0: return "Jellybean";
554 | case 1: return "Jawbreaker";
555 | case 2: return "HackRF One";
556 | case 3: return "rad1o";
557 | case 4: return "HackRF One"; // (>=rev9)
558 | default: return "INVALID BOARD ID '" + boardID + "'";
559 | }
560 | }
561 |
562 | /**
563 | * Returns the Version String of the HackRF.
564 | *
565 | * Note: This function interacts with the USB Hardware and
566 | * should not be called from a GUI Thread!
567 | *
568 | * @return HackRF Version String
569 | * @throws HackrfUsbException
570 | */
571 | public String getVersionString() throws HackrfUsbException
572 | {
573 | byte[] buffer = new byte[255];
574 | int len = 0;
575 |
576 | len = this.sendUsbRequest(UsbConstants.USB_DIR_IN, HACKRF_VENDOR_REQUEST_VERSION_STRING_READ, 0, 0, buffer);
577 |
578 | if (len < 1)
579 | {
580 | Log.e(logTag, "getVersionString: USB Transfer failed!");
581 | throw(new HackrfUsbException("USB Transfer failed!"));
582 | }
583 |
584 | return new String(buffer);
585 | }
586 |
587 |
588 | /**
589 | * Returns the Part ID + Serial Number of the HackRF.
590 | *
591 | * Note: This function interacts with the USB Hardware and
592 | * should not be called from a GUI Thread!
593 | *
594 | * @return int[2+6] => int[0-1] is Part ID; int[2-5] is Serial No
595 | * @throws HackrfUsbException
596 | */
597 | public int[] getPartIdAndSerialNo() throws HackrfUsbException
598 | {
599 | byte[] buffer = new byte[8+16];
600 | int[] ret = new int[2+4];
601 |
602 | if(this.sendUsbRequest(UsbConstants.USB_DIR_IN, HACKRF_VENDOR_REQUEST_BOARD_PARTID_SERIALNO_READ,
603 | 0, 0, buffer) != 8+16)
604 | {
605 | Log.e(logTag, "getPartIdAndSerialNo: USB Transfer failed!");
606 | throw(new HackrfUsbException("USB Transfer failed!"));
607 | }
608 |
609 | for(int i = 0; i < 6; i++)
610 | {
611 | ret[i] = this.byteArrayToInt(buffer, 4*i);
612 | }
613 |
614 | return ret;
615 | }
616 |
617 | /**
618 | * Sets the Sample Rate of the HackRF.
619 | *
620 | * Note: This function interacts with the USB Hardware and
621 | * should not be called from a GUI Thread!
622 | *
623 | * @param sampRate Sample Rate in Hz
624 | * @param divider Divider
625 | * @return true on success
626 | * @throws HackrfUsbException
627 | */
628 | public boolean setSampleRate(int sampRate, int divider) throws HackrfUsbException
629 | {
630 | ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
631 |
632 | try {
633 | byteOut.write(this.intToByteArray(sampRate));
634 | byteOut.write(this.intToByteArray(divider));
635 | } catch (IOException e) {
636 | Log.e(logTag,"setSampleRate: Error while converting arguments to byte buffer.");
637 | return false;
638 | }
639 |
640 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_SAMPLE_RATE_SET,
641 | 0, 0, byteOut.toByteArray()) != 8)
642 | {
643 | Log.e(logTag, "setSampleRate: USB Transfer failed!");
644 | throw(new HackrfUsbException("USB Transfer failed!"));
645 | }
646 |
647 | return true;
648 | }
649 |
650 | /**
651 | * Computes a valid Baseband Filter Bandwidth that is closest to
652 | * a given Sample Rate. If there is no exact match, the returned
653 | * Bandwidth will be smaller than the Sample Rate.
654 | *
655 | * @param sampRate Bandwidth for the Baseband Filter
656 | * @return Baseband Filter Bandwidth
657 | * @throws HackrfUsbException
658 | */
659 | public static int computeBasebandFilterBandwidth(int sampRate)
660 | {
661 | int bandwidth = 1750000;
662 | int[] supportedBandwidthValues = {1750000, 2500000, 3500000, 5000000, 5500000,
663 | 6000000, 7000000, 8000000, 9000000, 10000000,
664 | 12000000, 14000000, 15000000, 20000000, 24000000,
665 | 28000000 };
666 |
667 | for(int candidate: supportedBandwidthValues)
668 | {
669 | if(sampRate < candidate)
670 | break;
671 | bandwidth = candidate;
672 | }
673 |
674 | return bandwidth;
675 | }
676 |
677 | /**
678 | * Sets the baseband filter bandwidth of the HackRF.
679 | *
680 | * Note: This function interacts with the USB Hardware and
681 | * should not be called from a GUI Thread!
682 | *
683 | * @param bandwidth Bandwidth for the Baseband Filter
684 | * @return true on success
685 | * @throws HackrfUsbException
686 | */
687 | public boolean setBasebandFilterBandwidth(int bandwidth) throws HackrfUsbException
688 | {
689 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_BASEBAND_FILTER_BANDWIDTH_SET,
690 | bandwidth & 0xffff, (bandwidth >> 16) & 0xffff, null) != 0)
691 | {
692 | Log.e(logTag, "setBasebandFilterBandwidth: USB Transfer failed!");
693 | throw(new HackrfUsbException("USB Transfer failed!"));
694 | }
695 |
696 | return true;
697 | }
698 |
699 | /**
700 | * Sets the RX VGA Gain of the HackRF.
701 | *
702 | * Note: This function interacts with the USB Hardware and
703 | * should not be called from a GUI Thread!
704 | *
705 | * @param gain RX VGA Gain (0-62 in steps of 2)
706 | * @return true on success
707 | * @throws HackrfUsbException
708 | */
709 | public boolean setRxVGAGain(int gain) throws HackrfUsbException
710 | {
711 | byte[] retVal = new byte[1];
712 |
713 | if(gain > 62)
714 | {
715 | Log.e(logTag,"setRxVGAGain: RX VGA Gain must be within 0-62!");
716 | return false;
717 | }
718 |
719 | // Must be in steps of two!
720 | if(gain % 2 != 0)
721 | gain = gain - (gain%2);
722 |
723 | if(this.sendUsbRequest(UsbConstants.USB_DIR_IN, HACKRF_VENDOR_REQUEST_SET_VGA_GAIN,
724 | 0, gain, retVal) != 1)
725 | {
726 | Log.e(logTag, "setRxVGAGain: USB Transfer failed!");
727 | throw(new HackrfUsbException("USB Transfer failed!"));
728 | }
729 |
730 | if (retVal[0] == 0)
731 | {
732 | Log.e(logTag,"setRxVGAGain: HackRF returned with an error!");
733 | return false;
734 | }
735 |
736 | return true;
737 | }
738 |
739 | /**
740 | * Sets the TX VGA Gain of the HackRF.
741 | *
742 | * Note: This function interacts with the USB Hardware and
743 | * should not be called from a GUI Thread!
744 | *
745 | * @param gain TX VGA Gain (0-62)
746 | * @return true on success
747 | * @throws HackrfUsbException
748 | */
749 | public boolean setTxVGAGain(int gain) throws HackrfUsbException
750 | {
751 | byte[] retVal = new byte[1];
752 |
753 | if(gain > 47)
754 | {
755 | Log.e(logTag,"setTxVGAGain: TX VGA Gain must be within 0-47!");
756 | return false;
757 | }
758 |
759 | if(this.sendUsbRequest(UsbConstants.USB_DIR_IN, HACKRF_VENDOR_REQUEST_SET_TXVGA_GAIN,
760 | 0, gain, retVal) != 1)
761 | {
762 | Log.e(logTag, "setTxVGAGain: USB Transfer failed!");
763 | throw(new HackrfUsbException("USB Transfer failed!"));
764 | }
765 |
766 | if (retVal[0] == 0)
767 | {
768 | Log.e(logTag,"setTxVGAGain: HackRF returned with an error!");
769 | return false;
770 | }
771 |
772 | return true;
773 | }
774 |
775 | /**
776 | * Sets the RX LNA Gain of the HackRF.
777 | *
778 | * Note: This function interacts with the USB Hardware and
779 | * should not be called from a GUI Thread!
780 | *
781 | * @param gain RX LNA Gain (0-40 in steps of 8)
782 | * @return true on success
783 | * @throws HackrfUsbException
784 | */
785 | public boolean setRxLNAGain(int gain) throws HackrfUsbException
786 | {
787 | byte[] retVal = new byte[1];
788 |
789 | if(gain > 40)
790 | {
791 | Log.e(logTag,"setRxLNAGain: RX LNA Gain must be within 0-40!");
792 | return false;
793 | }
794 |
795 | // Must be in steps of 8!
796 | if(gain % 8 != 0)
797 | gain = gain - (gain%8);
798 |
799 | if(this.sendUsbRequest(UsbConstants.USB_DIR_IN, HACKRF_VENDOR_REQUEST_SET_LNA_GAIN,
800 | 0, gain, retVal) != 1)
801 | {
802 | Log.e(logTag, "setRxLNAGain: USB Transfer failed!");
803 | throw(new HackrfUsbException("USB Transfer failed!"));
804 | }
805 |
806 | if (retVal[0] == 0)
807 | {
808 | Log.e(logTag,"setRxLNAGain: HackRF returned with an error!");
809 | return false;
810 | }
811 |
812 | return true;
813 | }
814 |
815 | /**
816 | * Sets the Frequency of the HackRF.
817 | *
818 | * Note: This function interacts with the USB Hardware and
819 | * should not be called from a GUI Thread!
820 | *
821 | * @param frequency Frequency in Hz
822 | * @return true on success
823 | * @throws HackrfUsbException
824 | */
825 | public boolean setFrequency(long frequency) throws HackrfUsbException
826 | {
827 | ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
828 | int mhz = (int) (frequency/1000000l);
829 | int hz = (int) (frequency%1000000l);
830 |
831 | Log.d(logTag, "Tune HackRF to " + mhz + "." + hz + "MHz...");
832 |
833 | try {
834 | byteOut.write(this.intToByteArray(mhz));
835 | byteOut.write(this.intToByteArray(hz));
836 | } catch (IOException e) {
837 | Log.e(logTag,"setFrequency: Error while converting arguments to byte buffer.");
838 | return false;
839 | }
840 |
841 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_SET_FREQ,
842 | 0, 0, byteOut.toByteArray()) != 8)
843 | {
844 | Log.e(logTag, "setFrequency: USB Transfer failed!");
845 | throw(new HackrfUsbException("USB Transfer failed!"));
846 | }
847 |
848 | return true;
849 | }
850 |
851 | /**
852 | * Sets the explicit IF and LO frequency of the HackRF.
853 | *
854 | * Note: This function interacts with the USB Hardware and
855 | * should not be called from a GUI Thread!
856 | *
857 | * @param ifFrequency Intermediate Frequency in Hz. Must be in [2150000000; 2750000000]
858 | * @param loFrequency Local Oscillator Frequency in Hz. Must be in [84375000; 5400000000]
859 | * @param rfPath RF_PATH_FILTER_BYPASS, *_HIGH_PASS or *_LOW_PASS
860 | * @return true on success
861 | * @throws HackrfUsbException
862 | */
863 | public boolean setFrequencyExplicit(long ifFrequency, long loFrequency, int rfPath) throws HackrfUsbException
864 | {
865 | ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
866 |
867 | // check range of IF Frequency:
868 | if (ifFrequency < 2150000000l || ifFrequency > 2750000000l) {
869 | Log.e(logTag,"setFrequencyExplicit: IF Frequency must be in [2150000000; 2750000000]!");
870 | return false;
871 | }
872 |
873 | if ((rfPath != RF_PATH_FILTER_BYPASS) && (loFrequency < 84375000l || loFrequency > 5400000000l)) {
874 | Log.e(logTag,"setFrequencyExplicit: LO Frequency must be in [84375000; 5400000000]!");
875 | return false;
876 | }
877 |
878 | // Check if path is in the valid range:
879 | if (rfPath < 0 || rfPath > 2)
880 | {
881 | Log.e(logTag,"setFrequencyExplicit: Invalid value for rf_path!");
882 | return false;
883 | }
884 |
885 | Log.d(logTag, "Tune HackRF to IF:" + ifFrequency + " Hz; LO:" + loFrequency + " Hz...");
886 |
887 | try {
888 | byteOut.write(this.longToByteArray(ifFrequency));
889 | byteOut.write(this.longToByteArray(loFrequency));
890 | byteOut.write(rfPath);
891 | } catch (IOException e) {
892 | Log.e(logTag,"setFrequencyExplicit: Error while converting arguments to byte buffer.");
893 | return false;
894 | }
895 |
896 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_SET_FREQ_EXPLICIT,
897 | 0, 0, byteOut.toByteArray()) != 17)
898 | {
899 | Log.e(logTag, "setFrequencyExplicit: USB Transfer failed!");
900 | throw(new HackrfUsbException("USB Transfer failed!"));
901 | }
902 |
903 | return true;
904 | }
905 |
906 | /**
907 | * Enables or Disables the Amplifier of the HackRF.
908 | *
909 | * Note: This function interacts with the USB Hardware and
910 | * should not be called from a GUI Thread!
911 | *
912 | * @param enable true for enable or false for disable
913 | * @return true on success
914 | * @throws HackrfUsbException
915 | */
916 | public boolean setAmp(boolean enable) throws HackrfUsbException
917 | {
918 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_AMP_ENABLE,
919 | (enable ? 1 : 0) , 0, null) != 0)
920 | {
921 | Log.e(logTag, "setAmp: USB Transfer failed!");
922 | throw(new HackrfUsbException("USB Transfer failed!"));
923 | }
924 |
925 | return true;
926 | }
927 |
928 | /**
929 | * Enables or Disables the Antenna Port Power of the HackRF.
930 | *
931 | * Note: This function interacts with the USB Hardware and
932 | * should not be called from a GUI Thread!
933 | *
934 | * @param enable true for enable or false for disable
935 | * @return true on success
936 | * @throws HackrfUsbException
937 | */
938 | public boolean setAntennaPower(boolean enable) throws HackrfUsbException
939 | {
940 | // The Jawbreaker doesn't support this command!
941 | if(this.getBoardID() == 1) { // == Jawbreaker
942 | Log.w(logTag, "setAntennaPower: Antenna Power is not supported for HackRF Jawbreaker. Ignore.");
943 | return false;
944 | }
945 | // The rad1o doesn't support this command!
946 | if(this.getBoardID() == 3) { // == rad1o
947 | Log.w(logTag, "setAntennaPower: Antenna Power is not supported for rad1o. Ignore.");
948 | return false;
949 | }
950 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_ANTENNA_ENABLE,
951 | (enable ? 1 : 0) , 0, null) != 0)
952 | {
953 | Log.e(logTag, "setAntennaPower: USB Transfer failed!");
954 | throw(new HackrfUsbException("USB Transfer failed!"));
955 | }
956 |
957 | return true;
958 | }
959 |
960 | /**
961 | * Sets the Transceiver Mode of the HackRF (OFF,RX,TX)
962 | *
963 | * Note: This function interacts with the USB Hardware and
964 | * should not be called from a GUI Thread!
965 | *
966 | * @param mode HACKRF_TRANSCEIVER_MODE_OFF, *_RECEIVE or *_TRANSMIT
967 | * @return true on success
968 | * @throws HackrfUsbException
969 | */
970 | public boolean setTransceiverMode(int mode) throws HackrfUsbException
971 | {
972 | if (mode < 0 || mode > 2)
973 | {
974 | Log.e(logTag,"Invalid Transceiver Mode: " + mode);
975 | return false;
976 | }
977 |
978 | this.transceiverMode = mode;
979 |
980 | if(this.sendUsbRequest(UsbConstants.USB_DIR_OUT, HACKRF_VENDOR_REQUEST_SET_TRANSCEIVER_MODE,
981 | mode , 0, null) != 0)
982 | {
983 | Log.e(logTag, "setTransceiverMode: USB Transfer failed!");
984 | throw(new HackrfUsbException("USB Transfer failed!"));
985 | }
986 |
987 | return true;
988 | }
989 |
990 | /**
991 | * Starts receiving.
992 | *
993 | * @return An ArrayBlockingQueue that will fill with the samples as they arrive.
994 | * Each queue element is a block of samples (byte[]) of size getPacketSize().
995 | * @throws HackrfUsbException
996 | */
997 | public ArrayBlockingQueue startRX() throws HackrfUsbException
998 | {
999 | // Flush the queue
1000 | this.queue.clear();
1001 |
1002 | // Signal the HackRF Device to start receiving:
1003 | this.setTransceiverMode(HACKRF_TRANSCEIVER_MODE_RECEIVE);
1004 |
1005 | // Start the Thread to queue the received samples:
1006 | this.usbThread = new Thread(this);
1007 | this.usbThread.start();
1008 |
1009 | // Reset the packet counter and start time for statistics:
1010 | this.transceiveStartTime = System.currentTimeMillis();
1011 | this.transceivePacketCounter = 0;
1012 |
1013 | return this.queue;
1014 | }
1015 |
1016 | /**
1017 | * Starts transmitting.
1018 | *
1019 | * @return An ArrayBlockingQueue from which the hackrf will read the samples to transmit.
1020 | * Each queue element must be a block of samples (byte[]) of size getPacketSize().
1021 | * @throws HackrfUsbException
1022 | */
1023 | public ArrayBlockingQueue startTX() throws HackrfUsbException
1024 | {
1025 | // Flush the queue
1026 | this.queue.clear();
1027 |
1028 | // Signal the HackRF Device to start transmitting:
1029 | this.setTransceiverMode(HACKRF_TRANSCEIVER_MODE_TRANSMIT);
1030 |
1031 | // Start the Thread to queue the received samples:
1032 | this.usbThread = new Thread(this);
1033 | this.usbThread.start();
1034 |
1035 | // Reset the packet counter and start time for statistics:
1036 | this.transceiveStartTime = System.currentTimeMillis();
1037 | this.transceivePacketCounter = 0;
1038 |
1039 | return this.queue;
1040 | }
1041 |
1042 | /**
1043 | * Stops receiving or transmitting.
1044 | *
1045 | * @throws HackrfUsbException
1046 | */
1047 | public void stop() throws HackrfUsbException
1048 | {
1049 | // Signal the HackRF Device to start receiving:
1050 | this.setTransceiverMode(HACKRF_TRANSCEIVER_MODE_OFF);
1051 | }
1052 |
1053 | /**
1054 | * This method will be executed in a separate Thread after the HackRF starts receiving
1055 | * Samples. It will return as soon as the transceiverMode changes or an error occurs.
1056 | */
1057 | private void receiveLoop()
1058 | {
1059 | UsbRequest[] usbRequests = new UsbRequest[numUsbRequests];
1060 | ByteBuffer buffer;
1061 |
1062 | try
1063 | {
1064 | // Create, initialize and queue all usb requests:
1065 | for(int i = 0; i < numUsbRequests; i++)
1066 | {
1067 | // Get a ByteBuffer for the request from the buffer pool:
1068 | buffer = ByteBuffer.wrap(this.getBufferFromBufferPool());
1069 |
1070 | // Initialize the USB Request:
1071 | usbRequests[i] = new UsbRequest();
1072 | usbRequests[i].initialize(usbConnection, usbEndpointIN);
1073 | usbRequests[i].setClientData(buffer);
1074 |
1075 | // Queue the request
1076 | if( usbRequests[i].queue(buffer, getPacketSize()) == false)
1077 | {
1078 | Log.e(logTag,"receiveLoop: Couldn't queue USB Request.");
1079 | this.stop();
1080 | break;
1081 | }
1082 | }
1083 |
1084 | // Run loop until transceiver mode changes...
1085 | while(this.transceiverMode == HACKRF_TRANSCEIVER_MODE_RECEIVE)
1086 | {
1087 | // Wait for a request to return. This will block until one of the requests is ready.
1088 | UsbRequest request = usbConnection.requestWait();
1089 |
1090 | if(request == null)
1091 | {
1092 | Log.e(logTag,"receiveLoop: Didn't receive USB Request.");
1093 | break;
1094 | }
1095 |
1096 | // Make sure we got an UsbRequest for the IN endpoint!
1097 | if(request.getEndpoint() != usbEndpointIN)
1098 | continue;
1099 |
1100 | // Extract the buffer
1101 | buffer = (ByteBuffer) request.getClientData();
1102 |
1103 | // Increment the packetCounter (for statistics)
1104 | this.transceivePacketCounter++;
1105 |
1106 | // Put the received samples into the queue, so that they can be read by the application
1107 | if(!this.queue.offer(buffer.array()))
1108 | {
1109 | // We hit the timeout.
1110 | Log.e(logTag,"receiveLoop: Queue is full. Stop receiving!");
1111 | break;
1112 | }
1113 |
1114 | // Get a fresh ByteBuffer for the request from the buffer pool:
1115 | buffer = ByteBuffer.wrap(this.getBufferFromBufferPool());
1116 | request.setClientData(buffer);
1117 |
1118 | // Queue the request again...
1119 | if(request.queue(buffer, getPacketSize()) == false){
1120 | Log.e(logTag,"receiveLoop: Couldn't queue USB Request.");
1121 | break;
1122 | }
1123 | }
1124 | } catch (HackrfUsbException e) {
1125 | Log.e(logTag,"receiveLoop: USB Error!");
1126 | }
1127 |
1128 | // Receiving is done. Cancel and close all usb requests:
1129 | for(UsbRequest request: usbRequests)
1130 | {
1131 | if(request != null) {
1132 | request.cancel();
1133 | //request.close(); <-- This will cause the VM to crash with a SIGABRT when the next transceive starts?!?
1134 | }
1135 | }
1136 |
1137 | // If the transceiverMode is still on RECEIVE, we stop Receiving:
1138 | if(this.transceiverMode == HACKRF_TRANSCEIVER_MODE_RECEIVE)
1139 | {
1140 | try {
1141 | this.stop();
1142 | } catch (HackrfUsbException e) {
1143 | Log.e(logTag,"receiveLoop: Error while stopping RX!");
1144 | }
1145 | }
1146 | }
1147 |
1148 | /**
1149 | * This method will be executed in a separate Thread after the HackRF starts transmitting
1150 | * Samples. It will return as soon as the transceiverMode changes or an error occurs.
1151 | */
1152 | private void transmitLoop()
1153 | {
1154 | UsbRequest[] usbRequests = new UsbRequest[numUsbRequests];
1155 | ByteBuffer buffer;
1156 | byte[] packet;
1157 |
1158 | try
1159 | {
1160 | // Create, initialize and queue all usb requests:
1161 | for(int i = 0; i < numUsbRequests; i++)
1162 | {
1163 | // Get a packet from the queue:
1164 | packet = (byte[]) queue.poll(1000, TimeUnit.MILLISECONDS);
1165 | if(packet == null || packet.length != getPacketSize())
1166 | {
1167 | Log.e(logTag,"transmitLoop: Queue empty or wrong packet format. Abort.");
1168 | this.stop();
1169 | break;
1170 | }
1171 |
1172 | // Wrap the packet in a ByteBuffer object:
1173 | buffer = ByteBuffer.wrap(packet);
1174 |
1175 | // Initialize the USB Request:
1176 | usbRequests[i] = new UsbRequest();
1177 | usbRequests[i].initialize(usbConnection, usbEndpointOUT);
1178 | usbRequests[i].setClientData(buffer);
1179 |
1180 | // Queue the request
1181 | if( usbRequests[i].queue(buffer, getPacketSize()) == false)
1182 | {
1183 | Log.e(logTag,"receiveLoop: Couldn't queue USB Request.");
1184 | this.stop();
1185 | break;
1186 | }
1187 | }
1188 |
1189 | // Run loop until transceiver mode changes...
1190 | while(this.transceiverMode == HACKRF_TRANSCEIVER_MODE_TRANSMIT)
1191 | {
1192 | // Wait for a request to return. This will block until one of the requests is ready.
1193 | UsbRequest request = usbConnection.requestWait();
1194 |
1195 | if(request == null)
1196 | {
1197 | Log.e(logTag,"transmitLoop: Didn't receive USB Request.");
1198 | break;
1199 | }
1200 |
1201 | // Make sure we got an UsbRequest for the OUT endpoint!
1202 | if(request.getEndpoint() != usbEndpointOUT)
1203 | continue;
1204 |
1205 | // Increment the packetCounter (for statistics)
1206 | this.transceivePacketCounter++;
1207 |
1208 | // Extract the buffer and return it to the buffer pool:
1209 | buffer = (ByteBuffer) request.getClientData();
1210 | this.returnBufferToBufferPool(buffer.array());
1211 |
1212 | // Get the next packet from the queue:
1213 | packet = (byte[]) queue.poll(1000, TimeUnit.MILLISECONDS);
1214 | if(packet == null || packet.length != getPacketSize())
1215 | {
1216 | Log.e(logTag,"transmitLoop: Queue empty or wrong packet format. Stop transmitting.");
1217 | break;
1218 | }
1219 |
1220 | // Wrap the packet in a ByteBuffer object:
1221 | buffer = ByteBuffer.wrap(packet);
1222 | request.setClientData(buffer);
1223 |
1224 | // Queue the request again...
1225 | if(request.queue(buffer, getPacketSize()) == false){
1226 | Log.e(logTag,"transmitLoop: Couldn't queue USB Request.");
1227 | break;
1228 | }
1229 | }
1230 | } catch (HackrfUsbException e) {
1231 | Log.e(logTag,"transmitLoop: USB Error!");
1232 | } catch (InterruptedException e) {
1233 | Log.e(logTag,"transmitLoop: Interrup while waiting on queue!");
1234 | }
1235 |
1236 | // Transmitting is done. Cancel and close all usb requests:
1237 | for(UsbRequest request: usbRequests)
1238 | {
1239 | if(request != null) {
1240 | request.cancel();
1241 | //request.close(); <-- This will cause the VM to crash with a SIGABRT when the next transceive starts?!?
1242 | }
1243 | }
1244 |
1245 | // If the transceiverMode is still on TRANSMIT, we stop Transmitting:
1246 | if(this.transceiverMode == HACKRF_TRANSCEIVER_MODE_TRANSMIT)
1247 | {
1248 | try {
1249 | this.stop();
1250 | } catch (HackrfUsbException e) {
1251 | Log.e(logTag,"transmitLoop: Error while stopping TX!");
1252 | }
1253 | }
1254 | }
1255 |
1256 | /**
1257 | * This method will run when a new Thread was created. It simply calls
1258 | * receiveLoop() or transmitLoop() according to the transceiveMode.
1259 | */
1260 | @Override
1261 | public void run() {
1262 | switch(this.transceiverMode)
1263 | {
1264 | case HACKRF_TRANSCEIVER_MODE_RECEIVE: receiveLoop();
1265 | break;
1266 | case HACKRF_TRANSCEIVER_MODE_TRANSMIT: transmitLoop();
1267 | break;
1268 | default:
1269 | }
1270 | }
1271 |
1272 |
1273 | }
1274 |
--------------------------------------------------------------------------------