Add BURST modulation to the generator (tinySA ULTRA)

MODULATION -> BURST keys the generator output on and off: ON TIME of every
PERIOD, continuously or COUNT bursts. In burst mode the FREQ / AM DEPTH /
FM DEVIATION buttons set PERIOD / ON TIME / COUNT, START SWEEP becomes
START BURST (fires COUNT bursts, restarts continuous bursts) and the CTCSS
button is blank. Console: "modulation burst|period|ontime|count|trigger".

The Si4468 stays in TX with its PLL untouched: it runs OOK in asynchronous
TX direct mode from GPIO1, driven by TIM8_CH3 PWM on PB9 (one pulse mode
with the repetition counter for COUNT bursts). GPIO1 is normally the CTS
line to PB9, so while burst is selected GPIO1 is an input and CTS is polled
over SPI (READ_CMD_BUFF, the reply is read in the same transaction). PB9
only drives while GPIO1 is an input and is released before every Si4468
reset. MODEM_FREQ_DEV is set to 0 in burst mode, OOK otherwise shifts the
carrier by the deviation of the RX configuration.

- Burst works where the Si4468 makes the output (up to 1.13 GHz and the
  mixer path above it), not from the ADF directly ("MOD: BURST N/A").
- Period 2us..89s, resolution about period/65536, timing from the MCU
  clock (about 1%).
- The new settings fill alignment holes of setting_t: its size and the
  stored presets stay valid.

F072 builds are unchanged. Tested on HW V0.5.4 with a tinySA basic in zero
span: ON level and frequency equal to CW, about 48 dB on/off ratio, COUNT
bursts exact, pulses from about 2 us.
main
erikkaashoek 3 days ago
parent ab5ecd5b51
commit 80292faa43

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