/* * Adalm Pluto Driver * ================== * Author: DJ0ABR * * (c) DJ0ABR * www.dj0abr.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * ========================= * FFT for Spectrum/Waterfall * ========================= * */ #include "qo100trx.h" #include void fftinit(); void calc_fft(uint8_t *data, int len); void findSignal(uint16_t *p16, int anz); void* fft_threadfunction(void* param); void init_fft() { fftinit(); pthread_t fftthread; pthread_create(&fftthread, NULL, fft_threadfunction, NULL); } void* fft_threadfunction(void* param) { int ph = 0; pthread_detach(pthread_self()); printf("entering FFT loop, *** PID:%ld ***\n",syscall(SYS_gettid)); while(keeprunning) { uint8_t data[PLUTOBUFSIZE*4]; if(read_fifo(FFTfifo, data, PLUTOBUFSIZE*4)) { if(++ph >= 2) // uncomment to reduce cpu load { ph=0; calc_fft(data, PLUTOBUFSIZE*4); } } usleep(1000); } printf("exit FFT loop\n"); pthread_exit(NULL); // self terminate this thread return NULL; } fftw_complex *din = NULL; fftw_complex *cpout = NULL; fftw_plan plan = NULL; #define FFT_RESOLUTION 25 // Hz per bin #define FFT_LENGTH (SAMPRATE / FFT_RESOLUTION) // 560kS/s / 25 = 22400 void fftinit() { int numofcpus = sysconf(_SC_NPROCESSORS_ONLN); // Get the number of logical CPUs. if(numofcpus > 1) { printf("found %d cores, running FFT in multithreading mode\n",numofcpus); fftw_init_threads(); fftw_plan_with_nthreads(numofcpus); } fftw_import_wisdom_from_filename("fftcfg"); din = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * FFT_LENGTH); cpout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * FFT_LENGTH); plan = fftw_plan_dft_1d(FFT_LENGTH, din, cpout, FFTW_FORWARD, FFTW_MEASURE); fftw_export_wisdom_to_filename("fftcfg"); printf("fft init ok\n"); } void close_fft() { if(din) fftw_free(din); din = NULL; if(cpout) fftw_free(cpout); cpout = NULL; } // convert a frequency offset (starting at ,470 and the kHz part only) to the bin index int FreqToBinIdx(int freq) { // bins: 0.. 22400 // freq: 0..560000 return freq * 224 / 5600; } void calc_fft(uint8_t *data, int len) { static int din_idx = 0; double real, imag; // values for small WF, calculate only once per loop int span = FFT_RESOLUTION*1120; // size of small waterfall in Hz (28000Hz, +/-14kHz) int start = FreqToBinIdx(RXoffsetfreq - span/2); int end = FreqToBinIdx(RXoffsetfreq + span/2); // bin position of RX signal at 0.2-2.8kHz above RXoffsetfreq int qsostart = FreqToBinIdx(RXoffsetfreq + 200); int qsoend = FreqToBinIdx(RXoffsetfreq + 2800); for(int i=0; i= FFT_LENGTH) { din_idx = 0; // if fftspeed > 0 then ignore every n fft line // this helps to run this software on slow SBCs static int fdel = 0; if(fdel++ < fftspeed) continue; fdel = 0; // the fft input buffer is full, now lets execute the fft fftw_execute(plan); // the FFT delivers FFT_LENGTH values (44800 bins) // but we only use the first half of them, which is the full range with the // requested resolution of 25 Hz per bin (22400 bins) int numbins = FFT_LENGTH; // 22400 (22400*25=560k) // the FFT generates the upper half folloed by the lower half // the tuned freq is in the center // range centerfreq(,750) - 560k/2 to +560k/2 // is ,470 ... 1,030 float bin[numbins]; int binidx = 0; // lower half for(int i=numbins/2; i 32768) printf("reduce multiplicator %f\n",bin[binidx]); binidx++; } // upper half for(int i=0; i 32768) printf("reduce multiplicator %f\n",bin[binidx]); binidx++; } // bins are in the range 0..32767 (16 bit) // ======== noise level ========== // measure the noise level on freq: ,475 to ,490 MHz, just below the lower beacon // ,470 is index 0, steps 25 Hz // ,475 is index 200 and ,490 is index 800 // so we calc the mid value if this range float gval = 0; float maxgval = 0; int gstart = FreqToBinIdx(485000 - 470000); int gend = FreqToBinIdx(495000 - 470000); // mean noise level for(int g=gstart; g maxgval) maxgval = bin[g]; // bring down a little, looks nicer gval = gval * 100 / 99; // and make the mid value over 10 values #define GMIDLEN 10 static float gmid[GMIDLEN]; static int gmididx = 0; gmid[gmididx] = gval; if(++gmididx >= GMIDLEN) gmididx = 0; float gvalmid = 0; for(int g=0; g mval) mval = bin[m]; // and make the mid value over 10 values #define MMIDLEN 10 static float mmid[MMIDLEN]; static int mmididx = 0; mmid[mmididx] = mval; if(++mmididx >= MMIDLEN) mmididx = 0; float mvalmid = 0; for(int m=0; m qmval) qmval = bin[m]; uint32_t qmvalmid_u = (uint32_t)qmval; //qmvalmid; // ======= QSO level above noise max level ======= float qdlev = qmval - (maxgval + gvalmid)/2; // and make the mean value #define DIFFMIDLEN 50 static float diffmid[DIFFMIDLEN]; static int diffmididx = 0; diffmid[diffmididx] = qdlev; if(++diffmididx >= DIFFMIDLEN) diffmididx = 0; float diffvalmid = 0; for(int m=0; m> 24; levels[2] = gvalmid_u >> 16; levels[3] = gvalmid_u >> 8; levels[4] = gvalmid_u & 0xff; levels[5] = mvalmid_u >> 24; levels[6] = mvalmid_u >> 16; levels[7] = mvalmid_u >> 8; levels[8] = mvalmid_u & 0xff; levels[9] = qmvalmid_u >> 24; levels[10] = qmvalmid_u >> 16; levels[11] = qmvalmid_u >> 8; levels[12] = qmvalmid_u & 0xff; levels[13] = gvalmax_u >> 24; levels[14] = gvalmax_u >> 16; levels[15] = gvalmax_u >> 8; levels[16] = gvalmax_u & 0xff; levels[17] = diffvalmid_u >> 24; levels[18] = diffvalmid_u >> 16; levels[19] = diffvalmid_u >> 8; levels[20] = diffvalmid_u & 0xff; levels[21] = ptt; sendUDP(gui_ip, GUI_UDPPORT, levels, 22); // ======== BIG Waterfall ======== // the big waterfall has a screen resulution of 1120 pixel // FFT_RESOLUTION=25 Hz/bin // the FFT delivers 1,12MS/s / 25 / 2 = 22400 values // so the BIG WF resolution is 22400 / 1120 = 20 static const int bigres = numbins / 1120; static const int midlen = 15; static uint32_t bigmid[midlen][FFT_LENGTH]; static int bmididx = 0; int didx = 0; for(int i=0; i max) max = fbin; } bigmid[bmididx][didx++] = (uint32_t)max; if(max > 32768) printf("16bit overflow %f\n",max); } uint8_t bigline[1 + 2 * (numbins/bigres)]; uint8_t biglineraw[1 + 2 * (numbins/bigres)]; int bigidx = 0; bigline[bigidx] = 0; // ID for big spectrum biglineraw[bigidx] = 2; // ID for big waterfall bigidx++; for(int i=0; i<(numbins/bigres); i++) { uint32_t uv = 0; for(int j=0; j= (int)sizeof(biglineraw)) printf("+++++++++++++++++++++++ %d %d\n",bmididx, numbins/bigres); biglineraw[bigidx] = bigmid[bmididx][i] >> 8; biglineraw[bigidx+1] = bigmid[bmididx][i] & 0xff; bigline[bigidx] = uv >> 8; bigline[bigidx+1] = uv & 0xff; bigidx+=2; } if(++bmididx >= midlen) bmididx = 0; // send the big fft bins to the GUI // raw values, no mid val, for waterfall sendUDP(gui_ip, GUI_UDPPORT, biglineraw, bigidx); // send the big fft bins to the GUI // mid vals for spectrum sendUDP(gui_ip, GUI_UDPPORT, bigline, bigidx); // ======== SMALL Waterfall ======== // the small waterfall is a piece of some kHz around the mid RX frequency "RXoffsetfreq" // RXoffsetfreq is in kHz above the left margin which is 750-280= 470k // the bin-index of RXoffsetfreq is: RXoffsetfreq/10 static const int small_midlen = 15; static uint32_t smallmid[small_midlen][FFT_LENGTH]; static int smididx = 0; for(int i=start; i=0 && i=0 && i= (int)sizeof(smalllineraw)) printf("*********************** %d %d %d\n",smallidx, start,end); smalllineraw[smallidx] = smallmid[smididx][i] >> 8; smalllineraw[smallidx+1] = smallmid[smididx][i] & 0xff; smallline[smallidx] = uv >> 8; smallline[smallidx+1] = uv & 0xff; } else { smalllineraw[smallidx] = 0; smalllineraw[smallidx+1] = 0; smallline[smallidx] = 0; smallline[smallidx+1] = 0; } smallidx += 2; } if(++smididx >= small_midlen) smididx = 0; findSignal((uint16_t *)smallline,smallidx/2); // send the small fft bins to the GUI sendUDP(gui_ip, GUI_UDPPORT, smalllineraw, smallidx); // send the small fft bins to the GUI sendUDP(gui_ip, GUI_UDPPORT, smallline, smallidx); } } } #define SRCHANZ 20 int srch[SRCHANZ]; int srchpos = 0; void findSignal(uint16_t *p16, int anz) { // measure frequency of a signal within +/-4kHz of the RXfrequency int st12 = (14000-6000)*1120/28000; // -4kHz int en12 = (14000+6000)*1120/28000; // +4kHz uint32_t mv16=0; // measure mean value over 28 kHz for(int i=0; i (mv16*vt/vb) && p16[i] > (mv16*vt/vb) && p16[i+1] > (mv16*vt/vb)) { filtval += i; filtanz++; } } // calculate the mid frequency of the signal if(filtanz > 0) { filtval /= filtanz; int freq = filtval * 25 - 14000; srch[srchpos] = freq; if(++srchpos >= SRCHANZ) srchpos = 0; // calculate a mean value of the found frequency int srchmid = 0; for(int i=0; i> 24; cf[2] = korrfact >> 16; cf[3] = korrfact >> 8; cf[4] = korrfact & 0xff; sendUDP(gui_ip, GUI_UDPPORT, cf, 5); } } }