Audible harmonic

pull/119/merge
erikkaashoek 4 weeks ago
parent 5eb29714bd
commit bc0908605a

@ -1259,6 +1259,7 @@ config_t config = {
.direct_stop = 985000000UL,
.overclock = 0,
.hide_21MHz = false,
.wfm_1khz_harmonic = 0, // Disabled by default; 0=off, 1-100=amplitude percentage for ~1kHz harmonic
#endif
#ifdef __USE_SD_CARD__
.sd_icon_save = SDIS_DEFAULT,

@ -893,6 +893,7 @@ typedef struct config {
#ifdef TINYSA4
uint8_t hide_21MHz;
uint8_t no_audio_agc;
uint8_t wfm_1khz_harmonic; // 0=off, 1-100=amplitude percentage for adding ~1kHz harmonic to low freq WFM
#endif
#ifdef __USE_SD_CARD__
uint8_t sd_icon_save; // enum

@ -71,7 +71,7 @@ freq_t frequency_step_x10 = 0;
uint16_t vbwSteps = 1;
freq_t minFreq = 0;
freq_t maxFreq = 520000000;
static float old_a = -150; // cached value to reduce writes to level registers
static float prev_output_level_dBm = -150; // cached output level to reduce redundant hardware writes
int spur_gate = 100;
#ifdef __BANDS__
@ -3672,7 +3672,7 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) /
set_calibration_freq(setting.refer);
update_rbw();
calculate_step_delay();
old_a = -150; // clear cached level setting
prev_output_level_dBm = -150; // clear cached level setting
// Initialize HW
scandirty = true; // This is the first pass with new settings
for (int t=0;t<TRACES_MAX;t++)
@ -3752,12 +3752,27 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) /
modulation_steps >>= 1;
}
if (modulation_steps >= 4) {
int hn = (config.wfm_1khz_harmonic > 0 && setting.modulation_frequency < 250.0) ? (int)((1000.0 / setting.modulation_frequency) + 0.5) : 0;
for (int i = 0; i < modulation_steps/4+1; i++) {
fm_modulation[i] = setting.modulation_deviation_div100 * sine_wave[i*sine_wave_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];
}
if (hn) {
for (int i = 0; i < modulation_steps; i++) {
int table_index = (i*sine_wave_index*hn) % MAX_MODULATION_STEPS;
int mul = 1;
if (table_index > MAX_MODULATION_STEPS/2) {
mul = -1;
table_index = MAX_MODULATION_STEPS - table_index;
}
if (table_index > MAX_MODULATION_STEPS/4)
table_index = MAX_MODULATION_STEPS/2 - table_index;
int v = setting.modulation_deviation_div100 * sine_wave[table_index] * mul / 100;
fm_modulation[i] += v;
}
}
} else {
modulation_steps = 2;
fm_modulation[0] = (setting.modulation_deviation_div100*347)/100;
@ -3847,71 +3862,91 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) /
}
else
#endif
if (setting.mode == M_GENLOW) {// if in low output mode and level sweep or frequency weep is active or at start of sweep
float ls=setting.level_sweep; // calculate and set the output level
if (ls > 0)
ls += 0.5;
else if (ls < 0)
ls -= 0.5;
float a = ((int)((setting.level + ((float)i / sweep_points) * ls)*2.0)) / 2.0 /* + get_level_offset() */ ;
#ifdef TINYSA4
set_output_path(f, a);
if (setting.mode == M_GENLOW) { // Low output mode: calculate and set output level
// Calculate level sweep amount with rounding
float level_sweep_amount = setting.level_sweep;
if (level_sweep_amount > 0)
level_sweep_amount += 0.5;
else if (level_sweep_amount < 0)
level_sweep_amount -= 0.5;
// Calculate target output level including sweep progression (rounded to 0.5 dB steps)
float target_output_level_dBm = ((int)((setting.level + ((float)i / sweep_points) * level_sweep_amount)*2.0)) / 2.0;
#ifdef TINYSA4
set_output_path(f, target_output_level_dBm);
#else
int d;
int drive_index;
#if 0
if (force_signal_path) {
setting.atten_step = test_output_switch;
d = test_output_drive;
drive_index = test_output_drive;
setting.attenuate_x2 = test_output_attenuate;
goto set_path;
}
#endif
// Apply frequency-dependent correction
correct_RSSI_freq = get_frequency_correction(f);
a += PURE_TO_float(correct_RSSI_freq);
if (a != old_a) {
old_a = a;
a = a - level_max(); // convert to all settings maximum power output equals a = zero
if (a < -SWITCH_ATTENUATION) {
a = a + SWITCH_ATTENUATION;
setting.atten_step = true;
target_output_level_dBm += PURE_TO_float(correct_RSSI_freq);
// Only update hardware if level changed (avoids unnecessary register writes)
if (target_output_level_dBm != prev_output_level_dBm) {
prev_output_level_dBm = target_output_level_dBm;
// Convert to relative level (0 = maximum power output)
float level_relative_to_max_dBm = target_output_level_dBm - level_max();
// Determine if path switching is needed for large attenuation
if (level_relative_to_max_dBm < -SWITCH_ATTENUATION) {
level_relative_to_max_dBm += SWITCH_ATTENUATION;
setting.atten_step = true; // Use attenuated signal path
} else {
setting.atten_step = false;
setting.atten_step = false; // Use direct signal path
}
#define LOWEST_LEVEL MIN_DRIVE
d = MAX_DRIVE-3; // Start in the middle
float blw = BELOW_MAX_DRIVE(d);
while (a > blw && d < MAX_DRIVE) { // Increase if needed
d++;
blw = BELOW_MAX_DRIVE(d);
// Search for optimal drive setting - start in middle range
drive_index = MAX_DRIVE-3;
float drive_offset_dBm = BELOW_MAX_DRIVE(drive_index);
// Increase drive if current setting is too low
while (level_relative_to_max_dBm > drive_offset_dBm && drive_index < MAX_DRIVE) {
drive_index++;
drive_offset_dBm = BELOW_MAX_DRIVE(drive_index);
}
while (a + 28 < blw && d > LOWEST_LEVEL) { // reduce till it fits attenuator (31 - 3)
d--;
blw = BELOW_MAX_DRIVE(d);
// Reduce drive while it still fits in attenuator range (31 - 3 = 28 dB)
while (level_relative_to_max_dBm + 28 < drive_offset_dBm && drive_index > LOWEST_LEVEL) {
drive_index--;
drive_offset_dBm = BELOW_MAX_DRIVE(drive_index);
}
a -= blw;
if (a > 0)
a = 0;
if (a < -31.5)
a = -31.5;
a = -a - 0.25; // Rounding
setting.attenuate_x2 = (int)(a * 2);
// Calculate attenuator setting
float attenuator_level_dBm = level_relative_to_max_dBm - drive_offset_dBm;
if (attenuator_level_dBm > 0)
attenuator_level_dBm = 0;
if (attenuator_level_dBm < -31.5)
attenuator_level_dBm = -31.5;
attenuator_level_dBm = -attenuator_level_dBm - 0.25; // Rounding
setting.attenuate_x2 = (int)(attenuator_level_dBm * 2);
// set_path:
SI4432_Sel = SI4432_RX ;
SI4432_Drive(d);
SI4432_Sel = SI4432_RX ;
if ( setting.atten_step)
set_switch_receive();
// Apply settings to hardware
SI4432_Sel = SI4432_RX;
SI4432_Drive(drive_index);
SI4432_Sel = SI4432_RX;
if (setting.atten_step)
set_switch_receive(); // Attenuated path
else
set_switch_transmit();
set_switch_transmit(); // Direct path
PE4302_Write_Byte(setting.attenuate_x2);
#if 0
if (debug_level && SDU1.config->usbp->state == USB_ACTIVE)
shell_printf ("level=%f, d=%d, a=%d, s=%d\r\n", setting.level, d, setting.attenuate_x2, (setting.atten_step ? 1 : 0));
shell_printf ("level=%f, d=%d, a=%d, s=%d\r\n", setting.level, drive_index, setting.attenuate_x2, (setting.atten_step ? 1 : 0));
#endif
}
#endif
}
else if (setting.mode == M_GENHIGH) {
else if (setting.mode == M_GENHIGH) { // High output mode: set output level
#ifdef TINYSA4
if (force_signal_path) {
enable_rx_output(!test_output_switch);
@ -3919,21 +3954,24 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) /
} else
#endif
{
float a = setting.level - level_max();
if (a <= -SWITCH_ATTENUATION) {
setting.atten_step = true;
a = a + SWITCH_ATTENUATION;
// Calculate level relative to maximum output power
float level_relative_to_max_dBm = setting.level - level_max();
// Determine if path switching is needed for large attenuation
if (level_relative_to_max_dBm <= -SWITCH_ATTENUATION) {
setting.atten_step = true; // Use attenuated signal path
level_relative_to_max_dBm += SWITCH_ATTENUATION;
#ifdef TINYSA3
SI4432_Sel = SI4432_LO ;
SI4432_Sel = SI4432_LO;
set_switch_receive();
#else
enable_ADF_output(true, false);
;
#endif
} else {
setting.atten_step = false;
setting.atten_step = false; // Use direct signal path
#ifdef TINYSA3
SI4432_Sel = SI4432_LO ;
SI4432_Sel = SI4432_LO;
set_switch_transmit();
#else
enable_ADF_output(true, true);
@ -3941,24 +3979,27 @@ pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) /
#endif
}
int d = MIN_DRIVE;
while (drive_dBm[d] - level_max() < a && d < MAX_DRIVE) // Find level equal or above requested level
d++;
// if (d == 8 && v < -12) // Round towards closest level
// d = 7;
setting.level = drive_dBm[d] + config.high_level_output_offset - (setting.atten_step ? SWITCH_ATTENUATION : 0);
// Find drive setting that meets or exceeds requested level
int drive_index = MIN_DRIVE;
while (drive_dBm[drive_index] - level_max() < level_relative_to_max_dBm && drive_index < MAX_DRIVE)
drive_index++;
// if (drive_index == 8 && v < -12) // Round towards closest level
// drive_index = 7;
// Update actual output level to closest achievable value
setting.level = drive_dBm[drive_index] + config.high_level_output_offset - (setting.atten_step ? SWITCH_ATTENUATION : 0);
#ifdef __SI4432__
SI4432_Sel = SI4432_LO ;
SI4432_Drive(d);
SI4432_Sel = SI4432_LO;
SI4432_Drive(drive_index);
#endif
#ifdef TINYSA4
#ifndef __NEW_SWITCHES__
if (config.high_out_adf4350)
SI4463_set_output_level(d);
SI4463_set_output_level(drive_index);
else
#endif
// ADF4351_aux_drive(d);
// ADF4351_aux_drive(drive_index);
#endif
}
}

16
ui.c

@ -2506,6 +2506,21 @@ static UI_FUNCTION_ADV_CALLBACK(menu_level_in_dBuV)
menu_move_back(false);
}
#ifdef TINYSA4
static UI_FUNCTION_ADV_CALLBACK(menu_audible_harmonic)
{
(void)item;
(void)data;
if (b){
b->icon = config.wfm_1khz_harmonic ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK;
return;
}
config.wfm_1khz_harmonic = config.wfm_1khz_harmonic ? 0 : 100; // Toggle between off and 100%
dirty = true;
// menu_move_back(false);
}
#endif
static UI_FUNCTION_ADV_CALLBACK(menu_smodulation_acb){
(void)item;
(void)data;
@ -4457,6 +4472,7 @@ static const menuitem_t menu_modulation[] = {
{ 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, "Add Audible harmonic", 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

Loading…
Cancel
Save

Powered by TurnKey Linux.