diff --git a/nanovna.h b/nanovna.h index a222960..ed8cc0c 100644 --- a/nanovna.h +++ b/nanovna.h @@ -266,6 +266,10 @@ void set_marker_time(int m, float f); void set_marker_index(int m, int16_t idx); void toggle_sweep(void); void resume_once(uint16_t c); +#ifdef TINYSA4 +void set_deviation(int d); +void set_depth(int d); +#endif void toggle_mute(void); void toggle_pulse(void); void toggle_draw_line(void); @@ -1152,6 +1156,10 @@ typedef struct setting int16_t refer; // -1 disabled uint16_t modulation_frequency; // 50...6000 +#ifdef TINYSA4 + uint16_t modulation_depth_x100; // AM (30% - 100%) multiplied by 100 + uint16_t modulation_deviation_div100; // FM (2.5kHz to 100kHz) divided by 100 +#endif int decay; // KM_DECAY < 1000000 int attack; // KM_ATTACK < 20000 diff --git a/sa_core.c b/sa_core.c index 2ecbaee..074c609 100644 --- a/sa_core.c +++ b/sa_core.c @@ -597,6 +597,8 @@ void reset_settings(int m) setting.tracking = false; setting.modulation = MO_NONE; setting.modulation_frequency = 1000; + setting.modulation_depth_x100 = 80; // %80 + setting.modulation_deviation_div100 = 30; // 3kHz setting.step_delay = 0; setting.offset_delay = 0; setting.step_delay_mode = SD_NORMAL; @@ -1033,9 +1035,21 @@ void set_modulation(int m) dirty = true; } +void set_deviation(int d) +{ + setting.modulation_deviation_div100 = d/100; + dirty = true; +} + +void set_depth(int d) +{ + setting.modulation_depth_x100 = d; + dirty = true; +} + void set_modulation_frequency(int f) { - if (50 <= f && f <= 6000) { + if (50 <= f && f <= 10000) { setting.modulation_frequency = f; dirty = true; } @@ -1293,24 +1307,31 @@ void set_attenuation(float a) // Is used both only in high/low input mode a -= BELOW_MAX_DRIVE(setting.rx_drive); a = -a; } else +#endif +#ifdef USE_INPUT_STEP_ATTENUATE + if (setting.mode == M_LOW && a > MAX_ATTENUATE) { + setting.atten_step = 1; + a = a - RECEIVE_SWITCH_ATTENUATION; + } else + if (setting.mode == M_HIGH && a > 0) { + setting.atten_step = 1; + a = a - SWITCH_ATTENUATION; + } else #endif { - if (setting.mode == M_LOW && a > MAX_ATTENUATE) { - setting.atten_step = 1; - a = a - RECEIVE_SWITCH_ATTENUATION; - } else if (setting.mode == M_HIGH && a > 0) { - setting.atten_step = 1; - a = a - SWITCH_ATTENUATION; - } else - setting.atten_step = 0; + setting.atten_step = 0; setting.auto_attenuation = false; - dirty = true; } + dirty = true; if (a<0.0) a = 0; if (a> MAX_ATTENUATE) a = MAX_ATTENUATE; - if (setting.mode == M_HIGH) // No attenuator in high mode + if (setting.mode == M_HIGH // No attenuator in high mode +#ifdef TINYSA4 + || (setting.mode == M_LOW && setting.extra_lna) // No attenuator with LNA +#endif + ) a = 0; if (setting.attenuate_x2 == a*2) return; @@ -3124,49 +3145,57 @@ int avoid_spur(freq_t f) // find if this frequency should be a static int modulation_counter = 0; -#define MODULATION_STEPS 8 -static const int am_modulation[MODULATION_STEPS] = { 5, 1, 0, 1, 5, 9, 11, 9 }; // AM modulation +#define AM_MODULATION_STEPS 8 +// AM modulation always +static const int am_modulation[AM_MODULATION_STEPS] = { 5, 1, 0, 1, 5, 10, 12, 10 }; // AM modulation + -#ifdef TINYSA3 -// -// Offset is 156.25Hz when below 600MHz and 312.5 when above. -// -#define LND 16 // Total NFM deviation is LND * 4 * 156.25 = 5kHz when below 600MHz or 600MHz - 434MHz -#define HND 8 -#define LWD 96 // Total WFM deviation is LWD * 4 * 156.25 = 30kHz when below 600MHz -#define HWD 48 -#define MODULATION_TABLES 4 -#endif #ifdef TINYSA4 +static int modulation_steps = 8; +#define MAX_MODULATION_STEPS 128 + +// Max = 10000 / 28.8 +static const int sinus[MAX_MODULATION_STEPS/4+1] = {0, 17, 34, 51, 68, 84, 101, 117, 133, 148, 164, 179, 193, 207, 220, 233, 246, 257, 268, 279, 289, 298, 306, 314, 321, 327, 332, 337, 341, 343, 346, 347, 347 }; // // Offset is 14.4Hz when below 600MHz and 28.8 when above. // -//#define LND 96 // low range near FM #define HND 36 // High range near FM #define HN2D 44 // High range near FM #define HN3D 80 // High range near FM -//#define LWD 1024 // Low range wide FM #define HWD 1300 // High range wide FM 512 #define MODULATION_TABLES 4 -#endif - -#define S1 1.5 -static const int fm_modulation[MODULATION_TABLES][MODULATION_STEPS] = +static int fm_modulation[MAX_MODULATION_STEPS+1]; +#if 0 += { -#ifdef TINYSA4 -// { 0*LND,(int)( 1.5*LND ), 2*LND, (int)(1.5*LND), 0*LND, (int)(-1.5*LND), (int)-2*LND, (int)(-1.5*LND)}, // High range, MO_NFM -// { 0*LWD,(int)( S1*LWD ), 2*LWD, (int)(S1*LWD), 0*LWD, (int)(-S1*LWD), (int)-2*LWD, (int)(-S1*LWD)}, // Low range, MO_WFM { 0*HND,(int)( 1.5*HND ), 2*HND, (int)(1.5*HND), 0*HND, (int)(-1.5*HND), (int)-2*HND, (int)(-1.5*HND)}, // High range, NFM { 0*HN2D,(int)( 1.5*HN2D ), 2*HN2D, (int)(1.5*HN2D), 0*HN2D, (int)(-1.5*HN2D), (int)-2*HN2D, (int)(-1.5*HN2D)}, // High range, NFM2 { 0*HN3D,(int)( 1.5*HN3D ), 2*HN3D, (int)(1.5*HN3D), 0*HN3D, (int)(-1.5*HN3D), (int)-2*HN3D, (int)(-1.5*HN3D)}, // High range, NFM3 { 0*HWD,(int)( 1.5*HWD ), 2*HWD, (int)(1.5*HWD), 0*HWD, (int)(-1.5*HWD), (int)-2*HWD, (int)(-1.5*HWD)}, // HIgh range, MO_WFM -#else // Avoid sign changes in NFM +}; +#endif +#endif +#ifdef TINYSA3// Avoid sign changes in NFM +#define modulation_steps 8 +#define MAX_MODULATION_STEPS 8 + +// +// Offset is 156.25Hz when below 600MHz and 312.5 when above. +// +#define LND 16 // Total NFM deviation is LND * 4 * 156.25 = 5kHz when below 600MHz or 600MHz - 434MHz +#define HND 8 +#define LWD 96 // Total WFM deviation is LWD * 4 * 156.25 = 30kHz when below 600MHz +#define HWD 48 +#define MODULATION_TABLES 4 +#define S1 1.5 +static const int fm_modulation[MODULATION_TABLES][MAX_MODULATION_STEPS] = +{ { 2*LND,(int)( (2+S1)*LND ), 4*LND, (int)((2+S1)*LND), 2*LND, (int)((2-S1)*LND), 0, (int)((2-S1)*LND)}, // Low range, MO_NFM { 0*LWD,(int)( S1*LWD ), 2*LWD, (int)(S1*LWD), 0*LWD, (int)(-S1*LWD), (int)-2*LWD, (int)(-S1*LWD)}, // Low range, MO_WFM { 2*HND,(int)( 3.5*HND ), 4*HND, (int)(3.5*HND), 2*HND, (int)(0.5*HND), 0, (int)(0.5*HND)}, // High range, MO_NFM { 0*HWD,(int)( 1.5*HWD ), 2*HWD, (int)(1.5*HWD), 0*HWD, (int)(-1.5*HWD), (int)-2*HWD, (int)(-1.5*HWD)}, // HIgh range, MO_WFM -#endif }; // narrow FM modulation avoid sign changes +#endif #undef S1 #ifdef TINYSA3 @@ -3266,7 +3295,9 @@ static float old_temp = 0.0; pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) // Measure the RSSI for one frequency, used from sweep and other measurement routines. Must do all HW setup { int modulation_delay = 0; - int modulation_index; +#ifdef TINYSA3 + int modulation_table; +#endif int modulation_count_iter = 0; #ifdef __NEW_SWITCHES__ // int direct = ((setting.mode == M_LOW && config.direct && f > DIRECT_START && f= MINIMUM_DIRECT_FREQ && f < ultra_start) ); @@ -3338,6 +3369,45 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) / #ifdef __MCU_CLOCK_SHIFT__ clock_at_48MHz(); #endif + +#ifdef TINYSA4 // Calculate FM modulation + if (setting.modulation == MO_AM) { + int sinus_index = 1; + modulation_steps = MAX_MODULATION_STEPS; // Search modulation steps that fit frequency + while ( (modulation_delay = (1000000/ modulation_steps ) / setting.modulation_frequency + config.cor_am) < 10 && modulation_steps > 4) { + sinus_index <<= 1; + modulation_steps >>= 1; + } + int offset347 = setting.modulation_depth_x100 * 347 / 100; + for (int i = 0; i < modulation_steps/4 + 1; i++) { + fm_modulation[i] = (694 - offset347 + offset347 * sinus[i*sinus_index]/347); + fm_modulation[modulation_steps/2 - i] = fm_modulation[i]; + fm_modulation[modulation_steps/2 + i] = (694 - offset347 - offset347 * sinus[i*sinus_index]/347); + fm_modulation[modulation_steps - i] = fm_modulation[modulation_steps/2 + i]; + } + for (int i=0; i < modulation_steps; i++) { + fm_modulation[i] = 10.0*logf(fm_modulation[i]*fm_modulation[i]/(694.0*694.0)); + if (fm_modulation[i] < -63) + fm_modulation[i] = -63; + } + } + if (setting.modulation == MO_WFM) { + int sinus_index = 1; + modulation_steps = MAX_MODULATION_STEPS; // Search modulation steps that fit frequency + while ( (modulation_delay = (1000000/ modulation_steps ) / setting.modulation_frequency + config.cor_wfm) < 10 && modulation_steps > 4) { + sinus_index <<= 1; + modulation_steps >>= 1; + } + for (int i = 0; i < modulation_steps/4 + 1; i++) { + fm_modulation[i] = setting.modulation_deviation_div100 * sinus[i*sinus_index]/100; + fm_modulation[modulation_steps/2 - i] = fm_modulation[i]; + fm_modulation[modulation_steps/2 + i] = -fm_modulation[i]; + fm_modulation[modulation_steps - i] = -fm_modulation[i]; + } + } +#endif + + } // if (MODE_OUTPUT(setting.mode) && setting.additional_step_delay_us < 500) // Minimum wait time to prevent LO from lockup during output frequency sweep // setting.additional_step_delay_us = 500; @@ -3550,48 +3620,57 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) / if (setting.modulation != MO_NONE && setting.modulation != MO_EXTERNAL && setting.modulation_frequency != 0) { #ifdef TINYSA3 #define MO_FREQ_COR 65000 -#else + modulation_table = 0; + #else #define MO_FREQ_COR 0 #endif - modulation_delay = ((1000000-MO_FREQ_COR)/ MODULATION_STEPS ) / setting.modulation_frequency; // 8 steps so 1MHz/8 modulation_counter = 0; - modulation_index = 0; - if (setting.modulation == MO_AM) // -14 default +// if (in_selftest) { +// modulation_steps = 8; +// modulation_delay = 0; +// } else + if (setting.modulation == MO_AM) { // -14 default + modulation_delay = (1000000/ modulation_steps ) / setting.modulation_frequency; // 8 steps so 1MHz/8 modulation_delay += config.cor_am; - else { +#ifdef TINYSA4 +// modulation_steps = 8; +#endif + } else { + modulation_delay = (1000000/ modulation_steps ) / setting.modulation_frequency; // 8 steps so 1MHz/8 #ifdef TINYSA4 switch(setting.modulation){ case MO_NFM: modulation_delay += config.cor_nfm; break; + case MO_NFM2: modulation_delay += config.cor_nfm2; break; + case MO_NFM3: modulation_delay += config.cor_nfm3; break; + case MO_WFM: modulation_delay += config.cor_wfm; break; } - modulation_index = setting.modulation - MO_NFM; +// modulation_table = setting.modulation - MO_NFM; #else // must be FM if (setting.modulation == MO_WFM) { // -17 default modulation_delay += config.cor_wfm; - modulation_index = 1; + modulation_table = 1; } else { // must be NFM modulation_delay += config.cor_nfm; // -17 default - // modulation_index = 0; // default value + // modulation_table = 0; // default value } #endif #ifdef TINYSA4 - if (false) + current_fm_modulation = (int *)fm_modulation; #else if ((setting.mode == M_GENLOW && f > ((freq_t)480000000) - DEFAULT_IF) || (setting.mode == M_GENHIGH && f > ((freq_t)480000000) ) ) -#endif - modulation_index += 2; - current_fm_modulation = (int *)fm_modulation[modulation_index]; -#ifdef TINYSA3 - f -= fm_modulation_offset[modulation_index]; // Shift output frequency + modulation_table += 2; + current_fm_modulation = (int *)fm_modulation[modulation_table]; + f -= fm_modulation_offset[modulation_table]; // Shift output frequency #endif } } @@ -3600,7 +3679,11 @@ modulation_again: // ----------------------------------------------------- apply modulation for output modes --------------------------------------- if (MODE_OUTPUT(setting.mode) && setting.modulation != MO_NONE){ if (setting.modulation == MO_AM) { // AM modulation +#ifdef TINYSA4 + int p = setting.attenuate_x2 - fm_modulation[modulation_counter]; +#else int p = setting.attenuate_x2 + am_modulation[modulation_counter]; +#endif if (p>63) p = 63; else if (p< 0) p = 0; #ifdef __PE4302__ @@ -3619,9 +3702,9 @@ modulation_again: #endif } modulation_counter++; - if (modulation_counter == MODULATION_STEPS) + if (modulation_counter >= modulation_steps) modulation_counter = 0; - if (setting.modulation != MO_EXTERNAL) { + if (setting.modulation != MO_EXTERNAL && !in_selftest) { // latter is for frequency calculation my_microsecond_delay(modulation_delay); } } @@ -4261,9 +4344,9 @@ again: // Spur redu return(0); // abort if ( i==1 && MODE_OUTPUT(setting.mode) && setting.modulation != MO_NONE && setting.modulation != MO_EXTERNAL) { // if in output mode with modulation and LO setup done // i = 1; // Everything set so skip LO setting -#define MODULATION_CYCLES_TEST 10000 - if (in_selftest && modulation_count_iter++ >= 10000) { - start_of_sweep_timestamp = sa_ST2US(chVTGetSystemTimeX() - start_of_sweep_timestamp)*MODULATION_STEPS/MODULATION_CYCLES_TEST; // uS per cycle +#define MODULATION_CYCLES_TEST 100000 + if (in_selftest && modulation_count_iter++ >= MODULATION_CYCLES_TEST) { + start_of_sweep_timestamp = sa_ST2US(chVTGetSystemTimeX() - start_of_sweep_timestamp)/MODULATION_CYCLES_TEST; // uS per cycle return 0; } goto modulation_again; // Keep repeating sweep loop till user aborts by input @@ -4559,12 +4642,15 @@ static bool sweep(bool break_on_operation) if (MODE_OUTPUT(setting.mode) && config.cor_nfm == 0) { // Calibrate the modulation frequencies at first use calibrate_modulation(MO_AM, &config.cor_am); +#ifdef TINYSA3 calibrate_modulation(MO_NFM, &config.cor_nfm); -#ifdef TINYSA4 +#endif +#if 0 calibrate_modulation(MO_NFM2, &config.cor_nfm2); calibrate_modulation(MO_NFM3, &config.cor_nfm3); #endif - calibrate_modulation(MO_WFM, &config.cor_wfm); + // calibrate_modulation(MO_WFM, &config.cor_wfm); + config.cor_wfm = -14; } if (dirty) { // Calculate new scanning solution @@ -7061,11 +7147,12 @@ void calibrate_modulation(int modulation, int8_t *correction) if (*correction == 0) { setting.modulation = modulation; setting.modulation_frequency = 7000; + dirty = true; in_selftest = true; perform(false,0, 30000000, false); perform(false,1, 30000000, false); in_selftest = false; - *correction = -(start_of_sweep_timestamp - (ONE_SECOND_TIME / setting.modulation_frequency ))/8; + *correction = -start_of_sweep_timestamp; // uses 10 microsecond delay setting.modulation = M_OFF; setting.modulation_frequency = 1000; } diff --git a/si4468.c b/si4468.c index ed32455..918e823 100644 --- a/si4468.c +++ b/si4468.c @@ -221,8 +221,8 @@ void PE4302_init(void) { static unsigned char old_attenuation = 255; bool PE4302_Write_Byte(unsigned char DATA ) { - if (old_attenuation == DATA) - return false; +// if (old_attenuation == DATA) /// Must always have same execution time +// return false; old_attenuation = DATA; #ifdef __ARW621__ DATA = DATA << 1; diff --git a/ui_sa.c b/ui_sa.c index 2e8e478..98e2223 100644 --- a/ui_sa.c +++ b/ui_sa.c @@ -416,6 +416,7 @@ enum { KM_HIGHOUTLEVEL, #ifdef TINYSA4 KM_COR_AM, KM_COR_WFM, KM_COR_NFM, + KM_DEVIATION, KM_DEPTH, KM_IF2, // #30 KM_R,KM_MOD,KM_CP, @@ -487,6 +488,8 @@ static const struct { [KM_COR_AM] = {keypads_plusmin , "COR\nAM"}, // KM_COR_AM [KM_COR_WFM] = {keypads_plusmin , "COR\nWFM"}, // KM_COR_WFM [KM_COR_NFM] = {keypads_plusmin , "COR\nNFM"}, // KM_COR_NFM +[KM_DEVIATION] = {keypads_freq , "DEVIATION"}, // KM_DEVIATION +[KM_DEPTH] = {keypads_positive , "DEPTH%"}, // KM_DEPTH [KM_IF2] = {keypads_freq , "IF2"}, // KM_IF2 [KM_R] = {keypads_plusmin , "R"}, // KM_R #30 [KM_MOD] = {keypads_positive , "MODULO"}, // KM_MOD @@ -1083,7 +1086,14 @@ static UI_FUNCTION_ADV_CALLBACK(menu_smodulation_acb){ if (setting.modulation == MO_NONE || setting.modulation == MO_EXTERNAL) plot_printf(b->text, sizeof b->text, "MOD: %s", menu_modulation_text[setting.modulation]); else { +#ifdef TINYSA4 + if (setting.modulation == MO_AM) + plot_printf(b->text, sizeof b->text, "MOD: %4dHz %s", (int)(setting.modulation_frequency), menu_modulation_text[setting.modulation]); + else + plot_printf(b->text, sizeof b->text, "MOD: %4dHz FM %4QHz DEVIATION", (int)(setting.modulation_frequency), (freq_t)(setting.modulation_deviation_div100*100)); +#else plot_printf(b->text, sizeof b->text, "MOD: %4dHz %s", (int)(setting.modulation_frequency), menu_modulation_text[setting.modulation]); +#endif } return; } @@ -2588,12 +2598,16 @@ static const menuitem_t menu_modulation[] = { { MT_FORM | MT_TITLE, 0, "MODULATION",NULL}, { MT_FORM | MT_ADV_CALLBACK, MO_NONE, MT_CUSTOM_LABEL, menu_modulation_acb}, { MT_FORM | MT_ADV_CALLBACK | MT_LOW, MO_AM, MT_CUSTOM_LABEL, menu_modulation_acb}, - { MT_FORM | MT_ADV_CALLBACK, MO_NFM, MT_CUSTOM_LABEL, menu_modulation_acb}, #ifdef TINYSA4 - { MT_FORM | MT_ADV_CALLBACK, MO_NFM2, MT_CUSTOM_LABEL, menu_modulation_acb}, - { MT_FORM | MT_ADV_CALLBACK, MO_NFM3, MT_CUSTOM_LABEL, menu_modulation_acb}, -#endif + { MT_FORM | MT_KEYPAD, KM_DEPTH, "DEPTH: %s%%", "0..100"}, + { MT_FORM | MT_ADV_CALLBACK, MO_WFM, "FM", menu_modulation_acb}, + { MT_FORM | MT_KEYPAD, KM_DEVIATION, "DEVIATION: %s", "1kHz..300kHz"}, +// { MT_FORM | MT_ADV_CALLBACK, MO_NFM2, MT_CUSTOM_LABEL, menu_modulation_acb}, +// { MT_FORM | MT_ADV_CALLBACK, MO_NFM3, MT_CUSTOM_LABEL, menu_modulation_acb}, +#else + { MT_FORM | MT_ADV_CALLBACK, MO_NFM, MT_CUSTOM_LABEL, menu_modulation_acb}, { MT_FORM | MT_ADV_CALLBACK, MO_WFM, MT_CUSTOM_LABEL, menu_modulation_acb}, +#endif #ifndef TINYSA4 { MT_FORM | MT_ADV_CALLBACK | MT_LOW, MO_EXTERNAL,MT_CUSTOM_LABEL, menu_modulation_acb}, #endif @@ -3528,6 +3542,16 @@ static void fetch_numeric_target(uint8_t mode) plot_printf(uistat.text, sizeof uistat.text, "%7.0fHz", uistat.value); } break; +#ifdef TINYSA4 + case KM_DEVIATION: + uistat.freq_value = setting.modulation_deviation_div100 * 100; + plot_printf(uistat.text, sizeof uistat.text, "%.3QHz", uistat.freq_value); + break; + case KM_DEPTH: + uistat.value = setting.modulation_depth_x100; + plot_printf(uistat.text, sizeof uistat.text, "%3d", (int)uistat.value); + break; +#endif case KM_VAR: uistat.freq_value = setting.frequency_var; if ( setting.frequency_var) @@ -3739,6 +3763,12 @@ set_numeric_value(void) config_save(); dirty = true; break; + case KM_DEVIATION: + set_deviation((int)uistat.freq_value); + break; + case KM_DEPTH: + set_depth((int)uistat.value); + break; #endif case KM_VAR: setting.frequency_var = uistat.freq_value;