├── .gitignore ├── src ├── tx_test │ ├── .gitignore │ ├── Makefile │ └── main.cpp ├── noise_gen │ ├── noise_gen │ ├── Makefile │ ├── filter.txt │ ├── main.cpp │ └── top_block.py ├── RADAR │ ├── remote_tx.sh │ ├── CMakeLists.txt │ ├── plotPrecision.m │ ├── untitled.m │ ├── filterThis3.m │ ├── filterThis.m │ ├── filterThis2.m │ ├── collect_data.sh │ ├── collect_data_continuous.sh │ ├── plot_first_pulse.m │ ├── plot_as_rdm.m │ ├── main_rx.cpp │ ├── plot_all_pulses.m │ ├── plot_and_wait.m │ ├── main_tx.cpp │ ├── fm_cw.m │ └── top_block.py ├── GUI │ ├── .gitignore │ ├── main.cpp │ ├── GUI.pro │ ├── receiver.h │ ├── mainwindow.h │ ├── receiver.cpp │ ├── mainwindow.ui │ └── mainwindow.cpp ├── utilities │ ├── plot_tx.py │ ├── plot_rx.py │ ├── plot_detector_log.py │ ├── CMakeLists.txt │ ├── fft.h │ ├── test_pulse_detector.cpp │ ├── waveforms.h │ ├── fft.cpp │ ├── myhackrf.py │ ├── pulse_detector.h │ └── zhelpers.hpp ├── hackrf_simple_server │ ├── CMakeLists.txt │ ├── SocketException.h │ ├── ServerSocket.h │ ├── Socket.h │ ├── ServerSocket.cpp │ ├── Socket.cpp │ └── main.cpp ├── sdr_server │ └── CMakeLists.txt ├── multi_sdr_server │ └── CMakeLists.txt ├── freq_monitor │ ├── CMakeLists.txt │ ├── stop.sh │ ├── start.sh │ └── main.cpp ├── websock │ ├── run_server.sh │ ├── webrelay.py │ ├── fft.html │ ├── iq.html │ ├── fft_chart.js │ ├── fft.js │ ├── js │ │ ├── iq_packet.js │ │ ├── pdw_packet.js │ │ ├── fft_packet.js │ │ ├── packet_header.js │ │ └── packet.js │ └── iq_chart.js ├── protobuffers │ ├── test.py │ ├── plot_iq.py │ ├── plot_waterfall.py │ ├── CMakeLists.txt │ ├── packet.proto │ └── myhackrf.py ├── devices │ ├── CMakeLists.txt │ ├── test_sdr_receiver.cpp │ ├── test_rtl.cpp │ ├── SDRReceiver.h │ ├── RFDevice.h │ ├── RTLSDRDevice.h │ ├── test_hackrf.cpp │ ├── HackRFDevice.h │ ├── SDRReceiver.cpp │ └── RTLSDRDevice.cpp └── blink_server │ └── blink_server.py ├── README.md └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode/* 3 | -------------------------------------------------------------------------------- /src/tx_test/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | txtest 3 | 4 | -------------------------------------------------------------------------------- /src/noise_gen/noise_gen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lemontongs/myhackrf/HEAD/src/noise_gen/noise_gen -------------------------------------------------------------------------------- /src/RADAR/remote_tx.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ssh 10.1.10.215 ./git/myhackrf/src/RADAR/radar_tx 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/GUI/.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.o 3 | moc_*.cpp 4 | ui_*.h 5 | qwt-6.1.2 6 | GUI 7 | Makefile 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/utilities/plot_tx.py: -------------------------------------------------------------------------------- 1 | 2 | from matplotlib import pyplot 3 | import numpy 4 | import pandas 5 | 6 | data = pandas.read_csv('tx.csv') 7 | 8 | pyplot.plot(data.t, data.i) 9 | pyplot.plot(data.t, data.q) 10 | pyplot.show() 11 | -------------------------------------------------------------------------------- /src/GUI/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(hackrf_simple_server Socket.cpp ServerSocket.cpp main.cpp) 3 | target_link_libraries(hackrf_simple_server devices) 4 | target_include_directories(hackrf_simple_server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | -------------------------------------------------------------------------------- /src/sdr_server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(sdr_server main.cpp) 3 | target_link_libraries(sdr_server fftw3 devices) 4 | target_include_directories(sdr_server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | install(TARGETS sdr_server RUNTIME DESTINATION bin) 7 | -------------------------------------------------------------------------------- /src/multi_sdr_server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(multi_sdr_server main.cpp) 3 | target_link_libraries(multi_sdr_server fftw3 devices) 4 | target_include_directories(multi_sdr_server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | install(TARGETS multi_sdr_server) 7 | -------------------------------------------------------------------------------- /src/freq_monitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(freq_monitor main.cpp) 3 | target_link_libraries(freq_monitor protobuf zmq protobuffers devices) 4 | target_include_directories(freq_monitor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | install(TARGETS freq_monitor RUNTIME DESTINATION bin) 7 | -------------------------------------------------------------------------------- /src/utilities/plot_rx.py: -------------------------------------------------------------------------------- 1 | 2 | from matplotlib import pyplot 3 | import numpy 4 | 5 | f = open('rx.bin', 'rb') 6 | data = f.read() 7 | f.close() 8 | 9 | iq = numpy.array([int(d) for d in data]).astype(numpy.int8) 10 | sig = iq[0::2] + 1j*iq[1::2] 11 | 12 | pyplot.plot(20.0*numpy.log10(numpy.abs(sig))) 13 | pyplot.show() 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # myhackrf 2 | HackRF experimentation 3 | 4 | Dependencies: 5 | sudo apt install cmake libhackrf-dev librtlsdr-dev libzmq3-dev libprotobuf-dev libfftw3-dev 6 | 7 | Optional: 8 | sudo apt install python3-ipython python3-numpy python3-matplotlib python3-zmq python3-protobuf 9 | 10 | GUI: 11 | sudo apt install qt5-default libqwt-qt5-dev libqt5svg5-dev 12 | -------------------------------------------------------------------------------- /src/utilities/plot_detector_log.py: -------------------------------------------------------------------------------- 1 | 2 | from matplotlib import pyplot 3 | 4 | import pandas 5 | 6 | data = pandas.read_csv('detector.log') 7 | 8 | pyplot.plot(data['index'], data['amp']) 9 | pyplot.plot(data['index'], data['det_count']) 10 | pyplot.plot(data['index'], data['noise_average']) 11 | pyplot.plot(data['index'], data['state']) 12 | pyplot.show() 13 | -------------------------------------------------------------------------------- /src/websock/run_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 4 | 5 | cd $SCRIPT_DIR 6 | 7 | protoc --proto_path=../protobuffers/ --js_out=binary:./js/ packet.proto 8 | 9 | python2 -m SimpleHTTPServer & 10 | HTTP_PID=$! 11 | 12 | ./webrelay.py 13 | 14 | kill -SIGHUP $HTTP_PID 15 | 16 | cd - 17 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/SocketException.h: -------------------------------------------------------------------------------- 1 | #ifndef SocketException_class 2 | #define SocketException_class 3 | 4 | #include 5 | 6 | class SocketException 7 | { 8 | public: 9 | SocketException ( std::string s ) : m_s ( s ) {}; 10 | ~SocketException (){}; 11 | 12 | std::string description() { return m_s; } 13 | 14 | private: 15 | 16 | std::string m_s; 17 | 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/utilities/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(utilities fft.cpp) 3 | target_link_libraries(utilities fftw3 protobuffers devices) 4 | target_include_directories(utilities PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | add_executable(test_pulse_detector test_pulse_detector.cpp) 7 | target_link_libraries(test_pulse_detector utilities) 8 | target_include_directories(test_pulse_detector PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 9 | -------------------------------------------------------------------------------- /src/protobuffers/test.py: -------------------------------------------------------------------------------- 1 | import zmq 2 | import packet_pb2 3 | from matplotlib import pyplot 4 | 5 | ctx = zmq.Context() 6 | sock = ctx.socket(zmq.SUB) 7 | sock.connect("tcp://localhost:5555") 8 | sock.setsockopt(zmq.SUBSCRIBE, b"") 9 | 10 | while True: 11 | msg = sock.recv() 12 | p = packet_pb2.Packet() 13 | p.ParseFromString(msg) 14 | 15 | print(p.num_bins) 16 | break 17 | 18 | pyplot.plot(p.freq_bins_mhz, p.signal) 19 | pyplot.show() 20 | 21 | -------------------------------------------------------------------------------- /src/RADAR/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(radar_tx main_tx.cpp) 3 | target_link_libraries(radar_tx devices) 4 | target_include_directories(radar_tx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | install(TARGETS radar_tx RUNTIME DESTINATION bin) 7 | 8 | 9 | add_executable(radar_rx main_rx.cpp) 10 | target_link_libraries(radar_rx devices) 11 | target_include_directories(radar_rx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 12 | 13 | install(TARGETS radar_rx RUNTIME DESTINATION bin) 14 | -------------------------------------------------------------------------------- /src/tx_test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | EXECUTABLE=txtest 4 | 5 | CXXFLAGS=-I/usr/local/include/libhackrf/ -I../hackrfcpp/ 6 | LDLIBS=-lhackrf 7 | #LDFLAGS=-L./ 8 | CXX=g++ 9 | 10 | SRCS=../hackrfcpp/HackRFDevice.cpp main.cpp 11 | OBJS=$(subst .cpp,.o,$(SRCS)) 12 | 13 | all: %(EXECUTABLE) 14 | 15 | %(EXECUTABLE): $(OBJS) 16 | $(CXX) $(LDFLAGS) -o $(EXECUTABLE) $(OBJS) $(LDLIBS) 17 | 18 | %.o: %.cpp 19 | $(CXX) $(CXXFLAGS) -c $< -o $@ 20 | 21 | clean: 22 | $(RM) $(OBJS) 23 | $(RM) $(EXECUTABLE) 24 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/ServerSocket.h: -------------------------------------------------------------------------------- 1 | #ifndef ServerSocket_class 2 | #define ServerSocket_class 3 | 4 | #include "Socket.h" 5 | 6 | 7 | class ServerSocket : private Socket 8 | { 9 | public: 10 | 11 | ServerSocket ( int port ); 12 | ServerSocket (){}; 13 | virtual ~ServerSocket(); 14 | 15 | const ServerSocket& operator << ( const std::string& ) const; 16 | const ServerSocket& operator >> ( std::string& ) const; 17 | 18 | void accept ( ServerSocket& ); 19 | 20 | }; 21 | 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/RADAR/plotPrecision.m: -------------------------------------------------------------------------------- 1 | function output_txt = myfunction(obj,event_obj) 2 | % Display the position of the data cursor 3 | % obj Currently not used (empty) 4 | % event_obj Handle to event object 5 | % output_txt Data cursor text string (string or cell array of strings). 6 | 7 | pos = get(event_obj,'Position'); 8 | output_txt = {['X: ',num2str(pos(1),10)],... 9 | ['Y: ',num2str(pos(2),10)]}; 10 | 11 | % If there is a Z-coordinate in the position, display it as well 12 | if length(pos) > 2 13 | output_txt{end+1} = ['Z: ',num2str(pos(3),10)]; 14 | end 15 | -------------------------------------------------------------------------------- /src/devices/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(devices HackRFDevice.cpp RTLSDRDevice.cpp SDRReceiver.cpp) 3 | target_link_libraries(devices protobuf zmq pthread utilities hackrf rtlsdr) 4 | target_include_directories(devices PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 5 | 6 | add_executable(test_hackrf_cpp test_hackrf.cpp) 7 | target_link_libraries(test_hackrf_cpp devices) 8 | 9 | add_executable(test_rtlsdr_cpp test_rtl.cpp) 10 | target_link_libraries(test_rtlsdr_cpp devices) 11 | 12 | add_executable(test_sdr_receiver test_sdr_receiver.cpp) 13 | target_link_libraries(test_sdr_receiver devices) 14 | -------------------------------------------------------------------------------- /src/noise_gen/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | EXECUTABLE=noise_gen 4 | 5 | 6 | # Compilers 7 | CXX=g++ 8 | 9 | # Compiler flags 10 | CXXFLAGS=-I/usr/local/include/libhackrf/ -I/usr/local/include/gnuradio/filter/ -I../hackrfcpp/ 11 | LDLIBS=-L/usr/local/lib/ -lhackrf -lgnuradio-filter 12 | 13 | # Items 14 | SRCS=../hackrfcpp/HackRFDevice.cpp main.cpp 15 | 16 | OBJS=$(subst .cpp,.o,$(SRCS)) 17 | 18 | # Rules 19 | all: %(EXECUTABLE) 20 | 21 | %(EXECUTABLE): $(PROTOLIB) $(OBJS) 22 | $(CXX) $(LDFLAGS) -o $(EXECUTABLE) $(OBJS) $(LDLIBS) 23 | 24 | %.o: %.cpp 25 | $(CXX) $(CXXFLAGS) -c $< -o $@ 26 | 27 | clean: 28 | $(RM) $(OBJS) 29 | $(RM) $(EXECUTABLE) 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/freq_monitor/stop.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | 4 | blink_server_pid=`ps -elf | grep blink_server.py | grep -v "grep" | awk '{print $4}'` 5 | 6 | if [[ $blink_server_pid -ne "" ]]; then 7 | # Kill the process 8 | if [[ $1 == "-f" ]]; then 9 | kill -9 $blink_server_pid 10 | else 11 | kill -2 $blink_server_pid 12 | fi 13 | fi 14 | 15 | hackrf_server_pid=`ps -elf | grep hackrf_server | grep -v "grep" | awk '{print $4}'` 16 | 17 | if [[ $hackrf_server_pid -ne "" ]]; then 18 | # Kill the process 19 | if [[ $1 == "-f" ]]; then 20 | kill -9 $hackrf_server_pid 21 | else 22 | kill -2 $hackrf_server_pid 23 | fi 24 | fi 25 | 26 | -------------------------------------------------------------------------------- /src/RADAR/untitled.m: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | filename = 'rx.dat'; 4 | 5 | fd = fopen(filename,'r'); 6 | 7 | save_data = []; 8 | try 9 | while 1 10 | data = fread(fd, 10e7, 'int8'); 11 | 12 | data = reshape(data, 2, []); 13 | amp = abs(complex(data(1,:), data(2,:))); 14 | 15 | inds = find(amp > 80); 16 | 17 | for i = 1:length(inds) 18 | save_data = [ save_data ; amp(inds(i):inds(i)+19) ]; 19 | end 20 | 21 | if size(save_data,1) == 25 22 | fclose(fd); 23 | break 24 | end 25 | fprintf('%d\n', size(save_data,1)); 26 | end 27 | catch e 28 | disp(e) 29 | fclose(fd); 30 | end 31 | 32 | clear data 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.13) 3 | 4 | set(CMAKE_INSTALL_PREFIX /tmp/myhackrf/) 5 | 6 | project(myHackRF) 7 | 8 | set(CMAKE_CXX_STANDARD 17) 9 | set(CMAKE_CXX_FLAGS "-Wall -Wno-psabi -Wno-deprecated-declarations ") 10 | #set(CMAKE_CXX_FLAGS "-Wall -Wno-psabi -Wno-deprecated-declarations -fsanitize=address -static-libasan") 11 | 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 13 | 14 | add_subdirectory(src/protobuffers) 15 | add_subdirectory(src/utilities) 16 | add_subdirectory(src/devices) 17 | add_subdirectory(src/sdr_server) 18 | add_subdirectory(src/multi_sdr_server) 19 | add_subdirectory(src/hackrf_simple_server) 20 | add_subdirectory(src/freq_monitor) 21 | add_subdirectory(src/RADAR) 22 | 23 | -------------------------------------------------------------------------------- /src/utilities/fft.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FFT_H 3 | #define FFT_H 4 | 5 | #include "RFDevice.h" 6 | #include "packet.pb.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace utilities 15 | { 16 | // 17 | // Calculate Frequency Bins 18 | // 19 | // f = -(N/2):(N/2)-1; // matlab code 20 | // f = f*fs/N; 21 | // f = f + fc; 22 | // f = f / 1e6; 23 | // 24 | void calcFrequencyBins(FFT_Packet* packet, uint32_t N, double fs, uint64_t fc); 25 | 26 | // 27 | // fft 28 | // 29 | void fft( FFT_Packet* result, SampleChunk &buffer, uint32_t num_fft_bins, double fs, uint64_t fc ); 30 | } 31 | 32 | #endif /* FFT_H */ 33 | 34 | -------------------------------------------------------------------------------- /src/GUI/GUI.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2015-02-28T15:44:33 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | CONFIG += qwt 10 | 11 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 12 | 13 | TARGET = GUI 14 | TEMPLATE = app 15 | 16 | LIBS += -L/usr/local/lib/ -lzmq -lprotobuf -lpthread 17 | 18 | SOURCES += main.cpp\ 19 | mainwindow.cpp \ 20 | receiver.cpp \ 21 | ../../build/src/protobuffers/packet.pb.cc 22 | 23 | HEADERS += mainwindow.h \ 24 | receiver.h 25 | 26 | INCLUDEPATH += ../protobuffers ../utilities ../../build/src/protobuffers/ 27 | 28 | FORMS += mainwindow.ui 29 | 30 | #include( /usr/local/qwt-6.1.2/features/qwt.prf ) 31 | 32 | -------------------------------------------------------------------------------- /src/RADAR/filterThis3.m: -------------------------------------------------------------------------------- 1 | function Hd = filterThis3 2 | %FILTERTHIS3 Returns a discrete-time filter object. 3 | 4 | % MATLAB Code 5 | % Generated by MATLAB(R) 8.5 and the Signal Processing Toolbox 7.0. 6 | % Generated on: 18-Apr-2016 19:23:13 7 | 8 | % FIR Window Lowpass filter designed using the FIR1 function. 9 | 10 | % All frequency values are in Hz. 11 | Fs = 8000000; % Sampling Frequency 12 | 13 | N = 200; % Order 14 | Fc = 200000; % Cutoff Frequency 15 | flag = 'scale'; % Sampling Flag 16 | Beta = 0.5; % Window Parameter 17 | 18 | % Create the window vector for the design algorithm. 19 | win = kaiser(N+1, Beta); 20 | 21 | % Calculate the coefficients using the FIR1 function. 22 | b = fir1(N, Fc/(Fs/2), 'low', win, flag); 23 | Hd = dfilt.dffir(b); 24 | 25 | % [EOF] 26 | -------------------------------------------------------------------------------- /src/protobuffers/plot_iq.py: -------------------------------------------------------------------------------- 1 | from matplotlib import pyplot 2 | import numpy 3 | import myhackrf 4 | 5 | dev = myhackrf.MyHackrf('localhost') 6 | dev.send("set-fs 1024000") 7 | #dev.send("set-fc 462562500") 8 | dev.send("set-fc 101100000") 9 | dev.send("set-rx-gain 297") 10 | dev.send("set-rx-mode iq") 11 | 12 | 13 | p = dev.recv() 14 | if p.header.type == myhackrf.packet_pb2.Packet_Header.IQ: 15 | print("GOT IQ", len(p.iq_packet.signal)) 16 | complex_sig = numpy.array(p.iq_packet.signal[0::2]) + 1j * numpy.array(p.iq_packet.signal[1::2]) 17 | 18 | pyplot.figure() 19 | pyplot.plot(complex_sig.real[:100000], '.-') 20 | pyplot.plot(complex_sig.imag[:100000], '.-') 21 | #pyplot.plot(numpy.array(range(len(complex_sig)))/1024000, 20.0*numpy.log10(numpy.abs(complex_sig))) 22 | 23 | pyplot.show() 24 | -------------------------------------------------------------------------------- /src/protobuffers/plot_waterfall.py: -------------------------------------------------------------------------------- 1 | from matplotlib import pyplot 2 | import myhackrf 3 | 4 | dev = myhackrf.MyHackrf('localhost') 5 | dev.send("set-fs 1024000") 6 | dev.send("set-fc 462562500") 7 | dev.send("set-fc 101100000") 8 | dev.send("set-rx-gain 402") 9 | dev.send("set-rx-mode fft") 10 | 11 | waterfall = [] 12 | f = [] 13 | while len(waterfall) < 100: 14 | p = dev.recv() 15 | if p.header.type == myhackrf.packet_pb2.Packet_Header.FFT: 16 | f = p.fft_packet.freq_bins_hz 17 | waterfall.append(p.fft_packet.fft) 18 | print("GOT FFT " + str(len(waterfall))) 19 | 20 | pyplot.figure() 21 | pyplot.imshow(waterfall, extent=[min(f)/1e6, max(f)/1e6, 0, len(waterfall)], origin='lower', aspect='auto') 22 | pyplot.colorbar() 23 | pyplot.xlabel('Freq (MHz)') 24 | pyplot.ylabel('Time') 25 | pyplot.show() 26 | -------------------------------------------------------------------------------- /src/RADAR/filterThis.m: -------------------------------------------------------------------------------- 1 | % function [b] = filterThis 2 | %BPFILTER_1-45E6_1-75E6 Returns a discrete-time filter object. 3 | 4 | % MATLAB Code 5 | % Generated by MATLAB(R) 8.5 and the Signal Processing Toolbox 7.0. 6 | % Generated on: 17-Apr-2016 19:42:07 7 | 8 | % FIR Window Bandpass filter designed using the FIR1 function. 9 | 10 | % All frequency values are in Hz. 11 | Fs = 8000000; % Sampling Frequency 12 | 13 | N = 100; % Order 14 | Fc1 = 1450000; % First Cutoff Frequency 15 | Fc2 = 1750000; % Second Cutoff Frequency 16 | flag = 'scale'; % Sampling Flag 17 | Alpha = 2.5; % Window Parameter 18 | % Create the window vector for the design algorithm. 19 | win = gausswin(N+1, Alpha); 20 | 21 | % Calculate the coefficients using the FIR1 function. 22 | b = fir1(N, [Fc1 Fc2]/(Fs/2), 'bandpass', win, flag); 23 | Hd = dfilt.dffir(b); 24 | 25 | % [EOF] 26 | -------------------------------------------------------------------------------- /src/protobuffers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | find_package(Protobuf REQUIRED) 3 | include_directories(${PROTOBUF_INCLUDE_DIR}) 4 | PROTOBUF_GENERATE_CPP(PROTO_SRC PROTO_HEADER packet.proto) 5 | add_library(protobuffers ${PROTO_HEADER} ${PROTO_SRC}) 6 | target_include_directories(protobuffers PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/../protobuffers/) 7 | 8 | PROTOBUF_GENERATE_PYTHON(PROTO_PY packet.proto) 9 | add_custom_target(protobuffers_py ALL 10 | COMMAND cp ${PROTO_PY} ${CMAKE_CURRENT_BINARY_DIR}/../../bin/ 11 | COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/myhackrf.py ${CMAKE_CURRENT_BINARY_DIR}/../../bin/ 12 | COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/plot_waterfall.py ${CMAKE_CURRENT_BINARY_DIR}/../../bin/ 13 | COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/plot_iq.py ${CMAKE_CURRENT_BINARY_DIR}/../../bin/ 14 | DEPENDS ${PROTO_PY}) 15 | -------------------------------------------------------------------------------- /src/freq_monitor/start.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | make 4 | 5 | # 6 | # Start the blink server 7 | # 8 | blink_server_pid=`ps -elf | grep blink_server.py | grep -v "grep" | awk '{print $4}'` 9 | if [[ $blink_server_pid -eq "" ]]; then 10 | echo "Not running, starting..." 11 | ../blink_server/blink_server.py & 12 | blink_server_pid=$! 13 | fi 14 | echo "blink sever: $blink_server_pid" 15 | 16 | # 17 | # Start the hackrf server 18 | # 19 | # TODO: Add support for specifying an external server, python script sending 20 | # a test message expecting the INVALID response. 21 | # 22 | hackrf_server_pid=`ps -elf | grep hackrf_server | grep -v "grep" | awk '{print $4}'` 23 | if [[ $hackrf_server_pid -eq "" ]]; then 24 | echo "Not running, starting..." 25 | ../hackrf_server/hackrf_server & 26 | hackrf_server_pid=$! 27 | fi 28 | echo "hackrf sever: $hackrf_server_pid" 29 | 30 | 31 | ./freq_monitor 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/RADAR/filterThis2.m: -------------------------------------------------------------------------------- 1 | function Hd = filterThis2 2 | %FILTERTHIS2 Returns a discrete-time filter object. 3 | 4 | % MATLAB Code 5 | % Generated by MATLAB(R) 8.5 and the Signal Processing Toolbox 7.0. 6 | % Generated on: 18-Apr-2016 18:58:15 7 | 8 | % Equiripple Lowpass filter designed using the FIRPM function. 9 | 10 | % All frequency values are in Hz. 11 | Fs = 8000000; % Sampling Frequency 12 | 13 | Fpass = 100000; % Passband Frequency 14 | Fstop = 200000; % Stopband Frequency 15 | Dpass = 0.057501127785; % Passband Ripple 16 | Dstop = 0.01; % Stopband Attenuation 17 | dens = 20; % Density Factor 18 | 19 | % Calculate the order from the parameters using FIRPMORD. 20 | [N, Fo, Ao, W] = firpmord([Fpass, Fstop]/(Fs/2), [1 0], [Dpass, Dstop]); 21 | 22 | % Calculate the coefficients using the FIRPM function. 23 | b = firpm(N, Fo, Ao, W, {dens}); 24 | Hd = dfilt.dffir(b); 25 | 26 | % [EOF] 27 | -------------------------------------------------------------------------------- /src/devices/test_sdr_receiver.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "SDRReceiver.h" 3 | #include "packet.pb.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | // Interrupt handler. Sends quit message 12 | void signal_handler(int s) 13 | { 14 | std::cout << "Caught signal " << s << std::endl; 15 | exit(1); 16 | } 17 | 18 | void callback( Packet &p, void* args ) 19 | { 20 | std::cout << "Got a packet! (" << p.header().type() << ")" << std::endl; 21 | } 22 | 23 | // 24 | // MAIN 25 | // 26 | int main() 27 | { 28 | // Setup the interrupt signal handler 29 | struct sigaction sigIntHandler; 30 | sigIntHandler.sa_handler = signal_handler; 31 | sigemptyset(&sigIntHandler.sa_mask); 32 | sigIntHandler.sa_flags = 0; 33 | sigaction(SIGINT, &sigIntHandler, NULL); 34 | 35 | // Setup the data receiver 36 | SDRReceiver rcv; 37 | rcv.initialize( callback, NULL ); 38 | rcv.tune( 101100000 ); 39 | 40 | sleep(5); 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/protobuffers/packet.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | 3 | message Packet_Header { 4 | 5 | enum PacketType { 6 | IQ = 0; 7 | FFT = 1; 8 | PDW = 2; 9 | } 10 | 11 | required PacketType type = 1; 12 | required uint32 fs = 2; 13 | required uint32 fc = 3; 14 | } 15 | 16 | message IQ_Packet { 17 | repeated float signal = 1; 18 | } 19 | 20 | 21 | message FFT_Packet { 22 | repeated double freq_bins_hz = 1; 23 | repeated double fft = 2; 24 | } 25 | 26 | message PDW { 27 | required double toa_s = 1; 28 | required double pw_s = 2; 29 | required double mean_amp_db = 3; 30 | required double peak_amp_db = 4; 31 | required double noise_amp_db = 5; 32 | required double freq_offset_hz = 6; 33 | repeated float signal = 7; 34 | } 35 | 36 | message PDW_Packet { 37 | repeated PDW pdws = 1; 38 | } 39 | 40 | message Packet { 41 | required Packet_Header header = 1; 42 | oneof body { 43 | IQ_Packet iq_packet = 2; 44 | FFT_Packet fft_packet = 3; 45 | PDW_Packet pdw_packet = 4; 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/blink_server/blink_server.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import signal 6 | import zmq 7 | 8 | def signal_handler(signal, frame): 9 | print('Blink server exiting') 10 | os.system("blink1-tool -q -m 0 --off") 11 | sys.exit(0) 12 | 13 | signal.signal(signal.SIGINT, signal_handler) 14 | 15 | port = "5558" 16 | if len(sys.argv) > 1: 17 | port = sys.argv[1] 18 | int(port) 19 | 20 | context = zmq.Context() 21 | socket = context.socket(zmq.SUB) 22 | socket.bind("tcp://*:%s" % port) 23 | socket.setsockopt(zmq.SUBSCRIBE, "blink") 24 | 25 | while True: 26 | try: 27 | msg = socket.recv() 28 | except KeyboardInterrupt: 29 | signal_handler(0,0) 30 | 31 | # remove the topic 32 | args = msg.split() 33 | topic = args.pop(0) 34 | 35 | for arg in args: 36 | if arg == "on": 37 | os.system("blink1-tool -q -m 0 --hsb=130,200,80") 38 | if arg == "off": 39 | os.system("blink1-tool -q -m 0 --off") 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/GUI/receiver.h: -------------------------------------------------------------------------------- 1 | #ifndef RECEIVER_H 2 | #define RECEIVER_H 3 | 4 | #include 5 | 6 | #include "packet.pb.h" 7 | #include "zhelpers.hpp" 8 | 9 | class Receiver : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | public: 14 | Receiver(); 15 | bool initialize(); 16 | void setIP(QString ipAddress); 17 | void tune(uint64_t fc_hz); 18 | void setSampleRate(uint64_t fs_hz); 19 | 20 | public slots: 21 | void receivePackets(); 22 | void stop(); 23 | 24 | signals: 25 | void newPacket(Packet pkt); 26 | void newParameters(uint64_t fc_hz, uint64_t fs_hz, QString data_target); 27 | 28 | private: 29 | void updateParameters(); 30 | 31 | bool isRunning; 32 | bool isInitialized; 33 | QString data_port; 34 | QString comm_port; 35 | QString data_target; 36 | QString comm_target; 37 | 38 | zmq::context_t * comm_context; 39 | zmq::socket_t * comm_socket; 40 | 41 | uint64_t fc_hz; // center freq 42 | uint64_t fs_hz; // sample rate 43 | uint32_t lna_gain; // gain 44 | }; 45 | 46 | #endif // RECEIVER_H 47 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/Socket.h: -------------------------------------------------------------------------------- 1 | #ifndef Socket_class 2 | #define Socket_class 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | const int MAXHOSTNAME = 200; 15 | const int MAXCONNECTIONS = 5; 16 | const int MAXRECV = 500; 17 | 18 | class Socket 19 | { 20 | public: 21 | Socket(); 22 | virtual ~Socket(); 23 | 24 | // Server initialization 25 | bool create(); 26 | bool bind ( const int port ); 27 | bool listen() const; 28 | bool accept ( Socket& ) const; 29 | 30 | // Client initialization 31 | bool connect ( const std::string host, const int port ); 32 | 33 | // Data Transimission 34 | bool send ( const std::string ) const; 35 | bool send ( const uint8_t* data, const int size ); 36 | int recv ( std::string& ) const; 37 | 38 | 39 | void set_non_blocking ( const bool ); 40 | 41 | bool is_valid() const { return m_sock != -1; } 42 | 43 | private: 44 | 45 | int m_sock; 46 | sockaddr_in m_addr; 47 | 48 | 49 | }; 50 | 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/devices/test_rtl.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include // cout 4 | #include // exit 5 | #include // sin cos M_PI 6 | #include // sleep 7 | 8 | #include "RTLSDRDevice.h" 9 | 10 | uint64_t fc_hz = 462610000; // center freq 11 | double fs_hz = 1000000; // sample rate 12 | 13 | void signal_handler(int s) 14 | { 15 | exit(1); 16 | } 17 | 18 | 19 | // 20 | // Callback function for Rx samples 21 | // 22 | int rx_callback(SampleChunk* samples, void* args) 23 | { 24 | std::cout << "got samples! (" << samples->size() << ")" << std::endl; 25 | } 26 | 27 | // 28 | // MAIN 29 | // 30 | int main() 31 | { 32 | // Setup the interrupt signal handler 33 | struct sigaction sigIntHandler; 34 | sigIntHandler.sa_handler = signal_handler; 35 | sigemptyset(&sigIntHandler.sa_mask); 36 | sigIntHandler.sa_flags = 0; 37 | sigaction(SIGINT, &sigIntHandler, NULL); 38 | 39 | RTLSDRDevice rtlsdr; 40 | 41 | rtlsdr.initialize(); 42 | rtlsdr.start_Rx( rx_callback, nullptr ); 43 | 44 | sleep(1); 45 | 46 | return 0; 47 | } 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/devices/SDRReceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef SDRRECEIVER_H 2 | #define SDRRECEIVER_H 3 | 4 | #include "packet.pb.h" 5 | #include "zhelpers.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | typedef void(*sdr_receive_callback_t)( Packet &p, void* args ); 12 | 13 | class SDRReceiver 14 | { 15 | public: 16 | SDRReceiver(); 17 | ~SDRReceiver(); 18 | bool initialize(sdr_receive_callback_t callback, void* callback_args = NULL); 19 | void stop(); 20 | void setIP(std::string ipAddress); 21 | void tune(uint64_t fc_hz); 22 | void setSampleRate(double fs_hz); 23 | double getSampleRate(); 24 | uint64_t getCenterFrequency(); 25 | 26 | private: 27 | void receivePackets(); 28 | 29 | pthread_t m_thread_context; 30 | sdr_receive_callback_t m_callback; 31 | void* m_callback_args; 32 | 33 | bool m_isRunning; 34 | bool m_isInitialized; 35 | std::string m_data_port; 36 | std::string m_comm_port; 37 | std::string m_data_target; 38 | std::string m_comm_target; 39 | 40 | zmq::context_t * m_comm_context; 41 | zmq::socket_t * m_comm_socket; 42 | 43 | friend void* thread_func(void* args); 44 | }; 45 | 46 | #endif // SDRRECEIVER_H 47 | -------------------------------------------------------------------------------- /src/RADAR/collect_data.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [[ $# -lt 1 ]] 4 | then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | 10 | # 11 | # Make the programs 12 | # 13 | if [[ ! $(make) ]] 14 | then 15 | echo "Build error, aborting." 16 | exit 1 17 | fi 18 | 19 | DURATION=$1 20 | SAMPLE_RATE=20000000 21 | NUM_SAMPLES_RX=$(echo "(${SAMPLE_RATE} * ${DURATION})/1" | bc) 22 | DATA_FILE=rx.dat 23 | 24 | 25 | # 26 | # Start the transmitter 27 | # 28 | ./radar_tx & 29 | TX_PID=$! 30 | 31 | # Allow it to check for hardware and begin 32 | sleep 1 33 | 34 | if ! kill -0 $TX_PID 35 | then 36 | echo "Failed to start transmitter!" 37 | exit 1 38 | fi 39 | 40 | # 41 | # Start the receiver 42 | # 43 | if [[ -e ${DATA_FILE} ]] 44 | then 45 | rm ${DATA_FILE} 46 | fi 47 | 48 | hackrf_transfer -r ${DATA_FILE} -d 22be1 -f 2480000000 -a 1 -l 40 -g 0 -s ${SAMPLE_RATE} -n ${NUM_SAMPLES_RX} 49 | 50 | # Kill the transmitter 51 | kill -INT $TX_PID 52 | 53 | 54 | if [ ! -e ${DATA_FILE} ] || [ $(du -b ${DATA_FILE} | awk '{print $1}') -lt 1 ] 55 | then 56 | echo "Failed to receive data" 57 | exit 1 58 | fi 59 | 60 | # 61 | # Process the received file 62 | # 63 | matlab -nodesktop -nosplash -r plot_first_pulse 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/noise_gen/filter.txt: -------------------------------------------------------------------------------- 1 | filter 2 | -6.25225e-19 3 | 2.95823e-19 4 | 0.000845602 5 | -5.23711e-18 6 | -0.00172831 7 | 7.69172e-19 8 | 0.00232308 9 | -8.72562e-18 10 | -0.00196757 11 | 2.17368e-19 12 | -1.67819e-18 13 | 5.59946e-18 14 | 0.00361199 15 | 3.93158e-19 16 | -0.00773058 17 | 3.07264e-17 18 | 0.0100613 19 | 3.99798e-18 20 | -0.00798022 21 | 1.4451e-17 22 | -4.22027e-18 23 | -3.22236e-18 24 | 0.0127528 25 | -5.53112e-17 26 | -0.0258762 27 | 2.79802e-17 28 | 0.0324639 29 | -2.55178e-17 30 | -0.0253316 31 | 1.08047e-17 32 | -6.76235e-18 33 | 1.0507e-17 34 | 0.042769 35 | -2.83064e-17 36 | -0.0960841 37 | 3.77083e-17 38 | 0.148318 39 | -3.49752e-17 40 | -0.186491 41 | 3.53753e-17 42 | 0.200487 43 | 3.53753e-17 44 | -0.186491 45 | -3.49752e-17 46 | 0.148318 47 | 3.77083e-17 48 | -0.0960841 49 | -2.83064e-17 50 | 0.042769 51 | 1.0507e-17 52 | -6.76235e-18 53 | 1.08047e-17 54 | -0.0253316 55 | -2.55178e-17 56 | 0.0324639 57 | 2.79802e-17 58 | -0.0258762 59 | -5.53112e-17 60 | 0.0127528 61 | -3.22236e-18 62 | -4.22027e-18 63 | 1.4451e-17 64 | -0.00798022 65 | 3.99798e-18 66 | 0.0100613 67 | 3.07264e-17 68 | -0.00773058 69 | 3.93158e-19 70 | 0.00361199 71 | 5.59946e-18 72 | -1.67819e-18 73 | 2.17368e-19 74 | -0.00196757 75 | -8.72562e-18 76 | 0.00232308 77 | 7.69172e-19 78 | -0.00172831 79 | -5.23711e-18 80 | 0.000845602 81 | 2.95823e-19 82 | -6.25225e-19 83 | -------------------------------------------------------------------------------- /src/GUI/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "packet.pb.h" 13 | #include "receiver.h" 14 | 15 | namespace Ui { 16 | class MainWindow; 17 | } 18 | 19 | class SpectrogramData; 20 | 21 | class MainWindow : public QMainWindow 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | explicit MainWindow(QWidget *parent = 0); 27 | ~MainWindow(); 28 | 29 | public slots: 30 | void handleNewPacket(Packet p); 31 | void handleNewParameters(uint64_t fc_hz, uint64_t fs_hz, QString host); 32 | 33 | signals: 34 | void startReceivingPackets(void); 35 | 36 | private slots: 37 | void on_pushButton_setIP_clicked(); 38 | void on_pushButton_tune_clicked(); 39 | void on_pushButton_setSampleRate_clicked(); 40 | 41 | private: 42 | 43 | void closeEvent(QCloseEvent *event); 44 | 45 | Ui::MainWindow *ui; 46 | 47 | Receiver m_receiver; 48 | QThread m_receiveThread; 49 | 50 | QwtPlot * p1; 51 | QwtPlotCurve d_curve; 52 | 53 | QwtPlot * p2; 54 | QwtPlotSpectrogram *d_spectrogram; 55 | SpectrogramData* m_specData; 56 | 57 | QVector data; 58 | int num_fft_bins; 59 | std::vector freq_bins_mhz; 60 | }; 61 | 62 | #endif // MAINWINDOW_H 63 | -------------------------------------------------------------------------------- /src/RADAR/collect_data_continuous.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | 4 | # 5 | # Make the programs 6 | # 7 | if [[ ! $(make) ]] 8 | then 9 | echo "Build error, aborting." 10 | exit 1 11 | fi 12 | 13 | DURATION=0.5 14 | SAMPLE_RATE=20000000 15 | NUM_SAMPLES_RX=$(echo "(${SAMPLE_RATE} * ${DURATION})/1" | bc) 16 | DATA_FILE=rx.dat 17 | DATA_MUTEX_FILE=rx.dat.lock 18 | 19 | 20 | 21 | # 22 | # Start the file processor 23 | # 24 | matlab -nodesktop -nosplash -r plot_and_wait & 25 | MATLAB_PID=$! 26 | 27 | # Allow it to check for hardware and begin 28 | sleep 5 29 | 30 | if ! kill -0 $MATLAB_PID 31 | then 32 | echo "Failed to start matlab!" 33 | exit 1 34 | fi 35 | 36 | 37 | # 38 | # Start the transmitter 39 | # 40 | ./radar_tx & 41 | TX_PID=$! 42 | 43 | # Allow it to check for hardware and begin 44 | sleep 1 45 | 46 | if ! kill -0 $TX_PID 47 | then 48 | echo "Failed to start transmitter!" 49 | exit 1 50 | fi 51 | 52 | 53 | # 54 | # Start the receiver 55 | # 56 | 57 | # trap ctrl-c and call ctrl_c() 58 | trap ctrl_c INT 59 | 60 | function ctrl_c() 61 | { 62 | echo "Stopping" 63 | kill -INT $TX_PID 64 | kill -INT $MATLAB_PID 65 | exit 0 66 | } 67 | 68 | 69 | while kill -0 $MATLAB_PID 70 | do 71 | # If the file exists wait for matlab to process it, otherwise, create a new one 72 | if [[ -e ${DATA_MUTEX_FILE} ]] 73 | then 74 | sleep 1 75 | else 76 | hackrf_transfer -r ${DATA_FILE} -d 22be1 -f 2480000000 -a 1 -l 40 -g 0 -s ${SAMPLE_RATE} -n ${NUM_SAMPLES_RX} 77 | touch $DATA_MUTEX_FILE 78 | fi 79 | done 80 | 81 | kill -INT $TX_PID 82 | rm $DATA_FILE 83 | rm $DATA_MUTEX_FILE 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/devices/RFDevice.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RFDEVICE_H 3 | #define RFDEVICE_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define STANDBY_MODE 0 10 | #define RX_MODE 1 11 | #define TX_MODE 2 12 | 13 | typedef std::vector> SampleChunk; 14 | 15 | //typedef int (device_sample_block_cb_fn)(SampleChunk* samples, void* args); 16 | typedef std::function device_sample_block_cb_fn; 17 | 18 | class RFDevice 19 | { 20 | public: 21 | virtual ~RFDevice() = default; 22 | 23 | virtual bool initialize(const int desired_device_index) = 0; 24 | virtual bool initialize(const char* const desired_serial_number=nullptr) = 0; 25 | virtual bool cleanup() = 0; 26 | 27 | virtual bool start_Rx( device_sample_block_cb_fn callback, void* args ) = 0; 28 | virtual bool stop_Rx() = 0; 29 | virtual bool start_Tx( device_sample_block_cb_fn callback, void* args ) = 0; 30 | virtual bool stop_Tx() = 0; 31 | 32 | virtual bool set_center_freq( double fc_hz ) = 0; 33 | virtual bool set_sample_rate( double fs_hz ) = 0; 34 | virtual bool set_rx_gain( double rx_gain ) = 0; 35 | virtual bool set_tx_gain( double tx_gain ) = 0; 36 | 37 | double get_center_freq() { return m_fc_hz; }; 38 | double get_sample_rate() { return m_fs_hz; }; 39 | double get_rx_gain() { return m_rx_gain; }; 40 | double get_tx_gain() { return m_tx_gain; }; 41 | 42 | protected: 43 | 44 | // PRIVATE MEMBERS 45 | bool m_is_initialized; 46 | double m_fc_hz; // center freq 47 | double m_fs_hz; // sample rate 48 | double m_rx_gain; 49 | double m_tx_gain; 50 | }; 51 | 52 | 53 | 54 | #endif /* RFDEVICE */ 55 | -------------------------------------------------------------------------------- /src/RADAR/plot_first_pulse.m: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | filename = 'rx.dat'; 4 | 5 | fs = 20e6; 6 | fc = 2480e6; 7 | 8 | N = 2^12; 9 | f = -(N/2):(N/2)-1; 10 | f = f*fs/N; 11 | f = f + fc; 12 | f_mhz = f/1e6; 13 | 14 | fd = fopen(filename,'r'); 15 | data = fread(fd, 'int8'); 16 | fclose(fd); 17 | 18 | data = reshape(data, 2, []); 19 | iq = complex(data(1,:), data(2,:)); 20 | 21 | t = [0:length(iq)-1]*(1/fs); 22 | 23 | clear data 24 | 25 | lim_l=1; 26 | lim_h=length(iq); 27 | 28 | iq = iq(lim_l:lim_h); 29 | t = t(lim_l:lim_h); 30 | amp = abs(iq); 31 | 32 | 33 | thresh = 50; 34 | 35 | inds = find(amp>thresh); 36 | if length(inds) > 0 37 | 38 | pre_time = 0.0000005; 39 | post_time = 0.000003; 40 | 41 | start_index = max( inds(1) - ( pre_time * fs), 1); 42 | end_index = min( start_index + (post_time * fs), length(amp)); 43 | 44 | frame_iq = iq(start_index:end_index); 45 | frame_amp = amp(start_index:end_index); 46 | frame_t = ([start_index:end_index]-inds(1)).*(1e6/fs); % us 47 | frame_d = frame_t ./ 1e6 .* 299792458 .* 3.28084; % feet 48 | 49 | 50 | % Plots 51 | fig = figure; 52 | set(fig, 'Position', get(0,'Screensize')); % Maximize figure. 53 | 54 | % I and Q 55 | subplot(211) 56 | plot(frame_t, real(frame_iq), '.-') 57 | hold on 58 | plot(frame_t, imag(frame_iq), 'r.-') 59 | xlabel('Time (us)'); 60 | legend('I','Q'); 61 | 62 | % Amplitude 63 | subplot(212) 64 | plot(frame_d, frame_amp, '.-') 65 | hold on 66 | plot( [frame_d(1) frame_d(end)], [ thresh thresh ], 'r') 67 | xlabel('Distance (feet)'); 68 | else 69 | fprintf('No pulse detected\n') 70 | exit 71 | end 72 | 73 | 74 | % Wait for the figure to close 75 | while ishandle(fig) 76 | pause(0.1) 77 | end 78 | exit 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/devices/RTLSDRDevice.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "RFDevice.h" 6 | 7 | #define STANDBY_MODE 0 8 | #define RX_MODE 1 9 | 10 | void* thread_func(void* args); 11 | 12 | class RTLSDRDevice : public RFDevice 13 | { 14 | public: 15 | RTLSDRDevice(); 16 | ~RTLSDRDevice(); 17 | 18 | bool initialize(const int desired_device_index); 19 | bool initialize(const char* const desired_serial_number=nullptr); 20 | bool cleanup(); 21 | 22 | bool start_Rx( device_sample_block_cb_fn callback, void* args ); 23 | bool stop_Rx(); 24 | bool start_Tx( device_sample_block_cb_fn callback, void* args ) { return false; }; 25 | bool stop_Tx() { return false; }; 26 | 27 | //bool tune( uint64_t fc_hz ); 28 | bool set_center_freq( double fc_hz ); 29 | 30 | //bool set_sample_rate( uint32_t fs_hz ); 31 | bool set_sample_rate( double fs_hz ); 32 | 33 | bool set_lna_gain( uint32_t lna_gain ); 34 | bool set_rx_gain( double rx_gain ); 35 | bool set_tx_gain( double tx_gain ) { return false; }; 36 | 37 | uint64_t get_center_frequency() { return m_fc_hz; }; 38 | uint32_t get_sample_rate() { return m_fs_hz; }; 39 | uint32_t get_rx_gain() { return m_rx_gain; }; 40 | 41 | private: 42 | 43 | // PRIVATE MEMBERS 44 | int m_device_mode; // mode: 0=standby 1=Rx 2=Tx 45 | rtlsdr_dev_t* m_device; // device handle 46 | pthread_t m_thread_context; // Rx thread context 47 | 48 | std::pair* m_callback_args; 49 | 50 | // PRIVATE FUNCTIONS 51 | bool check_error(int error); 52 | 53 | friend void* thread_func(void* args); 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 | -------------------------------------------------------------------------------- /src/websock/webrelay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import asyncio 4 | import websockets 5 | import sys 6 | sys.path.append('../../build/src/protobuffers/') 7 | sys.path.append('../protobuffers/') 8 | import myhackrf 9 | 10 | 11 | radio = myhackrf.MyHackrf('localhost') 12 | radio.send("set-rx-mode fft") 13 | radio.send("set-fs 2000000") 14 | radio.send("set-fc 101100000") 15 | radio.send("set-rx-gain 40") 16 | 17 | 18 | async def stream(websocket, uri): 19 | print('stream') 20 | 21 | while True: 22 | print('waiting for start') 23 | message = await websocket.recv() 24 | 25 | if message == "start": 26 | print('got start') 27 | 28 | # Receive Rx data from SDR 29 | try: 30 | while True: 31 | # wait for a request for data 32 | try: 33 | print('waiting for request...') 34 | message = await websocket.recv() 35 | print('Got:', message) 36 | except websockets.ConnectionClosedOK: 37 | break 38 | if message == "more": 39 | # Get data 40 | await websocket.send(radio.rx_sock.recv()) 41 | elif message == "stop": 42 | break 43 | except KeyboardInterrupt: 44 | pass 45 | 46 | 47 | # Workaround for ctrl-c signals.... 48 | def wakeup(): 49 | loop.call_later(0.1, wakeup) 50 | 51 | 52 | if __name__ == '__main__': 53 | loop = asyncio.get_event_loop() 54 | server = websockets.serve(stream, "0.0.0.0", 8765) 55 | loop.call_later(0.1, wakeup) 56 | 57 | try: 58 | loop.run_until_complete(server) 59 | print("run_forever") 60 | loop.run_forever() 61 | except KeyboardInterrupt: 62 | print("ctrl-c") 63 | loop.stop() 64 | finally: 65 | print("closing") 66 | loop.close() 67 | -------------------------------------------------------------------------------- /src/websock/fft.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 30 | 31 | 32 |
33 |
34 | 35 | 36 | 37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/RADAR/plot_as_rdm.m: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | %filename = 'first_contact.dat'; 4 | filename = 'rx.dat'; 5 | %filename = 'data/rx.dat'; 6 | %filename = 'data/rx_far.dat'; 7 | %filename = 'data/rx_tree.dat'; 8 | 9 | fd = fopen(filename,'r'); 10 | 11 | prf = 20e6/262144; 12 | pulse_size = 30; 13 | pulse_offset = 4; % number of pulses in waveform 14 | pulses = []; 15 | try 16 | while 1 17 | data = fread(fd, 10e7, 'int8'); 18 | 19 | if isempty(data) 20 | fclose(fd); 21 | break 22 | end 23 | 24 | data = reshape(data, 2, []); 25 | data = complex(data(1,:), data(2,:)); 26 | amp = abs(data); 27 | 28 | inds = find(amp > 80); 29 | 30 | last = -1; 31 | for ii = 1:length(inds) 32 | 33 | if inds(ii) ~= (last+1) 34 | pulses = [ pulses ; data( inds(ii)+pulse_offset : inds(ii)+pulse_size-1+pulse_offset ) ]; 35 | end 36 | 37 | last = inds(ii); 38 | end 39 | 40 | fprintf('num pulses: %d\n', size(pulses,1)); 41 | end 42 | catch e 43 | disp(e) 44 | fclose(fd); 45 | end 46 | clear data 47 | 48 | pulses(117,:) = []; 49 | 50 | num_pulses = size(pulses,1); 51 | 52 | % Compute range doppler map 53 | rdm = zeros(size(pulses)); 54 | for ii = 1:pulse_size 55 | rdm(:,ii) = fftshift(abs(fft(pulses(:,ii)))); 56 | end 57 | 58 | % 59 | % r = tc/2 60 | % 61 | range = ((pulse_offset:size(pulses,2)-1+pulse_offset) .* (1/8e6)) .* 3e8 ./ 2; 62 | 63 | subplot(221); 64 | imagesc(range, 1:num_pulses, abs(pulses)); 65 | title('Pulse Amplitude') 66 | xlabel('Range (m)') 67 | ylabel('Pulse Number') 68 | colorbar; 69 | 70 | subplot(222); 71 | imagesc(linspace(-num_pulses/2, num_pulses/2, num_pulses), range, rdm'); 72 | set(gca,'YDir','normal') 73 | title('Range Doppler Map') 74 | xlabel('Doppler shift (Hz)') 75 | ylabel('Range (m)') 76 | colorbar; 77 | 78 | subplot(223); 79 | plot(range, sum(abs(pulses))./num_pulses, '.-'); 80 | title('Mean Amplitude') 81 | xlabel('Range (m)') 82 | ylabel('Amplitude (counts)') 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/ServerSocket.cpp: -------------------------------------------------------------------------------- 1 | #include "ServerSocket.h" 2 | #include "SocketException.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | ServerSocket::ServerSocket ( int port ) 9 | { 10 | if ( ! Socket::create() ) 11 | { 12 | int e = errno; 13 | std::stringstream error; 14 | error << "Could not create server socket: error: (" << e << ") " << strerror(e); 15 | throw SocketException ( error.str() ); 16 | } 17 | 18 | if ( ! Socket::bind ( port ) ) 19 | { 20 | int e = errno; 21 | std::stringstream error; 22 | error << "Could not bind to port: error: (" << e << ") " << strerror(e); 23 | throw SocketException ( error.str() ); 24 | } 25 | 26 | if ( ! Socket::listen() ) 27 | { 28 | int e = errno; 29 | std::stringstream error; 30 | error << "Could not listen to socket: error: (" << e << ") " << strerror(e); 31 | throw SocketException ( error.str() ); 32 | } 33 | 34 | } 35 | 36 | ServerSocket::~ServerSocket() 37 | { 38 | } 39 | 40 | 41 | const ServerSocket& ServerSocket::operator << ( const std::string& s ) const 42 | { 43 | if ( ! Socket::send ( s ) ) 44 | { 45 | int e = errno; 46 | std::stringstream error; 47 | error << "Could not write to socket: error: (" << e << ") " << strerror(e); 48 | throw SocketException ( error.str() ); 49 | } 50 | 51 | return *this; 52 | 53 | } 54 | 55 | 56 | const ServerSocket& ServerSocket::operator >> ( std::string& s ) const 57 | { 58 | if ( ! Socket::recv ( s ) ) 59 | { 60 | int e = errno; 61 | std::stringstream error; 62 | error << "Could not read from socket: error: (" << e << ") " << strerror(e); 63 | throw SocketException ( error.str() ); 64 | } 65 | 66 | return *this; 67 | } 68 | 69 | void ServerSocket::accept ( ServerSocket& sock ) 70 | { 71 | if ( ! Socket::accept ( sock ) ) 72 | { 73 | int e = errno; 74 | std::stringstream error; 75 | error << "Could not accept socket: error: (" << e << ") " << strerror(e); 76 | throw SocketException ( error.str() ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/devices/test_hackrf.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include // cout 4 | #include // exit 5 | #include // sin cos M_PI 6 | #include // sleep 7 | 8 | #include "HackRFDevice.h" 9 | 10 | uint64_t fc_hz = 462610000; // center freq 11 | double fs_hz = 1000000; // sample rate 12 | 13 | void signal_handler(int s) 14 | { 15 | exit(1); 16 | } 17 | 18 | // 19 | // Callback function for Tx samples 20 | // 21 | //262144 22 | double t = 0.0; 23 | double dt = 1/1000000.0; 24 | bool print_signal = true; 25 | double df = 1000; 26 | int tx_cb_fn(hackrf_transfer* transfer) 27 | { 28 | for (int ii = 0; ii < transfer->valid_length; ii+=2) 29 | { 30 | double i = 128.0 * cos( 2.0 * M_PI * df * t ); // I 31 | double q = 128.0 * sin( 2.0 * M_PI * df * t ); // Q 32 | 33 | uint8_t i8 = uint8_t(i); 34 | uint8_t q8 = uint8_t(q); 35 | 36 | transfer->buffer[ii+0] = i8; 37 | transfer->buffer[ii+1] = q8; 38 | 39 | t = t+dt; 40 | if ( t >= 1.0 ) 41 | { 42 | t = 0.0; 43 | } 44 | } 45 | 46 | df = -df; 47 | 48 | return 0; 49 | } 50 | 51 | // 52 | // Callback function for Rx samples 53 | // 54 | int rx_cb_fn(SampleChunk* samples, void* args) 55 | { 56 | std::cout << "got a packet with " << samples->size() << " samples!" << std::endl; 57 | return 0; 58 | } 59 | 60 | // 61 | // MAIN 62 | // 63 | int main() 64 | { 65 | // Setup the interrupt signal handler 66 | struct sigaction sigIntHandler; 67 | sigIntHandler.sa_handler = signal_handler; 68 | sigemptyset(&sigIntHandler.sa_mask); 69 | sigIntHandler.sa_flags = 0; 70 | sigaction(SIGINT, &sigIntHandler, NULL); 71 | 72 | HackRFDevice hackrf; 73 | 74 | bool rv = hackrf.initialize(); 75 | if ( ! rv ) 76 | { 77 | std::cout << "Failed to initialize!" << std::endl; 78 | } 79 | 80 | rv = hackrf.start_Rx( rx_cb_fn, NULL ); 81 | if ( ! rv ) 82 | { 83 | std::cout << "Failed to start Rx!" << std::endl; 84 | } 85 | 86 | sleep(1); 87 | 88 | return 0; 89 | } 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/websock/iq.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | -------------------------------------------------------------------------------- /src/devices/HackRFDevice.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef HACKRFDEVICE_H 3 | #define HACKRFDEVICE_H 4 | 5 | #include "RFDevice.h" 6 | 7 | #include 8 | 9 | class HackRFDevice : public RFDevice 10 | { 11 | public: 12 | HackRFDevice(); 13 | ~HackRFDevice(); 14 | 15 | bool initialize(const int desired_device_index); 16 | bool initialize(const char* const desired_serial_number=nullptr); 17 | bool cleanup(); 18 | 19 | bool start_Rx( device_sample_block_cb_fn callback, void* args ); 20 | bool stop_Rx(); 21 | bool start_Tx( device_sample_block_cb_fn callback, void* args ); 22 | bool stop_Tx(); 23 | 24 | int get_mode(); // returns m_device_mode 25 | 26 | bool set_center_freq( double fc_hz ); 27 | bool set_rx_gain( double rx_gain ); 28 | bool set_tx_gain( double tx_gain ); 29 | 30 | 31 | bool force_sample_rate( double fs_hz ); // not recommended 32 | bool set_sample_rate( double fs_hz ); 33 | bool set_lna_gain( uint32_t lna_gain ); 34 | bool set_amp_enable( uint8_t amp_enable ); 35 | bool set_antenna_enable( uint8_t value ); 36 | bool set_rxvga_gain( uint32_t rxvga_gain ); 37 | bool set_txvga_gain( uint32_t txvga_gain ); 38 | 39 | uint32_t get_lna_gain() { return m_lna_gain; }; 40 | uint8_t get_amp_enable() { return m_amp_enable; }; 41 | uint8_t get_antenna_enable() { return m_antenna_enable; }; 42 | uint32_t get_rxvga_gain() { return m_rxvga_gain; }; 43 | uint32_t get_txvga_gain() { return m_txvga_gain; }; 44 | 45 | private: 46 | 47 | // PRIVATE MEMBERS 48 | int m_device_mode; // mode: 0=standby 1=Rx 2=Tx 49 | hackrf_device* m_device; // device handle 50 | uint32_t m_lna_gain; 51 | uint8_t m_amp_enable; 52 | uint8_t m_antenna_enable; 53 | uint32_t m_rxvga_gain; 54 | uint32_t m_txvga_gain; 55 | std::pair* m_callback_args; 56 | 57 | // PRIVATE FUNCTIONS 58 | bool check_error(int error); 59 | }; 60 | 61 | 62 | 63 | #endif /* HACKRFDEVICE */ 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 | -------------------------------------------------------------------------------- /src/websock/fft_chart.js: -------------------------------------------------------------------------------- 1 | 2 | goog.require('proto.Packet'); 3 | 4 | var n_fft_size = 2048; 5 | var fs = 2000000; 6 | 7 | var fft_chart = new Chart(document.getElementById("fft-chart"), { 8 | type: 'line', 9 | data: { 10 | labels: linspace(-fs/2, fs/2, n_fft_size, false), 11 | datasets: [{ 12 | data: Int16Array.from(Array(n_fft_size).keys()), 13 | pointRadius: 0, 14 | label: "FFT", 15 | borderColor: "#3e95cd", 16 | fill: false 17 | } 18 | ] 19 | }, 20 | options: { 21 | title: { 22 | display: true, 23 | text: 'FFT' 24 | }, 25 | animation: false, 26 | maintainAspectRatio: false, 27 | responsive: true 28 | } 29 | }); 30 | 31 | var paused = false; 32 | function pause() 33 | { 34 | if (paused) 35 | { 36 | paused = false; 37 | send('more'); 38 | } 39 | else 40 | { 41 | paused = true; 42 | } 43 | } 44 | 45 | function linspace(start, stop, num, endpoint = true) 46 | { 47 | const div = endpoint ? (num - 1) : num; 48 | const step = (stop - start) / div; 49 | return Array.from({length: num}, (_, i) => start + step * i); 50 | } 51 | 52 | // Make a websocket connection 53 | var connection = new WebSocket('ws://rpi4:8765') 54 | connection.binaryType = "arraybuffer"; 55 | 56 | connection.onopen = function () 57 | { 58 | // Kick off a request for some IQ data 59 | connection.send('more'); 60 | }; 61 | 62 | // Log errors 63 | connection.onerror = function (error) 64 | { 65 | console.log('WebSocket Error ' + error); 66 | }; 67 | 68 | // Log messages from the server 69 | connection.onmessage = function (e) 70 | { 71 | var message = proto.Packet.deserializeBinary(e.data); 72 | 73 | if (message.getHeader().getType() != proto.Packet_Header.PacketType.FFT) 74 | return; 75 | 76 | // Update charts 77 | fft_chart.data.datasets[0].data = message.getFftPacket().getFftList(); 78 | fft_chart.data.labels = message.getFftPacket().getFreqBinsHzList(); 79 | fft_chart.options.scales.y.max = 20; 80 | fft_chart.options.scales.y.min = -50; 81 | fft_chart.update(); 82 | 83 | console.log('Got FFT'); 84 | if (!paused) 85 | { 86 | connection.send('more'); // Ask for more data 87 | } 88 | }; 89 | -------------------------------------------------------------------------------- /src/RADAR/main_rx.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HackRFDevice.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define PI 3.141592653589793 13 | 14 | HackRFDevice hackrf; 15 | 16 | uint64_t fc_hz = 2500e6; // center freq 17 | double fs_hz = 20e6; // sample rate 18 | uint32_t lna_gain = 32; // 0-40 in steps of 8 19 | uint8_t amp_enable = 0; 20 | uint32_t txvga_gain = 0; 21 | 22 | 23 | void signal_handler(int s) 24 | { 25 | printf("Caught signal %d\n",s); 26 | hackrf.cleanup(); 27 | exit(0); 28 | } 29 | 30 | 31 | // 32 | // Callback function for tx samples 33 | // 34 | //262144 35 | double t = 0.0; 36 | double dt = 1/fs_hz; 37 | 38 | //int sample_block_cb_fn(hackrf_transfer* transfer) 39 | int device_sample_block_cb(SampleChunk* samples, void* args) 40 | { 41 | std::cout << "P " << std::flush; 42 | 43 | double max_amp = -9999999.0; 44 | std::size_t ii = 0; 45 | double i,q; 46 | 47 | while (ii < samples->size()) 48 | { 49 | i = (*samples)[ii].real(); 50 | q = (*samples)[ii].imag(); 51 | ii += 1; 52 | 53 | double amp = 20.0 * log10( sqrt( pow(i,2) + pow(q,2) ) ); 54 | 55 | if ( amp > max_amp ) 56 | max_amp = amp; 57 | } 58 | 59 | std::cout << i << " " << max_amp << std::endl << std::flush; 60 | 61 | return 0; 62 | } 63 | 64 | 65 | // 66 | // MAIN 67 | // 68 | int main() 69 | { 70 | // Setup the interrupt signal handler 71 | struct sigaction sigIntHandler; 72 | sigIntHandler.sa_handler = signal_handler; 73 | sigemptyset(&sigIntHandler.sa_mask); 74 | sigIntHandler.sa_flags = 0; 75 | sigaction(SIGINT, &sigIntHandler, NULL); 76 | 77 | 78 | if ( ! hackrf.initialize( "22be1" ) ) 79 | { 80 | std::cout << "Error initializing hackrf device!" << std::endl; 81 | return 1; 82 | } 83 | 84 | hackrf.set_center_freq( fc_hz ); 85 | hackrf.set_sample_rate( fs_hz ); 86 | hackrf.set_lna_gain( lna_gain ); 87 | hackrf.set_amp_enable( amp_enable ); 88 | hackrf.set_txvga_gain( txvga_gain ); 89 | hackrf.start_Rx( device_sample_block_cb, (void*)(NULL) ); 90 | 91 | // Wait a while 92 | sleep(300); 93 | 94 | return 0; 95 | } 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/RADAR/plot_all_pulses.m: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | filename = 'rx.dat'; 4 | 5 | fs = 20e6; 6 | fc = 2480e6; 7 | 8 | N = 2^12; 9 | f = -(N/2):(N/2)-1; 10 | f = f*fs/N; 11 | f = f + fc; 12 | f_mhz = f/1e6; 13 | 14 | fd = fopen(filename,'r'); 15 | data = fread(fd, 20e7, 'int8'); 16 | fclose(fd); 17 | 18 | data = reshape(data, 2, []); 19 | iq = complex(data(1,:), data(2,:)); 20 | 21 | t = [0:length(iq)-1]*(1/fs); 22 | 23 | clear data 24 | 25 | lim_l=1; 26 | lim_h=length(iq); 27 | 28 | iq = iq(lim_l:lim_h); 29 | t = t(lim_l:lim_h); 30 | amp = abs(iq); 31 | 32 | 33 | thresh = 20; 34 | inds = find(amp>thresh); 35 | 36 | if length(inds) == 0 37 | fprintf('No pulse detected\n') 38 | exit 39 | end 40 | 41 | 42 | % Window size 43 | pre_time = 0.0000005; % seconds before the detection 44 | post_time = 0.000003; % seconds after the detection 45 | window_size_samples = (pre_time + post_time) * fs; 46 | 47 | stack_size = 500; 48 | stack_index = 1; 49 | 50 | data = []; 51 | 52 | fig = figure; 53 | set(fig, 'Position', get(0,'Screensize')); % Maximize figure. 54 | 55 | while length(inds) > 0 && stack_index <= stack_size 56 | 57 | % Grab first index 58 | index = inds(1); 59 | 60 | % Throw away all inds between this one and the next "window size" 61 | inds = inds(inds > (index + window_size_samples)); 62 | 63 | % Find the data 64 | start_index = max( index - ( pre_time * fs), 1); 65 | end_index = min( start_index + (post_time * fs), length(amp)); 66 | 67 | % Copy the data 68 | frame_iq = iq(start_index:end_index); 69 | frame_amp = amp(start_index:end_index); 70 | frame_t = ([start_index:end_index]-index).*(1e6/fs); % us 71 | frame_d = frame_t ./ 1e6 .* 299792458 .* 3.28084; % feet 72 | 73 | data(stack_index,:) = frame_amp; 74 | 75 | stack_index = stack_index + 1; 76 | end 77 | 78 | % Plot the pulses 79 | subplot(211) 80 | surf(data) 81 | view( 20, 15 ) 82 | title(['Amplitude of ' num2str(stack_index) ' pulses']) 83 | 84 | subplot(212) 85 | plot(frame_d, mean(data), '.-') 86 | hold on 87 | 88 | % Plot the threshold 89 | plot( [frame_d(1) frame_d(end)], [ thresh thresh ], 'r') 90 | xlabel('Distance (feet)'); 91 | title(['Mean of ' num2str(stack_index) ' pulse sample values']) 92 | 93 | 94 | % Wait for the figure to close 95 | while ishandle(fig) 96 | pause(0.1) 97 | end 98 | %exit 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/utilities/test_pulse_detector.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "pulse_detector.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | void add_pulse(SampleChunk &samples, double toa, double pw, double amp, double fs) 10 | { 11 | uint64_t start_sample = uint64_t( toa * fs ); 12 | uint64_t stop_sample = uint64_t( (toa + pw) * fs ); 13 | 14 | if ( samples.size() < stop_sample ) 15 | samples.resize(stop_sample); 16 | 17 | for (uint64_t x = start_sample; x < stop_sample; x++) 18 | samples[x] = std::complex(0,amp); 19 | } 20 | 21 | 22 | void make_noise(SampleChunk &samples, double duration, double noise_std_dev, double fs) 23 | { 24 | std::default_random_engine generator; 25 | std::normal_distribution distribution(0.0, noise_std_dev); 26 | 27 | uint64_t num_samples = uint64_t( duration * fs ); 28 | 29 | samples.resize(num_samples); 30 | for (uint64_t x = 0; x < num_samples; x++) 31 | samples[x] = std::complex(distribution(generator), distribution(generator)); 32 | } 33 | 34 | 35 | int main() 36 | { 37 | PDW_Packet result; 38 | SampleChunk samples; 39 | const double fs = 1e6; 40 | 41 | make_noise(samples, 0.003, 0.01, fs); 42 | 43 | add_pulse(samples, 0.001, 0.000100, 0.5, fs); // 100us pulse 44 | 45 | add_pulse(samples, 0.0012, 0.000050, 0.2, fs); // 10us pulse 46 | add_pulse(samples, 0.0013, 0.000050, 0.2, fs); // 10us pulse 47 | add_pulse(samples, 0.0014, 0.000050, 0.2, fs); // 10us pulse 48 | add_pulse(samples, 0.0015, 0.000050, 0.2, fs); // 10us pulse 49 | 50 | std::cout << "Waveform size: " << samples.size() << " samples" << std::endl; 51 | 52 | utilities::detectPulses(&result, &samples, 0, fs, 5.0); 53 | 54 | std::cout << std::setw(10) << "TOA" 55 | << std::setw(10) << "PW" 56 | << std::setw(10) << "MEAN" 57 | << std::setw(10) << "PEAK" 58 | << std::setw(10) << "NOISE" 59 | << std::setw(15) << "FREQ" 60 | << std::endl; 61 | 62 | for (int x = 0; x < result.pdws_size(); x++) 63 | { 64 | std::cout 65 | << std::setw(10) << result.pdws(x).toa_s() 66 | << std::setw(10) << result.pdws(x).pw_s() 67 | << std::setw(10) << result.pdws(x).mean_amp_db() 68 | << std::setw(10) << result.pdws(x).peak_amp_db() 69 | << std::setw(10) << result.pdws(x).noise_amp_db() 70 | << std::setw(15) << result.pdws(x).freq_offset_hz() 71 | << std::endl; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/RADAR/plot_and_wait.m: -------------------------------------------------------------------------------- 1 | clear 2 | 3 | mutex_filename = 'rx.dat.lock'; 4 | filename = 'rx.dat'; 5 | 6 | fs = 20e6; 7 | 8 | fig = []; 9 | 10 | while 1 11 | 12 | % Wait for a new file to appear 13 | while exist(mutex_filename, 'file') ~= 2 14 | fprintf('Waiting for file...\n'); 15 | pause(2); 16 | end 17 | 18 | % Read the file 19 | fd = fopen(filename,'r'); 20 | data = fread(fd, 'int8'); 21 | fclose(fd); 22 | 23 | % Delete the file 24 | delete(filename); 25 | delete(mutex_filename); 26 | 27 | % Process the file 28 | data = reshape(data, 2, []); 29 | iq = complex(data(1,:), data(2,:)); 30 | amp = abs(iq); 31 | 32 | 33 | thresh = 100; 34 | inds = find(amp>thresh); 35 | 36 | if length(inds) == 0 37 | fprintf('No pulse detected\n') 38 | exit 39 | end 40 | 41 | 42 | % Window size 43 | pre_time = 0.0000005; % seconds before the detection 44 | post_time = 0.000003; % seconds after the detection 45 | window_size_samples = (pre_time + post_time) * fs; 46 | 47 | stack_size = 50; 48 | stack_index = 1; 49 | 50 | data = []; 51 | 52 | while length(inds) > 0 && stack_index <= stack_size 53 | 54 | % Grab first index 55 | index = inds(1); 56 | 57 | % Throw away all inds between this one and the next "window size" 58 | inds = inds(inds > (index + window_size_samples)); 59 | 60 | % Find the data 61 | start_index = max( index - ( pre_time * fs), 1); 62 | end_index = min( start_index + (post_time * fs), length(amp)); 63 | 64 | % Copy the data 65 | frame_iq = iq(start_index:end_index); 66 | frame_amp = amp(start_index:end_index); 67 | frame_t = ([start_index:end_index]-index).*(1e6/fs); % us 68 | frame_d = frame_t ./ 1e6 .* 299792458 .* 3.28084; % feet 69 | 70 | data(stack_index,:) = frame_amp; 71 | 72 | stack_index = stack_index + 1; 73 | end 74 | 75 | if ~ishandle(fig) 76 | fig = figure; 77 | %set(fig, 'Position', get(0,'Screensize')); % Maximize figure. 78 | end 79 | 80 | % Plot the pulses 81 | subplot(211) 82 | surf(data) 83 | view( 20, 15 ) 84 | title(['Amplitude of ' num2str(stack_size) ' pulses']) 85 | 86 | subplot(212) 87 | plot(frame_d, mean(data), '.-') 88 | hold on 89 | 90 | % Plot the threshold 91 | plot( [frame_d(1) frame_d(end)], [ thresh thresh ], 'r') 92 | xlabel('Distance (feet)'); 93 | title(['Mean of ' num2str(stack_size) ' pulse sample values']) 94 | 95 | drawnow; 96 | 97 | end 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/utilities/waveforms.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WAVEFORMS_H 3 | #define WAVEFORMS_H 4 | 5 | #include 6 | #include 7 | 8 | #define PI 3.141592653589793 9 | 10 | //#define SAVE_FILE 11 | 12 | void createTone(std::deque> &out_sig, double fs, double freq_offset, double pw_s, double mag) 13 | { 14 | uint32_t num_samples = uint32_t(fs * pw_s); 15 | out_sig.resize(num_samples); 16 | 17 | std::cout << "---------------" << std::endl; 18 | std::cout << "Making waveform" << std::endl; 19 | std::cout << "fs: " << uint32_t(fs) << std::endl; 20 | std::cout << "freq_offset: " << freq_offset << std::endl; 21 | std::cout << "pw (s): " << pw_s << std::endl; 22 | std::cout << "pw (samps): " << num_samples << std::endl; 23 | std::cout << "mag: " << mag << std::endl; 24 | 25 | double dt = 1.0/fs; 26 | for ( std::size_t ii = 0; ii < num_samples; ii++) 27 | { 28 | double i = mag * cos( 2.0 * PI * freq_offset * (ii*dt) ); // I 29 | double q = mag * sin( 2.0 * PI * freq_offset * (ii*dt) ); // Q 30 | 31 | out_sig[ii] = std::complex(i,q); 32 | } 33 | 34 | std::cout << "---------------" << std::endl; 35 | } 36 | 37 | 38 | void createChirp(std::deque> &out_sig, double fs, double start_freq_hz, double stop_freq_hz, double pw_s, double mag) 39 | { 40 | uint32_t num_samples = uint32_t(fs * pw_s); 41 | out_sig.resize(num_samples); 42 | 43 | std::cout << "---------------" << std::endl; 44 | std::cout << "Making waveform" << std::endl; 45 | std::cout << "fs: " << uint32_t(fs) << std::endl; 46 | std::cout << "start_freq_hz: " << start_freq_hz << std::endl; 47 | std::cout << "stop_freq_hz: " << stop_freq_hz << std::endl; 48 | std::cout << "pw (s): " << pw_s << std::endl; 49 | std::cout << "pw (samps): " << num_samples << std::endl; 50 | std::cout << "mag: " << mag << std::endl; 51 | 52 | double start_phi = 2.0 * PI * start_freq_hz; 53 | double stop_phi = 2.0 * PI * stop_freq_hz; 54 | double delta_phi = stop_phi - start_phi; 55 | double dt = 1.0/fs; 56 | 57 | #ifdef SAVE_FILE 58 | std::ofstream ofile("tx.csv", std::ofstream::out); 59 | ofile << "t,i,q" << std::endl; 60 | #endif 61 | 62 | for ( std::size_t ii = 0; ii < num_samples; ii++) 63 | { 64 | double t = ii*dt; 65 | double phi = start_phi * t + delta_phi * t * t / (2.0*pw_s); 66 | double i = mag * cos( phi ); // I 67 | double q = mag * sin( phi ); // Q 68 | 69 | out_sig[ii] = std::complex(i,q); 70 | 71 | #ifdef SAVE_FILE 72 | ofile << t << "," << i << "," << q << std::endl; 73 | #endif 74 | 75 | } 76 | 77 | std::cout << "---------------" << std::endl; 78 | 79 | #ifdef SAVE_FILE 80 | ofile.close(); 81 | #endif 82 | 83 | } 84 | 85 | 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/utilities/fft.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "fft.h" 3 | 4 | 5 | namespace utilities 6 | { 7 | // 8 | // Calculate Frequency Bins 9 | // 10 | // f = -(N/2):(N/2)-1; // matlab code 11 | // f = f*fs/N; 12 | // f = f + fc; 13 | // f = f / 1e6; 14 | // 15 | void calcFrequencyBins(FFT_Packet* packet, uint32_t N, double fs, uint64_t fc) 16 | { 17 | double fs_over_N = double(fs)/double(N); 18 | packet->clear_freq_bins_hz(); 19 | for (int ii = (-1.0 * double(N)/2.0); ii < (double(N)/2.0); ii++) 20 | packet->add_freq_bins_hz( ( double(ii) * fs_over_N ) + fc ); 21 | } 22 | 23 | inline double hamming_window( uint32_t N, double n ) 24 | { 25 | return ( 0.54 - ( 0.46 * cos( 2.0 * M_PI * ( n / N ) ) ) ); 26 | } 27 | 28 | // 29 | // fft 30 | // 31 | void fft( FFT_Packet* result, SampleChunk &buffer, uint32_t num_fft_bins, double fs, uint64_t fc ) 32 | { 33 | uint32_t num_samples( buffer.size() ); 34 | calcFrequencyBins( result, num_fft_bins, fs, fc ); 35 | 36 | fftw_complex *in, *out; 37 | in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*num_samples); 38 | out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*num_fft_bins); 39 | 40 | for (std::size_t ii = 0; ii < buffer.size(); ii++) 41 | { 42 | in[ii][0] = buffer[ii].real() * hamming_window( num_samples, ii ); 43 | in[ii][1] = buffer[ii].imag() * hamming_window( num_samples, ii ); 44 | } 45 | 46 | fftw_plan my_plan; 47 | my_plan = fftw_plan_dft_1d(int(num_fft_bins), in, out, FFTW_FORWARD, FFTW_ESTIMATE); 48 | fftw_execute(my_plan); 49 | 50 | result->clear_fft(); 51 | 52 | for (uint32_t ii = 0; ii < num_fft_bins; ii++) 53 | { 54 | // 55 | // fftshift 56 | // 57 | int idx = ii+(num_fft_bins/2); 58 | if (ii >= (num_fft_bins/2)) 59 | idx = ii - (num_fft_bins/2); 60 | 61 | // 62 | // 20*log10(val) 63 | // 64 | double val = fabs(out[idx][0]); 65 | if ( 0.0 != val ) 66 | val = 20.0 * log10(val); 67 | 68 | // save the bin 69 | result->add_fft( val ); 70 | 71 | // 72 | // Find the average value across the FFT 73 | // This should give us the center of the noise 74 | // (assuming the noise dominates the bandwidth) 75 | // 76 | // result.set_mean_db( result.mean_db() + val ); 77 | 78 | // if ( val > result.peak_db() ) 79 | // { 80 | // result.set_peak_bin_index( ii ); 81 | // result.set_peak_db( val ); 82 | // } 83 | 84 | // if ( val < result.low_db() ) 85 | // result.set_low_db( val ); 86 | } 87 | 88 | // noise mean for this FFT 89 | // result.set_mean_db( result.mean_db() / num_fft_bins ); 90 | 91 | fftw_destroy_plan(my_plan); 92 | fftw_free(in); 93 | fftw_free(out); 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/utilities/myhackrf.py: -------------------------------------------------------------------------------- 1 | import zmq 2 | import packet_pb2 3 | 4 | # Supported commands: 5 | # get-fc 6 | # get-fs 7 | # get-rx-gain 8 | # set-fc 9 | # set-fs 10 | # set-rx-gain 11 | # set-rx-mode iq|pdw|fft 12 | # quit 13 | 14 | class MyHackrf: 15 | def __init__(self, host): 16 | ctx = zmq.Context() 17 | self.rx_sock = ctx.socket(zmq.SUB) 18 | self.rx_sock.connect("tcp://" + host + ":5555") 19 | self.rx_sock.setsockopt(zmq.SUBSCRIBE, b"") 20 | 21 | self.tx_sock = ctx.socket(zmq.REQ) 22 | self.tx_sock.connect("tcp://" + host + ":5556") 23 | 24 | def get_params(self): 25 | self.tx_sock.send_string("get-fc") 26 | print(self.tx_sock.recv()) 27 | 28 | def send(self, cmd): 29 | self.tx_sock.send_string(cmd) 30 | rv = self.tx_sock.recv() 31 | print(cmd, ":", rv) 32 | return rv 33 | 34 | def recv(self): 35 | msg = self.rx_sock.recv() 36 | p = packet_pb2.Packet() 37 | p.ParseFromString(msg) 38 | return p 39 | 40 | 41 | if __name__ == "__main__": 42 | from matplotlib import pyplot 43 | import numpy 44 | 45 | dev = MyHackrf('rpi4') 46 | dev.send("set-fs 2000000") 47 | dev.send("set-fc 915500000") 48 | dev.send("set-rx-gain 60") 49 | 50 | if 0: 51 | dev.send("set-rx-mode fft") 52 | 53 | waterfall = [] 54 | f = [] 55 | while len(waterfall) < 100: 56 | p = dev.recv() 57 | if p.header.type == packet_pb2.Packet_Header.FFT: 58 | f = p.fft_packet.freq_bins_hz 59 | waterfall.append(p.fft_packet.fft) 60 | print("GOT FFT " + str(len(waterfall))) 61 | 62 | pyplot.figure() 63 | pyplot.imshow(waterfall, extent=[min(f)/1e6, max(f)/1e6, 0, len(waterfall)], origin='lower', aspect='auto') 64 | pyplot.colorbar() 65 | pyplot.xlabel('Freq (MHz)') 66 | pyplot.ylabel('Time') 67 | 68 | if 1: 69 | dev.send("set-rx-mode pdw") 70 | 71 | while True: 72 | p = dev.recv() 73 | print(p.header.type) 74 | if p.header.type == packet_pb2.Packet_Header.PDW: 75 | print("GOT %d PDWs" % len(p.pdw_packet.pdws)) 76 | break 77 | 78 | pyplot.figure() 79 | 80 | for pdw in p.pdw_packet.pdws: 81 | complex_sig = numpy.array(pdw.signal[0::2]) + 1j * numpy.array(pdw.signal[1::2]) 82 | pyplot.plot(pdw.toa_s + numpy.array(range(len(complex_sig)))/8e6, 20.0*numpy.log10(numpy.abs(complex_sig))) 83 | 84 | if 0: 85 | dev.send("set-rx-mode iq") 86 | 87 | print("b4 recv") 88 | p = dev.recv() 89 | print("af recv") 90 | print(p.header.type) 91 | if p.header.type == packet_pb2.Packet_Header.IQ: 92 | print("GOT IQ", len(p.iq_packet.signal)) 93 | complex_sig = numpy.array(p.iq_packet.signal[0::2]) + 1j * numpy.array(p.iq_packet.signal[1::2]) 94 | 95 | pyplot.figure() 96 | pyplot.plot(numpy.array(range(len(complex_sig)))/8e6, 20.0*numpy.log10(numpy.abs(complex_sig))) 97 | 98 | 99 | pyplot.show() 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/noise_gen/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HackRFDevice.h" 3 | #include "firdes.h" 4 | #include "fir_filter.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // Device driver 16 | HackRFDevice hackrf; 17 | 18 | using namespace gr::filter; 19 | 20 | void signal_handler(int s) 21 | { 22 | printf("Caught signal %d\n",s); 23 | hackrf.cleanup(); 24 | exit(0); 25 | } 26 | 27 | 28 | void write_sig_to_file(const char * filename, const char * varname, std::vector sig) 29 | { 30 | std::ofstream ofs( filename, std::ofstream::out ); 31 | ofs << varname << std::endl; 32 | for (int ii = 0; ii < sig.size(); ii++) 33 | { 34 | ofs << sig[ii] << std::endl; 35 | } 36 | ofs.close(); 37 | } 38 | 39 | bool print_once = true; 40 | 41 | // 42 | // Callback function for tx samples 43 | // 44 | int sample_block_cb_fn(hackrf_transfer* transfer) 45 | { 46 | std::vector rand_sig; 47 | 48 | for (int ii = 0; ii < transfer->valid_length/2; ii++) 49 | { 50 | float val = (float(rand() % 256)-128.0)/128.0; 51 | 52 | rand_sig.push_back( val ); 53 | } 54 | 55 | std::vector< float > taps = 56 | firdes::band_pass( 1.0, // gain 57 | 1000000, // fs 58 | 200000, // low cutoff 59 | 300000, // high cutoff 60 | 30000);// transition width 61 | 62 | 63 | kernel::fir_filter_fff filter_obj(0,taps); 64 | 65 | 66 | 67 | filter_obj.filter( rand_sig.data() ); 68 | 69 | //std::cout << "filter: " << taps.size() << std::endl; 70 | std::vector< float > sig_8; 71 | for (int ii = 0; ii < transfer->valid_length; ii+=2) 72 | { 73 | uint8_t i8 = uint8_t(rand_sig[ii/2]); 74 | uint8_t q8 = uint8_t(rand_sig[ii/2]); 75 | 76 | sig_8.push_back(i8); 77 | 78 | transfer->buffer[ii+0] = i8; 79 | transfer->buffer[ii+1] = q8; 80 | } 81 | if (print_once) 82 | { 83 | write_sig_to_file("sig8.txt","sig8",sig_8); 84 | write_sig_to_file("sig.txt","sig",rand_sig); 85 | write_sig_to_file("filter.txt","filter",taps); 86 | std::cout << "Done" << std::endl; 87 | print_once=false; 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | 94 | // 95 | // MAIN 96 | // 97 | int main() 98 | { 99 | // Setup the interrupt signal handler 100 | struct sigaction sigIntHandler; 101 | sigIntHandler.sa_handler = signal_handler; 102 | sigemptyset(&sigIntHandler.sa_mask); 103 | sigIntHandler.sa_flags = 0; 104 | sigaction(SIGINT, &sigIntHandler, NULL); 105 | 106 | if ( hackrf.initialize() ) 107 | { 108 | hackrf.set_sample_rate( 1e6 ); 109 | hackrf.tune( 2460e6 ); 110 | hackrf.set_txvga_gain( 47 ); // range 0-47 step 1db 111 | 112 | // Start receiving data 113 | hackrf.start_Tx( sample_block_cb_fn, NULL ); 114 | 115 | while (true) 116 | { 117 | for ( uint64_t fc = 2402e6; fc < 2480e6; fc += 20e6) 118 | { 119 | // hackrf.tune( fc ); 120 | // std::cout << "Tuning to: " << fc << std::endl; 121 | sleep(0.1); 122 | } 123 | } 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /src/GUI/receiver.cpp: -------------------------------------------------------------------------------- 1 | #include "receiver.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | Receiver::Receiver() 8 | { 9 | isRunning = false; 10 | isInitialized = false; 11 | data_port = "5555"; 12 | comm_port = "5556"; 13 | data_target = "tcp://localhost:" + data_port; 14 | comm_target = "tcp://localhost:" + comm_port; 15 | 16 | // Setup the data receiver 17 | comm_context = new zmq::context_t(1); 18 | comm_socket = new zmq::socket_t(*comm_context, ZMQ_REQ); 19 | } 20 | 21 | void Receiver::setIP(QString ipAddress) 22 | { 23 | data_target = "tcp://" + ipAddress + ":" + data_port; 24 | comm_target = "tcp://" + ipAddress + ":" + comm_port; 25 | } 26 | 27 | bool Receiver::initialize() 28 | { 29 | if(!isInitialized) 30 | { 31 | try 32 | { 33 | comm_socket->connect(comm_target.toLocal8Bit().data()); 34 | } 35 | catch (zmq::error_t &e) 36 | { 37 | qDebug() << QString(e.what()); 38 | isInitialized = false; 39 | return false; 40 | } 41 | 42 | isInitialized = true; 43 | 44 | s_send(*comm_socket, std::string("set-rx-mode fft")); 45 | s_recv(*comm_socket); 46 | 47 | updateParameters(); 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | void Receiver::stop() 54 | { 55 | isRunning = false; 56 | 57 | while(isInitialized) 58 | sleep(1); 59 | 60 | comm_socket->close(); 61 | } 62 | 63 | void Receiver::updateParameters() 64 | { 65 | qDebug() << "Requesting Fc from: " << comm_target; 66 | s_send(*comm_socket, std::string("get-fc")); 67 | std::stringstream ss1(s_recv(*comm_socket)); 68 | ss1 >> fc_hz; 69 | qDebug() << "Got: " << fc_hz; 70 | 71 | qDebug() << "Requesting Fs..."; 72 | s_send(*comm_socket, std::string("get-fs")); 73 | std::stringstream ss2(s_recv(*comm_socket)); 74 | ss2 >> fs_hz; 75 | qDebug() << "Got: " << fs_hz; 76 | 77 | emit newParameters(fc_hz, fs_hz, data_target); 78 | qDebug() << "Emitting new parameters"; 79 | } 80 | 81 | void Receiver::receivePackets() 82 | { 83 | // Setup the data receiver 84 | zmq::context_t recv_context(1); 85 | zmq::socket_t socket(recv_context, ZMQ_SUB); 86 | socket.connect(data_target.toLocal8Bit().data()); 87 | socket.setsockopt(ZMQ_SUBSCRIBE, "", 0); 88 | int timeout = 100; // ms 89 | socket.setsockopt(ZMQ_RCVTIMEO, &timeout, sizeof(timeout)); 90 | 91 | isRunning = true; 92 | while (isRunning) 93 | { 94 | zmq::message_t msg; 95 | if ( socket.recv(&msg) ) 96 | { 97 | char* data_p = static_cast(msg.data()); 98 | std::string data(data_p, msg.size()); 99 | 100 | Packet p; 101 | p.ParseFromString(data); 102 | 103 | emit newPacket(p); 104 | } 105 | } 106 | 107 | isInitialized = false; 108 | } 109 | 110 | 111 | void Receiver::tune(uint64_t fc_hz) 112 | { 113 | QString command = QString("set-fc ") + QString::number(fc_hz); 114 | s_send(*comm_socket, command.toStdString()); 115 | std::string response = s_recv(*comm_socket); 116 | 117 | updateParameters(); 118 | } 119 | 120 | void Receiver::setSampleRate(uint64_t fs_hz) 121 | { 122 | QString command = QString("set-fs ") + QString::number(fs_hz); 123 | s_send(*comm_socket, command.toStdString()); 124 | std::string response = s_recv(*comm_socket); 125 | 126 | updateParameters(); 127 | } 128 | -------------------------------------------------------------------------------- /src/RADAR/main_tx.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HackRFDevice.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define PI 3.141592653589793 14 | 15 | HackRFDevice hackrf; 16 | 17 | uint64_t fc_hz = 916e6; // center freq 18 | double fs_hz = 1e6; // sample rate 19 | uint32_t lna_gain = 0; 20 | uint8_t amp_enable = 1; 21 | uint32_t txvga_gain = 10; 22 | 23 | 24 | void signal_handler(int s) 25 | { 26 | printf("Caught signal %d\n",s); 27 | hackrf.stop_Tx(); 28 | hackrf.cleanup(); 29 | exit(0); 30 | } 31 | 32 | 33 | // 34 | // Callback function for tx samples 35 | // 36 | //262144 37 | 38 | std::size_t waveform_index = 0; 39 | std::vector waveform_i; 40 | std::vector waveform_q; 41 | 42 | 43 | int device_sample_block_cb(SampleChunk* samples, void* args) 44 | { 45 | //std::cout << "In Radar Tx" << std::endl; 46 | 47 | for (std::size_t ii = 0; ii < samples->size(); ii++) 48 | { 49 | (*samples)[ii] = std::complex(waveform_i[waveform_index], waveform_q[waveform_index]); 50 | 51 | waveform_index++; 52 | if ( waveform_index > waveform_i.size() ) 53 | waveform_index = 0; 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | 60 | double dt = 1.0/fs_hz; 61 | double df = 0.0; // hz 62 | double pw = 0.500; 63 | double pri = 1.000; 64 | double amp = 0.99; 65 | double chirp_width = df + 100e3; 66 | double slopeFactor = (chirp_width - df)/(2.0*pw); 67 | 68 | void createWaveform() 69 | { 70 | 71 | //#define SAVE_FILE 72 | #ifdef SAVE_FILE 73 | std::ofstream ofile("tx.csv", std::ofstream::out); 74 | ofile << "t,i,q" << std::endl; 75 | #endif 76 | 77 | double t = 0.0; 78 | while ( t <= pw ) 79 | { 80 | // Chirp 81 | //double i = amp * cos( 2.0 * PI * ((df*t)+(slopeFactor*pow(t,2))) ); // I 82 | //double q = amp * sin( 2.0 * PI * ((df*t)+(slopeFactor*pow(t,2))) ); // Q 83 | 84 | // Tone 85 | double i = amp * cos( 2.0 * PI * df * t ); // I 86 | double q = amp * sin( 2.0 * PI * df * t ); // Q 87 | 88 | waveform_i.push_back(i); 89 | waveform_q.push_back(q); 90 | 91 | #ifdef SAVE_FILE 92 | ofile << t << "," 93 | << i << "," 94 | << q << std::endl; 95 | #endif 96 | 97 | t = t+dt; 98 | } 99 | 100 | while (t < pri) 101 | { 102 | waveform_i.push_back(0); 103 | waveform_q.push_back(0); 104 | 105 | t = t+dt; 106 | } 107 | 108 | #ifdef SAVE_FILE 109 | ofile.close(); 110 | #endif 111 | } 112 | 113 | 114 | // 115 | // MAIN 116 | // 117 | int main() 118 | { 119 | // Setup the interrupt signal handler 120 | struct sigaction sigIntHandler; 121 | sigIntHandler.sa_handler = signal_handler; 122 | sigemptyset(&sigIntHandler.sa_mask); 123 | sigIntHandler.sa_flags = 0; 124 | sigaction(SIGINT, &sigIntHandler, NULL); 125 | 126 | createWaveform(); 127 | 128 | if ( ! hackrf.initialize() ) 129 | { 130 | std::cout << "Error initializing hackrf device!" << std::endl; 131 | return 1; 132 | } 133 | 134 | hackrf.set_center_freq( fc_hz ); 135 | hackrf.set_sample_rate( fs_hz ); 136 | hackrf.set_lna_gain( lna_gain ); 137 | hackrf.set_amp_enable( amp_enable ); 138 | hackrf.set_txvga_gain( txvga_gain ); 139 | hackrf.start_Tx( device_sample_block_cb, (void*)(NULL) ); 140 | 141 | // Wait a while 142 | sleep(3000000); 143 | 144 | return 0; 145 | } 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /src/RADAR/fm_cw.m: -------------------------------------------------------------------------------- 1 | 2 | clear; close all; clc 3 | 4 | %% Collect data 5 | 6 | !hackrf_transfer -r rx.dat -d 22be1 -f 2490000000 -a 1 -l 40 -g 0 -s 8000000 -n 8000000 7 | 8 | %% Process 9 | 10 | filename = 'rx.dat'; 11 | fd = fopen(filename,'r'); 12 | data = fread(fd, 'int8'); 13 | fclose(fd); 14 | data = reshape(data, 2, []); 15 | data = complex(data(1,:), data(2,:)); 16 | 17 | 18 | pri = 0.001; 19 | prf = 1/pri; 20 | fs = 8e6; 21 | pw = 1e-3; 22 | df = 1.4998e6; 23 | chirp_width = 200e3; 24 | 25 | 26 | 27 | % filter 28 | N = 200; % Order 29 | Fc = 200000; % Cutoff Frequency 30 | flag = 'scale'; % Sampling Flag 31 | Beta = 0.5; % Window Parameter 32 | win = kaiser(N+1, Beta); 33 | 34 | % Calculate the coefficients using the FIR1 function. 35 | b = fir1(N, Fc/(fs/2), 'low', win, flag); 36 | t = 0:1/fs:length(data)/fs; t(end) = []; 37 | 38 | data = data - mean(data); 39 | data = data.*exp(1i*2*pi*(-(df+(chirp_width/2)))*t); % shifted version 40 | data = filtfilt(b,1, data); 41 | 42 | data = data(8000*10:end); % Remove initial charge up 43 | t = t(1:length(data)); 44 | 45 | % figure(100); plot(linspace(-fs/2,fs/2,length(data)), 10*log10(fftshift(abs(fft(data))))); 46 | % figure(101); plot(t,real(data)); 47 | 48 | % match filter 49 | slopeFactor = (chirp_width)/(2 * pw); 50 | t2 = 0:1/fs:pri-(1/fs); 51 | match_signal = exp(1i * 2.0 * pi * (((-chirp_width/2)*t2)+(slopeFactor*(t2.^2)))); 52 | % match_signal= 100 * exp(1i * 2 * pi * ( t2*(-chirp_width/2) + slopeFactor*t2.^2)); 53 | 54 | % data = data(3799198:end); 55 | % corrResult = xcorr(data,match_signal); 56 | % corrResult(1:length(data)-1) = []; 57 | 58 | DATA = fft(data); 59 | MATCH_SIGNAL = fft(match_signal, length(DATA)); 60 | CORR_RESULT = DATA.*conj(MATCH_SIGNAL); 61 | corrResult = ifft(CORR_RESULT); 62 | % figure; plot(10*log10(abs(ifft(CORR_RESULT)))) 63 | 64 | [pks,locs] = findpeaks(abs(corrResult), 'MINPEAKDISTANCE', 7500); 65 | 66 | 67 | % figure(102); plot(linspace(-fs/2,fs/2,length(corrResult)), 10*log10(fftshift(abs(fft(corrResult))))); 68 | 69 | 70 | pulses = []; 71 | nsamples = pri*fs; 72 | for k = 1:length(locs)-3 73 | pulses(k,:) = data(locs(k):locs(k)+nsamples-1) - mean(data(locs(k):locs(k)+nsamples-1)); 74 | end 75 | 76 | num_pulses = size(pulses,1)-1; 77 | pulse_size = size(pulses,2); 78 | 79 | y = zeros(num_pulses, pulse_size); 80 | for k = 1:num_pulses 81 | % y(k,:) = pulses(k,:) - 2*pulses(k+1,:) + pulses(k+2,:); % -2 from num_pulse 82 | y(k,:) = pulses(k,:) - pulses(k+1,:); 83 | end 84 | 85 | 86 | % Compute range time intensity 87 | rti = zeros(pulse_size,num_pulses); 88 | for ii = 1:num_pulses 89 | tmp = abs(xcorr(y(ii,:), match_signal)); 90 | rti(:,ii) = tmp(pulse_size:end); 91 | end 92 | 93 | 94 | % Compute range doppler map 95 | rdm = zeros(size(y)); 96 | for ii = 1:pulse_size 97 | rdm(:,ii) = fftshift(abs(fft(y(:,ii)))); 98 | end 99 | 100 | % 101 | % r = tc/2 102 | % 103 | range = ((0:size(pulses,2)-1) .* (1/fs)) .* 3e8 ./ 2; 104 | 105 | figure(10); 106 | 107 | subplot(221); 108 | imagesc(1:num_pulses, range, abs(y')); 109 | set(gca,'YDir','normal') 110 | title('Pulse Amplitude') 111 | xlabel('Pulse Number') 112 | ylabel('Range (m)') 113 | colorbar; 114 | 115 | subplot(222); 116 | imagesc(linspace(-prf/2, prf/2, num_pulses), range, 20*log10(rdm')); 117 | set(gca,'YDir','normal') 118 | title('Range Doppler Map') 119 | xlabel('Doppler shift (Hz)') 120 | ylabel('Range (m)') 121 | colorbar; 122 | 123 | subplot(223); 124 | imagesc(range, 1:num_pulses, 20*log10(rti)); 125 | title('Range Time Intensity') 126 | xlabel('Range (m)') 127 | ylabel('Pulse Number') 128 | 129 | 130 | subplot(224); 131 | plot(10*log10(abs(corrResult))); 132 | hold on 133 | plot(locs,10*log10(pks), 'r*'); 134 | title('Match Filter Response') 135 | -------------------------------------------------------------------------------- /src/tx_test/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HackRFDevice.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define PI 3.14159265 13 | #define MESSAGE "Hello World! " 14 | 15 | char msg[] = MESSAGE; 16 | bool bits[sizeof(msg)*8]; 17 | 18 | HackRFDevice hackrf; 19 | 20 | uint64_t fc_hz = 2480000000; // center freq 21 | double fs_hz = 8000000; // sample rate 22 | uint32_t lna_gain = 0; 23 | uint8_t amp_enable = 0; 24 | uint32_t txvga_gain = 47; 25 | 26 | 27 | void signal_handler(int s) 28 | { 29 | printf("Caught signal %d\n",s); 30 | hackrf.cleanup(); 31 | exit(0); 32 | } 33 | 34 | 35 | // 36 | // Callback function for tx samples 37 | // 38 | //262144 39 | double t = 0.0; 40 | double dt = 1/fs_hz; 41 | bool print_signal = true; 42 | double df = 1000; 43 | int bit_index = 0; 44 | 45 | int sample_block_cb_fn(hackrf_transfer* transfer) 46 | { 47 | bool bit = bits[bit_index++]; 48 | 49 | if (bit_index >= sizeof(bits)) 50 | bit_index = 0; 51 | 52 | for (int ii = 0; ii < transfer->valid_length; ii+=2) 53 | { 54 | /* 55 | // Manchester encode 56 | bool first_half = ( ii < ( transfer->valid_length / 2 ) ); 57 | 58 | // High bit means transition low to high 59 | if ( bit ) 60 | if ( first_half ) 61 | df = -1000; 62 | else 63 | df = 1000; 64 | // Low bit means transition high to low 65 | else 66 | if ( first_half ) 67 | df = 1000; 68 | else 69 | df = -1000; 70 | 71 | double i = 128.0 * cos( 2.0 * PI * df * t ); // I 72 | double q = 128.0 * sin( 2.0 * PI * df * t ); // Q 73 | 74 | uint8_t i8 = uint8_t(i); 75 | uint8_t q8 = uint8_t(q); 76 | 77 | transfer->buffer[ii+0] = i8; 78 | transfer->buffer[ii+1] = q8; 79 | */ 80 | 81 | transfer->buffer[ii+0] = 100; 82 | transfer->buffer[ii+1] = 100; 83 | 84 | 85 | t = t+dt; 86 | if ( t >= 1.0 ) 87 | { 88 | t = 0.0; 89 | } 90 | } 91 | 92 | return 0; 93 | } 94 | 95 | 96 | // 97 | // MAIN 98 | // 99 | int main() 100 | { 101 | // Setup the interrupt signal handler 102 | struct sigaction sigIntHandler; 103 | sigIntHandler.sa_handler = signal_handler; 104 | sigemptyset(&sigIntHandler.sa_mask); 105 | sigIntHandler.sa_flags = 0; 106 | sigaction(SIGINT, &sigIntHandler, NULL); 107 | 108 | // Convert message into bits 109 | printf("String: %s\nHex: ", msg); 110 | for (int ii = 0; ii < sizeof(msg); ii++ ) 111 | { 112 | printf("%d ", msg[ii]); 113 | } 114 | printf("\nBinary: "); 115 | 116 | for (int ii = 0; ii < sizeof(msg); ii++ ) 117 | { 118 | for ( int jj = 0; jj < 8; jj++ ) 119 | { 120 | bits[(ii*8) + jj] = (msg[ii] & (0x80 >> jj)) > 0; 121 | printf("%d ", bits[(ii*8) + jj]?1:0); 122 | } 123 | } 124 | printf("\n"); 125 | 126 | if ( ! hackrf.initialize("23ec7") ) 127 | { 128 | std::cout << "Error initializing hackrf device!" << std::endl; 129 | return 1; 130 | } 131 | 132 | hackrf.set_center_freq( fc_hz ); 133 | hackrf.set_sample_rate( fs_hz ); 134 | hackrf.set_lna_gain( lna_gain ); 135 | hackrf.set_amp_enable( amp_enable ); 136 | hackrf.set_txvga_gain( txvga_gain ); 137 | hackrf.start_Tx( sample_block_cb_fn, (void*)(NULL) ); 138 | 139 | // Wait a while 140 | sleep(300); 141 | 142 | return 0; 143 | } 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/devices/SDRReceiver.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "SDRReceiver.h" 3 | 4 | #include 5 | #include 6 | 7 | SDRReceiver::SDRReceiver() 8 | { 9 | m_isRunning = false; 10 | m_isInitialized = false; 11 | 12 | m_data_port = "5555"; 13 | m_comm_port = "5556"; 14 | 15 | m_data_target = "tcp://localhost:" + m_data_port; 16 | m_comm_target = "tcp://localhost:" + m_comm_port; 17 | 18 | // Setup the comm bits 19 | m_comm_context = new zmq::context_t(1); 20 | m_comm_socket = new zmq::socket_t(*m_comm_context, ZMQ_REQ); 21 | } 22 | 23 | SDRReceiver::~SDRReceiver() 24 | { 25 | delete m_comm_socket; 26 | delete m_comm_context; 27 | } 28 | 29 | void SDRReceiver::setIP(std::string ipAddress) 30 | { 31 | m_data_target = "tcp://" + ipAddress + ":" + m_data_port; 32 | m_comm_target = "tcp://" + ipAddress + ":" + m_comm_port; 33 | } 34 | 35 | void* thread_func(void* args) 36 | { 37 | std::cout << "Rx thread started" << std::endl; 38 | 39 | SDRReceiver* sdr_obj = (SDRReceiver*)args; 40 | 41 | sdr_obj->receivePackets(); 42 | 43 | return nullptr; 44 | } 45 | 46 | 47 | bool SDRReceiver::initialize(sdr_receive_callback_t callback, void* callback_args) 48 | { 49 | if(!m_isInitialized) 50 | { 51 | try 52 | { 53 | m_comm_socket->connect( m_comm_target.c_str() ); 54 | } 55 | catch (zmq::error_t &e) 56 | { 57 | std::cout << e.what() << std::endl; 58 | m_isInitialized = false; 59 | return false; 60 | } 61 | 62 | m_callback = callback; 63 | m_callback_args = callback_args; 64 | 65 | pthread_create( &m_thread_context, NULL, thread_func, (void*)this ); 66 | 67 | m_isInitialized = true; 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | void SDRReceiver::stop() 74 | { 75 | m_isRunning = false; 76 | 77 | while(m_isInitialized) 78 | sleep(1); 79 | 80 | m_comm_socket->close(); 81 | } 82 | 83 | void SDRReceiver::receivePackets() 84 | { 85 | // Setup the data receiver 86 | zmq::context_t recv_context(1); 87 | zmq::socket_t socket(recv_context, ZMQ_SUB); 88 | socket.connect( m_data_target.c_str() ); 89 | socket.setsockopt(ZMQ_SUBSCRIBE, "", 0); 90 | int timeout = 100; // ms 91 | socket.setsockopt(ZMQ_RCVTIMEO, &timeout, sizeof(timeout)); 92 | 93 | m_isRunning = true; 94 | while (m_isRunning) 95 | { 96 | zmq::message_t msg; 97 | if ( socket.recv(msg, zmq::recv_flags::none) ) 98 | { 99 | char* data_p = static_cast(msg.data()); 100 | std::string data( data_p, msg.size() ); 101 | 102 | Packet p; 103 | p.ParseFromString(data); 104 | 105 | m_callback( p, m_callback_args ); 106 | } 107 | } 108 | 109 | m_isInitialized = false; 110 | } 111 | 112 | 113 | void SDRReceiver::tune(uint64_t fc_hz) 114 | { 115 | std::stringstream ss; 116 | ss << "set-fc " << fc_hz; 117 | s_send(*m_comm_socket, ss.str()); 118 | std::string response = s_recv(*m_comm_socket); 119 | } 120 | 121 | void SDRReceiver::setSampleRate(double fs_hz) 122 | { 123 | std::stringstream ss; 124 | ss << "set-fs " << fs_hz; 125 | s_send(*m_comm_socket, ss.str()); 126 | std::string response = s_recv(*m_comm_socket); 127 | } 128 | 129 | double SDRReceiver::getSampleRate() 130 | { 131 | s_send(*m_comm_socket, std::string("get-fs")); 132 | std::stringstream ss(s_recv(*m_comm_socket)); 133 | double fs_hz; 134 | ss >> fs_hz; 135 | return fs_hz; 136 | } 137 | 138 | uint64_t SDRReceiver::getCenterFrequency() 139 | { 140 | s_send(*m_comm_socket, std::string("get-fc")); 141 | std::stringstream ss(s_recv(*m_comm_socket)); 142 | uint64_t fc_hz; 143 | ss >> fc_hz; 144 | return fc_hz; 145 | } 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/protobuffers/myhackrf.py: -------------------------------------------------------------------------------- 1 | import zmq 2 | import packet_pb2 3 | 4 | # Supported commands: 5 | # get-fc 6 | # get-fs 7 | # get-rx-gain 8 | # set-fc 9 | # set-fs 10 | # set-rx-gain 11 | # set-rx-mode iq|pdw|fft 12 | # quit 13 | 14 | class MyHackrf: 15 | def __init__(self, host): 16 | ctx = zmq.Context() 17 | self.rx_sock = ctx.socket(zmq.SUB) 18 | self.rx_sock.connect("tcp://" + host + ":5555") 19 | self.rx_sock.setsockopt(zmq.SUBSCRIBE, b"") 20 | 21 | self.tx_sock = ctx.socket(zmq.REQ) 22 | self.tx_sock.connect("tcp://" + host + ":5556") 23 | 24 | def get_params(self): 25 | self.tx_sock.send_string("get-fc") 26 | print(self.tx_sock.recv()) 27 | 28 | def send(self, cmd): 29 | self.tx_sock.send_string(cmd) 30 | rv = self.tx_sock.recv() 31 | print(cmd, ":", rv) 32 | return rv 33 | 34 | def recv(self): 35 | msg = self.rx_sock.recv() 36 | p = packet_pb2.Packet() 37 | p.ParseFromString(msg) 38 | return p 39 | 40 | 41 | if __name__ == "__main__": 42 | from matplotlib import pyplot 43 | import numpy 44 | 45 | dev = MyHackrf('rpi4') 46 | dev.send("set-fs 2000000") 47 | dev.send("set-fc 915500000") 48 | dev.send("set-rx-gain 60") 49 | 50 | if 0: 51 | dev.send("set-rx-mode fft") 52 | 53 | waterfall = [] 54 | f = [] 55 | while len(waterfall) < 100: 56 | p = dev.recv() 57 | if p.header.type == packet_pb2.Packet_Header.FFT: 58 | f = p.fft_packet.freq_bins_hz 59 | waterfall.append(p.fft_packet.fft) 60 | print("GOT FFT " + str(len(waterfall))) 61 | 62 | pyplot.figure() 63 | pyplot.imshow(waterfall, extent=[min(f)/1e6, max(f)/1e6, 0, len(waterfall)], origin='lower', aspect='auto') 64 | pyplot.colorbar() 65 | pyplot.xlabel('Freq (MHz)') 66 | pyplot.ylabel('Time') 67 | 68 | if 1: 69 | dev.send("set-rx-mode pdw") 70 | 71 | while True: 72 | p = dev.recv() 73 | print(p.header.type) 74 | if p.header.type == packet_pb2.Packet_Header.PDW: 75 | print("GOT %d PDWs" % len(p.pdw_packet.pdws)) 76 | for pdw in p.pdw_packet.pdws: 77 | print("TOA: {0} {1} {2} {3} {4} {5}".format( 78 | ("%0.3f" % pdw.toa_s).rjust(10), 79 | ("%0.6f" % pdw.pw_s).rjust(10), 80 | ("%0.3f" % pdw.mean_amp_db).rjust(10), 81 | ("%0.3f" % pdw.peak_amp_db).rjust(10), 82 | ("%0.3f" % pdw.noise_amp_db).rjust(10), 83 | ("%0.3f" % pdw.freq_offset_hz).rjust(10)) 84 | ) 85 | 86 | # pyplot.figure() 87 | 88 | # for pdw in p.pdw_packet.pdws: 89 | # complex_sig = numpy.array(pdw.signal[0::2]) + 1j * numpy.array(pdw.signal[1::2]) 90 | # pyplot.plot(pdw.toa_s + numpy.array(range(len(complex_sig)))/8e6, 20.0*numpy.log10(numpy.abs(complex_sig))) 91 | 92 | if 0: 93 | dev.send("set-rx-mode iq") 94 | 95 | print("b4 recv") 96 | p = dev.recv() 97 | print("af recv") 98 | print(p.header.type) 99 | if p.header.type == packet_pb2.Packet_Header.IQ: 100 | print("GOT IQ", len(p.iq_packet.signal)) 101 | complex_sig = numpy.array(p.iq_packet.signal[0::2]) + 1j * numpy.array(p.iq_packet.signal[1::2]) 102 | 103 | pyplot.figure() 104 | pyplot.plot(numpy.array(range(len(complex_sig)))/8e6, 20.0*numpy.log10(numpy.abs(complex_sig))) 105 | 106 | 107 | pyplot.show() 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/Socket.cpp: -------------------------------------------------------------------------------- 1 | #include "Socket.h" 2 | #include "string.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | Socket::Socket() : 11 | m_sock ( -1 ) 12 | { 13 | 14 | memset ( &m_addr, 15 | 0, 16 | sizeof ( m_addr ) ); 17 | 18 | } 19 | 20 | Socket::~Socket() 21 | { 22 | if ( is_valid() ) 23 | ::close ( m_sock ); 24 | } 25 | 26 | bool Socket::create() 27 | { 28 | m_sock = socket ( AF_INET, 29 | SOCK_STREAM, 30 | 0 ); 31 | 32 | if ( ! is_valid() ) 33 | return false; 34 | 35 | 36 | // TIME_WAIT - argh 37 | int on = 1; 38 | if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 ) 39 | return false; 40 | 41 | 42 | return true; 43 | 44 | } 45 | 46 | 47 | 48 | bool Socket::bind ( const int port ) 49 | { 50 | 51 | if ( ! is_valid() ) 52 | { 53 | return false; 54 | } 55 | 56 | 57 | 58 | m_addr.sin_family = AF_INET; 59 | m_addr.sin_addr.s_addr = INADDR_ANY; 60 | m_addr.sin_port = htons ( port ); 61 | 62 | int bind_return = ::bind ( m_sock, 63 | ( struct sockaddr * ) &m_addr, 64 | sizeof ( m_addr ) ); 65 | 66 | 67 | if ( bind_return == -1 ) 68 | { 69 | return false; 70 | } 71 | 72 | return true; 73 | } 74 | 75 | 76 | bool Socket::listen() const 77 | { 78 | if ( ! is_valid() ) 79 | { 80 | return false; 81 | } 82 | 83 | int listen_return = ::listen ( m_sock, MAXCONNECTIONS ); 84 | 85 | 86 | if ( listen_return == -1 ) 87 | { 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | 94 | 95 | bool Socket::accept ( Socket& new_socket ) const 96 | { 97 | int addr_length = sizeof ( m_addr ); 98 | new_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length ); 99 | 100 | if ( new_socket.m_sock <= 0 ) 101 | return false; 102 | else 103 | return true; 104 | } 105 | 106 | 107 | bool Socket::send ( const std::string s ) const 108 | { 109 | int status = ::send ( m_sock, s.c_str(), s.size(), MSG_NOSIGNAL ); 110 | if ( status == -1 ) 111 | { 112 | return false; 113 | } 114 | else 115 | { 116 | return true; 117 | } 118 | } 119 | 120 | bool Socket::send ( const uint8_t* data, const int size ) 121 | { 122 | int status = ::send ( m_sock, data, size, MSG_NOSIGNAL ); 123 | if ( status == -1 ) 124 | { 125 | return false; 126 | } 127 | else 128 | { 129 | return true; 130 | } 131 | } 132 | 133 | 134 | 135 | int Socket::recv ( std::string& s ) const 136 | { 137 | char buf [ MAXRECV + 1 ]; 138 | 139 | s = ""; 140 | 141 | memset ( buf, 0, MAXRECV + 1 ); 142 | 143 | int status = ::recv ( m_sock, buf, MAXRECV, 0 ); 144 | 145 | if ( status == -1 ) 146 | { 147 | std::cout << "status == -1 errno == " << errno << " in Socket::recv\n"; 148 | return 0; 149 | } 150 | else if ( status == 0 ) 151 | { 152 | return 0; 153 | } 154 | else 155 | { 156 | s = buf; 157 | return status; 158 | } 159 | } 160 | 161 | 162 | 163 | bool Socket::connect ( const std::string host, const int port ) 164 | { 165 | if ( ! is_valid() ) return false; 166 | 167 | m_addr.sin_family = AF_INET; 168 | m_addr.sin_port = htons ( port ); 169 | 170 | int status = inet_pton ( AF_INET, host.c_str(), &m_addr.sin_addr ); 171 | 172 | if ( errno == EAFNOSUPPORT ) return false; 173 | 174 | status = ::connect ( m_sock, ( sockaddr * ) &m_addr, sizeof ( m_addr ) ); 175 | 176 | if ( status == 0 ) 177 | return true; 178 | else 179 | return false; 180 | } 181 | 182 | void Socket::set_non_blocking ( const bool b ) 183 | { 184 | 185 | int opts; 186 | 187 | opts = fcntl ( m_sock, 188 | F_GETFL ); 189 | 190 | if ( opts < 0 ) 191 | { 192 | return; 193 | } 194 | 195 | if ( b ) 196 | opts = ( opts | O_NONBLOCK ); 197 | else 198 | opts = ( opts & ~O_NONBLOCK ); 199 | 200 | fcntl ( m_sock, 201 | F_SETFL,opts ); 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/GUI/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 741 10 | 592 11 | 12 | 13 | 14 | MyHackRF 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 3 27 | 28 | 29 | 20.000000000000000 30 | 31 | 32 | 6000.000000000000000 33 | 34 | 35 | 101.099999999999994 36 | 37 | 38 | 39 | 40 | 41 | 42 | 1000000.000000000000000 43 | 44 | 45 | 20000000.000000000000000 46 | 47 | 48 | 8000000.000000000000000 49 | 50 | 51 | 52 | 53 | 54 | 55 | Sample Rate (samples/s) 56 | 57 | 58 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 59 | 60 | 61 | 62 | 63 | 64 | 65 | Set 66 | 67 | 68 | 69 | 70 | 71 | 72 | Tune 73 | 74 | 75 | 76 | 77 | 78 | 79 | Center Frequency (MHz) 80 | 81 | 82 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 83 | 84 | 85 | 86 | 87 | 88 | 89 | IP Address 90 | 91 | 92 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | Set 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 0 114 | 0 115 | 741 116 | 25 117 | 118 | 119 | 120 | 121 | 122 | TopToolBarArea 123 | 124 | 125 | false 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/hackrf_simple_server/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HackRFDevice.h" 3 | #include "Socket.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // Device driver 17 | HackRFDevice hackrf; 18 | 19 | uint64_t fc_hz = 5981e6; // center freq 20 | double fs_hz = 20e6; // sample rate 21 | uint32_t lna_gain = 40; // 0-40 in steps of 8 22 | uint8_t amp_enable = 0; 23 | uint32_t txvga_gain = 0; 24 | 25 | 26 | bool transfer_ok = true; 27 | 28 | 29 | void signal_handler(int s) 30 | { 31 | printf("Caught signal %d\n",s); 32 | hackrf.cleanup(); 33 | exit(0); 34 | } 35 | 36 | 37 | // 38 | // Callback function for rx samples 39 | // 40 | int sample_block_cb_fn(SampleChunk* samples, void* args) 41 | { 42 | // send the packet out over the network 43 | Socket* client = (Socket*)args; 44 | 45 | if ( ! client->is_valid() ) 46 | { 47 | printf("Invalid socket\n" ); 48 | transfer_ok = false; 49 | return 0; 50 | } 51 | 52 | 53 | for (int ii = 0; ii < 10; ii++) 54 | { 55 | std::cout << (*samples).at(ii).real() << ", " << (*samples).at(ii).imag() << " "; 56 | } 57 | std::cout << std::endl << std::flush; 58 | 59 | // if ( ! client->send( transfer->buffer, transfer->valid_length ) ) 60 | // { 61 | // int e = errno; 62 | // if ( e == EAGAIN ) 63 | // { 64 | // std::cout << "U" << std::flush; 65 | // } 66 | // else 67 | // { 68 | // std::cout << "Could not send to client: (" << e << ") " << strerror(e) << std::endl; 69 | // transfer_ok = false; 70 | // } 71 | // return 0; 72 | // } 73 | 74 | return 0; 75 | } 76 | 77 | 78 | // 79 | // MAIN 80 | // 81 | int main() 82 | { 83 | // Setup the interrupt signal handler 84 | struct sigaction sigIntHandler; 85 | sigIntHandler.sa_handler = signal_handler; 86 | sigemptyset(&sigIntHandler.sa_mask); 87 | sigIntHandler.sa_flags = 0; 88 | sigaction(SIGINT, &sigIntHandler, NULL); 89 | 90 | 91 | if ( hackrf.initialize( "22be1" /*"23ec7"*/ ) ) 92 | { 93 | hackrf.set_center_freq( 5981e6 ); 94 | hackrf.set_sample_rate( fs_hz ); 95 | hackrf.set_rx_gain( lna_gain ); 96 | hackrf.set_amp_enable( amp_enable ); 97 | hackrf.set_txvga_gain( txvga_gain ); 98 | 99 | printf("Starting server...\n"); 100 | // Data stream interface 101 | Socket server; 102 | 103 | if ( ! server.create() ) 104 | { 105 | int e = errno; 106 | std::cout << "Could not create server socket: error: (" << e << ") " << strerror(e); 107 | hackrf.cleanup(); 108 | return 1; 109 | } 110 | 111 | if ( ! server.bind( 30000 ) ) 112 | { 113 | int e = errno; 114 | std::cout << "Could not bind to port: error: (" << e << ") " << strerror(e); 115 | hackrf.cleanup(); 116 | return 1; 117 | } 118 | 119 | if ( ! server.listen() ) 120 | { 121 | int e = errno; 122 | std::cout << "Could not listen to socket: error: (" << e << ") " << strerror(e); 123 | hackrf.cleanup(); 124 | return 1; 125 | } 126 | 127 | 128 | while (1) 129 | { 130 | printf("Waiting for client...\n"); 131 | Socket client; 132 | 133 | if ( ! server.accept( client ) ) 134 | { 135 | int e = errno; 136 | std::cout << "Could not accept client: error: (" << e << ") " << strerror(e); 137 | continue; 138 | } 139 | printf("Got client!\n"); 140 | 141 | client.set_non_blocking( true ); 142 | 143 | transfer_ok = true; 144 | 145 | // Start receiving data 146 | if ( ! hackrf.start_Rx( sample_block_cb_fn, (void*)(&client) ) ) 147 | { 148 | std::cout << "HackRF could not enter Rx mode" << std::endl; 149 | hackrf.stop_Rx(); 150 | continue; 151 | } 152 | 153 | // Wait for ctrl+c or client disconnect 154 | printf("Waiting for disconnect...\n"); 155 | while ( transfer_ok ) 156 | sleep(0.1); 157 | 158 | hackrf.stop_Rx(); 159 | } 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /src/websock/fft.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Free FFT and convolution (JavaScript) 3 | * 4 | * Copyright (c) 2014 Project Nayuki 5 | * http://www.nayuki.io/page/free-small-fft-in-multiple-languages 6 | * 7 | * (MIT License) 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 12 | * the Software, and to permit persons to whom the Software is furnished to do so, 13 | * subject to the following conditions: 14 | * - The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * - The Software is provided "as is", without warranty of any kind, express or 17 | * implied, including but not limited to the warranties of merchantability, 18 | * fitness for a particular purpose and noninfringement. In no event shall the 19 | * authors or copyright holders be liable for any claim, damages or other 20 | * liability, whether in an action of contract, tort or otherwise, arising from, 21 | * out of or in connection with the Software or the use or other dealings in the 22 | * Software. 23 | * 24 | * Slightly restructured by Chris Cannam, cannam@all-day-breakfast.com 25 | */ 26 | 27 | //"use strict"; 28 | 29 | /* 30 | * Construct an object for calculating the discrete Fourier transform (DFT) of size n, where n is a power of 2. 31 | */ 32 | class FFTNayuki { 33 | constructor(n) { 34 | 35 | this.n = n; 36 | this.levels = -1; 37 | 38 | for (var i = 0; i < 32; i++) { 39 | if (1 << i == n) { 40 | this.levels = i; // Equal to log2(n) 41 | } 42 | } 43 | if (this.levels == -1) { 44 | throw "Length is not a power of 2"; 45 | } 46 | 47 | this.cosTable = new Array(n / 2); 48 | this.sinTable = new Array(n / 2); 49 | for (var i = 0; i < n / 2; i++) { 50 | this.cosTable[i] = Math.cos(2 * Math.PI * i / n); 51 | this.sinTable[i] = Math.sin(2 * Math.PI * i / n); 52 | } 53 | 54 | /* 55 | * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector. 56 | * The vector's length must be equal to the size n that was passed to the object constructor, and this must be a power of 2. Uses the Cooley-Tukey decimation-in-time radix-2 algorithm. 57 | */ 58 | this.forward = function (real, imag) { 59 | 60 | var n = this.n; 61 | 62 | // Bit-reversed addressing permutation 63 | for (var i = 0; i < n; i++) { 64 | var j = reverseBits(i, this.levels); 65 | if (j > i) { 66 | var temp = real[i]; 67 | real[i] = real[j]; 68 | real[j] = temp; 69 | temp = imag[i]; 70 | imag[i] = imag[j]; 71 | imag[j] = temp; 72 | } 73 | } 74 | 75 | // Cooley-Tukey decimation-in-time radix-2 FFT 76 | for (var size = 2; size <= n; size *= 2) { 77 | var halfsize = size / 2; 78 | var tablestep = n / size; 79 | for (var i = 0; i < n; i += size) { 80 | for (var j = i, k = 0; j < i + halfsize; j++, k += tablestep) { 81 | var tpre = real[j + halfsize] * this.cosTable[k] + 82 | imag[j + halfsize] * this.sinTable[k]; 83 | var tpim = -real[j + halfsize] * this.sinTable[k] + 84 | imag[j + halfsize] * this.cosTable[k]; 85 | real[j + halfsize] = real[j] - tpre; 86 | imag[j + halfsize] = imag[j] - tpim; 87 | real[j] += tpre; 88 | imag[j] += tpim; 89 | } 90 | } 91 | } 92 | 93 | // Returns the integer whose value is the reverse of the lowest 'bits' bits of the integer 'x'. 94 | function reverseBits(x, bits) { 95 | var y = 0; 96 | for (var i = 0; i < bits; i++) { 97 | y = (y << 1) | (x & 1); 98 | x >>>= 1; 99 | } 100 | return y; 101 | } 102 | }; 103 | 104 | /* 105 | * Computes the inverse discrete Fourier transform (IDFT) of the given complex vector, storing the result back into the vector. 106 | * The vector's length must be equal to the size n that was passed to the object constructor, and this must be a power of 2. This is a wrapper function. This transform does not perform scaling, so the inverse is not a true inverse. 107 | */ 108 | this.inverse = function (real, imag) { 109 | forward(imag, real); 110 | }; 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /src/GUI/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class SpectrogramData: public QwtRasterData 12 | { 13 | public: 14 | SpectrogramData() 15 | { 16 | setInterval( Qt::XAxis, QwtInterval( 0, 1 ) ); 17 | setInterval( Qt::YAxis, QwtInterval( 0, 100 ) ); 18 | setInterval( Qt::ZAxis, QwtInterval( -50.0, 10.0 ) ); 19 | } 20 | 21 | std::deque m_packets; 22 | 23 | void addPacket(Packet p) 24 | { 25 | m_packets.push_front(p); 26 | if ( m_packets.size() > 100 ) 27 | m_packets.pop_back(); 28 | 29 | setInterval( Qt::XAxis, QwtInterval( 0, p.fft_packet().fft().size() ) ); 30 | } 31 | 32 | virtual double value( double x, double y ) const 33 | { 34 | uint32_t xi = uint32_t(x); 35 | uint32_t yi = uint32_t(y); 36 | if ( yi < m_packets.size() && xi < m_packets[yi].fft_packet().fft().size() ) 37 | return m_packets[yi].fft_packet().fft(xi); 38 | else 39 | return -50.0; 40 | } 41 | }; 42 | 43 | class ColorMap: public QwtLinearColorMap 44 | { 45 | public: 46 | ColorMap(): 47 | QwtLinearColorMap( Qt::darkCyan, Qt::red ) 48 | { 49 | addColorStop( 0.1, Qt::cyan ); 50 | addColorStop( 0.6, Qt::green ); 51 | addColorStop( 0.95, Qt::yellow ); 52 | } 53 | }; 54 | 55 | MainWindow::MainWindow(QWidget *parent) : 56 | QMainWindow(parent), 57 | ui(new Ui::MainWindow) 58 | { 59 | ui->setupUi(this); 60 | 61 | num_fft_bins = 2048; 62 | 63 | qRegisterMetaType("Packet"); 64 | 65 | connect(&m_receiver, SIGNAL(newParameters(uint64_t,uint64_t,QString)), this, SLOT(handleNewParameters(uint64_t,uint64_t,QString))); 66 | connect(&m_receiver, SIGNAL(newPacket(Packet)), this, SLOT(handleNewPacket(Packet)), Qt::QueuedConnection); 67 | connect(&m_receiveThread, SIGNAL(finished()), &m_receiveThread, SLOT(deleteLater())); 68 | connect(this, SIGNAL(startReceivingPackets()), &m_receiver, SLOT(receivePackets())); 69 | connect(this, SIGNAL(destroyed()), &m_receiver, SLOT(stop())); 70 | 71 | m_receiver.setIP("rpi4"); 72 | m_receiver.initialize(); 73 | m_receiver.moveToThread(&m_receiveThread); 74 | m_receiveThread.start(); 75 | 76 | // 77 | // define curve style 78 | // 79 | p1 = new QwtPlot(); 80 | p1->setAutoReplot(true); 81 | 82 | d_curve.setPen( Qt::blue ); 83 | d_curve.setStyle( QwtPlotCurve::Lines ); 84 | d_curve.attach(p1); 85 | 86 | ui->gridLayout->addWidget( p1, 3, 0, 1, 3 ); 87 | 88 | p2 = new QwtPlot(); 89 | m_specData = new SpectrogramData(); 90 | d_spectrogram = new QwtPlotSpectrogram(); 91 | d_spectrogram->setRenderThreadCount( 0 ); // use system specific thread count 92 | d_spectrogram->setColorMap( new ColorMap() ); 93 | d_spectrogram->setData( m_specData ); 94 | d_spectrogram->attach( p2 ); 95 | 96 | ui->gridLayout->addWidget( p2, 4, 0, 1, 3 ); 97 | 98 | emit startReceivingPackets(); 99 | } 100 | 101 | MainWindow::~MainWindow() 102 | { 103 | m_receiveThread.quit(); 104 | if (!m_receiveThread.wait(1000)) 105 | m_receiveThread.terminate(); 106 | 107 | delete ui; 108 | } 109 | 110 | void MainWindow::closeEvent(QCloseEvent *event) 111 | { 112 | (void)event; 113 | m_receiver.stop(); 114 | } 115 | 116 | void MainWindow::handleNewParameters(uint64_t fc_hz, uint64_t fs_hz, QString host) 117 | { 118 | qDebug() << "Got new parameters " << num_fft_bins << " " << fc_hz << " " << fs_hz; 119 | ui->spinBox_centerFrequency->setValue(fc_hz/1e6); 120 | ui->spinBox_sampleRate->setValue(fs_hz); 121 | ui->lineEdit_IPaddr->setText(host); 122 | } 123 | 124 | void MainWindow::handleNewPacket(Packet pak) 125 | { 126 | data.resize(pak.fft_packet().fft_size()); 127 | for (int ii = 0; ii < pak.fft_packet().fft().size(); ii++) 128 | { 129 | data[ii] = QPointF( (pak.fft_packet().freq_bins_hz(ii)/1e6), pak.fft_packet().fft(ii)); 130 | } 131 | 132 | p1->setAxisAutoScale(0, false); 133 | p1->setAxisScale(0, -60.0, 50.0); 134 | 135 | p1->setAxisAutoScale(1, false); 136 | p1->setAxisScale(1, pak.fft_packet().freq_bins_hz(0), 137 | pak.fft_packet().freq_bins_hz(pak.fft_packet().freq_bins_hz().size()-1)); 138 | 139 | d_curve.setPen( Qt::blue ); 140 | d_curve.setStyle( QwtPlotCurve::Lines ); 141 | d_curve.setSamples( data ); 142 | 143 | m_specData->addPacket(pak); 144 | d_spectrogram->setData( m_specData ); 145 | 146 | p2->setAxisAutoScale(0, true); 147 | p2->setAxisAutoScale(1, false); 148 | p1->setAxisScale(1, 0, pak.fft_packet().freq_bins_hz().size()-1); 149 | p2->replot(); 150 | } 151 | 152 | void MainWindow::on_pushButton_setIP_clicked() 153 | { 154 | QString host = ui->lineEdit_IPaddr->text(); 155 | 156 | m_receiver.stop(); 157 | m_receiver.setIP(host); 158 | m_receiver.initialize(); 159 | emit startReceivingPackets(); 160 | } 161 | 162 | void MainWindow::on_pushButton_tune_clicked() 163 | { 164 | u_int64_t fc_hz = u_int64_t(ui->spinBox_centerFrequency->value() * double(1e6)); 165 | 166 | qDebug() << "Requesting tune to: " << fc_hz; 167 | m_receiver.tune( fc_hz ); 168 | } 169 | 170 | void MainWindow::on_pushButton_setSampleRate_clicked() 171 | { 172 | double fs_hz = ui->spinBox_sampleRate->value(); 173 | 174 | m_receiver.setSampleRate( fs_hz ); 175 | } 176 | -------------------------------------------------------------------------------- /src/websock/js/iq_packet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview 3 | * @enhanceable 4 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 5 | * field starts with 'MSG_' and isn't a translatable message. 6 | * @public 7 | */ 8 | // GENERATED CODE -- DO NOT EDIT! 9 | 10 | goog.provide('proto.IQ_Packet'); 11 | 12 | goog.require('jspb.BinaryReader'); 13 | goog.require('jspb.BinaryWriter'); 14 | goog.require('jspb.Message'); 15 | 16 | 17 | /** 18 | * Generated by JsPbCodeGenerator. 19 | * @param {Array=} opt_data Optional initial data array, typically from a 20 | * server response, or constructed directly in Javascript. The array is used 21 | * in place and becomes part of the constructed object. It is not cloned. 22 | * If no data is provided, the constructed object will be empty, but still 23 | * valid. 24 | * @extends {jspb.Message} 25 | * @constructor 26 | */ 27 | proto.IQ_Packet = function(opt_data) { 28 | jspb.Message.initialize(this, opt_data, 0, -1, proto.IQ_Packet.repeatedFields_, null); 29 | }; 30 | goog.inherits(proto.IQ_Packet, jspb.Message); 31 | if (goog.DEBUG && !COMPILED) { 32 | proto.IQ_Packet.displayName = 'proto.IQ_Packet'; 33 | } 34 | /** 35 | * List of repeated fields within this message type. 36 | * @private {!Array} 37 | * @const 38 | */ 39 | proto.IQ_Packet.repeatedFields_ = [1]; 40 | 41 | 42 | 43 | if (jspb.Message.GENERATE_TO_OBJECT) { 44 | /** 45 | * Creates an object representation of this proto suitable for use in Soy templates. 46 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 47 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 48 | * For the list of reserved names please see: 49 | * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. 50 | * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 51 | * for transitional soy proto support: http://goto/soy-param-migration 52 | * @return {!Object} 53 | */ 54 | proto.IQ_Packet.prototype.toObject = function(opt_includeInstance) { 55 | return proto.IQ_Packet.toObject(opt_includeInstance, this); 56 | }; 57 | 58 | 59 | /** 60 | * Static version of the {@see toObject} method. 61 | * @param {boolean|undefined} includeInstance Whether to include the JSPB 62 | * instance for transitional soy proto support: 63 | * http://goto/soy-param-migration 64 | * @param {!proto.IQ_Packet} msg The msg instance to transform. 65 | * @return {!Object} 66 | * @suppress {unusedLocalVariables} f is only used for nested messages 67 | */ 68 | proto.IQ_Packet.toObject = function(includeInstance, msg) { 69 | var f, obj = { 70 | signalList: jspb.Message.getRepeatedFloatingPointField(msg, 1) 71 | }; 72 | 73 | if (includeInstance) { 74 | obj.$jspbMessageInstance = msg; 75 | } 76 | return obj; 77 | }; 78 | } 79 | 80 | 81 | /** 82 | * Deserializes binary data (in protobuf wire format). 83 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 84 | * @return {!proto.IQ_Packet} 85 | */ 86 | proto.IQ_Packet.deserializeBinary = function(bytes) { 87 | var reader = new jspb.BinaryReader(bytes); 88 | var msg = new proto.IQ_Packet; 89 | return proto.IQ_Packet.deserializeBinaryFromReader(msg, reader); 90 | }; 91 | 92 | 93 | /** 94 | * Deserializes binary data (in protobuf wire format) from the 95 | * given reader into the given message object. 96 | * @param {!proto.IQ_Packet} msg The message object to deserialize into. 97 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 98 | * @return {!proto.IQ_Packet} 99 | */ 100 | proto.IQ_Packet.deserializeBinaryFromReader = function(msg, reader) { 101 | while (reader.nextField()) { 102 | if (reader.isEndGroup()) { 103 | break; 104 | } 105 | var field = reader.getFieldNumber(); 106 | switch (field) { 107 | case 1: 108 | var value = /** @type {number} */ (reader.readFloat()); 109 | msg.addSignal(value); 110 | break; 111 | default: 112 | reader.skipField(); 113 | break; 114 | } 115 | } 116 | return msg; 117 | }; 118 | 119 | 120 | /** 121 | * Serializes the message to binary data (in protobuf wire format). 122 | * @return {!Uint8Array} 123 | */ 124 | proto.IQ_Packet.prototype.serializeBinary = function() { 125 | var writer = new jspb.BinaryWriter(); 126 | proto.IQ_Packet.serializeBinaryToWriter(this, writer); 127 | return writer.getResultBuffer(); 128 | }; 129 | 130 | 131 | /** 132 | * Serializes the given message to binary data (in protobuf wire 133 | * format), writing to the given BinaryWriter. 134 | * @param {!proto.IQ_Packet} message 135 | * @param {!jspb.BinaryWriter} writer 136 | * @suppress {unusedLocalVariables} f is only used for nested messages 137 | */ 138 | proto.IQ_Packet.serializeBinaryToWriter = function(message, writer) { 139 | var f = undefined; 140 | f = message.getSignalList(); 141 | if (f.length > 0) { 142 | writer.writeRepeatedFloat( 143 | 1, 144 | f 145 | ); 146 | } 147 | }; 148 | 149 | 150 | /** 151 | * repeated float signal = 1; 152 | * @return {!Array.} 153 | */ 154 | proto.IQ_Packet.prototype.getSignalList = function() { 155 | return /** @type {!Array.} */ (jspb.Message.getRepeatedFloatingPointField(this, 1)); 156 | }; 157 | 158 | 159 | /** @param {!Array.} value */ 160 | proto.IQ_Packet.prototype.setSignalList = function(value) { 161 | jspb.Message.setField(this, 1, value || []); 162 | }; 163 | 164 | 165 | /** 166 | * @param {!number} value 167 | * @param {number=} opt_index 168 | */ 169 | proto.IQ_Packet.prototype.addSignal = function(value, opt_index) { 170 | jspb.Message.addToRepeatedField(this, 1, value, opt_index); 171 | }; 172 | 173 | 174 | proto.IQ_Packet.prototype.clearSignalList = function() { 175 | this.setSignalList([]); 176 | }; 177 | 178 | 179 | -------------------------------------------------------------------------------- /src/websock/js/pdw_packet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview 3 | * @enhanceable 4 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 5 | * field starts with 'MSG_' and isn't a translatable message. 6 | * @public 7 | */ 8 | // GENERATED CODE -- DO NOT EDIT! 9 | 10 | goog.provide('proto.PDW_Packet'); 11 | 12 | goog.require('jspb.BinaryReader'); 13 | goog.require('jspb.BinaryWriter'); 14 | goog.require('jspb.Message'); 15 | goog.require('proto.PDW'); 16 | 17 | 18 | /** 19 | * Generated by JsPbCodeGenerator. 20 | * @param {Array=} opt_data Optional initial data array, typically from a 21 | * server response, or constructed directly in Javascript. The array is used 22 | * in place and becomes part of the constructed object. It is not cloned. 23 | * If no data is provided, the constructed object will be empty, but still 24 | * valid. 25 | * @extends {jspb.Message} 26 | * @constructor 27 | */ 28 | proto.PDW_Packet = function(opt_data) { 29 | jspb.Message.initialize(this, opt_data, 0, -1, proto.PDW_Packet.repeatedFields_, null); 30 | }; 31 | goog.inherits(proto.PDW_Packet, jspb.Message); 32 | if (goog.DEBUG && !COMPILED) { 33 | proto.PDW_Packet.displayName = 'proto.PDW_Packet'; 34 | } 35 | /** 36 | * List of repeated fields within this message type. 37 | * @private {!Array} 38 | * @const 39 | */ 40 | proto.PDW_Packet.repeatedFields_ = [1]; 41 | 42 | 43 | 44 | if (jspb.Message.GENERATE_TO_OBJECT) { 45 | /** 46 | * Creates an object representation of this proto suitable for use in Soy templates. 47 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 48 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 49 | * For the list of reserved names please see: 50 | * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. 51 | * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 52 | * for transitional soy proto support: http://goto/soy-param-migration 53 | * @return {!Object} 54 | */ 55 | proto.PDW_Packet.prototype.toObject = function(opt_includeInstance) { 56 | return proto.PDW_Packet.toObject(opt_includeInstance, this); 57 | }; 58 | 59 | 60 | /** 61 | * Static version of the {@see toObject} method. 62 | * @param {boolean|undefined} includeInstance Whether to include the JSPB 63 | * instance for transitional soy proto support: 64 | * http://goto/soy-param-migration 65 | * @param {!proto.PDW_Packet} msg The msg instance to transform. 66 | * @return {!Object} 67 | * @suppress {unusedLocalVariables} f is only used for nested messages 68 | */ 69 | proto.PDW_Packet.toObject = function(includeInstance, msg) { 70 | var f, obj = { 71 | pdwsList: jspb.Message.toObjectList(msg.getPdwsList(), 72 | proto.PDW.toObject, includeInstance) 73 | }; 74 | 75 | if (includeInstance) { 76 | obj.$jspbMessageInstance = msg; 77 | } 78 | return obj; 79 | }; 80 | } 81 | 82 | 83 | /** 84 | * Deserializes binary data (in protobuf wire format). 85 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 86 | * @return {!proto.PDW_Packet} 87 | */ 88 | proto.PDW_Packet.deserializeBinary = function(bytes) { 89 | var reader = new jspb.BinaryReader(bytes); 90 | var msg = new proto.PDW_Packet; 91 | return proto.PDW_Packet.deserializeBinaryFromReader(msg, reader); 92 | }; 93 | 94 | 95 | /** 96 | * Deserializes binary data (in protobuf wire format) from the 97 | * given reader into the given message object. 98 | * @param {!proto.PDW_Packet} msg The message object to deserialize into. 99 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 100 | * @return {!proto.PDW_Packet} 101 | */ 102 | proto.PDW_Packet.deserializeBinaryFromReader = function(msg, reader) { 103 | while (reader.nextField()) { 104 | if (reader.isEndGroup()) { 105 | break; 106 | } 107 | var field = reader.getFieldNumber(); 108 | switch (field) { 109 | case 1: 110 | var value = new proto.PDW; 111 | reader.readMessage(value,proto.PDW.deserializeBinaryFromReader); 112 | msg.addPdws(value); 113 | break; 114 | default: 115 | reader.skipField(); 116 | break; 117 | } 118 | } 119 | return msg; 120 | }; 121 | 122 | 123 | /** 124 | * Serializes the message to binary data (in protobuf wire format). 125 | * @return {!Uint8Array} 126 | */ 127 | proto.PDW_Packet.prototype.serializeBinary = function() { 128 | var writer = new jspb.BinaryWriter(); 129 | proto.PDW_Packet.serializeBinaryToWriter(this, writer); 130 | return writer.getResultBuffer(); 131 | }; 132 | 133 | 134 | /** 135 | * Serializes the given message to binary data (in protobuf wire 136 | * format), writing to the given BinaryWriter. 137 | * @param {!proto.PDW_Packet} message 138 | * @param {!jspb.BinaryWriter} writer 139 | * @suppress {unusedLocalVariables} f is only used for nested messages 140 | */ 141 | proto.PDW_Packet.serializeBinaryToWriter = function(message, writer) { 142 | var f = undefined; 143 | f = message.getPdwsList(); 144 | if (f.length > 0) { 145 | writer.writeRepeatedMessage( 146 | 1, 147 | f, 148 | proto.PDW.serializeBinaryToWriter 149 | ); 150 | } 151 | }; 152 | 153 | 154 | /** 155 | * repeated PDW pdws = 1; 156 | * @return {!Array.} 157 | */ 158 | proto.PDW_Packet.prototype.getPdwsList = function() { 159 | return /** @type{!Array.} */ ( 160 | jspb.Message.getRepeatedWrapperField(this, proto.PDW, 1)); 161 | }; 162 | 163 | 164 | /** @param {!Array.} value */ 165 | proto.PDW_Packet.prototype.setPdwsList = function(value) { 166 | jspb.Message.setRepeatedWrapperField(this, 1, value); 167 | }; 168 | 169 | 170 | /** 171 | * @param {!proto.PDW=} opt_value 172 | * @param {number=} opt_index 173 | * @return {!proto.PDW} 174 | */ 175 | proto.PDW_Packet.prototype.addPdws = function(opt_value, opt_index) { 176 | return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.PDW, opt_index); 177 | }; 178 | 179 | 180 | proto.PDW_Packet.prototype.clearPdwsList = function() { 181 | this.setPdwsList([]); 182 | }; 183 | 184 | 185 | -------------------------------------------------------------------------------- /src/websock/js/fft_packet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview 3 | * @enhanceable 4 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 5 | * field starts with 'MSG_' and isn't a translatable message. 6 | * @public 7 | */ 8 | // GENERATED CODE -- DO NOT EDIT! 9 | 10 | goog.provide('proto.FFT_Packet'); 11 | 12 | goog.require('jspb.BinaryReader'); 13 | goog.require('jspb.BinaryWriter'); 14 | goog.require('jspb.Message'); 15 | 16 | 17 | /** 18 | * Generated by JsPbCodeGenerator. 19 | * @param {Array=} opt_data Optional initial data array, typically from a 20 | * server response, or constructed directly in Javascript. The array is used 21 | * in place and becomes part of the constructed object. It is not cloned. 22 | * If no data is provided, the constructed object will be empty, but still 23 | * valid. 24 | * @extends {jspb.Message} 25 | * @constructor 26 | */ 27 | proto.FFT_Packet = function(opt_data) { 28 | jspb.Message.initialize(this, opt_data, 0, -1, proto.FFT_Packet.repeatedFields_, null); 29 | }; 30 | goog.inherits(proto.FFT_Packet, jspb.Message); 31 | if (goog.DEBUG && !COMPILED) { 32 | proto.FFT_Packet.displayName = 'proto.FFT_Packet'; 33 | } 34 | /** 35 | * List of repeated fields within this message type. 36 | * @private {!Array} 37 | * @const 38 | */ 39 | proto.FFT_Packet.repeatedFields_ = [1,2]; 40 | 41 | 42 | 43 | if (jspb.Message.GENERATE_TO_OBJECT) { 44 | /** 45 | * Creates an object representation of this proto suitable for use in Soy templates. 46 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 47 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 48 | * For the list of reserved names please see: 49 | * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. 50 | * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 51 | * for transitional soy proto support: http://goto/soy-param-migration 52 | * @return {!Object} 53 | */ 54 | proto.FFT_Packet.prototype.toObject = function(opt_includeInstance) { 55 | return proto.FFT_Packet.toObject(opt_includeInstance, this); 56 | }; 57 | 58 | 59 | /** 60 | * Static version of the {@see toObject} method. 61 | * @param {boolean|undefined} includeInstance Whether to include the JSPB 62 | * instance for transitional soy proto support: 63 | * http://goto/soy-param-migration 64 | * @param {!proto.FFT_Packet} msg The msg instance to transform. 65 | * @return {!Object} 66 | * @suppress {unusedLocalVariables} f is only used for nested messages 67 | */ 68 | proto.FFT_Packet.toObject = function(includeInstance, msg) { 69 | var f, obj = { 70 | freqBinsHzList: jspb.Message.getRepeatedField(msg, 1), 71 | fftList: jspb.Message.getRepeatedFloatingPointField(msg, 2) 72 | }; 73 | 74 | if (includeInstance) { 75 | obj.$jspbMessageInstance = msg; 76 | } 77 | return obj; 78 | }; 79 | } 80 | 81 | 82 | /** 83 | * Deserializes binary data (in protobuf wire format). 84 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 85 | * @return {!proto.FFT_Packet} 86 | */ 87 | proto.FFT_Packet.deserializeBinary = function(bytes) { 88 | var reader = new jspb.BinaryReader(bytes); 89 | var msg = new proto.FFT_Packet; 90 | return proto.FFT_Packet.deserializeBinaryFromReader(msg, reader); 91 | }; 92 | 93 | 94 | /** 95 | * Deserializes binary data (in protobuf wire format) from the 96 | * given reader into the given message object. 97 | * @param {!proto.FFT_Packet} msg The message object to deserialize into. 98 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 99 | * @return {!proto.FFT_Packet} 100 | */ 101 | proto.FFT_Packet.deserializeBinaryFromReader = function(msg, reader) { 102 | while (reader.nextField()) { 103 | if (reader.isEndGroup()) { 104 | break; 105 | } 106 | var field = reader.getFieldNumber(); 107 | switch (field) { 108 | case 1: 109 | var value = /** @type {number} */ (reader.readUint64()); 110 | msg.addFreqBinsHz(value); 111 | break; 112 | case 2: 113 | var value = /** @type {number} */ (reader.readDouble()); 114 | msg.addFft(value); 115 | break; 116 | default: 117 | reader.skipField(); 118 | break; 119 | } 120 | } 121 | return msg; 122 | }; 123 | 124 | 125 | /** 126 | * Serializes the message to binary data (in protobuf wire format). 127 | * @return {!Uint8Array} 128 | */ 129 | proto.FFT_Packet.prototype.serializeBinary = function() { 130 | var writer = new jspb.BinaryWriter(); 131 | proto.FFT_Packet.serializeBinaryToWriter(this, writer); 132 | return writer.getResultBuffer(); 133 | }; 134 | 135 | 136 | /** 137 | * Serializes the given message to binary data (in protobuf wire 138 | * format), writing to the given BinaryWriter. 139 | * @param {!proto.FFT_Packet} message 140 | * @param {!jspb.BinaryWriter} writer 141 | * @suppress {unusedLocalVariables} f is only used for nested messages 142 | */ 143 | proto.FFT_Packet.serializeBinaryToWriter = function(message, writer) { 144 | var f = undefined; 145 | f = message.getFreqBinsHzList(); 146 | if (f.length > 0) { 147 | writer.writeRepeatedUint64( 148 | 1, 149 | f 150 | ); 151 | } 152 | f = message.getFftList(); 153 | if (f.length > 0) { 154 | writer.writeRepeatedDouble( 155 | 2, 156 | f 157 | ); 158 | } 159 | }; 160 | 161 | 162 | /** 163 | * repeated uint64 freq_bins_hz = 1; 164 | * @return {!Array.} 165 | */ 166 | proto.FFT_Packet.prototype.getFreqBinsHzList = function() { 167 | return /** @type {!Array.} */ (jspb.Message.getRepeatedField(this, 1)); 168 | }; 169 | 170 | 171 | /** @param {!Array.} value */ 172 | proto.FFT_Packet.prototype.setFreqBinsHzList = function(value) { 173 | jspb.Message.setField(this, 1, value || []); 174 | }; 175 | 176 | 177 | /** 178 | * @param {!number} value 179 | * @param {number=} opt_index 180 | */ 181 | proto.FFT_Packet.prototype.addFreqBinsHz = function(value, opt_index) { 182 | jspb.Message.addToRepeatedField(this, 1, value, opt_index); 183 | }; 184 | 185 | 186 | proto.FFT_Packet.prototype.clearFreqBinsHzList = function() { 187 | this.setFreqBinsHzList([]); 188 | }; 189 | 190 | 191 | /** 192 | * repeated double fft = 2; 193 | * @return {!Array.} 194 | */ 195 | proto.FFT_Packet.prototype.getFftList = function() { 196 | return /** @type {!Array.} */ (jspb.Message.getRepeatedFloatingPointField(this, 2)); 197 | }; 198 | 199 | 200 | /** @param {!Array.} value */ 201 | proto.FFT_Packet.prototype.setFftList = function(value) { 202 | jspb.Message.setField(this, 2, value || []); 203 | }; 204 | 205 | 206 | /** 207 | * @param {!number} value 208 | * @param {number=} opt_index 209 | */ 210 | proto.FFT_Packet.prototype.addFft = function(value, opt_index) { 211 | jspb.Message.addToRepeatedField(this, 2, value, opt_index); 212 | }; 213 | 214 | 215 | proto.FFT_Packet.prototype.clearFftList = function() { 216 | this.setFftList([]); 217 | }; 218 | 219 | 220 | -------------------------------------------------------------------------------- /src/RADAR/top_block.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | ################################################## 4 | # GNU Radio Python Flow Graph 5 | # Title: Top Block 6 | # Generated: Fri Mar 4 19:27:02 2016 7 | ################################################## 8 | 9 | if __name__ == '__main__': 10 | import ctypes 11 | import sys 12 | if sys.platform.startswith('linux'): 13 | try: 14 | x11 = ctypes.cdll.LoadLibrary('libX11.so') 15 | x11.XInitThreads() 16 | except: 17 | print "Warning: failed to XInitThreads()" 18 | 19 | from PyQt4 import Qt 20 | from gnuradio import eng_notation 21 | from gnuradio import gr 22 | from gnuradio import qtgui 23 | from gnuradio.eng_option import eng_option 24 | from gnuradio.filter import firdes 25 | from optparse import OptionParser 26 | import osmosdr 27 | import sip 28 | import sys 29 | import time 30 | 31 | 32 | class top_block(gr.top_block, Qt.QWidget): 33 | 34 | def __init__(self): 35 | gr.top_block.__init__(self, "Top Block") 36 | Qt.QWidget.__init__(self) 37 | self.setWindowTitle("Top Block") 38 | try: 39 | self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) 40 | except: 41 | pass 42 | self.top_scroll_layout = Qt.QVBoxLayout() 43 | self.setLayout(self.top_scroll_layout) 44 | self.top_scroll = Qt.QScrollArea() 45 | self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) 46 | self.top_scroll_layout.addWidget(self.top_scroll) 47 | self.top_scroll.setWidgetResizable(True) 48 | self.top_widget = Qt.QWidget() 49 | self.top_scroll.setWidget(self.top_widget) 50 | self.top_layout = Qt.QVBoxLayout(self.top_widget) 51 | self.top_grid_layout = Qt.QGridLayout() 52 | self.top_layout.addLayout(self.top_grid_layout) 53 | 54 | self.settings = Qt.QSettings("GNU Radio", "top_block") 55 | self.restoreGeometry(self.settings.value("geometry").toByteArray()) 56 | 57 | ################################################## 58 | # Variables 59 | ################################################## 60 | self.samp_rate = samp_rate = 8000000 61 | self.fft_size = fft_size = 16383 62 | self.center_freq = center_freq = 2482000000 63 | 64 | ################################################## 65 | # Blocks 66 | ################################################## 67 | self.qtgui_freq_sink_x_0 = qtgui.freq_sink_c( 68 | fft_size, #size 69 | firdes.WIN_BLACKMAN_hARRIS, #wintype 70 | center_freq, #fc 71 | samp_rate, #bw 72 | "", #name 73 | 1 #number of inputs 74 | ) 75 | self.qtgui_freq_sink_x_0.set_update_time(1) 76 | self.qtgui_freq_sink_x_0.set_y_axis(-140, 10) 77 | self.qtgui_freq_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, 0.0, 0, "") 78 | self.qtgui_freq_sink_x_0.enable_autoscale(False) 79 | self.qtgui_freq_sink_x_0.enable_grid(False) 80 | self.qtgui_freq_sink_x_0.set_fft_average(1.0) 81 | self.qtgui_freq_sink_x_0.enable_control_panel(False) 82 | 83 | if not True: 84 | self.qtgui_freq_sink_x_0.disable_legend() 85 | 86 | if "complex" == "float" or "complex" == "msg_float": 87 | self.qtgui_freq_sink_x_0.set_plot_pos_half(not True) 88 | 89 | labels = ["", "", "", "", "", 90 | "", "", "", "", ""] 91 | widths = [1, 1, 1, 1, 1, 92 | 1, 1, 1, 1, 1] 93 | colors = ["blue", "red", "green", "black", "cyan", 94 | "magenta", "yellow", "dark red", "dark green", "dark blue"] 95 | alphas = [1.0, 1.0, 1.0, 1.0, 1.0, 96 | 1.0, 1.0, 1.0, 1.0, 1.0] 97 | for i in xrange(1): 98 | if len(labels[i]) == 0: 99 | self.qtgui_freq_sink_x_0.set_line_label(i, "Data {0}".format(i)) 100 | else: 101 | self.qtgui_freq_sink_x_0.set_line_label(i, labels[i]) 102 | self.qtgui_freq_sink_x_0.set_line_width(i, widths[i]) 103 | self.qtgui_freq_sink_x_0.set_line_color(i, colors[i]) 104 | self.qtgui_freq_sink_x_0.set_line_alpha(i, alphas[i]) 105 | 106 | self._qtgui_freq_sink_x_0_win = sip.wrapinstance(self.qtgui_freq_sink_x_0.pyqwidget(), Qt.QWidget) 107 | self.top_layout.addWidget(self._qtgui_freq_sink_x_0_win) 108 | self.osmosdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + "hackrf=23ec7" ) 109 | self.osmosdr_source_0.set_sample_rate(samp_rate) 110 | self.osmosdr_source_0.set_center_freq(center_freq, 0) 111 | self.osmosdr_source_0.set_freq_corr(0, 0) 112 | self.osmosdr_source_0.set_dc_offset_mode(2, 0) 113 | self.osmosdr_source_0.set_iq_balance_mode(2, 0) 114 | self.osmosdr_source_0.set_gain_mode(False, 0) 115 | self.osmosdr_source_0.set_gain(0, 0) 116 | self.osmosdr_source_0.set_if_gain(20, 0) 117 | self.osmosdr_source_0.set_bb_gain(0, 0) 118 | self.osmosdr_source_0.set_antenna("", 0) 119 | self.osmosdr_source_0.set_bandwidth(0, 0) 120 | 121 | 122 | ################################################## 123 | # Connections 124 | ################################################## 125 | self.connect((self.osmosdr_source_0, 0), (self.qtgui_freq_sink_x_0, 0)) 126 | 127 | def closeEvent(self, event): 128 | self.settings = Qt.QSettings("GNU Radio", "top_block") 129 | self.settings.setValue("geometry", self.saveGeometry()) 130 | event.accept() 131 | 132 | 133 | def get_samp_rate(self): 134 | return self.samp_rate 135 | 136 | def set_samp_rate(self, samp_rate): 137 | self.samp_rate = samp_rate 138 | self.osmosdr_source_0.set_sample_rate(self.samp_rate) 139 | self.qtgui_freq_sink_x_0.set_frequency_range(self.center_freq, self.samp_rate) 140 | 141 | def get_fft_size(self): 142 | return self.fft_size 143 | 144 | def set_fft_size(self, fft_size): 145 | self.fft_size = fft_size 146 | 147 | def get_center_freq(self): 148 | return self.center_freq 149 | 150 | def set_center_freq(self, center_freq): 151 | self.center_freq = center_freq 152 | self.osmosdr_source_0.set_center_freq(self.center_freq, 0) 153 | self.qtgui_freq_sink_x_0.set_frequency_range(self.center_freq, self.samp_rate) 154 | 155 | 156 | def main(top_block_cls=top_block, options=None): 157 | 158 | from distutils.version import StrictVersion 159 | if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"): 160 | style = gr.prefs().get_string('qtgui', 'style', 'raster') 161 | Qt.QApplication.setGraphicsSystem(style) 162 | qapp = Qt.QApplication(sys.argv) 163 | 164 | tb = top_block_cls() 165 | tb.start() 166 | tb.show() 167 | 168 | def quitting(): 169 | tb.stop() 170 | tb.wait() 171 | qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting) 172 | qapp.exec_() 173 | 174 | 175 | if __name__ == '__main__': 176 | main() 177 | -------------------------------------------------------------------------------- /src/websock/iq_chart.js: -------------------------------------------------------------------------------- 1 | 2 | goog.require('proto.Packet'); 3 | 4 | var buffer_size = 131072; 5 | var n_fft_size = 2048; 6 | var fs = 2000000; 7 | var iq_chart_real_values = Int16Array.from(Array(buffer_size).keys()); 8 | var iq_chart_imag_values = Int16Array.from(Array(buffer_size).keys()); 9 | var mag_chart_y_values = Int16Array.from(Array(buffer_size).keys()); 10 | var fft_chart_y_values = Int16Array.from(Array(n_fft_size).keys()); 11 | 12 | var iq_chart = new Chart(document.getElementById("iq-chart"), { 13 | type: 'line', 14 | data: { 15 | labels: Array.from(Array(buffer_size).keys()), 16 | datasets: [{ 17 | data: iq_chart_real_values, 18 | pointRadius: 0, 19 | label: "I", 20 | borderColor: "#3e95cd", 21 | fill: false 22 | }, { 23 | data: iq_chart_imag_values, 24 | pointRadius: 0, 25 | label: "Q", 26 | borderColor: "#8e5ea2", 27 | fill: false 28 | } 29 | ] 30 | }, 31 | options: { 32 | title: { 33 | display: true, 34 | text: 'IQ Data' 35 | }, 36 | animation: false, 37 | maintainAspectRatio: false, 38 | responsive: true 39 | } 40 | }); 41 | 42 | var mag_chart = new Chart(document.getElementById("mag-chart"), { 43 | type: 'line', 44 | data: { 45 | labels: Array.from(Array(buffer_size).keys()), 46 | datasets: [{ 47 | data: mag_chart_y_values, 48 | pointRadius: 0, 49 | label: "Abs", 50 | borderColor: "#3e95cd", 51 | fill: false 52 | } 53 | ] 54 | }, 55 | options: { 56 | title: { 57 | display: true, 58 | text: 'Magnitude' 59 | }, 60 | animation: false, 61 | maintainAspectRatio: false, 62 | responsive: true 63 | } 64 | }); 65 | 66 | var fft_chart = new Chart(document.getElementById("fft-chart"), { 67 | type: 'line', 68 | data: { 69 | labels: linspace(-fs/2/1e3, fs/2/1e3, n_fft_size, false), 70 | datasets: [{ 71 | data: fft_chart_y_values, 72 | pointRadius: 0, 73 | label: "FFT", 74 | borderColor: "#3e95cd", 75 | fill: false 76 | } 77 | ] 78 | }, 79 | options: { 80 | title: { 81 | display: true, 82 | text: 'FFT' 83 | }, 84 | animation: false, 85 | maintainAspectRatio: false, 86 | responsive: true 87 | } 88 | }); 89 | 90 | 91 | // Create a Canvas element 92 | const canvas = document.getElementById('waterfall-chart'); 93 | const canvasParent = document.getElementById('waterfall-chart').parentElement; 94 | canvas.width = canvasParent.offsetWidth - 20; 95 | canvas.height = canvasParent.offsetHeight - 20; 96 | const ctx = canvas.getContext('2d'); 97 | 98 | var num_x_bins = (n_fft_size/16); 99 | var bin_width = canvas.width / num_x_bins; 100 | var num_y_bins = canvas.height / bin_width; 101 | var bin_hight = canvas.height / num_y_bins; 102 | 103 | // // Matrix 104 | // var waterfall = math.ones(num_x_bins, num_y_bins); 105 | 106 | // function redraw_waterfall() 107 | // { 108 | // function fill_bin(bin_x, bin_y, color) 109 | // { 110 | // ctx.fillStyle = color; 111 | // ctx.fillRect(bin_x * bin_width, bin_y * bin_hight, bin_width, bin_hight); 112 | // }; 113 | 114 | // waterfall.forEach(function (value, index, matrix) { 115 | // var value = Math.random()*255; 116 | // fill_bin(x, y, `rgb(0,0,${value})`); 117 | // }); 118 | 119 | // for (var x = 0; x < num_x_bins; x++) 120 | // { 121 | // for (var y = 0; y < num_y_bins; y++) 122 | // { 123 | // var value = Math.random()*255; 124 | // fill_bin(x, y, `rgb(0,0,${value})`); 125 | // } 126 | // } 127 | // } 128 | 129 | // redraw_waterfall(); 130 | 131 | 132 | var paused = false; 133 | function pause() 134 | { 135 | if (paused) 136 | { 137 | paused = false; 138 | send('more'); 139 | } 140 | else 141 | { 142 | paused = true; 143 | } 144 | } 145 | 146 | function linspace(start, stop, num, endpoint = true) 147 | { 148 | const div = endpoint ? (num - 1) : num; 149 | const step = (stop - start) / div; 150 | return Array.from({length: num}, (_, i) => start + step * i); 151 | } 152 | 153 | // Make a websocket connection 154 | var connection = new WebSocket('ws://rpi4:8765') 155 | connection.binaryType = "arraybuffer"; 156 | 157 | connection.onopen = function () 158 | { 159 | // Kick off a request for some IQ data 160 | connection.send('more'); 161 | }; 162 | 163 | // Log errors 164 | connection.onerror = function (error) 165 | { 166 | console.log('WebSocket Error ' + error); 167 | }; 168 | 169 | // Log messages from the server 170 | connection.onmessage = function (e) 171 | { 172 | var message = proto.Packet.deserializeBinary(e.data); 173 | 174 | if (message.getHeader().getType() != proto.Packet_Header.PacketType.IQ) 175 | return; 176 | 177 | var iq = message.getIqPacket().getSignalList(); 178 | 179 | // Separate the real and imag parts 180 | let real = iq.filter((element, index) => { 181 | return index % 2 === 0; 182 | }); 183 | let imag = iq.filter((element, index) => { 184 | return index % 2 === 1; 185 | }); 186 | 187 | // Compute 20 log magnitude 188 | var mag = Array(); 189 | for (var i=0; i= StrictVersion("4.5.0"): 148 | Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster')) 149 | qapp = Qt.QApplication(sys.argv) 150 | tb = top_block() 151 | tb.start() 152 | tb.show() 153 | 154 | def quitting(): 155 | tb.stop() 156 | tb.wait() 157 | qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting) 158 | qapp.exec_() 159 | tb = None # to clean up Qt widgets 160 | -------------------------------------------------------------------------------- /src/devices/RTLSDRDevice.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "RTLSDRDevice.h" 5 | 6 | RTLSDRDevice::RTLSDRDevice() 7 | { 8 | m_is_initialized = false; 9 | m_device_mode = STANDBY_MODE; // mode: 0=standby 1=Rx 2=Tx 10 | m_fc_hz = 462610000; // center freq 11 | m_fs_hz = 1000000; // sample rate 12 | m_rx_gain = 0; 13 | } 14 | 15 | RTLSDRDevice::~RTLSDRDevice() 16 | { 17 | cleanup(); 18 | } 19 | 20 | bool RTLSDRDevice::initialize(const char* const desired_serial_number) 21 | { 22 | int res = rtlsdr_get_index_by_serial(desired_serial_number); 23 | if ( res < 0 ) 24 | { 25 | std::cout << "Failed to open device with serial: '" << desired_serial_number << "'" << std::endl; 26 | return false; 27 | } 28 | return initialize(res); 29 | } 30 | 31 | bool RTLSDRDevice::initialize(const int desired_device_index) 32 | { 33 | if ( 0 >= rtlsdr_get_device_count() ) 34 | { 35 | std::cout << "No device" << std::endl; 36 | return false; 37 | } 38 | 39 | if ( ! check_error( rtlsdr_open( &m_device, desired_device_index ) ) ) 40 | { 41 | std::cout << "Failed to open device" << std::endl; 42 | return false; 43 | } 44 | 45 | std::cout << "Found rtlsdr board" << std::endl; 46 | 47 | m_is_initialized = true; 48 | 49 | // 50 | // Set device parameters 51 | // 52 | if ( ! set_center_freq( m_fc_hz ) || 53 | ! set_sample_rate( m_fs_hz ) || 54 | ! set_rx_gain( m_rx_gain ) ) 55 | { 56 | cleanup(); 57 | return false; 58 | } 59 | 60 | m_is_initialized = true; 61 | return true; 62 | } 63 | 64 | // 65 | // Callback function for Rx samples 66 | // 67 | void rx_cb_fn(unsigned char *buf, uint32_t len, void *ctx) 68 | { 69 | std::pair* callback_args_pair = (std::pair*)ctx; 70 | 71 | device_sample_block_cb_fn callback_function = callback_args_pair->first; 72 | void* callback_args = callback_args_pair->second; 73 | 74 | SampleChunk sc; 75 | sc.resize(len/2); 76 | 77 | for (uint32_t ii = 0; ii < len; ii+=2) 78 | { 79 | sc[ii/2] = std::complex( ( double( buf[ii+0] ) - double(127.5) ) / double(127.5), 80 | ( double( buf[ii+1] ) - double(127.5) ) / double(127.5) ); 81 | } 82 | 83 | callback_function(&sc, callback_args); 84 | } 85 | 86 | void* thread_func(void* args) 87 | { 88 | std::cout << "Rx thread started" << std::endl; 89 | 90 | RTLSDRDevice* rtl_obj = (RTLSDRDevice*)args; 91 | 92 | rtl_obj->m_device_mode = RX_MODE; 93 | 94 | rtl_obj->check_error( rtlsdr_reset_buffer( rtl_obj->m_device ) ); 95 | 96 | rtl_obj->check_error( 97 | rtlsdr_read_async( 98 | rtl_obj->m_device, 99 | rx_cb_fn, 100 | rtl_obj->m_callback_args, 101 | 0, 102 | 0 ) ); // Blocking 103 | 104 | return nullptr; 105 | } 106 | 107 | bool RTLSDRDevice::start_Rx( device_sample_block_cb_fn callback, void* args ) 108 | { 109 | if ( m_is_initialized && m_device_mode == STANDBY_MODE ) 110 | { 111 | std::cout << "Starting Rx thread " << m_is_initialized << std::endl; 112 | 113 | m_callback_args = new std::pair(callback, args); 114 | 115 | pthread_create( &m_thread_context, NULL, thread_func, (void*)this ); 116 | 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | bool RTLSDRDevice::stop_Rx() 123 | { 124 | if ( m_is_initialized && m_device_mode == RX_MODE ) 125 | { 126 | std::cout << "Stopping Rx" << std::endl; 127 | if ( ! check_error( rtlsdr_cancel_async( m_device ) ) ) 128 | { 129 | std::cout << "Error: failed to stop rx mode!" << std::endl; 130 | delete m_callback_args; 131 | return false; 132 | } 133 | pthread_join( m_thread_context, NULL ); 134 | m_device_mode = STANDBY_MODE; 135 | delete m_callback_args; 136 | return true; 137 | } 138 | return false; 139 | } 140 | 141 | bool RTLSDRDevice::set_center_freq( double fc_hz ) 142 | { 143 | if ( m_is_initialized ) 144 | { 145 | if ( fc_hz >= 20000000 && fc_hz <= 6000000000 ) 146 | { 147 | std::cout << "Tuning to " << fc_hz << std::endl; 148 | if ( check_error( rtlsdr_set_center_freq( m_device, fc_hz ) ) ) 149 | { 150 | m_fc_hz = fc_hz; 151 | return true; 152 | } 153 | } 154 | else 155 | { 156 | std::cout << "WARNING: invalid tune frequency: " << fc_hz << std::endl; 157 | } 158 | } 159 | return false; 160 | } 161 | 162 | bool RTLSDRDevice::set_sample_rate( double fs_hz ) 163 | { 164 | if ( m_is_initialized ) 165 | { 166 | // 225001 - 300000 Hz 167 | // 900001 - 3200000 Hz 168 | if ( ( fs_hz > 225000 && fs_hz <= 300000 ) || 169 | ( fs_hz > 900000 && fs_hz <= 3200000 ) ) 170 | { 171 | std::cout << "Setting sample rate to " << fs_hz << std::endl; 172 | if ( check_error( rtlsdr_set_sample_rate( m_device, uint32_t(fs_hz) ) ) ) 173 | { 174 | m_fs_hz = fs_hz; 175 | return true; 176 | } 177 | } 178 | else 179 | std::cout << "WARNING: invalid sample rate: " << fs_hz << std::endl; 180 | } 181 | return false; 182 | } 183 | 184 | bool RTLSDRDevice::set_rx_gain( double lna_gain ) 185 | { 186 | if ( m_is_initialized ) 187 | { 188 | int num_gains = rtlsdr_get_tuner_gains( m_device, NULL ); 189 | 190 | int* gains = new int[num_gains]; 191 | 192 | rtlsdr_get_tuner_gains( m_device, gains ); 193 | 194 | for (int gg = 0; gg < num_gains; gg++) 195 | { 196 | if ( gains[gg] == int(lna_gain) ) 197 | { 198 | std::cout << "Setting LNA gain..." << std::endl; 199 | rtlsdr_set_tuner_gain( m_device, lna_gain ); 200 | delete [] gains; 201 | return true; 202 | } 203 | } 204 | 205 | std::cout << "Invalid LNA gain, valid values are"; 206 | for (int gg = 0; gg < num_gains; gg++) 207 | std::cout << ", " << gains[gg]; 208 | std::cout << std::endl; 209 | 210 | delete [] gains; 211 | } 212 | return false; 213 | } 214 | 215 | bool RTLSDRDevice::cleanup() 216 | { 217 | if ( m_is_initialized ) 218 | { 219 | stop_Rx(); 220 | 221 | if ( ! check_error( rtlsdr_close( m_device ) ) ) 222 | std::cout << "Error: failed to release device!" << std::endl; 223 | 224 | m_is_initialized = false; 225 | 226 | std::cout << "Cleanup complete!" << std::endl; 227 | return true; 228 | } 229 | return false; 230 | } 231 | 232 | 233 | bool RTLSDRDevice::check_error(int error) 234 | { 235 | if ( 0 == error ) 236 | return true; 237 | else 238 | std::cout << "Error: " << error << std::endl; 239 | return false; 240 | } 241 | 242 | 243 | -------------------------------------------------------------------------------- /src/websock/js/packet_header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview 3 | * @enhanceable 4 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 5 | * field starts with 'MSG_' and isn't a translatable message. 6 | * @public 7 | */ 8 | // GENERATED CODE -- DO NOT EDIT! 9 | 10 | goog.provide('proto.Packet_Header'); 11 | goog.provide('proto.Packet_Header.PacketType'); 12 | 13 | goog.require('jspb.BinaryReader'); 14 | goog.require('jspb.BinaryWriter'); 15 | goog.require('jspb.Message'); 16 | 17 | 18 | /** 19 | * Generated by JsPbCodeGenerator. 20 | * @param {Array=} opt_data Optional initial data array, typically from a 21 | * server response, or constructed directly in Javascript. The array is used 22 | * in place and becomes part of the constructed object. It is not cloned. 23 | * If no data is provided, the constructed object will be empty, but still 24 | * valid. 25 | * @extends {jspb.Message} 26 | * @constructor 27 | */ 28 | proto.Packet_Header = function(opt_data) { 29 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 30 | }; 31 | goog.inherits(proto.Packet_Header, jspb.Message); 32 | if (goog.DEBUG && !COMPILED) { 33 | proto.Packet_Header.displayName = 'proto.Packet_Header'; 34 | } 35 | 36 | 37 | if (jspb.Message.GENERATE_TO_OBJECT) { 38 | /** 39 | * Creates an object representation of this proto suitable for use in Soy templates. 40 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 41 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 42 | * For the list of reserved names please see: 43 | * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. 44 | * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 45 | * for transitional soy proto support: http://goto/soy-param-migration 46 | * @return {!Object} 47 | */ 48 | proto.Packet_Header.prototype.toObject = function(opt_includeInstance) { 49 | return proto.Packet_Header.toObject(opt_includeInstance, this); 50 | }; 51 | 52 | 53 | /** 54 | * Static version of the {@see toObject} method. 55 | * @param {boolean|undefined} includeInstance Whether to include the JSPB 56 | * instance for transitional soy proto support: 57 | * http://goto/soy-param-migration 58 | * @param {!proto.Packet_Header} msg The msg instance to transform. 59 | * @return {!Object} 60 | * @suppress {unusedLocalVariables} f is only used for nested messages 61 | */ 62 | proto.Packet_Header.toObject = function(includeInstance, msg) { 63 | var f, obj = { 64 | type: jspb.Message.getField(msg, 1), 65 | fs: jspb.Message.getField(msg, 2), 66 | fc: jspb.Message.getField(msg, 3) 67 | }; 68 | 69 | if (includeInstance) { 70 | obj.$jspbMessageInstance = msg; 71 | } 72 | return obj; 73 | }; 74 | } 75 | 76 | 77 | /** 78 | * Deserializes binary data (in protobuf wire format). 79 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 80 | * @return {!proto.Packet_Header} 81 | */ 82 | proto.Packet_Header.deserializeBinary = function(bytes) { 83 | var reader = new jspb.BinaryReader(bytes); 84 | var msg = new proto.Packet_Header; 85 | return proto.Packet_Header.deserializeBinaryFromReader(msg, reader); 86 | }; 87 | 88 | 89 | /** 90 | * Deserializes binary data (in protobuf wire format) from the 91 | * given reader into the given message object. 92 | * @param {!proto.Packet_Header} msg The message object to deserialize into. 93 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 94 | * @return {!proto.Packet_Header} 95 | */ 96 | proto.Packet_Header.deserializeBinaryFromReader = function(msg, reader) { 97 | while (reader.nextField()) { 98 | if (reader.isEndGroup()) { 99 | break; 100 | } 101 | var field = reader.getFieldNumber(); 102 | switch (field) { 103 | case 1: 104 | var value = /** @type {!proto.Packet_Header.PacketType} */ (reader.readEnum()); 105 | msg.setType(value); 106 | break; 107 | case 2: 108 | var value = /** @type {number} */ (reader.readUint32()); 109 | msg.setFs(value); 110 | break; 111 | case 3: 112 | var value = /** @type {number} */ (reader.readUint32()); 113 | msg.setFc(value); 114 | break; 115 | default: 116 | reader.skipField(); 117 | break; 118 | } 119 | } 120 | return msg; 121 | }; 122 | 123 | 124 | /** 125 | * Serializes the message to binary data (in protobuf wire format). 126 | * @return {!Uint8Array} 127 | */ 128 | proto.Packet_Header.prototype.serializeBinary = function() { 129 | var writer = new jspb.BinaryWriter(); 130 | proto.Packet_Header.serializeBinaryToWriter(this, writer); 131 | return writer.getResultBuffer(); 132 | }; 133 | 134 | 135 | /** 136 | * Serializes the given message to binary data (in protobuf wire 137 | * format), writing to the given BinaryWriter. 138 | * @param {!proto.Packet_Header} message 139 | * @param {!jspb.BinaryWriter} writer 140 | * @suppress {unusedLocalVariables} f is only used for nested messages 141 | */ 142 | proto.Packet_Header.serializeBinaryToWriter = function(message, writer) { 143 | var f = undefined; 144 | f = /** @type {!proto.Packet_Header.PacketType} */ (jspb.Message.getField(message, 1)); 145 | if (f != null) { 146 | writer.writeEnum( 147 | 1, 148 | f 149 | ); 150 | } 151 | f = /** @type {number} */ (jspb.Message.getField(message, 2)); 152 | if (f != null) { 153 | writer.writeUint32( 154 | 2, 155 | f 156 | ); 157 | } 158 | f = /** @type {number} */ (jspb.Message.getField(message, 3)); 159 | if (f != null) { 160 | writer.writeUint32( 161 | 3, 162 | f 163 | ); 164 | } 165 | }; 166 | 167 | 168 | /** 169 | * @enum {number} 170 | */ 171 | proto.Packet_Header.PacketType = { 172 | IQ: 0, 173 | FFT: 1, 174 | PDW: 2 175 | }; 176 | 177 | /** 178 | * required PacketType type = 1; 179 | * @return {!proto.Packet_Header.PacketType} 180 | */ 181 | proto.Packet_Header.prototype.getType = function() { 182 | return /** @type {!proto.Packet_Header.PacketType} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); 183 | }; 184 | 185 | 186 | /** @param {!proto.Packet_Header.PacketType} value */ 187 | proto.Packet_Header.prototype.setType = function(value) { 188 | jspb.Message.setField(this, 1, value); 189 | }; 190 | 191 | 192 | proto.Packet_Header.prototype.clearType = function() { 193 | jspb.Message.setField(this, 1, undefined); 194 | }; 195 | 196 | 197 | /** 198 | * Returns whether this field is set. 199 | * @return {!boolean} 200 | */ 201 | proto.Packet_Header.prototype.hasType = function() { 202 | return jspb.Message.getField(this, 1) != null; 203 | }; 204 | 205 | 206 | /** 207 | * required uint32 fs = 2; 208 | * @return {number} 209 | */ 210 | proto.Packet_Header.prototype.getFs = function() { 211 | return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0)); 212 | }; 213 | 214 | 215 | /** @param {number} value */ 216 | proto.Packet_Header.prototype.setFs = function(value) { 217 | jspb.Message.setField(this, 2, value); 218 | }; 219 | 220 | 221 | proto.Packet_Header.prototype.clearFs = function() { 222 | jspb.Message.setField(this, 2, undefined); 223 | }; 224 | 225 | 226 | /** 227 | * Returns whether this field is set. 228 | * @return {!boolean} 229 | */ 230 | proto.Packet_Header.prototype.hasFs = function() { 231 | return jspb.Message.getField(this, 2) != null; 232 | }; 233 | 234 | 235 | /** 236 | * required uint32 fc = 3; 237 | * @return {number} 238 | */ 239 | proto.Packet_Header.prototype.getFc = function() { 240 | return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0)); 241 | }; 242 | 243 | 244 | /** @param {number} value */ 245 | proto.Packet_Header.prototype.setFc = function(value) { 246 | jspb.Message.setField(this, 3, value); 247 | }; 248 | 249 | 250 | proto.Packet_Header.prototype.clearFc = function() { 251 | jspb.Message.setField(this, 3, undefined); 252 | }; 253 | 254 | 255 | /** 256 | * Returns whether this field is set. 257 | * @return {!boolean} 258 | */ 259 | proto.Packet_Header.prototype.hasFc = function() { 260 | return jspb.Message.getField(this, 3) != null; 261 | }; 262 | 263 | 264 | -------------------------------------------------------------------------------- /src/utilities/pulse_detector.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PULSE_DETECTOR_H 3 | #define PULSE_DETECTOR_H 4 | 5 | #include "packet.pb.h" 6 | 7 | #include "fft.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace utilities 16 | { 17 | class MovingAverage 18 | { 19 | public: 20 | MovingAverage(const uint32_t window_size) : 21 | m_window(), 22 | m_sum(0.0), 23 | m_max_size(window_size) 24 | {} 25 | 26 | double addItem(double item) 27 | { 28 | if ( isinf(item) ) 29 | return addItem(0.0); 30 | 31 | m_window.push_front(item); 32 | m_sum += item; 33 | 34 | if ( m_window.size() > m_max_size ) 35 | { 36 | m_sum -= m_window[m_window.size()-1]; 37 | m_window.pop_back(); 38 | } 39 | 40 | return getAverage(); 41 | } 42 | 43 | double getAverage() 44 | { 45 | if ( m_window.size() > 0 ) 46 | return m_sum / m_window.size(); 47 | else 48 | return 0.0; 49 | } 50 | 51 | double getMax() 52 | { 53 | double max = -9999; 54 | for ( auto val : m_window ) 55 | { 56 | max = std::max(max, val); 57 | } 58 | return max; 59 | } 60 | 61 | void clear() 62 | { 63 | m_window.clear(); 64 | m_sum = 0.0; 65 | } 66 | 67 | std::deque m_window; 68 | double m_sum; 69 | uint32_t m_max_size; 70 | }; 71 | 72 | enum DetectionState 73 | { 74 | NOISE, 75 | DETECTION 76 | }; 77 | 78 | struct LogItem 79 | { 80 | double index; 81 | double i; 82 | double q; 83 | double amp; 84 | double state; 85 | double det_count; 86 | double noise_average; 87 | double num_pdws; 88 | }; 89 | typedef std::vector LogItemList; 90 | 91 | // 92 | // Find pulses in IQ data 93 | // 94 | void detectPulses(PDW_Packet* result, 95 | SampleChunk* samples, 96 | uint64_t base_sample_count, 97 | double fs, 98 | double threshold_over_mean_db) 99 | { 100 | static bool log = true; 101 | LogItemList log_data; 102 | 103 | // For each sample 104 | double max_iq_db = -INFINITY; 105 | double sum_iq_db = 0.0; 106 | std::vector sample_power(std::size_t(samples->size())); 107 | 108 | for (std::size_t ii = 0; ii < samples->size(); ii++) 109 | { 110 | // Calc power 111 | double iq_db = 20.0f * std::log10( std::abs( (*samples).at(ii) ) ); 112 | 113 | // Store amplitude for pulse detection 114 | sample_power[ii] = iq_db; 115 | 116 | if ( ! isinf(iq_db) ) 117 | { 118 | // Calc max power 119 | max_iq_db = std::max(max_iq_db, iq_db); 120 | 121 | // Store sum for mean 122 | sum_iq_db += iq_db; 123 | } 124 | } 125 | 126 | double mean_iq_db = sum_iq_db / double(samples->size()); 127 | 128 | // Rising edge = M of N samples over threshold 129 | // Falling edge = M of N samples below threshold 130 | double pulse_declaration_threshold = (mean_iq_db + threshold_over_mean_db); 131 | const double M_s = 0.000012; 132 | const double N_s = 0.000015; 133 | const uint32_t M = uint32_t(M_s * fs); 134 | const uint32_t N = uint32_t(N_s * fs); 135 | 136 | // Loop sample power 137 | uint32_t det_count = 0; 138 | DetectionState state = NOISE; 139 | uint32_t toa = 0; 140 | uint32_t pw = 0; 141 | MovingAverage noise_average(sample_power.size()); 142 | log_data.reserve(sample_power.size()); 143 | 144 | for (std::size_t ii = 0; ii < sample_power.size(); ii++) 145 | { 146 | // Check for sample detection 147 | if ( sample_power[ii] > pulse_declaration_threshold ) 148 | { 149 | det_count++; 150 | } 151 | 152 | // Decrement det count more than N samples ago 153 | if ( ii >= N && sample_power[ii-N] > pulse_declaration_threshold ) 154 | { 155 | det_count--; 156 | } 157 | 158 | // Detector state machine 159 | if ( state == NOISE ) 160 | { 161 | // Rising edge 162 | if ( det_count > M ) 163 | { 164 | state = DETECTION; 165 | toa = ii - det_count; 166 | } 167 | else 168 | { 169 | noise_average.addItem(sample_power[ii]); 170 | } 171 | } 172 | 173 | if ( state == DETECTION ) 174 | { 175 | // Falling edge 176 | if ( det_count < M ) 177 | { 178 | state = NOISE; 179 | 180 | // Find the real pulse edges, rather than the M of N boundaries 181 | for (std::size_t jj = toa; jj < ii; jj++) 182 | { 183 | if ( sample_power[jj] > pulse_declaration_threshold ) 184 | { 185 | toa = jj; 186 | break; 187 | } 188 | } 189 | for (std::size_t jj = (ii-(2*M)); jj < ii; jj++) 190 | { 191 | if ( sample_power[jj] < pulse_declaration_threshold ) 192 | { 193 | pw = jj - toa; 194 | break; 195 | } 196 | } 197 | 198 | PDW* new_pdw = result->add_pdws(); 199 | new_pdw->set_toa_s( double(base_sample_count + toa) / fs ); 200 | new_pdw->set_pw_s( double(pw) / fs ); 201 | 202 | // Compute center frequency 203 | FFT_Packet result; 204 | SampleChunk buffer; 205 | for (std::size_t jj = 0; jj < pw; jj++) 206 | buffer.push_back((*samples).at(toa + jj)); 207 | 208 | fft( &result, buffer, 512, fs, 0.0 ); 209 | double max_val = -9999999.0; 210 | std::size_t max_ind = 0; 211 | for (int jj = 0; jj < result.fft_size(); jj++) 212 | { 213 | if ( result.fft(jj) > max_val ) 214 | { 215 | max_val = result.fft(jj); 216 | max_ind = jj; 217 | } 218 | } 219 | new_pdw->set_freq_offset_hz( result.freq_bins_hz(max_ind) ); 220 | 221 | // Measure average power 222 | MovingAverage pulse_average(pw); 223 | for (std::size_t jj = 0; jj < pw; jj++) 224 | { 225 | new_pdw->add_signal((*samples).at(toa + jj).real()); 226 | new_pdw->add_signal((*samples).at(toa + jj).imag()); 227 | pulse_average.addItem(sample_power[toa + jj]); 228 | } 229 | 230 | new_pdw->set_mean_amp_db(pulse_average.getAverage()); 231 | new_pdw->set_peak_amp_db(pulse_average.getMax()); 232 | new_pdw->set_noise_amp_db(noise_average.getAverage()); 233 | } 234 | } 235 | LogItem it; 236 | it.index = ii; 237 | it.i = (*samples).at(ii).real(); 238 | it.q = (*samples).at(ii).imag(); 239 | it.amp = sample_power[ii]; 240 | it.state = state; 241 | it.det_count = det_count; 242 | it.noise_average = noise_average.getAverage(); 243 | it.num_pdws = result->pdws_size(); 244 | 245 | log_data.push_back(it); 246 | } 247 | 248 | 249 | if (log && result->pdws_size() > 0 && result->pdws()[0].signal().size() > 20) 250 | { 251 | std::ofstream log_stream; 252 | log_stream.open("detector.log", std::ofstream::out); 253 | log_stream << 254 | "index" << "," << 255 | "i" << "," << 256 | "q" << "," << 257 | "amp" << "," << 258 | "state" << "," << 259 | "det_count" << "," << 260 | "noise_average" << "," << 261 | "num_pdws" << std::endl; 262 | 263 | for (auto it : log_data) 264 | { 265 | log_stream << 266 | it.index << "," << 267 | it.i << "," << 268 | it.q << "," << 269 | it.amp << "," << 270 | it.state << "," << 271 | it.det_count << "," << 272 | it.noise_average << "," << 273 | it.num_pdws << std::endl; 274 | } 275 | 276 | std::cout << "PULSE LOG!" << std::endl; 277 | 278 | log = false; 279 | } 280 | 281 | // if (result->pdws_size() > 0) 282 | // std::cout << "PULSES: " << result->pdws_size() << " MEAN: " << mean_iq_db << " PEAK: " << max_iq_db << std::endl; 283 | } 284 | } 285 | 286 | #endif /* PULSE_DETECTOR_H */ 287 | 288 | -------------------------------------------------------------------------------- /src/websock/js/packet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview 3 | * @enhanceable 4 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 5 | * field starts with 'MSG_' and isn't a translatable message. 6 | * @public 7 | */ 8 | // GENERATED CODE -- DO NOT EDIT! 9 | 10 | goog.provide('proto.Packet'); 11 | 12 | goog.require('jspb.BinaryReader'); 13 | goog.require('jspb.BinaryWriter'); 14 | goog.require('jspb.Message'); 15 | goog.require('proto.FFT_Packet'); 16 | goog.require('proto.IQ_Packet'); 17 | goog.require('proto.PDW_Packet'); 18 | goog.require('proto.Packet_Header'); 19 | 20 | 21 | /** 22 | * Generated by JsPbCodeGenerator. 23 | * @param {Array=} opt_data Optional initial data array, typically from a 24 | * server response, or constructed directly in Javascript. The array is used 25 | * in place and becomes part of the constructed object. It is not cloned. 26 | * If no data is provided, the constructed object will be empty, but still 27 | * valid. 28 | * @extends {jspb.Message} 29 | * @constructor 30 | */ 31 | proto.Packet = function(opt_data) { 32 | jspb.Message.initialize(this, opt_data, 0, -1, null, proto.Packet.oneofGroups_); 33 | }; 34 | goog.inherits(proto.Packet, jspb.Message); 35 | if (goog.DEBUG && !COMPILED) { 36 | proto.Packet.displayName = 'proto.Packet'; 37 | } 38 | /** 39 | * Oneof group definitions for this message. Each group defines the field 40 | * numbers belonging to that group. When of these fields' value is set, all 41 | * other fields in the group are cleared. During deserialization, if multiple 42 | * fields are encountered for a group, only the last value seen will be kept. 43 | * @private {!Array>} 44 | * @const 45 | */ 46 | proto.Packet.oneofGroups_ = [[2,3,4]]; 47 | 48 | /** 49 | * @enum {number} 50 | */ 51 | proto.Packet.BodyCase = { 52 | BODY_NOT_SET: 0, 53 | IQ_PACKET: 2, 54 | FFT_PACKET: 3, 55 | PDW_PACKET: 4 56 | }; 57 | 58 | /** 59 | * @return {proto.Packet.BodyCase} 60 | */ 61 | proto.Packet.prototype.getBodyCase = function() { 62 | return /** @type {proto.Packet.BodyCase} */(jspb.Message.computeOneofCase(this, proto.Packet.oneofGroups_[0])); 63 | }; 64 | 65 | 66 | 67 | if (jspb.Message.GENERATE_TO_OBJECT) { 68 | /** 69 | * Creates an object representation of this proto suitable for use in Soy templates. 70 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 71 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 72 | * For the list of reserved names please see: 73 | * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. 74 | * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 75 | * for transitional soy proto support: http://goto/soy-param-migration 76 | * @return {!Object} 77 | */ 78 | proto.Packet.prototype.toObject = function(opt_includeInstance) { 79 | return proto.Packet.toObject(opt_includeInstance, this); 80 | }; 81 | 82 | 83 | /** 84 | * Static version of the {@see toObject} method. 85 | * @param {boolean|undefined} includeInstance Whether to include the JSPB 86 | * instance for transitional soy proto support: 87 | * http://goto/soy-param-migration 88 | * @param {!proto.Packet} msg The msg instance to transform. 89 | * @return {!Object} 90 | * @suppress {unusedLocalVariables} f is only used for nested messages 91 | */ 92 | proto.Packet.toObject = function(includeInstance, msg) { 93 | var f, obj = { 94 | header: (f = msg.getHeader()) && proto.Packet_Header.toObject(includeInstance, f), 95 | iqPacket: (f = msg.getIqPacket()) && proto.IQ_Packet.toObject(includeInstance, f), 96 | fftPacket: (f = msg.getFftPacket()) && proto.FFT_Packet.toObject(includeInstance, f), 97 | pdwPacket: (f = msg.getPdwPacket()) && proto.PDW_Packet.toObject(includeInstance, f) 98 | }; 99 | 100 | if (includeInstance) { 101 | obj.$jspbMessageInstance = msg; 102 | } 103 | return obj; 104 | }; 105 | } 106 | 107 | 108 | /** 109 | * Deserializes binary data (in protobuf wire format). 110 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 111 | * @return {!proto.Packet} 112 | */ 113 | proto.Packet.deserializeBinary = function(bytes) { 114 | var reader = new jspb.BinaryReader(bytes); 115 | var msg = new proto.Packet; 116 | return proto.Packet.deserializeBinaryFromReader(msg, reader); 117 | }; 118 | 119 | 120 | /** 121 | * Deserializes binary data (in protobuf wire format) from the 122 | * given reader into the given message object. 123 | * @param {!proto.Packet} msg The message object to deserialize into. 124 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 125 | * @return {!proto.Packet} 126 | */ 127 | proto.Packet.deserializeBinaryFromReader = function(msg, reader) { 128 | while (reader.nextField()) { 129 | if (reader.isEndGroup()) { 130 | break; 131 | } 132 | var field = reader.getFieldNumber(); 133 | switch (field) { 134 | case 1: 135 | var value = new proto.Packet_Header; 136 | reader.readMessage(value,proto.Packet_Header.deserializeBinaryFromReader); 137 | msg.setHeader(value); 138 | break; 139 | case 2: 140 | var value = new proto.IQ_Packet; 141 | reader.readMessage(value,proto.IQ_Packet.deserializeBinaryFromReader); 142 | msg.setIqPacket(value); 143 | break; 144 | case 3: 145 | var value = new proto.FFT_Packet; 146 | reader.readMessage(value,proto.FFT_Packet.deserializeBinaryFromReader); 147 | msg.setFftPacket(value); 148 | break; 149 | case 4: 150 | var value = new proto.PDW_Packet; 151 | reader.readMessage(value,proto.PDW_Packet.deserializeBinaryFromReader); 152 | msg.setPdwPacket(value); 153 | break; 154 | default: 155 | reader.skipField(); 156 | break; 157 | } 158 | } 159 | return msg; 160 | }; 161 | 162 | 163 | /** 164 | * Serializes the message to binary data (in protobuf wire format). 165 | * @return {!Uint8Array} 166 | */ 167 | proto.Packet.prototype.serializeBinary = function() { 168 | var writer = new jspb.BinaryWriter(); 169 | proto.Packet.serializeBinaryToWriter(this, writer); 170 | return writer.getResultBuffer(); 171 | }; 172 | 173 | 174 | /** 175 | * Serializes the given message to binary data (in protobuf wire 176 | * format), writing to the given BinaryWriter. 177 | * @param {!proto.Packet} message 178 | * @param {!jspb.BinaryWriter} writer 179 | * @suppress {unusedLocalVariables} f is only used for nested messages 180 | */ 181 | proto.Packet.serializeBinaryToWriter = function(message, writer) { 182 | var f = undefined; 183 | f = message.getHeader(); 184 | if (f != null) { 185 | writer.writeMessage( 186 | 1, 187 | f, 188 | proto.Packet_Header.serializeBinaryToWriter 189 | ); 190 | } 191 | f = message.getIqPacket(); 192 | if (f != null) { 193 | writer.writeMessage( 194 | 2, 195 | f, 196 | proto.IQ_Packet.serializeBinaryToWriter 197 | ); 198 | } 199 | f = message.getFftPacket(); 200 | if (f != null) { 201 | writer.writeMessage( 202 | 3, 203 | f, 204 | proto.FFT_Packet.serializeBinaryToWriter 205 | ); 206 | } 207 | f = message.getPdwPacket(); 208 | if (f != null) { 209 | writer.writeMessage( 210 | 4, 211 | f, 212 | proto.PDW_Packet.serializeBinaryToWriter 213 | ); 214 | } 215 | }; 216 | 217 | 218 | /** 219 | * required Packet_Header header = 1; 220 | * @return {!proto.Packet_Header} 221 | */ 222 | proto.Packet.prototype.getHeader = function() { 223 | return /** @type{!proto.Packet_Header} */ ( 224 | jspb.Message.getWrapperField(this, proto.Packet_Header, 1, 1)); 225 | }; 226 | 227 | 228 | /** @param {!proto.Packet_Header} value */ 229 | proto.Packet.prototype.setHeader = function(value) { 230 | jspb.Message.setWrapperField(this, 1, value); 231 | }; 232 | 233 | 234 | proto.Packet.prototype.clearHeader = function() { 235 | jspb.Message.setField(this, 1, undefined); 236 | }; 237 | 238 | 239 | /** 240 | * Returns whether this field is set. 241 | * @return {!boolean} 242 | */ 243 | proto.Packet.prototype.hasHeader = function() { 244 | return jspb.Message.getField(this, 1) != null; 245 | }; 246 | 247 | 248 | /** 249 | * optional IQ_Packet iq_packet = 2; 250 | * @return {?proto.IQ_Packet} 251 | */ 252 | proto.Packet.prototype.getIqPacket = function() { 253 | return /** @type{?proto.IQ_Packet} */ ( 254 | jspb.Message.getWrapperField(this, proto.IQ_Packet, 2)); 255 | }; 256 | 257 | 258 | /** @param {?proto.IQ_Packet|undefined} value */ 259 | proto.Packet.prototype.setIqPacket = function(value) { 260 | jspb.Message.setOneofWrapperField(this, 2, proto.Packet.oneofGroups_[0], value); 261 | }; 262 | 263 | 264 | proto.Packet.prototype.clearIqPacket = function() { 265 | this.setIqPacket(undefined); 266 | }; 267 | 268 | 269 | /** 270 | * Returns whether this field is set. 271 | * @return {!boolean} 272 | */ 273 | proto.Packet.prototype.hasIqPacket = function() { 274 | return jspb.Message.getField(this, 2) != null; 275 | }; 276 | 277 | 278 | /** 279 | * optional FFT_Packet fft_packet = 3; 280 | * @return {?proto.FFT_Packet} 281 | */ 282 | proto.Packet.prototype.getFftPacket = function() { 283 | return /** @type{?proto.FFT_Packet} */ ( 284 | jspb.Message.getWrapperField(this, proto.FFT_Packet, 3)); 285 | }; 286 | 287 | 288 | /** @param {?proto.FFT_Packet|undefined} value */ 289 | proto.Packet.prototype.setFftPacket = function(value) { 290 | jspb.Message.setOneofWrapperField(this, 3, proto.Packet.oneofGroups_[0], value); 291 | }; 292 | 293 | 294 | proto.Packet.prototype.clearFftPacket = function() { 295 | this.setFftPacket(undefined); 296 | }; 297 | 298 | 299 | /** 300 | * Returns whether this field is set. 301 | * @return {!boolean} 302 | */ 303 | proto.Packet.prototype.hasFftPacket = function() { 304 | return jspb.Message.getField(this, 3) != null; 305 | }; 306 | 307 | 308 | /** 309 | * optional PDW_Packet pdw_packet = 4; 310 | * @return {?proto.PDW_Packet} 311 | */ 312 | proto.Packet.prototype.getPdwPacket = function() { 313 | return /** @type{?proto.PDW_Packet} */ ( 314 | jspb.Message.getWrapperField(this, proto.PDW_Packet, 4)); 315 | }; 316 | 317 | 318 | /** @param {?proto.PDW_Packet|undefined} value */ 319 | proto.Packet.prototype.setPdwPacket = function(value) { 320 | jspb.Message.setOneofWrapperField(this, 4, proto.Packet.oneofGroups_[0], value); 321 | }; 322 | 323 | 324 | proto.Packet.prototype.clearPdwPacket = function() { 325 | this.setPdwPacket(undefined); 326 | }; 327 | 328 | 329 | /** 330 | * Returns whether this field is set. 331 | * @return {!boolean} 332 | */ 333 | proto.Packet.prototype.hasPdwPacket = function() { 334 | return jspb.Message.getField(this, 4) != null; 335 | }; 336 | 337 | 338 | -------------------------------------------------------------------------------- /src/utilities/zhelpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __ZHELPERS_HPP_INCLUDED__ 2 | #define __ZHELPERS_HPP_INCLUDED__ 3 | 4 | // Include a bunch of headers that we will need in the examples 5 | 6 | #include // https://github.com/zeromq/cppzmq 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include // random() RAND_MAX 16 | #include 17 | #include 18 | #include 19 | #if (!defined(WIN32)) 20 | # include 21 | # include 22 | #endif 23 | 24 | // Bring Windows MSVC up to C99 scratch 25 | #if (defined (WIN32)) 26 | typedef unsigned long ulong; 27 | typedef unsigned int uint; 28 | typedef __int64 int64_t; 29 | #endif 30 | 31 | // On some version of Windows, POSIX subsystem is not installed by default. 32 | // So define srandom and random ourself. 33 | // 34 | #if (defined (WIN32)) 35 | # define srandom srand 36 | # define random rand 37 | #endif 38 | 39 | // Visual Studio versions below 2015 do not support sprintf properly. This is a workaround. 40 | // Taken from http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 41 | #if defined(_MSC_VER) && _MSC_VER < 1900 42 | 43 | #define snprintf c99_snprintf 44 | #define vsnprintf c99_vsnprintf 45 | 46 | inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) 47 | { 48 | int count = -1; 49 | 50 | if (size != 0) 51 | count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); 52 | if (count == -1) 53 | count = _vscprintf(format, ap); 54 | 55 | return count; 56 | } 57 | 58 | inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) 59 | { 60 | int count; 61 | va_list ap; 62 | 63 | va_start(ap, format); 64 | count = c99_vsnprintf(outBuf, size, format, ap); 65 | va_end(ap); 66 | 67 | return count; 68 | } 69 | 70 | #endif 71 | 72 | // Provide random number from 0..(num-1) 73 | #define within(num) (int) ((float)((num) * random ()) / (RAND_MAX + 1.0)) 74 | 75 | // Receive 0MQ string from socket and convert into C string 76 | // Caller must free returned string. 77 | inline static char * 78 | s_recv(void *socket, int flags = 0) { 79 | zmq_msg_t message; 80 | zmq_msg_init(&message); 81 | 82 | int rc = zmq_msg_recv(&message, socket, flags); 83 | 84 | if (rc < 0) 85 | return nullptr; // Context terminated, exit 86 | 87 | size_t size = zmq_msg_size(&message); 88 | char *string = (char*)malloc(size + 1); 89 | memcpy(string, zmq_msg_data(&message), size); 90 | zmq_msg_close(&message); 91 | string[size] = 0; 92 | return (string); 93 | } 94 | 95 | // Receive 0MQ string from socket and convert into string 96 | inline static std::string 97 | s_recv (zmq::socket_t & socket, int flags = 0) { 98 | 99 | zmq::message_t message; 100 | socket.recv(&message, flags); 101 | 102 | return std::string(static_cast(message.data()), message.size()); 103 | } 104 | 105 | inline static bool s_recv(zmq::socket_t & socket, std::string & ostring, int flags = 0) 106 | { 107 | zmq::message_t message; 108 | bool rc = socket.recv(&message, flags); 109 | 110 | if (rc) { 111 | ostring = std::string(static_cast(message.data()), message.size()); 112 | } 113 | 114 | return (rc); 115 | } 116 | 117 | // Convert C string to 0MQ string and send to socket 118 | inline static int 119 | s_send(void *socket, const char *string, int flags = 0) { 120 | int rc; 121 | zmq_msg_t message; 122 | zmq_msg_init_size(&message, strlen(string)); 123 | memcpy(zmq_msg_data(&message), string, strlen(string)); 124 | rc = zmq_msg_send(&message, socket, flags); 125 | assert(-1 != rc); 126 | zmq_msg_close(&message); 127 | return (rc); 128 | } 129 | 130 | // Convert string to 0MQ string and send to socket 131 | inline static bool 132 | s_send (zmq::socket_t & socket, const std::string & string, int flags = 0) { 133 | 134 | zmq::message_t message(string.size()); 135 | memcpy (message.data(), string.data(), string.size()); 136 | 137 | bool rc = socket.send (message, flags); 138 | return (rc); 139 | } 140 | 141 | // Sends string as 0MQ string, as multipart non-terminal 142 | inline static int 143 | s_sendmore(void *socket, char *string) { 144 | int rc; 145 | zmq_msg_t message; 146 | zmq_msg_init_size(&message, strlen(string)); 147 | memcpy(zmq_msg_data(&message), string, strlen(string)); 148 | //rc = zmq_send(socket, string, strlen(string), ZMQ_SNDMORE); 149 | rc = zmq_msg_send(&message, socket, ZMQ_SNDMORE); 150 | assert(-1 != rc); 151 | zmq_msg_close(&message); 152 | return (rc); 153 | } 154 | 155 | // Sends string as 0MQ string, as multipart non-terminal 156 | inline static bool 157 | s_sendmore (zmq::socket_t & socket, const std::string & string) { 158 | 159 | zmq::message_t message(string.size()); 160 | memcpy (message.data(), string.data(), string.size()); 161 | 162 | bool rc = socket.send (message, ZMQ_SNDMORE); 163 | return (rc); 164 | } 165 | 166 | // Receives all message parts from socket, prints neatly 167 | // 168 | inline static void 169 | s_dump (zmq::socket_t & socket) 170 | { 171 | std::cout << "----------------------------------------" << std::endl; 172 | 173 | while (1) { 174 | // Process all parts of the message 175 | zmq::message_t message; 176 | socket.recv(&message); 177 | 178 | // Dump the message as text or binary 179 | size_t size = message.size(); 180 | std::string data(static_cast(message.data()), size); 181 | 182 | bool is_text = true; 183 | 184 | size_t char_nbr; 185 | unsigned char byte; 186 | for (char_nbr = 0; char_nbr < size; char_nbr++) { 187 | byte = data [char_nbr]; 188 | if (byte < 32 || byte > 127) 189 | is_text = false; 190 | } 191 | std::cout << "[" << std::setfill('0') << std::setw(3) << size << "]"; 192 | for (char_nbr = 0; char_nbr < size; char_nbr++) { 193 | if (is_text) 194 | std::cout << (char)data [char_nbr]; 195 | else 196 | std::cout << std::setfill('0') << std::setw(2) 197 | << std::hex << (unsigned int) data [char_nbr]; 198 | } 199 | std::cout << std::endl; 200 | 201 | int more = 0; // Multipart detection 202 | size_t more_size = sizeof (more); 203 | socket.getsockopt (ZMQ_RCVMORE, &more, &more_size); 204 | if (!more) 205 | break; // Last message part 206 | } 207 | } 208 | 209 | #if (!defined (WIN32)) 210 | // Set simple random printable identity on socket 211 | // Caution: 212 | // DO NOT call this version of s_set_id from multiple threads on MS Windows 213 | // since s_set_id will call rand() on MS Windows. rand(), however, is not 214 | // reentrant or thread-safe. See issue #521. 215 | inline std::string 216 | s_set_id (zmq::socket_t & socket) 217 | { 218 | std::stringstream ss; 219 | ss << std::hex << std::uppercase 220 | << std::setw(4) << std::setfill('0') << within (0x10000) << "-" 221 | << std::setw(4) << std::setfill('0') << within (0x10000); 222 | socket.setsockopt(ZMQ_IDENTITY, ss.str().c_str(), ss.str().length()); 223 | return ss.str(); 224 | } 225 | #else 226 | // Fix #521 227 | inline std::string 228 | s_set_id(zmq::socket_t & socket, intptr_t id) 229 | { 230 | std::stringstream ss; 231 | ss << std::hex << std::uppercase 232 | << std::setw(4) << std::setfill('0') << id; 233 | socket.setsockopt(ZMQ_IDENTITY, ss.str().c_str(), ss.str().length()); 234 | return ss.str(); 235 | } 236 | #endif 237 | 238 | // Report 0MQ version number 239 | // 240 | inline static void 241 | s_version (void) 242 | { 243 | int major, minor, patch; 244 | zmq_version (&major, &minor, &patch); 245 | std::cout << "Current 0MQ version is " << major << "." << minor << "." << patch << std::endl; 246 | } 247 | 248 | inline static void 249 | s_version_assert (int want_major, int want_minor) 250 | { 251 | int major, minor, patch; 252 | zmq_version (&major, &minor, &patch); 253 | if (major < want_major 254 | || (major == want_major && minor < want_minor)) { 255 | std::cout << "Current 0MQ version is " << major << "." << minor << std::endl; 256 | std::cout << "Application needs at least " << want_major << "." << want_minor 257 | << " - cannot continue" << std::endl; 258 | exit (EXIT_FAILURE); 259 | } 260 | } 261 | 262 | // Return current system clock as milliseconds 263 | inline static int64_t 264 | s_clock (void) 265 | { 266 | #if (defined (WIN32)) 267 | FILETIME fileTime; 268 | GetSystemTimeAsFileTime(&fileTime); 269 | unsigned __int64 largeInt = fileTime.dwHighDateTime; 270 | largeInt <<= 32; 271 | largeInt |= fileTime.dwLowDateTime; 272 | largeInt /= 10000; // FILETIME is in units of 100 nanoseconds 273 | return (int64_t)largeInt; 274 | #else 275 | struct timeval tv; 276 | gettimeofday (&tv, NULL); 277 | return (int64_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000); 278 | #endif 279 | } 280 | 281 | // Sleep for a number of milliseconds 282 | inline static void 283 | s_sleep (int msecs) 284 | { 285 | #if (defined (WIN32)) 286 | Sleep (msecs); 287 | #else 288 | struct timespec t; 289 | t.tv_sec = msecs / 1000; 290 | t.tv_nsec = (msecs % 1000) * 1000000; 291 | nanosleep (&t, NULL); 292 | #endif 293 | } 294 | 295 | inline static void 296 | s_console (const char *format, ...) 297 | { 298 | time_t curtime = time (NULL); 299 | struct tm *loctime = localtime (&curtime); 300 | char *formatted = new char[20]; 301 | strftime (formatted, 20, "%y-%m-%d %H:%M:%S ", loctime); 302 | printf ("%s", formatted); 303 | delete[] formatted; 304 | 305 | va_list argptr; 306 | va_start (argptr, format); 307 | vprintf (format, argptr); 308 | va_end (argptr); 309 | printf ("\n"); 310 | } 311 | 312 | // --------------------------------------------------------------------- 313 | // Signal handling 314 | // 315 | // Call s_catch_signals() in your application at startup, and then exit 316 | // your main loop if s_interrupted is ever 1. Works especially well with 317 | // zmq_poll. 318 | 319 | static int s_interrupted = 0; 320 | inline static void s_signal_handler (int signal_value) 321 | { 322 | s_interrupted = 1; 323 | } 324 | 325 | inline static void s_catch_signals () 326 | { 327 | #if (!defined(WIN32)) 328 | struct sigaction action; 329 | action.sa_handler = s_signal_handler; 330 | action.sa_flags = 0; 331 | sigemptyset (&action.sa_mask); 332 | sigaction (SIGINT, &action, NULL); 333 | sigaction (SIGTERM, &action, NULL); 334 | #endif 335 | } 336 | 337 | 338 | 339 | #endif -------------------------------------------------------------------------------- /src/freq_monitor/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "packet.pb.h" 3 | #include "SDRReceiver.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct timespec program_start; 16 | 17 | bool isRunning = true; 18 | bool led_is_on = false; 19 | uint64_t fc = 0; 20 | double fs = 0.0; 21 | double thresh = 0.0; 22 | std::string host = ""; 23 | uint32_t print_count = 0; 24 | 25 | // 26 | // Returns time (seconds) since program start 27 | // 28 | double get_duration() 29 | { 30 | struct timespec now; 31 | clock_gettime( CLOCK_REALTIME, &now ); 32 | 33 | return double(now.tv_sec - program_start.tv_sec) + ( double(now.tv_nsec - program_start.tv_nsec) / 1e9 ); 34 | } 35 | 36 | // 37 | // LED ON 38 | // 39 | void blink_on( zmq::socket_t* socket ) 40 | { 41 | if (!led_is_on) 42 | { 43 | led_is_on = true; 44 | s_send( *socket, std::string("blink on") ); 45 | } 46 | } 47 | 48 | // 49 | // LED OFF 50 | // 51 | void blink_off( zmq::socket_t* socket ) 52 | { 53 | if (led_is_on) 54 | { 55 | led_is_on = false; 56 | s_send( *socket, std::string("blink off") ); 57 | } 58 | } 59 | 60 | std::vector< std::pair > monitor_ranges; 61 | 62 | // 63 | // fft 64 | // 65 | void process_data( const FFT_Packet &p, zmq::socket_t* blink_interface, SDRReceiver* receiver ) 66 | { 67 | double peak = -9999.0; 68 | double fft_sum = 0.0; 69 | // 70 | // Find the peak in the target frequency range 71 | // 72 | for (std::size_t mm = 0; mm < monitor_ranges.size(); mm++) 73 | { 74 | for (int ii = 0; ii < p.fft_size(); ii++) 75 | { 76 | if ( ( p.freq_bins_hz(ii) > monitor_ranges[mm].first ) && \ 77 | ( p.freq_bins_hz(ii) < monitor_ranges[mm].second ) && \ 78 | ( p.fft(ii) > peak ) ) 79 | peak = p.fft(ii); 80 | 81 | fft_sum += p.fft(ii); 82 | } 83 | } 84 | 85 | // threshold is some constant over the mean 86 | double fft_mean_db = (fft_sum / p.fft_size()); 87 | double threshold = fft_mean_db + thresh; 88 | double signal_to_noise = peak - threshold; 89 | 90 | // Blink the LED (and print sone info) if we have detected a signal 91 | if ( signal_to_noise > 0.0 ) 92 | blink_on( blink_interface ); 93 | else 94 | blink_off( blink_interface ); 95 | 96 | if ( signal_to_noise > 0.0 ) 97 | { 98 | if ( print_count == 0 ) 99 | std::cout << " Time Mean Thresh Peak SNR" << std::endl; 100 | std::cout << std::left << std::setw(8) << get_duration(); 101 | for (std::size_t mm = 0; mm < monitor_ranges.size(); mm++) 102 | { 103 | std::cout << " "; 104 | for (int ii = 0; ii < p.fft_size(); ii++) 105 | { 106 | if ( p.freq_bins_hz(ii) > monitor_ranges[mm].first && p.freq_bins_hz(ii) < monitor_ranges[mm].second ) 107 | { 108 | std::cout << "\e[48;5;" << std::max(232, std::min(int(232 + int(p.fft(ii)-threshold)),255)) << "m \e[0m"; 109 | } 110 | } 111 | } 112 | std::cout << " " << std::left << std::setw(8) << std::setprecision(4) << fft_mean_db 113 | << " " << std::left << std::setw(8) << std::setprecision(4) << threshold 114 | << " " << std::left << std::setw(8) << std::setprecision(4) << peak 115 | << " " << std::left << std::setw(8) << std::setprecision(4) << signal_to_noise 116 | << std::endl; 117 | print_count = ( print_count + 1 ) % 20; 118 | } 119 | } 120 | 121 | // 122 | // Parse arguments 123 | // 124 | void parse_args(int argc, char* argv[]) 125 | { 126 | for (int aa = 0; aa < argc; aa++) 127 | { 128 | std::string arg( argv[aa] ); 129 | 130 | if ( arg == "-h" || arg == "--help") 131 | { 132 | std::cout << "Usage: " << argv[0] << " -c
-s -r " << std::endl; 133 | std::cout << " -i hostname of the SDR server" << std::endl; 134 | std::cout << " -t amplitude threshold for detection reporting" << std::endl; 135 | std::cout << " -c
center frequencie to tune to" << std::endl; 136 | std::cout << " -s device sample rate to use" << std::endl; 137 | std::cout << " -r low and high are frequency (Hz) ranges to monitor (can be many -r specifications" << std::endl << std::endl; 138 | } 139 | else if ( arg == "-i" ) 140 | { 141 | if (aa + 1 >= argc) 142 | { 143 | std::cout << "Missing host!" << std::endl; 144 | exit(1); 145 | } 146 | 147 | // host argument 148 | std::istringstream ss(argv[aa+1]); 149 | if (!(ss >> host)) 150 | { 151 | std::cout << "ERROR: Invalid host argument! '" << argv[aa+1] << "'" << std::endl; 152 | exit(1); 153 | } 154 | 155 | aa += 1; 156 | } 157 | else if ( arg == "-t" ) 158 | { 159 | if (aa + 1 >= argc) 160 | { 161 | std::cout << "Missing threshold!" << std::endl; 162 | exit(1); 163 | } 164 | 165 | // threshold argument 166 | std::istringstream ss(argv[aa+1]); 167 | if (!(ss >> thresh)) 168 | { 169 | std::cout << "ERROR: Invalid threshold argument! '" << argv[aa+1] << "'" << std::endl; 170 | exit(1); 171 | } 172 | 173 | aa += 1; 174 | } 175 | else if ( arg == "-c" ) 176 | { 177 | if (aa + 1 >= argc) 178 | { 179 | std::cout << "Missing center frequency!" << std::endl; 180 | exit(1); 181 | } 182 | 183 | // fc argument 184 | std::istringstream ss(argv[aa+1]); 185 | if (!(ss >> fc)) 186 | { 187 | std::cout << "ERROR: Invalid frequency argument! '" << argv[aa+1] << "'" << std::endl; 188 | exit(1); 189 | } 190 | 191 | aa += 1; 192 | } 193 | else if ( arg == "-s" ) 194 | { 195 | if (aa + 1 >= argc) 196 | { 197 | std::cout << "Missing sample rate!" << std::endl; 198 | exit(1); 199 | } 200 | 201 | // fc argument 202 | std::istringstream ss(argv[aa+1]); 203 | if (!(ss >> fs)) 204 | { 205 | std::cout << "ERROR: Invalid sample rate argument! '" << argv[aa+1] << "'" << std::endl; 206 | exit(1); 207 | } 208 | 209 | aa += 1; 210 | } 211 | else if ( arg == "-f" ) 212 | { 213 | if (aa + 1 >= argc) 214 | { 215 | std::cout << "Missing filename!" << std::endl; 216 | exit(1); 217 | } 218 | 219 | aa += 1; 220 | 221 | std::cout << "Not implemented yet :(" << std::endl; 222 | exit(1); 223 | 224 | //TODO: parse file 225 | } 226 | else if ( arg == "-r" ) 227 | { 228 | if (aa + 2 >= argc) 229 | { 230 | std::cout << "Missing frequency argument!" << std::endl; 231 | exit(1); 232 | } 233 | 234 | // low argument 235 | std::istringstream ss_lo(argv[aa+1]); 236 | uint64_t freq_lo; 237 | if (!(ss_lo >> freq_lo)) 238 | { 239 | std::cout << "ERROR: Invalid frequency argument! '" << argv[aa+1] << "'" << std::endl; 240 | exit(1); 241 | } 242 | 243 | // high argument 244 | std::istringstream ss_hi(argv[aa+2]); 245 | uint64_t freq_hi; 246 | if (!(ss_hi >> freq_hi)) 247 | { 248 | std::cout << "ERROR: Invalid frequency argument! '" << argv[aa+2] << "'" << std::endl; 249 | exit(1); 250 | } 251 | 252 | aa += 2; 253 | 254 | monitor_ranges.push_back( std::make_pair( freq_lo, freq_hi ) ); 255 | } 256 | } 257 | 258 | if ( fc == 0 ) 259 | { 260 | std::cout << "ERROR: no center frequency specified!" << std::endl; 261 | exit(2); 262 | } 263 | 264 | if ( fs == 0.0 ) 265 | { 266 | std::cout << "ERROR: no sample rate specified!" << std::endl; 267 | exit(2); 268 | } 269 | 270 | 271 | if ( monitor_ranges.size() == 0 ) 272 | { 273 | std::cout << "ERROR: no ranges specified!" << std::endl; 274 | exit(2); 275 | } 276 | 277 | std::cout << monitor_ranges.size() << " ranges specified!" << std::endl; 278 | } 279 | 280 | // 281 | // receiver callback 282 | // 283 | void receive_callback( Packet &p, void* args ) 284 | { 285 | std::pair< zmq::socket_t*, SDRReceiver*>* p_args = (std::pair< zmq::socket_t*, SDRReceiver*>*)args; 286 | 287 | if ( p.has_fft_packet() ) 288 | { 289 | process_data( p.fft_packet(), p_args->first, p_args->second ); 290 | } 291 | else 292 | { 293 | std::cout << "WARNING! Packet has no FFT data." << std::endl; 294 | } 295 | } 296 | 297 | // 298 | // MAIN 299 | // 300 | int main(int argc, char* argv[]) 301 | { 302 | parse_args(argc, argv); 303 | 304 | clock_gettime(CLOCK_REALTIME, &program_start); 305 | 306 | // Start the blink server interface 307 | zmq::context_t blink_context(1); 308 | zmq::socket_t blink_interface(blink_context, ZMQ_PUB); 309 | blink_interface.connect("tcp://localhost:5558"); 310 | 311 | blink_on( &blink_interface ); 312 | sleep(1); 313 | blink_off( &blink_interface ); 314 | 315 | // Setup the data receiver 316 | SDRReceiver rcv; 317 | rcv.setIP(host); 318 | std::pair< zmq::socket_t*, SDRReceiver*> args = std::make_pair( &blink_interface, &rcv ); 319 | rcv.initialize( receive_callback, (void*)&args ); 320 | rcv.tune( fc ); 321 | rcv.setSampleRate( fs ); 322 | 323 | while(1) 324 | sleep(1); 325 | 326 | return 0; 327 | } 328 | 329 | 330 | 331 | 332 | 333 | --------------------------------------------------------------------------------