Proof of concept for fft beacon finder

This commit is contained in:
Sebastian 2021-12-12 20:30:24 +01:00
parent 1201e2fdf3
commit 22fcda3276
3 changed files with 494 additions and 37 deletions

View File

@ -5,61 +5,114 @@
#include <stdio.h>
const float SAMPLINGRATE = 1000000.0;
const float FFT_LEN = 256;
const float FFT_LEN = 512;
int spectral_bin_to_fft_idx(int bin) {
if(bin == 0) {
return FFT_LEN/2;
} else if(bin > 0) {
return bin - 1;
} else {
return bin + FFT_LEN;
}
}
int main() {
FILE* output = fopen("output.csv", "w");
FILE* output_csv = fopen("output.csv", "w");
FILE* output = fopen("output.raw", "w");
FILE* input = fopen("input.raw", "r");
nco_crcf correction = nco_crcf_create(LIQUID_NCO);
nco_crcf_set_phase(correction, 0.0f);
float complex * fft_in = (float complex*) malloc(FFT_LEN * sizeof(float complex));
float complex * fft_out = (float complex*) malloc(FFT_LEN * sizeof(float complex));
// create FFT plan
fftplan fft = fft_create_plan(FFT_LEN, fft_in, fft_out, LIQUID_FFT_FORWARD, 0);
int next_fft_in = 0;
int pos = 0;
float in[2];
while(fread(in, sizeof(float), 2, input) == 2) {
fft_in[pos] = (in[1] + I * in[0]);
pos += 1;
if(pos == FFT_LEN) {
pos = 0;
fft_execute(fft);
complex float cplx_in = (in[1] + I * in[0]);
float fft_min = cabsf(fft_out[0]);
float fft_max = cabsf(fft_out[0]);
for(int i = 0; i < FFT_LEN; i++) {
float mag = cabsf(fft_out[i]);
if(mag < fft_min) {
fft_min = mag;
if(next_fft_in <= 0) {
fft_in[pos] = cplx_in;
pos += 1;
if(pos == FFT_LEN) {
pos = 0;
fft_execute(fft);
float fft_max = cabsf(fft_out[0]);
for(int i = 0; i < FFT_LEN; i++) {
float mag = cabsf(fft_out[i]);
if(mag > fft_max) {
fft_max = mag;
}
}
if(mag > fft_max) {
fft_max = mag;
//printf("Min: %f Max: %f\n", fft_min, fft_max);
for(int bin = -FFT_LEN/2; bin < FFT_LEN/2; bin++) {
int idx = spectral_bin_to_fft_idx(bin);
float mag = cabsf(fft_out[idx]);
mag = mag / fft_max;
fprintf(output_csv, "%f;", mag);
}
printf("===========\n");
float max_levels = 0;
int max_center = 0;
for(int bin = -50; bin <= 50; bin++) {
int center_idx = spectral_bin_to_fft_idx(bin);
float center_val = cabsf(fft_out[center_idx]) / fft_max;
if(center_val > 0.25) {
printf("Found peak candidate at %d\n", bin);
int left_idx = spectral_bin_to_fft_idx(bin - 127);
int right_idx = spectral_bin_to_fft_idx(bin + 127);
float left_val = cabsf(fft_out[left_idx]) / fft_max;
float right_val = cabsf(fft_out[right_idx]) / fft_max;
if(center_val + left_val + right_val > max_levels) {
max_levels = center_val + left_val + right_val;
max_center = bin;
}
}
}
if(max_levels > 0.0) {
float center_freq = max_center * SAMPLINGRATE / FFT_LEN;
printf("Found center at %f\n", center_freq);
nco_crcf_set_frequency(correction, -(2 * M_PI * center_freq) / SAMPLINGRATE);
}
fprintf(output_csv, "\n");
next_fft_in = SAMPLINGRATE / 10;
}
//printf("Min: %f Max: %f\n", fft_min, fft_max);
for(int i = FFT_LEN / 2; i < FFT_LEN; i++) {
float mag = cabsf(fft_out[i]);
mag = (mag - fft_min) / (fft_max - fft_min);
fprintf(output, "%f;", mag);
}
for(int i = 0; i < FFT_LEN / 2; i++) {
float mag = cabsf(fft_out[i]);
mag = (mag - fft_min) / (fft_max - fft_min);
fprintf(output, "%f;", mag);
}
fprintf(output, "\n");
} else {
next_fft_in--;
}
float complex x;
// increment internal phase
nco_crcf_step(correction);
// compute complex exponential
nco_crcf_cexpf(correction, &x);
float complex cplx_out = cplx_in * x;
float buffer[] = {creal(cplx_out), cimag(cplx_out)};
fwrite(buffer, sizeof(float), 2, output);
}
fft_destroy_plan(fft);
free(fft_in);
free(fft_out);
fclose(output_csv);
fclose(output);
fclose(input);

View File

@ -32,6 +32,27 @@ options:
state: enabled
blocks:
- name: offset
id: variable_qtgui_range
parameters:
comment: ''
gui_hint: ''
label: ''
min_len: '200'
orient: QtCore.Qt.Horizontal
rangeType: float
start: '-35000'
step: '1000'
stop: '35000'
value: '0'
widget: counter_slider
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [448, 12.0]
rotation: 0
state: true
- name: samp_rate
id: variable
parameters:
@ -60,7 +81,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [984, 148.0]
coordinate: [1152, 148.0]
rotation: 0
state: true
- name: analog_sig_source_x_0
@ -151,6 +172,28 @@ blocks:
coordinate: [208, 404.0]
rotation: 0
state: true
- name: analog_sig_source_x_1_0_0_0
id: analog_sig_source_x
parameters:
affinity: ''
alias: ''
amp: '1'
comment: ''
freq: offset
maxoutbuf: '0'
minoutbuf: '0'
offset: '0'
phase: '0'
samp_rate: samp_rate
type: complex
waveform: analog.GR_COS_WAVE
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [952, 476.0]
rotation: 0
state: true
- name: blocks_add_xx_0
id: blocks_add_xx
parameters:
@ -166,7 +209,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [1192, 240.0]
coordinate: [1360, 240.0]
rotation: 0
state: true
- name: blocks_add_xx_1
@ -202,7 +245,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [1320, 236.0]
coordinate: [1488, 236.0]
rotation: 0
state: true
- name: blocks_multiply_xx_0
@ -223,6 +266,24 @@ blocks:
coordinate: [1048, 256.0]
rotation: 0
state: true
- name: blocks_multiply_xx_0_0
id: blocks_multiply_xx
parameters:
affinity: ''
alias: ''
comment: ''
maxoutbuf: '0'
minoutbuf: '0'
num_inputs: '2'
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [1192, 272.0]
rotation: 0
state: true
- name: blocks_multiply_xx_1
id: blocks_multiply_xx
parameters:
@ -251,7 +312,7 @@ blocks:
maxoutbuf: '0'
minoutbuf: '0'
samp_rate: samp_rate
sensitivity: '10000'
sensitivity: '40000'
states:
bus_sink: false
bus_source: false
@ -337,7 +398,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [1320, 124.0]
coordinate: [1488, 124.0]
rotation: 0
state: true
- name: rational_resampler_xxx_0
@ -367,10 +428,12 @@ connections:
- [analog_sig_source_x_1, '0', blocks_add_xx_1, '0']
- [analog_sig_source_x_1_0, '0', blocks_add_xx_1, '1']
- [analog_sig_source_x_1_0_0, '0', blocks_multiply_xx_1, '0']
- [analog_sig_source_x_1_0_0_0, '0', blocks_multiply_xx_0_0, '1']
- [blocks_add_xx_0, '0', blocks_file_sink_0, '0']
- [blocks_add_xx_0, '0', qtgui_waterfall_sink_x_0, '0']
- [blocks_add_xx_1, '0', blocks_multiply_xx_0, '0']
- [blocks_multiply_xx_0, '0', blocks_add_xx_0, '1']
- [blocks_multiply_xx_0, '0', blocks_multiply_xx_0_0, '0']
- [blocks_multiply_xx_0_0, '0', blocks_add_xx_0, '1']
- [blocks_multiply_xx_1, '0', blocks_add_xx_1, '2']
- [blocks_vco_c_0, '0', blocks_multiply_xx_0, '1']
- [blocks_vector_source_x_0, '0', rational_resampler_xxx_0, '0']

View File

@ -0,0 +1,341 @@
options:
parameters:
author: ''
catch_exceptions: 'True'
category: '[GRC Hier Blocks]'
cmake_opt: ''
comment: ''
copyright: ''
description: ''
gen_cmake: 'On'
gen_linking: dynamic
generate_options: qt_gui
hier_block_src_path: '.:'
id: view_waterfall
max_nouts: '0'
output_language: python
placement: (0,0)
qt_qss_theme: ''
realtime_scheduling: ''
run: 'True'
run_command: '{python} -u {filename}'
run_options: prompt
sizing_mode: fixed
thread_safe_setters: ''
title: Not titled yet
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [8, 8]
rotation: 0
state: enabled
blocks:
- name: samp_rate
id: variable
parameters:
comment: ''
value: '1000000'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [184, 12]
rotation: 0
state: enabled
- name: blocks_file_source_0
id: blocks_file_source
parameters:
affinity: ''
alias: ''
begin_tag: pmt.PMT_NIL
comment: ''
file: /home/sebastian/projects/QO100-trx/prototypes/fft-beacon-finder/output.raw
length: '0'
maxoutbuf: '0'
minoutbuf: '0'
offset: '0'
repeat: 'False'
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [184, 212.0]
rotation: 0
state: true
- name: blocks_file_source_0_0
id: blocks_file_source
parameters:
affinity: ''
alias: ''
begin_tag: pmt.PMT_NIL
comment: ''
file: /home/sebastian/projects/QO100-trx/prototypes/fft-beacon-finder/input.raw
length: '0'
maxoutbuf: '0'
minoutbuf: '0'
offset: '0'
repeat: 'False'
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [176, 92.0]
rotation: 0
state: true
- name: blocks_throttle_0
id: blocks_throttle
parameters:
affinity: ''
alias: ''
comment: ''
ignoretag: 'True'
maxoutbuf: '0'
minoutbuf: '0'
samples_per_second: samp_rate
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [496, 244.0]
rotation: 0
state: true
- name: blocks_throttle_1
id: blocks_throttle
parameters:
affinity: ''
alias: ''
comment: ''
ignoretag: 'True'
maxoutbuf: '0'
minoutbuf: '0'
samples_per_second: samp_rate
type: complex
vlen: '1'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [400, 124.0]
rotation: 0
state: true
- name: qtgui_freq_sink_x_0
id: qtgui_freq_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
autoscale: 'False'
average: '1.0'
axislabels: 'True'
bw: samp_rate
color1: '"blue"'
color10: '"dark blue"'
color2: '"red"'
color3: '"green"'
color4: '"black"'
color5: '"cyan"'
color6: '"magenta"'
color7: '"yellow"'
color8: '"dark red"'
color9: '"dark green"'
comment: ''
ctrlpanel: 'False'
fc: '0'
fftsize: '4096'
freqhalf: 'True'
grid: 'True'
gui_hint: ''
label: Relative Gain
label1: ''
label10: ''''''
label2: ''''''
label3: ''''''
label4: ''''''
label5: ''''''
label6: ''''''
label7: ''''''
label8: ''''''
label9: ''''''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
norm_window: 'False'
showports: 'False'
tr_chan: '0'
tr_level: '0.0'
tr_mode: qtgui.TRIG_MODE_FREE
tr_tag: '""'
type: complex
units: dB
update_time: '0.10'
width1: '1'
width10: '1'
width2: '1'
width3: '1'
width4: '1'
width5: '1'
width6: '1'
width7: '1'
width8: '1'
width9: '1'
wintype: window.WIN_BLACKMAN_hARRIS
ymax: '10'
ymin: '-140'
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [712, 156.0]
rotation: 0
state: true
- name: qtgui_waterfall_sink_x_0
id: qtgui_waterfall_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
axislabels: 'True'
bw: samp_rate
color1: '0'
color10: '0'
color2: '0'
color3: '0'
color4: '0'
color5: '0'
color6: '0'
color7: '0'
color8: '0'
color9: '0'
comment: ''
fc: '0'
fftsize: '4096'
freqhalf: 'True'
grid: 'True'
gui_hint: ''
int_max: '10'
int_min: '-140'
label1: ''
label10: ''
label2: ''
label3: ''
label4: ''
label5: ''
label6: ''
label7: ''
label8: ''
label9: ''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
showports: 'False'
type: complex
update_time: '0.10'
wintype: window.WIN_BLACKMAN_hARRIS
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [704, 348.0]
rotation: 0
state: true
- name: qtgui_waterfall_sink_x_0_0
id: qtgui_waterfall_sink_x
parameters:
affinity: ''
alias: ''
alpha1: '1.0'
alpha10: '1.0'
alpha2: '1.0'
alpha3: '1.0'
alpha4: '1.0'
alpha5: '1.0'
alpha6: '1.0'
alpha7: '1.0'
alpha8: '1.0'
alpha9: '1.0'
axislabels: 'True'
bw: samp_rate
color1: '0'
color10: '0'
color2: '0'
color3: '0'
color4: '0'
color5: '0'
color6: '0'
color7: '0'
color8: '0'
color9: '0'
comment: ''
fc: '0'
fftsize: '4096'
freqhalf: 'True'
grid: 'True'
gui_hint: ''
int_max: '10'
int_min: '-140'
label1: ''
label10: ''
label2: ''
label3: ''
label4: ''
label5: ''
label6: ''
label7: ''
label8: ''
label9: ''
legend: 'True'
maxoutbuf: '0'
minoutbuf: '0'
name: '""'
nconnections: '1'
showports: 'False'
type: complex
update_time: '0.10'
wintype: window.WIN_BLACKMAN_hARRIS
states:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [696, 60.0]
rotation: 0
state: true
connections:
- [blocks_file_source_0, '0', blocks_throttle_0, '0']
- [blocks_file_source_0_0, '0', blocks_throttle_1, '0']
- [blocks_throttle_0, '0', qtgui_freq_sink_x_0, '0']
- [blocks_throttle_0, '0', qtgui_waterfall_sink_x_0, '0']
- [blocks_throttle_1, '0', qtgui_waterfall_sink_x_0_0, '0']
metadata:
file_format: 1