diff --git a/nanovna.h b/nanovna.h index 03b06f0..46a7c5a 100644 --- a/nanovna.h +++ b/nanovna.h @@ -118,6 +118,7 @@ #define __NOISE_FIGURE__ #define __VBW__ #define __SWEEP_RESTART__ +#define __BURST__ // Burst modulation: TIM8 on PB9 keys the OOK of the Si4468 (GPIO1, normally CTS) // #define DIRECT_CORRECTION // Not enough space for config in one flash page. #define DB_PER_DEGREE_BELOW 0.056 #define DB_PER_DEGREE_ABOVE 0.069 @@ -349,7 +350,11 @@ enum { #ifdef TINYSA4 MO_NFM2, MO_NFM3, #endif - MO_WFM, MO_EXTERNAL, MO_MAX + MO_WFM, MO_EXTERNAL, +#ifdef __BURST__ + MO_BURST, +#endif + MO_MAX }; #define MODE_OUTPUT(x) ((x) == M_GENLOW || (x) == M_GENHIGH ) @@ -507,6 +512,16 @@ void toggle_tracking_output(void); extern int32_t frequencyExtra; void set_modulation(int); void set_modulation_frequency(float); +#ifdef __BURST__ +#define BURST_MIN_TIME 1e-6 // shortest ON and OFF time [s] +#define BURST_MAX_PERIOD 89.0 // TIM8: 65536 * 65536 ticks at 48 MHz +#define BURST_DEFAULT_PERIOD 1e-3 +#define BURST_DEFAULT_ON_TIME 100e-6 +void set_burst_period(float); +void set_burst_on_time(float); +void set_burst_count(int); +void burst_trigger(void); +#endif int search_maximum(int m, freq_t center, int span); //extern int setting.modulation; void set_measurement(int); @@ -1281,6 +1296,9 @@ typedef struct setting bool pulse; // bool bool stored[TRACES_MAX]; // enum bool normalized[TRACES_MAX]; // enum +#ifdef __BURST__ + float burst_period; // seconds, fills an alignment hole so old presets stay valid +#endif #ifdef __BANDS__ band_t bands[BANDS_MAX]; #endif @@ -1351,6 +1369,9 @@ typedef struct setting #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 +#ifdef __BURST__ + uint16_t burst_count; // 0 = continuous, fills an alignment hole #endif int decay; // KM_DECAY < 1000000 int attack; // KM_ATTACK < 20000 @@ -1372,6 +1393,9 @@ typedef struct setting float unit_scale; float normalize_level; // Level to set normalize to, zero if not doing anything +#ifdef __BURST__ + float burst_on_time; // seconds, fills an alignment hole +#endif freq_t frequency_step; freq_t frequency0; @@ -1490,6 +1514,9 @@ typedef char assert_config[sizeof(config_t)> SAVE_CONFIG_SIZE ? -1 : 1]; #endif typedef char assert_setting[sizeof(setting_t)> SAVE_PROP_CONFIG_SIZE ? -1 : 1]; // Check setting size +#ifdef __BURST__ +typedef char assert_burst_setting[sizeof(setting_t) == 1584 ? 1 : -1]; // The burst fields fill alignment holes: presets saved before them stay valid +#endif // Should include all save slots #define SAVE_CONFIG_AREA_SIZE (SAVE_CONFIG_SIZE + SAVEAREA_MAX * SAVE_PROP_CONFIG_SIZE) @@ -1993,6 +2020,11 @@ extern bool ADF4351_frequency_changed; extern bool SI4463_frequency_changed; extern bool SI4463_offset_changed; extern int SI4463_is_in_tx_mode(void); +#ifdef __BURST__ +extern void SI4463_burst_mode(int on); +extern void SI4463_burst_start(float period, float on_time, uint16_t count); +extern void SI4463_burst_stop(void); +#endif extern int16_t SI4463_noise_correction_x10; void switch_SI4463_RSSI_correction(bool); extern int old_R; diff --git a/sa_cmd.c b/sa_cmd.c index c09c070..56a2eab 100644 --- a/sa_cmd.c +++ b/sa_cmd.c @@ -84,17 +84,25 @@ VNA_SHELL_FUNCTION(cmd_mode) VNA_SHELL_FUNCTION(cmd_modulation ) { -#ifdef TINYSA4 +#ifdef __BURST__ + static const char cmd_mod[] = "off|am|fm|freq|depth|deviation|burst|period|ontime|count|trigger"; +#elif defined(TINYSA4) static const char cmd_mod[] = "off|am|fm|freq|depth|deviation"; #else static const char cmd_mod[] = "off|am|nfm|wfm|extern|freq"; #endif if (argc < 1) { usage: +#ifdef __BURST__ + usage_printf("modulation %s [value]\r\n", cmd_mod); +#else usage_printf("modulation %s 100..6000\r\n", cmd_mod); +#endif return; } -#ifdef TINYSA4 +#ifdef __BURST__ + static const int cmd_mod_val[] = { MO_NONE, MO_AM, MO_WFM, -1, -1, -1, MO_BURST, -1, -1, -1, -1}; +#elif defined(TINYSA4) static const int cmd_mod_val[] = { MO_NONE, MO_AM, MO_WFM, -1, -1, -1}; #else static const int cmd_mod_val[] = { MO_NONE, MO_AM, MO_NFM, MO_WFM, MO_EXTERNAL, -1}; @@ -104,6 +112,10 @@ VNA_SHELL_FUNCTION(cmd_modulation ) goto usage; if (cmd_mod_val[m] >=0) set_modulation(cmd_mod_val[m]); +#ifdef __BURST__ + else if (m == 10) + burst_trigger(); +#endif else { if (argc != 2) goto usage; @@ -113,8 +125,16 @@ VNA_SHELL_FUNCTION(cmd_modulation ) case 3: set_modulation_frequency(a); break; case 4: set_depth(a); break; case 5: set_deviation(a); break; - +#ifdef __BURST__ + case 7: set_burst_period(my_atof(argv[1])); break; // seconds, m/u suffix allowed + case 8: set_burst_on_time(my_atof(argv[1])); break; + case 9: set_burst_count(a); break; +#endif } +#ifdef __BURST__ + if (m >= 7 && MODE_OUTPUT(setting.mode) && !msc_disk_mode) + draw_menu(); // burst settings do not set dirty: update the MOD button +#endif #else set_modulation_frequency(a); #endif diff --git a/sa_core.c b/sa_core.c index f022447..4d3a8f6 100644 --- a/sa_core.c +++ b/sa_core.c @@ -343,6 +343,10 @@ void set_output_path(freq_t f, float level) signal_path = PATH_OFF; set_path: +#ifdef __BURST__ + // Before the TX (re)start: burst only where the Si4468 makes the output, not from the ADF directly + SI4463_burst_mode(setting.modulation == MO_BURST && (signal_path == PATH_LOW || signal_path == PATH_DIRECT || signal_path == PATH_ULTRA)); +#endif switch(signal_path) { case PATH_OFF: if (SI4463_is_in_tx_mode()) @@ -686,6 +690,11 @@ void reset_settings(int m) #ifdef TINYSA4 setting.modulation_depth_x100 = 80; // %80 setting.modulation_deviation_div100 = 30; // 3kHz +#endif +#ifdef __BURST__ + setting.burst_period = BURST_DEFAULT_PERIOD; + setting.burst_on_time = BURST_DEFAULT_ON_TIME; + setting.burst_count = 0; // continuous #endif setting.step_delay = 0; setting.offset_delay = 0; @@ -1129,9 +1138,65 @@ void toggle_below_IF(void) void set_modulation(int m) { setting.modulation = m; +#ifdef __BURST__ + if (m == MO_BURST && setting.burst_period == 0) { // preset saved before burst existed + setting.burst_period = BURST_DEFAULT_PERIOD; + setting.burst_on_time = BURST_DEFAULT_ON_TIME; + } +#endif dirty = true; } +#ifdef __BURST__ +enum { BURST_CHANGED = 1, BURST_TRIGGER }; +static volatile uint8_t burst_request; // handled by the modulation loop in perform(), no RF re-setup + +void set_burst_period(float p) +{ + if (p < 2 * BURST_MIN_TIME) + p = 2 * BURST_MIN_TIME; + if (p > BURST_MAX_PERIOD) + p = BURST_MAX_PERIOD; + setting.burst_period = p; + set_burst_on_time(setting.burst_on_time); // shorten the ON time when it no longer fits +} + +void set_burst_on_time(float t) +{ + if (t > setting.burst_period - BURST_MIN_TIME) + t = setting.burst_period - BURST_MIN_TIME; + if (t < BURST_MIN_TIME) + t = BURST_MIN_TIME; + setting.burst_on_time = t; + burst_request = BURST_CHANGED; +} + +void set_burst_count(int c) +{ + if (c < 0) + c = 0; + if (c > 65535) + c = 65535; + setting.burst_count = c; + burst_request = BURST_CHANGED; +} + +void burst_trigger(void) +{ + burst_request = BURST_TRIGGER; +} + +static void burst_start(void) // continuous, or COUNT bursts after a trigger +{ + uint8_t r = burst_request; + burst_request = 0; + if (setting.burst_count == 0 || r == BURST_TRIGGER) + SI4463_burst_start(setting.burst_period, setting.burst_on_time, setting.burst_count); + else + SI4463_burst_stop(); // COUNT bursts wait for a trigger +} +#endif + #ifdef TINYSA4 void set_deviation(int d) { @@ -4015,6 +4080,12 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) / int *current_fm_modulation = 0; if (MODE_OUTPUT(setting.mode)) { +#ifdef __BURST__ + if (setting.modulation == MO_BURST) { + if (i == 1) // LO's are set: TIM8 keys the Si4468 from here, the PLL's are not touched + burst_start(); + } else +#endif if (setting.modulation != MO_NONE && setting.modulation != MO_EXTERNAL && setting.modulation_frequency != 0) { modulation_counter = 0; cycle_counter = 0; @@ -4038,6 +4109,13 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) / } } modulation_again: +#ifdef __BURST__ + if (MODE_OUTPUT(setting.mode) && setting.modulation == MO_BURST) { + if (i == 1 && burst_request) // new burst settings or a trigger: reload TIM8 only + burst_start(); + chThdSleepMilliseconds(1); + } else +#endif // ----------------------------------------------------- apply modulation for output modes --------------------------------------- if (MODE_OUTPUT(setting.mode) && setting.modulation != MO_NONE && setting.modulation != MO_EXTERNAL){ if (setting.modulation == MO_AM) { // AM modulation @@ -4755,8 +4833,12 @@ again: // Spur redu start_of_sweep_timestamp = chVTGetSystemTimeX(); // initialize start sweep time if (MODE_OUTPUT(setting.mode)) { // No substepping and no RSSI in output mode - if (break_on_operation && operation_requested) // break subscanning if requested + if (break_on_operation && operation_requested) { // break subscanning if requested +#ifdef __BURST__ + SI4463_burst_stop(); // carrier off while the input is handled +#endif 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 diff --git a/si4468.c b/si4468.c index fe71385..8a8335c 100644 --- a/si4468.c +++ b/si4468.c @@ -1202,6 +1202,23 @@ static uint8_t SI4463_channel = 0; static uint8_t SI4463_in_tx_mode = false; //int SI4463_R = 5; static int SI4463_output_level = 0x20; +#ifdef __BURST__ +static uint8_t SI4463_burst = false; // GPIO1 is the OOK input keyed by TIM8 on PB9, so CTS is polled over SPI + +static uint8_t SI4463_read_CTS_SPI(void) +{ + set_SPI_mode(SPI_MODE_SI); + while (SPI_RX_IS_NOT_EMPTY(SI4432_SPI)) + (void)SPI_READ_8BIT(SI4432_SPI); // a lingering byte would be taken for the CTS + SI_CS_LOW; + SPI_WRITE_8BIT(SI4432_SPI, SI446X_CMD_READ_CMD_BUFF); + SPI_WRITE_8BIT(SI4432_SPI, 0xFF); + while (SPI_IS_BUSY(SI4432_SPI)); + uint8_t cts = SPI_READ_16BIT(SI4432_SPI) >> 8; // drop SI446X_CMD_READ_CMD_BUFF + SI_CS_HIGH; + return cts; +} +#endif static si446x_state_t SI4463_get_state(void); static int SI4463_set_state(si446x_state_t); @@ -1222,6 +1239,15 @@ extern volatile int sleep; int SI4463_wait_CTS(void) { int t=0; +#ifdef __BURST__ + if (SI4463_burst) { // CTS over SPI: GPIO1 is the burst input + while (SI4463_read_CTS_SPI() != 0xFF) { + if (++t >= SI4463_CTS_TIMEOUT/16) + return -1; + } + return 0; + } +#endif while (!SI4463_READ_CTS) { t++; if (t >=SI4463_CTS_TIMEOUT) @@ -1370,14 +1396,30 @@ int SI4463_do_api(void* data, uint8_t len, void* out, uint8_t outLen) while (SPI_RX_IS_NOT_EMPTY(SI4432_SPI)) (void)SPI_READ_8BIT(SI4432_SPI); // Remove lingering bytes from SPI RX buffer +#ifdef __BURST__ + int t = 0; + if (!SI4463_burst) // While bursting CTS is checked in the response read itself +#endif if (SI4463_WAIT_CTS) return -1; // Wait for CTS +#ifdef __BURST__ +read_again: +#endif SI_CS_LOW; #if 1 SPI_WRITE_8BIT(SI4432_SPI, SI446X_CMD_READ_CMD_BUFF); SPI_WRITE_8BIT(SI4432_SPI, 0xFF); while (SPI_IS_BUSY(SI4432_SPI)); +#ifdef __BURST__ + if ((SPI_READ_16BIT(SI4432_SPI) >> 8) != 0xFF) { // drop SI446X_CMD_READ_CMD_BUFF, the response follows only a CTS of 0xFF + SI_CS_HIGH; + if (++t >= SI4463_CTS_TIMEOUT/16) + return -1; + goto read_again; + } +#else SPI_READ_16BIT(SI4432_SPI); // drop SI446X_CMD_READ_CMD_BUFF and CTS 0xFF +#endif #else shiftOut( SI446X_CMD_READ_CMD_BUFF ); shiftIn(); // Should always be 0xFF @@ -1508,6 +1550,73 @@ void SI4463_set_gpio(int i, int s) SI4463_refresh_gpio(); } +#ifdef __BURST__ +// +// Burst: TIM8_CH3 on PB9 (AF10) drives GPIO1, the TX direct mode OOK input, while the Si4468 stays in TX. +// GPIO1 is the CTS output to PB9 otherwise, so PB9 only drives while GPIO1 is an input. +// +void SI4463_burst_stop(void) +{ + TIM8->CCMR2 = TIM_CCMR2_OC3M_2; // force inactive: carrier off + TIM8->CR1 = 0; +} + +static void SI4463_burst_release_pin(void) +{ + SI4463_burst_stop(); + palSetLineMode(LINE_RX_CTS, PAL_MODE_INPUT_PULLUP); // stop driving before GPIO1 is the CTS output again + gpio_state[1] = SI446X_GPIO_MODE_CTS; +} + +void SI4463_burst_mode(int on) +{ + if (on == SI4463_burst) + return; + if (on) { + palSetLineMode(LINE_RX_CTS, PAL_MODE_INPUT_PULLDOWN); // keeps the carrier off once GPIO1 is an input + gpio_state[1] = SI446X_GPIO_MODE_INPUT; + SI4463_refresh_gpio(); + SI4463_burst = true; // from here CTS is polled over SPI + SI4463_wait_CTS(); + rccEnableTIM8(false); + SI4463_burst_stop(); + palSetLineMode(LINE_RX_CTS, PAL_MODE_ALTERNATE(10) | PAL_STM32_OSPEED_HIGHEST); + } else { + SI4463_burst_release_pin(); + SI4463_refresh_gpio(); // still polls CTS over SPI + SI4463_burst = false; + } +} + +// count 0: continuous, else count bursts. Each period is off first, then on. +void SI4463_burst_start(float period, float on_time, uint16_t count) +{ + if (!SI4463_burst) + return; + uint32_t ticks = period * STM32_TIM8CLK; + uint32_t psc = ticks >> 16; // 16 bit period + uint32_t arr = ticks / (psc + 1); + uint32_t on = on_time * STM32_TIM8CLK / (psc + 1) + 0.5; + if (arr < 2) + arr = 2; + if (on < 1) + on = 1; + if (on >= arr) + on = arr - 1; + TIM8->CR1 = 0; + TIM8->PSC = psc; + TIM8->ARR = arr - 1; + TIM8->CCR3 = arr - on; + TIM8->RCR = count ? count - 1 : 0; + TIM8->CCMR2 = TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC3PE; // PWM mode 2: on from CCR3, off when stopped at 0 + TIM8->CCER = TIM_CCER_CC3E; + TIM8->BDTR = TIM_BDTR_MOE; + TIM8->EGR = TIM_EGR_UG; // load prescaler, period and repetition counter + TIM8->SR = 0; + TIM8->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN | (count ? TIM_CR1_OPM : 0); // one pulse mode stops after count periods +} +#endif + #if 0 static void SI4463_clear_FIFO(void) { @@ -1558,10 +1667,20 @@ void SI4463_start_tx(uint8_t CHANNEL) { uint8_t data[] = { SI446X_CMD_SET_PROPERTY, 0x20, 0x01, 0x00, +#ifdef __BURST__ + SI4463_burst ? 0xA9 : 0x00, // OOK in asynchronous direct mode from GPIO1 (burst) or CW mode +#else 0x00, // CW mode +#endif }; SI4463_do_api(data, sizeof(data), NULL, 0); } +#ifdef __BURST__ + if (SI4463_burst) { // OOK also shifts by MODEM_FREQ_DEV (300kHz from the RX config): keep the carrier frequency + static const uint8_t no_deviation[] = { SI446X_CMD_SET_PROPERTY, 0x20, 0x03, 0x0a, 0x00, 0x00, 0x00 }; + SI4463_do_api((void *)no_deviation, sizeof(no_deviation), NULL, 0); + } +#endif #endif #if 1 { @@ -2612,6 +2731,12 @@ freq_t SI4463_set_freq(freq_t freq) void SI4463_init_rx(void) { +#ifdef __BURST__ + if (SI4463_burst) { // the reset makes GPIO1 the CTS output again + SI4463_burst_release_pin(); + SI4463_burst = false; + } +#endif reset: SI_SDN_LOW; my_microsecond_delay(10000); diff --git a/ui.c b/ui.c index bd1c030..bf60a74 100644 --- a/ui.c +++ b/ui.c @@ -1485,6 +1485,9 @@ enum { KM_FILENAME, #endif KM_TRIGGER_GRID, +#ifdef __BURST__ + KM_BURST_PERIOD, KM_BURST_ON, KM_BURST_COUNT, +#endif KM_NONE // always at enum end }; @@ -1577,6 +1580,11 @@ static const struct { [KM_FILENAME] = {keypads_text , "NAME"}, // filename #endif [KM_TRIGGER_GRID] = {keypads_time , "INTERVAL"}, // KM_CODE +#ifdef __BURST__ +[KM_BURST_PERIOD] = {keypads_plusmin_unit, "BURST\nPERIOD"}, +[KM_BURST_ON] = {keypads_plusmin_unit, "BURST\nON TIME"}, +[KM_BURST_COUNT] = {keypads_positive , "BURST\nCOUNT"}, +#endif }; #if 0 // Not used @@ -1714,6 +1722,11 @@ static UI_FUNCTION_ADV_CALLBACK(menu_restart_acb){ (void)item; (void)data; if(b){ +#ifdef __BURST__ + if (setting.modulation == MO_BURST) + plot_printf(b->text, sizeof b->text, "START BURST"); + else +#endif if (setting.sweep) { if (current_index >= 0) { float current_level = setting.level + ((float)current_index)* setting.level_sweep / (float)sweep_points; @@ -1725,6 +1738,12 @@ static UI_FUNCTION_ADV_CALLBACK(menu_restart_acb){ plot_printf(b->text, sizeof b->text, "START SWEEP"); return; } +#ifdef __BURST__ + if (setting.modulation == MO_BURST) { + burst_trigger(); // COUNT bursts, or restart the continuous bursts + return; + } +#endif setting.sweep = !setting.sweep; dirty = true; } @@ -2551,7 +2570,11 @@ const char * const menu_modulation_text[MO_MAX]= #else "FM 3kHz", #endif - "FM 75kHz", "External"}; + "FM 75kHz", "External", +#ifdef __BURST__ + "Burst", +#endif +}; static UI_FUNCTION_ADV_CALLBACK(menu_modulation_acb) { @@ -2588,9 +2611,20 @@ static UI_FUNCTION_ADV_CALLBACK(menu_audible_harmonic) (void)item; (void)data; if (b){ +#ifdef __BURST__ + if (setting.modulation == MO_BURST) { // blank button, CTCSS has no meaning for a burst + b->text[0] = 0; + return; + } +#endif + plot_printf(b->text, sizeof b->text, "CTCSS + 1 kHz"); b->icon = (config.wfm_1khz_harmonic && setting.modulation_frequency < MAX_CTCSS_FREQ) ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; return; } +#ifdef __BURST__ + if (setting.modulation == MO_BURST) + return; +#endif if (setting.modulation_frequency >= MAX_CTCSS_FREQ) return; config.wfm_1khz_harmonic = config.wfm_1khz_harmonic ? 0 : 100; // Toggle between off and 100% @@ -2607,6 +2641,15 @@ static UI_FUNCTION_ADV_CALLBACK(menu_smodulation_acb){ plot_printf(b->text, sizeof b->text, "MOD: %s", menu_modulation_text[setting.modulation]); else { #ifdef TINYSA4 +#ifdef __BURST__ + if (setting.modulation == MO_BURST) { + if (!setting.mixer_output && get_sweep_frequency(ST_STOP) >= MAX_LOW_OUTPUT_FREQ) // output straight from the ADF, the Si4468 can not key it + plot_printf(b->text, sizeof b->text, "MOD: BURST N/A"); + else + plot_printf(b->text, sizeof b->text, setting.burst_count ? "MOD: BURST %.3Fs/%.3Fs x%d" : "MOD: BURST %.3Fs/%.3Fs", + setting.burst_on_time, setting.burst_period, setting.burst_count); + } else +#endif if (setting.modulation == MO_AM) plot_printf(b->text, sizeof b->text, "MOD: %8.4FHz AM %d%%", setting.modulation_frequency, setting.modulation_depth_x100); else @@ -4715,10 +4758,13 @@ static const menuitem_t menu_modulation[] = { { MT_FORM | MT_ADV_CALLBACK | MT_LOW, MO_AM, "AM", menu_modulation_acb}, #ifdef TINYSA4 { MT_FORM | MT_ADV_CALLBACK, MO_WFM, "FM", menu_modulation_acb}, +#ifdef __BURST__ + { MT_FORM | MT_ADV_CALLBACK, MO_BURST, "BURST", menu_modulation_acb}, +#endif { MT_FORM | MT_KEYPAD, KM_MODULATION, "FREQ: %s", "1Hz..3.5kHz"}, { MT_FORM | MT_KEYPAD, KM_DEPTH, "AM DEPTH: %s%%", "0..100"}, { MT_FORM | MT_KEYPAD, KM_DEVIATION, "FM DEVIATION: %s", "1kHz..300kHz"}, - { MT_FORM | MT_ADV_CALLBACK, 0, "CTCSS + 1 kHz", menu_audible_harmonic}, + { MT_FORM | MT_ADV_CALLBACK, 0, MT_CUSTOM_LABEL, menu_audible_harmonic}, // { 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 @@ -4731,6 +4777,25 @@ static const menuitem_t menu_modulation[] = { { MT_FORM | MT_NONE, 0, NULL, menu_back} // next-> menu_back }; +#ifdef __BURST__ +// In burst mode the FREQ, AM DEPTH and FM DEVIATION buttons of menu_modulation set the burst +static const menuitem_t menu_burst_keypads[] = { + { MT_FORM | MT_KEYPAD, KM_BURST_PERIOD, "PERIOD: %s", "2us..89s"}, + { MT_FORM | MT_KEYPAD, KM_BURST_ON, "ON TIME: %s", "1us..PERIOD"}, + { MT_FORM | MT_KEYPAD, KM_BURST_COUNT, "COUNT: %s", "0=continuous..65535"}, +}; + +static const menuitem_t *burst_item(const menuitem_t *m) // m is a keypad item +{ + if (setting.modulation == MO_BURST) { + if (m->data == KM_MODULATION) return &menu_burst_keypads[0]; + if (m->data == KM_DEPTH) return &menu_burst_keypads[1]; + if (m->data == KM_DEVIATION) return &menu_burst_keypads[2]; + } + return m; +} +#endif + static const menuitem_t menu_sweep[] = { { MT_FORM | MT_KEYPAD, KM_SPAN, "SPAN: %s", VARIANT("0..350MHz", range_text)}, { MT_FORM | MT_KEYPAD | MT_LOW, KM_LEVELSWEEP,"LEVEL CHANGE: %s", VARIANT("-70..70","-90..90")}, @@ -6035,6 +6100,23 @@ static void fetch_numeric_target(uint8_t mode) uistat.value = setting.modulation_depth_x100; plot_printf(uistat.text, sizeof uistat.text, "%3d", (int)uistat.value); break; +#endif +#ifdef __BURST__ + case KM_BURST_PERIOD: + uistat.value = setting.burst_period; + plot_printf(uistat.text, sizeof uistat.text, "%.3Fs", uistat.value); + break; + case KM_BURST_ON: + uistat.value = setting.burst_on_time; + plot_printf(uistat.text, sizeof uistat.text, "%.3Fs", uistat.value); + break; + case KM_BURST_COUNT: + uistat.value = setting.burst_count; + if (setting.burst_count) + plot_printf(uistat.text, sizeof uistat.text, "%d", setting.burst_count); + else + plot_printf(uistat.text, sizeof uistat.text, "CONTINUOUS"); + break; #endif case KM_VAR: uistat.freq_value = setting.frequency_var; @@ -6331,6 +6413,17 @@ set_numeric_value(void) case KM_DEPTH: set_depth((int)uistat.value); break; +#ifdef __BURST__ + case KM_BURST_PERIOD: + set_burst_period(uistat.value); + break; + case KM_BURST_ON: + set_burst_on_time(uistat.value); + break; + case KM_BURST_COUNT: + set_burst_count((int)uistat.value); + break; +#endif case KM_PRESET_NAME: plot_printf(setting.preset_name, sizeof setting.preset_name, "%s", kp_buf); break; @@ -7012,6 +7105,9 @@ menu_invoke(int item) break; case MT_KEYPAD: +#ifdef __BURST__ + menu = burst_item(menu); +#endif uistat.auto_center_marker = false; if (current_menu_is_form()) { redraw_frame(); // Remove form numbers @@ -7368,8 +7464,12 @@ draw_menu_buttons(const menuitem_t *menu, uint32_t mask) text = (char *)m->label; // Only keypad retrieves value if (MT_MASK(m->type) == MT_KEYPAD) { - fetch_numeric_target(m->data); - plot_printf(button.text, sizeof button.text, m->label, uistat.text); + const menuitem_t *k = m; +#ifdef __BURST__ + k = burst_item(k); +#endif + fetch_numeric_target(k->data); + plot_printf(button.text, sizeof button.text, k->label, uistat.text); text = button.text; }