我后来找到了gr-tempest包,它基于gnuradio重置了tempestsdr,由于是gnuradio,比较方便我本地安装,因此我跑了一下,结果没跑出实际效果,可能因为它默认用usrp,我这用的是hackrf+重采样,硬件不太一样导致的。但是它里面还有个simulate的流图,可以导入图片再重新解出来。这个我玩成功了,通过调试,我发现在不同sample correction时,会出现类似第一篇文章的样子,也会出现比较像样的画面。所以我的信心大增。
后来我还是决定在我以前的c++/hackrf实现的模拟视频解调程序里试试。果然试成了,主要秘诀在于要去掉模拟视频解调里的各种同步,去掉隔行扫描,然后再耐心调整行宽,最后为了效果好一些,不乱跳,建议再人为限制刷新间隔。
下面就是我的代码:
#include <iostream>
#include <signal.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <libhackrf/hackrf.h>
#include <pthread.h>
#include <unistd.h>
#include <string>
#include <sstream>
using namespace std;
using namespace cv;
int decimate_rate = 1;
//#define original_width_x_height 65100
static volatile bool do_exit = false;
int result;
static hackrf_device* device = NULL;
double sample_rate_hz = 6e6/decimate_rate * 1.951047 ;
double baseband_filter_bw_hz = 1e6 ;
double freq_hz = 395.991*1000000;
Mat frame;
int x = 0;
int y = 0;
int correctX = 0;
int correctY = 0;
int autoCorrectX = 0;
int autoCorrectY = 0;
int width = 2081; //multiple of 347 like 2081 or 1387
int height = 800;
int original_width = width / decimate_rate;
int original_width_x_height = original_width * height;
int inverseVideo = 1;
double maxSignalLevel = 0;
double blackLevel = 0;
double coeff = 0;
double agcSignalLevel = 0;
int pixelCounter = 0;
double contrast = 40;
double bright = 40;
int delay_count = 0;
bool bufferIsFull = false;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
//double grayScaleValues[original_width_x_height];
double grayScaleValues[2000000];
void sigint_callback_handler(int signum)
{
cout << "Caught signal" << endl;
do_exit = true;
}
void draw_pixel(double mag)
{
if (x >= original_width)
{
y += 1;
x = 0;
}
if (y >= height)
{
y = 0;
delay_count++;
if (delay_count > 3)
{
bufferIsFull = true;
delay_count = 0;
}
}
double num2 = (blackLevel - mag) * coeff * contrast;
num2 += bright;
if (num2 > 255)
{
num2 = 255;
}
if (num2 < 0)
{
num2 = 0;
}
int num = y % height * original_width + x % original_width;
grayScaleValues[num] = num2;
x = x + 1;
}
int rx_callback(hackrf_transfer* transfer)
{
double im, re, mag;
double num = 0;
double num2 = 0;
double compare_re;
for( uint32_t i = 0; i < transfer->valid_length; i += 2)
{
// int8_t(transfer->buffer[i]) -128~127
im = (int8_t(transfer->buffer[i]))/128.0;
re = (int8_t(transfer->buffer[i + 1]))/128.0;
compare_re = abs(int8_t(transfer->buffer[i+1])) ;
if (compare_re > num)
{
num = compare_re;
}
double mag = sqrt( im * im + re * re); //mag 0~1.414
if (mag > num2)
{
num2 = mag;
}
if (inverseVideo)
{
mag = maxSignalLevel - mag;
}
pthread_mutex_lock(&mtx);
draw_pixel(mag);
pthread_mutex_unlock(&mtx);
//grey of opencv is 0~255
//0 is black 255 is white
}
maxSignalLevel = maxSignalLevel * 0.9 + num2 * 0.1;
blackLevel = maxSignalLevel * 0.4;
coeff = 255 / blackLevel;
agcSignalLevel = num;
return 0;
}
void *receiving_function(void *arg)
{
while (do_exit == false)
{
sleep(1);
}
cout << "Thread Exiting..." << endl;
pthread_exit(NULL);
}
int main()
{
pthread_t recv_th;
signal(SIGINT, &sigint_callback_handler);
frame = Mat::zeros(height, width, CV_8UC1);
result = hackrf_init();
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_init() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_open(&device);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_open() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_sample_rate(device, sample_rate_hz);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_sample_rate() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hz);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_baseband_filter_bandwidth_set() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_freq(device, freq_hz);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_freq() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_lna_gain(device, 40);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_lna_gain() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_vga_gain(device, 26);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_vga_gain() failed" << endl;
return EXIT_FAILURE;
}
int err = pthread_create(&recv_th, NULL, receiving_function, NULL);
if (err != 0)
{
cout << "Create thread failed" << endl;
}
result = hackrf_start_rx(device, rx_callback, NULL);
while( (hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false) )
{
if (bufferIsFull)
{
pthread_mutex_lock(&mtx);
for (int counter = 0; counter < original_width_x_height; counter++)
{
int new_x = counter % original_width;
int new_y = counter / original_width;
for (int c = 0; c < decimate_rate; c++)
{
frame.at<uchar>(new_y, new_x*decimate_rate + c) = grayScaleValues[counter];
}
grayScaleValues[counter] = 0;
}
bufferIsFull = false;
pthread_mutex_unlock(&mtx);
}
imshow("frame", frame);
if (waitKey(5) == 'q')
{
do_exit = true;
break;
}
}
if (do_exit)
{
cout << "Exiting..." << endl;
}
result = hackrf_close(device);
if(result != HACKRF_SUCCESS)
{
cout << "hackrf_close() failed" << endl;
}
else
{
cout << "hackrf_close() done" << endl;
}
hackrf_exit();
cout << "hackrf_exit() done" << endl;
pthread_join(recv_th,NULL);
return 0;
}
下面是编译用的命令:
g++ hackrf_tv_hdmi.cpp -o hackrf_tv_hdmi `pkg-config --cflags --libs opencv` -lhackrf -pthread
感兴趣的朋友可以试一下,我的显示器设置为1920x1080 60Hz。你的可能频点和我不一样。
目前这个c++版本代码肯定比tempestsdr简单好多,但是仍然不是最简化的,里面有不少我做模拟视频解调时的冗余代码。等我有时间再整理为python版本。
实测效果:
SDR接收HDMI泄露信号2 程序优化和泄漏源查找_哔哩哔哩_bilibili-https://www.bilibili.com/video/BV1sr4y1G7oL