2014年开源夏令营-android下使用libhackrf


完成libhackrf.so的编译之后,我们的android程序就可以通过libhackrf.so控制hackrf了。物理连接上,可以通过OTG线连接android设备和hackrf。libhackrf.so是动态库,android应用层需要通过JNI调用动态库,本文已简单FFT做为例子说一下android如何使用hackrf。


HackrfTouch这个app,总体的功能是能够显示信号FFT谱线图,能够改变频率等参数。从流程上看,HackrfTouch需要初始化hackrf,接收IQ数据,处理IQ数据,显示谱线图。为了保证效率,初始化和接收处理IQ数据这些操作都在c语言实现,最终结果发送到java层展现出来。


JNI方面

1)将编译好的库导入工程

拷贝哦libhackrf.h、libhackrf.so和libusb1.0.so到工程目录,创建libusb1.0.mk和libhackrf.mk

libusb1.0.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libusb1.0
LOCAL_SRC_FILES := libusb1.0.so 
include $(PREBUILT_SHARED_LIBRARY)

libhackrf.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libhackrf
LOCAL_SRC_FILES := libhackrf.so 
include $(PREBUILT_SHARED_LIBRARY)
include $(LOCAL_PATH)/libusb1.0.mk

通过libhackrf.mk即可将libhackrf连接到工程中。


2)初始化hackrf,注册接收函数。

JNI接口

JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_test(
		JNIEnv *env, jclass cls) {
	int result = HACKRF_SUCCESS;
	result = hackrf_init();
	if (result == HACKRF_SUCCESS) {
		return env->NewStringUTF("OK!");
	} else {
		return env->NewStringUTF("Error!");
	}
}

JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_init(
		JNIEnv *env, jobject thiz) {
	int result = HACKRF_SUCCESS;
	uint8_t board_id = BOARD_ID_INVALID;
	char pbuf[2048] = { 0 };
	result = hackrf_init();
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_init() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}

	result = hackrf_open(&device);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_open() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}

	result = hackrf_board_id_read(device, &board_id);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_board_id_read() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}

	sprintf(pbuf, "Board ID Number: %d (%s)\n", board_id,
			hackrf_board_id_name((hackrf_board_id) board_id));
	return env->NewStringUTF(pbuf);
}

JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_setSampleRateManual(
		JNIEnv *env, jobject thiz, jlong freq, jint divi) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_set_sample_rate_manual(device, freq, divi);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_set_sample_rate_manual() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}
JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_setVgaGain(
		JNIEnv *env, jobject thiz, jint vga) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_set_vga_gain(device, vga);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_set_vga_gain() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}

/*
 * Class:     com_pagekpang_hackrftouch_HackRFTouch
 * Method:    setLnaGain
 * Signature: (I)Ljava/lang/String;
 */JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_setLnaGain(
		JNIEnv *env, jobject thiz, jint lna) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_set_lna_gain(device, lna);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_set_lna_gain() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}

/*
 * Class:     com_pagekpang_hackrftouch_HackRFTouch
 * Method:    setFreq
 * Signature: (J)Ljava/lang/String;
 */JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_setFreq(
		JNIEnv *env, jobject thiz, jlong freq) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_set_freq(device, freq);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_set_freq() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}

/*
 * Class:     com_pagekpang_hackrftouch_HackRFTouch
 * Method:    setAmpEnable
 * Signature: (Ljava/lang/Boolean;)Ljava/lang/String;
 */JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_setAmpEnable(
		JNIEnv *env, jobject thiz, jint b) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_set_amp_enable(device, b == 0 ? false : true);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_set_amp_enable() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}

JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_startRX(
		JNIEnv *env, jobject thiz) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_start_rx(device, hackrf_rx_cb, env->NewGlobalRef(thiz));
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_start_rx() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}
/*
 * Class:     com_pagekpang_hackrftouch_HackRFTouch
 * Method:    stopRX
 * Signature: ()Ljava/lang/String;
 */JNIEXPORT jstring JNICALL Java_com_pagekpang_hackrftouch_HackRFTouch_stopRX(
		JNIEnv *env, jobject thiz) {
	int result = HACKRF_SUCCESS;
	char pbuf[2048] = { 0 };
	result = hackrf_stop_rx(device);
	if (result != HACKRF_SUCCESS) {
		sprintf(pbuf, "hackrf_stop_rx() failed: %s (%d)\n",
				hackrf_error_name((hackrf_error) result), result);
		return env->NewStringUTF(pbuf);
	}
	return env->NewStringUTF("ok");
}
java层与JNI接口的绑定

package com.pagekpang.hackrftouch;

public class HackRFTouch {

	static {
		System.loadLibrary("usb1.0");
		System.loadLibrary("hackrf");
		System.loadLibrary("HackrfTouch");
	}

	public static native String test();

	private native String init();

	private native String setSampleRateManual(long freq, int divi);

	private native String setVgaGain(int g);

	private native String setLnaGain(int g);

	private native String setFreq(long freq);

	private native String setAmpEnable(int f);

	public native String startRX();

	public native String stopRX();

	public native float[] readRx();

	private String retString = "";
	private Boolean isOpen = false;

	private ReadRxThread mReadRxThread = null;

	public HackRFTouch() {
		// TODO Auto-generated constructor stub
		retString = init();
		if (!retString.contains("failed")) {
			isOpen = true;
			mReadRxThread = new ReadRxThread(this);
		}
	}

	class ReadRxThread extends Thread {
		HackRFTouch mThisHackRFTouch = null;

		public ReadRxThread(HackRFTouch t) {
			// TODO Auto-generated constructor stub
			mThisHackRFTouch = t;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			while(true){
				mThisHackRFTouch.cb(mThisHackRFTouch.readRx());
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public Boolean isOpen() {
		return isOpen;
	}

	public String getLastError() {
		return retString;
	}

	public Boolean HsetSampleRateManual(long freq, int divi) {
		if (!isOpen) {
			return false;
		}
		retString = setSampleRateManual(freq, divi);
		if (retString.contains("failed")) {
			return false;
		} else {
			return true;
		}
	}

	public Boolean HsetLnaGain(int g) {
		if (!isOpen) {
			return false;
		}
		retString = setLnaGain(g);
		if (retString.contains("failed")) {
			return false;
		} else {
			return true;
		}
	}

	public Boolean HsetVgaGain(int g) {
		if (!isOpen) {
			return false;
		}
		retString = setVgaGain(g);
		if (retString.contains("failed")) {
			return false;
		} else {
			return true;
		}
	}

	public Boolean HsetFreq(long freq) {
		if (!isOpen) {
			return false;
		}
		retString = setFreq(freq);
		if (retString.contains("failed")) {
			return false;
		} else {
			return true;
		}
	}

	public Boolean HsetAmpEnable(int b) {
		if (!isOpen) {
			return false;
		}
		retString = setAmpEnable(b);
		if (retString.contains("failed")) {
			return false;
		} else {
			return true;
		}
	}

	public Boolean HstartRX() {
		if (!isOpen) {
			return false;
		}
		if (!isOpen) {
			retString = init();
			if (!retString.contains("failed")) {
				isOpen = true;
				mReadRxThread = new ReadRxThread(this);
			}else {
				return false;
			}
		}
		retString = startRX();
		if (retString.contains("failed")) {
			return false;
		} else {
			mReadRxThread.start();
			return true;
		}
	}

	public Boolean HstopRX() {
		if (!isOpen) {
			return false;
		}
		retString = stopRX();
		if (retString.contains("failed")) {
			return false;
		} else {
			return true;
		}
	}

	public interface HackrfEven {
		public void rx(float[] b);

		public float[] tx();
	}

	private HackrfEven mHackrfEven = null;

	public void HsetOnEvent(HackrfEven hackrfEven) {
		mHackrfEven = hackrfEven;
	}

	public void cb(float[] f) {
		if (mHackrfEven != null && f != null) {
			mHackrfEven.rx(f);
		}
	}

}
最后调用这些函数,配置参数,完成初始化
if (!mHackRFTouch.HsetAmpEnable(1)) {
					Toast.makeText(this, mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
					return;
				}
				if (!mHackRFTouch.HsetFreq((long) (977*1e5))) {
					Toast.makeText(this, mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
					return;
				}
				if (!mHackRFTouch.HsetLnaGain(24)) {
					Toast.makeText(this, mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
					return;
				}
				if (!mHackRFTouch.HsetSampleRateManual((long) 8e6, 4)) {
					Toast.makeText(this, mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
					return;
				}
				if (!mHackRFTouch.HsetVgaGain(10)) {
					Toast.makeText(this, mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
					return;
				}
				if (!mHackRFTouch.HstartRX()) {
					Toast.makeText(this, mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
				}else {
					Toast.makeText(this, "启动成功\n"+mHackRFTouch.getLastError(), Toast.LENGTH_LONG).show();
				}

3)FFT运算

	if (device != NULL) {
		float raw[1024];
		int dalen = transfer->valid_length;
		float realF, imagF, maxF;
		uint8_t *pbuf = transfer->buffer;
		while (dalen > 0 && runcount == 0) {
			complex* fdata = new complex [1024];
			complex* fout = new complex [1024];
			for (int i = 0; i < 2048; i += 2) {
				fdata[i / 2] = complex(meanN(&pbuf[i], 100), meanN(&pbuf[i + 1], 100));
			}
			FFT(fdata, fout, 10);
			//dft(fdata, 10, 0);
			//fout = fdata;
			maxF = 0.0;
			for (int i = 0; i < 1024; i++) {
				raw[i] = pow(pow(fout[i].real(), 2) + pow(fout[i].imag(), 2),
						0.5);
				if (maxF < raw[i]) {
					maxF = raw[i];
				}
			}

			for (int i = 0; i < 1024; i++) {
				raw[i] = raw[i] / maxF;
			}

			sendBuf(raw);
			//send(g_client, (char *)&raw, 4*1024, 0); //发送数据
			dalen -= 2048;
			pbuf += 2048;
			runcount = 2;
		}
		runcount--;
		//printf("E");
	} else {
		printf("O");
	}

因为Hackrf上送的数据量非常大, 基于性能考虑,使用runcount变量控制处理次数。


至此,问中提及的代码片可以在我的code中找到整体,android中使用OTG最主要还是权限问题,在移植libusb文章已经讲过解决方案,如果遇到libusb初始化失败,可以考虑一下这方面原因。



你可能感兴趣的:(hackrf,开源夏令营)