Merge branch 'burst-modulation'

main
erikkaashoek 4 days ago
commit f693ac6ef8

@ -1,3 +1,7 @@
V1.4305 2026-11-10 For all tinySA Ultra models
- Signal generator: BURST modulation switches the output on and off: ON TIME of every PERIOD, continuously or COUNT bursts. With MODULATION > BURST selected the FREQ, AM DEPTH and FM DEVIATION buttons set PERIOD (2us..89s), ON TIME and COUNT (0 = continuous) and START SWEEP becomes START BURST, which starts the COUNT bursts or restarts the continuous bursts. The frequency is not changed during a burst. Burst works up to 1.13 GHz and above it with OUTPUT "Highest accuracy", not with "Cleanest signal" (MOD: BURST N/A)
- Console: "modulation burst|period|ontime|count|trigger", e.g. "modulation period 1m", "modulation ontime 100u", "modulation count 5", "modulation trigger"
V1.4302 2026-11-09 For all tinySA models
- Signal generator: PRESET in the generator menu loads and stores presets (on the tinySA Ultra also from and to SD). A loaded preset opens the menu of its own mode, and a signal generator preset loaded from the menu starts with the output off
- The SW button of the signal generator shows the start and stop frequency of the sweep (Ultra only)

@ -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;

@ -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

@ -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

@ -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);

108
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;
}

Loading…
Cancel
Save

Powered by TurnKey Linux.