From a4714086718b86399a00ae4607cfd64ca69f27e3 Mon Sep 17 00:00:00 2001 From: erikkaashoek Date: Sun, 16 May 2021 13:27:24 +0200 Subject: [PATCH] Experiment deconv --- nanovna.h | 1 + sa_core.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++---- ui.c | 8 +- ui_sa.c | 12 ++- 4 files changed, 240 insertions(+), 22 deletions(-) diff --git a/nanovna.h b/nanovna.h index 6b3ea05..213b880 100644 --- a/nanovna.h +++ b/nanovna.h @@ -88,6 +88,7 @@ #define DB_PER_DEGREE_ABOVE 0.069 #define CENTER_TEMPERATURE 34.0 #define __WAIT_CTS_WHILE_SLEEPING__ +//#define __FFT_DECONV__ #else #endif diff --git a/sa_core.c b/sa_core.c index 5f79b10..5e08246 100644 --- a/sa_core.c +++ b/sa_core.c @@ -27,6 +27,13 @@ #pragma GCC optimize ("Os") #endif +#ifdef __FFT_DECONV__ +void FFT(float *real, float *imag, int length, bool inverse); +float *real = (float *) &spi_buffer[0]; +float *imag = (float *) &spi_buffer[512]; +float *real2 = (float *) &spi_buffer[1024]; +float *imag2 = (float *) &spi_buffer[1536]; +#endif //#define __DEBUG_AGC__ If set the AGC value will be shown in the stored trace and FAST_SWEEP rmmode will be disabled #ifdef __DEBUG_AGC__ @@ -454,7 +461,6 @@ static setting_t saved_setting; void set_measurement(int m) { - setting.measurement = m; #ifdef __LINEARITY__ if (m == M_LINEARITY) { for (int j = 0; j < setting._sweep_points; j++) @@ -464,6 +470,14 @@ void set_measurement(int m) setting.auto_attenuation = false; } #endif +#ifdef __FFT_DECONV__ + if (m == M_DECONV && sweep_points == 256) { + set_storage(); + set_reflevel(-20); + } else + return; +#endif + setting.measurement = m; dirty = true; } void set_lo_drive(int d) @@ -3818,10 +3832,99 @@ static bool sweep(bool break_on_operation) } #endif -#ifdef TINYSA4 - int d_half_width = 0; +#ifdef __FFT_DECONV__ + int d_width = 0; + float d_scale = 0.0; + float d_offset = 0.0; + int d_start = 0; if (setting.average == AV_DECONV && setting.frequency_step != 0) { - d_half_width = (frequencies[sweep_points-1] - frequencies[0]) / (actual_rbw_x10 * 100); + d_width = (sweep_points * (actual_rbw_x10 * 250) / (frequencies[sweep_points-1] - frequencies[0])); + d_start = sweep_points/2 - d_width/2; + d_offset = stored_t[d_start]; + for (int i=0; i stored_t[d_start + i]) + d_offset = stored_t[d_start + i]; +// d_offset -= 1; // To avoid divide by zero + for (int i=0; i temp_t[i]) + m = temp_t[i]; + real[i] = 0; + imag[i] = 0; + real2[0] = 0; + imag2[i] = 0; + actual_t[i] = -150; + } + for (int i=0;i0; i--) { - actual_t[i+d_half_width] = actual_t[i]; + +#ifdef __NOFFT_DECONV__ + + for (int i = sweep_points - 1 - d_width; i>0; i--) { + actual_t[i+d_width/2] = actual_t[i] + temp_t[0]; } - } + for (int i = 0; i < d_width/2+2; i++) { + actual_t[i] = temp_t[0]; + actual_t[sweep_points - 1 - i] = temp_t[0]; + } +#endif + } @@ -5647,6 +5763,101 @@ quit: } #endif +#ifdef TINYSA4 + +#define PI 3.1415926535897932384626433832795 + + +// Fast Fourier Transform. length must be exactly 2^n. +// inverse = true computes InverseFFT +// inverse = false computes FFT. +// Overwrites the real and imaginary arrays in-place + +void FFT(float *real, float *imag, int length, bool inverse) +{ + + float wreal, wpreal, wimag, wpimag, theta; + float tempreal, tempimag, tempwreal, direction; + + int Addr, Position, Mask, BitRevAddr, PairAddr; + int m, k; + + + direction = -1.0; // direction of rotating phasor for FFT + + if(inverse) + direction = 1.0; // direction of rotating phasor for IFFT + + // bit-reverse the addresses of both the real and imaginary arrays + // real[0..length-1] and imag[0..length-1] are the paired complex numbers + + for (Addr=0; Addr> 1; + Mask = Addr; + while (Mask) + { + if(Mask & 1) + BitRevAddr += Position; + Mask >>= 1; + Position >>= 1; + } + + if (BitRevAddr > Addr) // Swap + { + float s; + s = real[BitRevAddr]; // real part + real[BitRevAddr] = real[Addr]; + real[Addr] = s; + s = imag[BitRevAddr]; // imaginary part + imag[BitRevAddr] = imag[Addr]; + imag[Addr] = s; + } + } + + // FFT, IFFT Kernel + + for (k=1; k < length; k <<= 1) + { + theta = direction * PI / (float)k; + wpimag = sinf(theta); + wpreal = cosf(theta); + wreal = 1.0; + wimag = 0.0; + + for (m=0; m < k; m++) + { + for (Addr = m; Addr < length; Addr += (k*2)) + { + PairAddr = Addr + k; + + tempreal = wreal * real[PairAddr] - wimag * imag[PairAddr]; + tempimag = wreal * imag[PairAddr] + wimag * real[PairAddr]; + real[PairAddr] = real[Addr] - tempreal; + imag[PairAddr] = imag[Addr] - tempimag; + real[Addr] += tempreal; + imag[Addr] += tempimag; + } + tempwreal = wreal; + wreal = wreal * wpreal - wimag * wpimag; + wimag = wimag * wpreal + tempwreal * wpimag; + } + } + + if(inverse) // Normalize the IFFT coefficients + for(int i=0; i=0){ @@ -1459,7 +1460,6 @@ draw_menu_buttons(const menuitem_t *menu, int only) } else { int button_width = MENU_BUTTON_WIDTH; int button_start = LCD_WIDTH - MENU_BUTTON_WIDTH; - int button_height = menu_button_height; draw_button(button_start, y, button_width, button_height, &button); uint16_t text_offs = button_start + 7; if (button.icon >=0){ @@ -1474,7 +1474,7 @@ draw_menu_buttons(const menuitem_t *menu, int only) ili9341_drawstring(button.text, text_offs, y+(button_height-linesFONT_GET_HEIGHT)/2); #endif } - y += menu_button_height; + y += button_height; } // Cleanup other buttons (less flicker) // Erase empty buttons diff --git a/ui_sa.c b/ui_sa.c index 0415529..27b4206 100644 --- a/ui_sa.c +++ b/ui_sa.c @@ -1129,6 +1129,9 @@ static UI_FUNCTION_ADV_CALLBACK(menu_debug_freq_acb) } #endif + +const char * const averageText[] = { "OFF", "MIN", "MAX", "MAXD", " A 4", "A 16","QUASI", "DECONV"}; + static UI_FUNCTION_ADV_CALLBACK(menu_measure_acb) { (void)item; @@ -1333,6 +1336,8 @@ static UI_FUNCTION_ADV_CALLBACK(menu_measure_acb) set_sweep_frequency(ST_SPAN, uistat.value*3); // set_measurement(M_CP); break; +#endif +#ifdef __FFT_DECONV__ case M_DECONV: set_average(AV_DECONV); break; @@ -1912,7 +1917,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_enter_marker_acb) } #ifdef TINYSA4 -static const uint16_t points_setting[] = {51, 101, 201, 290, 450}; +static const uint16_t points_setting[] = {51, 101, 201, 256, 290, 450}; #else static const uint16_t points_setting[] = {51, 101, 145, 290}; #endif @@ -2345,6 +2350,7 @@ static const menuitem_t menu_sweep_points[] = { { MT_ADV_CALLBACK, 3, "%3d point", menu_points_acb }, #ifdef TINYSA4 { MT_ADV_CALLBACK, 4, "%3d point", menu_points_acb }, + { MT_ADV_CALLBACK, 5, "%3d point", menu_points_acb }, #endif { MT_CANCEL, 0, S_LARROW" BACK", NULL }, { MT_NONE, 0, NULL, NULL } // sentinel @@ -2357,6 +2363,7 @@ static const menuitem_t menu_sweep_points_form[] = { { MT_FORM | MT_ADV_CALLBACK, 3, "%3d point", menu_points_acb }, #ifdef TINYSA4 { MT_FORM | MT_ADV_CALLBACK, 4, "%3d point", menu_points_acb }, + { MT_FORM | MT_ADV_CALLBACK, 5, "%3d point", menu_points_acb }, #endif { MT_FORM | MT_CANCEL, 0, S_LARROW" BACK", NULL }, { MT_NONE, 0, NULL, NULL } // sentinel @@ -2551,7 +2558,7 @@ static const menuitem_t menu_measure2[] = { #ifdef __LINEARITY__ { MT_ADV_CALLBACK | MT_LOW, M_LINEARITY, "LINEAR", menu_measure_acb}, #endif -#ifdef TINYSA4 +#ifdef __FFT_DECONV__ { MT_ADV_CALLBACK, M_DECONV, "DECONV", menu_measure_acb}, #endif { MT_CANCEL, 0, S_LARROW" BACK", NULL }, @@ -3141,7 +3148,6 @@ menu_move_top(void) // -------------------------- CAL STATUS --------------------------------------------- -const char * const averageText[] = { "OFF", "MIN", "MAX", "MAXD", " A 4", "A 16","QUASI"}; const char * const dBText[] = { "1dB/", "2dB/", "5dB/", "10dB/", "20dB/"}; const int refMHz[] = { 30, 15, 10, 4, 3, 2, 1 };