Misc adaptive gain changes:
Update adaptive gain control loop to handle interactions between burst detection and dynamic range control. Internal doc updates & naming cleanups. Stats tweaks.
This commit is contained in:
parent
fd8f2d77e1
commit
ac97423249
362
adaptive.c
362
adaptive.c
|
|
@ -34,28 +34,31 @@ static float adaptive_gain_down_db;
|
|||
// block handling
|
||||
//
|
||||
|
||||
static unsigned adaptive_block_remaining;
|
||||
static unsigned adaptive_block_size;
|
||||
static unsigned adaptive_block_remaining; // samples in each block
|
||||
static unsigned adaptive_block_size; // samples remaining in the current block
|
||||
|
||||
void adaptive_init();
|
||||
void adaptive_update(uint16_t *buf, unsigned length, struct modesMessage *decoded);
|
||||
static void adaptive_update_single(uint16_t *buf, unsigned length, struct modesMessage *decoded);
|
||||
static void adaptive_end_of_block();
|
||||
static void adaptive_control_update();
|
||||
|
||||
//
|
||||
// burst handling
|
||||
//
|
||||
|
||||
static unsigned adaptive_burst_window_size;
|
||||
static unsigned adaptive_burst_window_remaining;
|
||||
static unsigned adaptive_burst_window_counter;
|
||||
static unsigned adaptive_burst_runlength;
|
||||
static unsigned adaptive_burst_block_counter;
|
||||
static unsigned adaptive_burst_block_loud_decodes;
|
||||
static double adaptive_burst_smoothed;
|
||||
static double adaptive_burst_loud_decodes_smoothed;
|
||||
static unsigned adaptive_burst_change_delay;
|
||||
static double adaptive_burst_loud_threshold;
|
||||
static unsigned adaptive_burst_window_size; // samples in each burst window
|
||||
static unsigned adaptive_burst_window_remaining; // samples remaining in the current burst window
|
||||
static unsigned adaptive_burst_window_counter; // loud samples seen in current burst window
|
||||
static unsigned adaptive_burst_runlength; // consecutive loud burst windows seen
|
||||
static unsigned adaptive_burst_block_loud_undecoded; // loud undecoded bursts seen in this block so far
|
||||
static unsigned adaptive_burst_block_loud_decoded; // loud decoded messages seen in this block so far
|
||||
static double adaptive_burst_loud_undecoded_smoothed; // smoothed rate of loud misdecodes per block
|
||||
static double adaptive_burst_loud_decoded_smoothed; // smoothed rate of loud successful decodes per block
|
||||
static unsigned adaptive_burst_change_timer; // countdown inhibiting control after changing gain
|
||||
static double adaptive_burst_loud_threshold; // current signal level threshold for a "loud decode"
|
||||
static unsigned adaptive_burst_loud_blocks = 0; // consecutive blocks with loud rate
|
||||
static unsigned adaptive_burst_quiet_blocks = 0; // consecutive blocks with quiet rate
|
||||
|
||||
static void adaptive_burst_update(uint16_t *buf, unsigned length);
|
||||
static void adaptive_burst_skip(unsigned length);
|
||||
|
|
@ -64,23 +67,23 @@ static void adaptive_burst_scan_windows(uint16_t *buf, unsigned windows);
|
|||
static void adaptive_burst_end_of_window(unsigned counter);
|
||||
static void adaptive_burst_end_of_block();
|
||||
|
||||
static void adaptive_burst_control_update();
|
||||
|
||||
//
|
||||
// noise floor measurement (adaptive dynamic range)
|
||||
//
|
||||
|
||||
static unsigned *adaptive_range_radix;
|
||||
static unsigned adaptive_range_counter;
|
||||
static double adaptive_range_smoothed;
|
||||
static unsigned *adaptive_range_radix; // radix-sort buckets for current block
|
||||
static unsigned adaptive_range_radix_counter; // sum of all radix-sort buckets (= number of samples sorted)
|
||||
static double adaptive_range_smoothed; // smoothed noise floor estimate, dBFS
|
||||
static enum { RANGE_SCAN_IDLE, RANGE_SCAN_UP, RANGE_SCAN_DOWN } adaptive_range_state = RANGE_SCAN_UP;
|
||||
static unsigned adaptive_range_delay;
|
||||
static unsigned adaptive_range_change_timer; // countdown inhibiting control after changing gain
|
||||
static unsigned adaptive_range_rescan_timer; // countdown to next upwards gain reprobe
|
||||
static int adaptive_range_gain_limit; // probed maximum gain step with acceptable dynamic range
|
||||
|
||||
static void adaptive_range_update(uint16_t *buf, unsigned length);
|
||||
static void adaptive_range_end_of_block();
|
||||
static void adaptive_range_control_update();
|
||||
|
||||
|
||||
// Try to change the SDR gain to 'step' and tell the user about it,
|
||||
// with 'why' as the reason to show. Return true if the gain actually changed.
|
||||
static bool adaptive_set_gain(int step, const char *why)
|
||||
{
|
||||
if (step < adaptive_gain_min)
|
||||
|
|
@ -96,9 +99,14 @@ static bool adaptive_set_gain(int step, const char *why)
|
|||
sdrGetGainDb(current_gain), current_gain, sdrGetGainDb(step), step, why);
|
||||
|
||||
int new_gain = sdrSetGain(step);
|
||||
return (current_gain != new_gain);
|
||||
bool changed = (current_gain != new_gain);
|
||||
if (changed)
|
||||
++Modes.stats_current.adaptive_gain_changes;
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Update internal state to reflect a gain change
|
||||
// (usually after adaptive_set_gain returns true, but also called during init)
|
||||
static void adaptive_gain_changed()
|
||||
{
|
||||
int new_gain = sdrGetGain();
|
||||
|
|
@ -107,8 +115,14 @@ static void adaptive_gain_changed()
|
|||
|
||||
double loud_threshold_dbfs = 0 - adaptive_gain_up_db - 3.0;
|
||||
adaptive_burst_loud_threshold = pow(10, loud_threshold_dbfs / 10.0);
|
||||
|
||||
adaptive_range_change_timer = Modes.adaptive_range_change_delay;
|
||||
adaptive_burst_change_timer = Modes.adaptive_burst_change_delay;
|
||||
adaptive_burst_loud_blocks = 0;
|
||||
adaptive_burst_quiet_blocks = 0;
|
||||
}
|
||||
|
||||
// External init entry point
|
||||
void adaptive_init()
|
||||
{
|
||||
int maxgain = sdrGetMaxGain();
|
||||
|
|
@ -130,16 +144,13 @@ void adaptive_init()
|
|||
adaptive_burst_window_size = Modes.sample_rate / 25000;
|
||||
adaptive_burst_window_remaining = adaptive_burst_window_size;
|
||||
adaptive_burst_window_counter = 0;
|
||||
adaptive_burst_change_delay = Modes.adaptive_burst_change_delay;
|
||||
|
||||
// Use an overall block size that is an exact multiple of the burst window, close to 1 second long
|
||||
adaptive_block_size = adaptive_burst_window_size * 25000;
|
||||
adaptive_block_remaining = adaptive_block_size;
|
||||
|
||||
adaptive_range_radix = calloc(sizeof(unsigned), 65536);
|
||||
|
||||
adaptive_range_state = RANGE_SCAN_UP;
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
|
||||
// select and enforce gain limits
|
||||
for (adaptive_gain_min = 0; adaptive_gain_min < maxgain; ++adaptive_gain_min) {
|
||||
|
|
@ -160,6 +171,8 @@ void adaptive_init()
|
|||
fprintf(stderr, "adaptive: enabled burst control\n");
|
||||
adaptive_set_gain(sdrGetGain(), "constraining gain to adaptive gain limits");
|
||||
adaptive_gain_changed();
|
||||
|
||||
adaptive_range_gain_limit = sdrGetGain();
|
||||
}
|
||||
|
||||
// Feed some samples into the adaptive system. Any number of samples might be passed in.
|
||||
|
|
@ -190,7 +203,7 @@ static void adaptive_update_single(uint16_t *buf, unsigned length, struct modesM
|
|||
{
|
||||
if (decoded) {
|
||||
if (/* decoded->msgbits == 112 && */ decoded->signalLevel >= adaptive_burst_loud_threshold)
|
||||
++adaptive_burst_block_loud_decodes;
|
||||
++adaptive_burst_block_loud_decoded;
|
||||
adaptive_burst_skip(length);
|
||||
} else {
|
||||
adaptive_burst_update(buf, length);
|
||||
|
|
@ -300,7 +313,7 @@ static void adaptive_burst_end_of_window(unsigned counter)
|
|||
// that as a candidate for an over-amplified message that was
|
||||
// not decoded.
|
||||
if (adaptive_burst_runlength >= 2 && adaptive_burst_runlength <= 5)
|
||||
++adaptive_burst_block_counter;
|
||||
++adaptive_burst_block_loud_undecoded;
|
||||
adaptive_burst_runlength = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -312,7 +325,7 @@ static void adaptive_range_update(uint16_t *buf, unsigned length)
|
|||
if (!Modes.adaptive_range_control)
|
||||
return;
|
||||
|
||||
adaptive_range_counter += length;
|
||||
adaptive_range_radix_counter += length;
|
||||
while (length--) {
|
||||
// do a very simple radix sort of sample magnitudes
|
||||
// so we can later find the Nth percentile value
|
||||
|
|
@ -331,7 +344,7 @@ static void adaptive_range_end_of_block()
|
|||
unsigned n = 0, i = 0;
|
||||
|
||||
// measure Nth percentile magnitude
|
||||
unsigned count_n = adaptive_range_counter * Modes.adaptive_range_percentile / 100;
|
||||
unsigned count_n = adaptive_range_radix_counter * Modes.adaptive_range_percentile / 100;
|
||||
while (i < 65536 && n <= count_n)
|
||||
n += adaptive_range_radix[i++];
|
||||
uint16_t percentile_n = i - 1;
|
||||
|
|
@ -347,7 +360,7 @@ static void adaptive_range_end_of_block()
|
|||
|
||||
// reset radix sort for the next block
|
||||
memset(adaptive_range_radix, 0, 65536 * sizeof(unsigned));
|
||||
adaptive_range_counter = 0;
|
||||
adaptive_range_radix_counter = 0;
|
||||
}
|
||||
|
||||
// Burst measurement: we reached the end of a block, update our burst rate estimate
|
||||
|
|
@ -356,25 +369,17 @@ static void adaptive_burst_end_of_block()
|
|||
if (!Modes.adaptive_burst_control)
|
||||
return;
|
||||
|
||||
// maintain an EMA of the number of bursts seen per block
|
||||
Modes.stats_current.adaptive_loud_undecoded += adaptive_burst_block_counter;
|
||||
adaptive_burst_smoothed = adaptive_burst_smoothed * (1 - Modes.adaptive_burst_alpha) + adaptive_burst_block_counter * Modes.adaptive_burst_alpha;
|
||||
adaptive_burst_block_counter = 0;
|
||||
// maintain an EMA of the number of undecoded loud bursts seen per block
|
||||
Modes.stats_current.adaptive_loud_undecoded += adaptive_burst_block_loud_undecoded;
|
||||
adaptive_burst_loud_undecoded_smoothed = adaptive_burst_loud_undecoded_smoothed * (1 - Modes.adaptive_burst_alpha) + adaptive_burst_block_loud_undecoded * Modes.adaptive_burst_alpha;
|
||||
adaptive_burst_block_loud_undecoded = 0;
|
||||
|
||||
// maintain an EMA of the number of decoded, but loud, messages seen per block
|
||||
Modes.stats_current.adaptive_loud_decoded += adaptive_burst_block_loud_decodes;
|
||||
adaptive_burst_loud_decodes_smoothed = adaptive_burst_loud_decodes_smoothed * (1 - Modes.adaptive_burst_alpha) + adaptive_burst_block_loud_decodes * Modes.adaptive_burst_alpha;
|
||||
adaptive_burst_block_loud_decodes = 0;
|
||||
Modes.stats_current.adaptive_loud_decoded += adaptive_burst_block_loud_decoded;
|
||||
adaptive_burst_loud_decoded_smoothed = adaptive_burst_loud_decoded_smoothed * (1 - Modes.adaptive_burst_alpha) + adaptive_burst_block_loud_decoded * Modes.adaptive_burst_alpha;
|
||||
adaptive_burst_block_loud_decoded = 0;
|
||||
}
|
||||
|
||||
// consecutive blocks with loud rate
|
||||
static unsigned adaptive_burst_loud_blocks = 0;
|
||||
// consecutive blocks with quiet rate
|
||||
static unsigned adaptive_burst_quiet_blocks = 0;
|
||||
// are we suppressing gain due to a burst?
|
||||
static bool adaptive_burst_suppressing = false;
|
||||
// what was the gain before we started suppressing?
|
||||
static int adaptive_burst_orig_gain = 0;
|
||||
|
||||
void flush_stats(uint64_t now);
|
||||
|
||||
|
|
@ -396,32 +401,41 @@ static void adaptive_end_of_block()
|
|||
adaptive_range_end_of_block();
|
||||
adaptive_burst_end_of_block();
|
||||
|
||||
adaptive_burst_control_update();
|
||||
adaptive_range_control_update();
|
||||
adaptive_control_update();
|
||||
|
||||
Modes.stats_current.adaptive_valid = true;
|
||||
unsigned current = Modes.stats_current.adaptive_gain = sdrGetGain();
|
||||
Modes.stats_current.adaptive_range_gain_limit = adaptive_range_gain_limit;
|
||||
++Modes.stats_current.adaptive_gain_seconds[current < STATS_GAIN_COUNT ? current : STATS_GAIN_COUNT-1];
|
||||
if (adaptive_burst_suppressing)
|
||||
++Modes.stats_current.adaptive_gain_reduced_seconds;
|
||||
}
|
||||
|
||||
static void adaptive_burst_control_update()
|
||||
static void adaptive_control_update()
|
||||
{
|
||||
if (!Modes.adaptive_burst_control)
|
||||
return;
|
||||
// votes for what to do with the gain
|
||||
// "gain_not_up" overlaps somewhat with "gain_down", but they are not identical;
|
||||
// burst control may want to prevent gain from increasing, but not necessarily
|
||||
// decrease gain.
|
||||
|
||||
if (adaptive_range_state != RANGE_SCAN_IDLE)
|
||||
return;
|
||||
|
||||
if (adaptive_burst_change_delay)
|
||||
--adaptive_burst_change_delay;
|
||||
bool gain_up = false;
|
||||
const char *gain_up_reason = NULL;
|
||||
bool gain_down = false;
|
||||
const char *gain_down_reason = NULL;
|
||||
bool gain_not_up = false;
|
||||
|
||||
if (!adaptive_burst_change_delay) {
|
||||
if (adaptive_burst_smoothed > Modes.adaptive_burst_loud_rate) {
|
||||
int current_gain = sdrGetGain();
|
||||
|
||||
if (adaptive_burst_change_timer)
|
||||
--adaptive_burst_change_timer;
|
||||
if (adaptive_range_change_timer > 0)
|
||||
--adaptive_range_change_timer;
|
||||
if (adaptive_range_rescan_timer > 0)
|
||||
--adaptive_range_rescan_timer;
|
||||
|
||||
if (Modes.adaptive_burst_control && !adaptive_burst_change_timer) {
|
||||
if (adaptive_burst_loud_undecoded_smoothed > Modes.adaptive_burst_loud_rate) {
|
||||
adaptive_burst_quiet_blocks = 0;
|
||||
++adaptive_burst_loud_blocks;
|
||||
} else if (adaptive_burst_loud_decodes_smoothed < Modes.adaptive_burst_quiet_rate) {
|
||||
} else if (adaptive_burst_loud_decoded_smoothed < Modes.adaptive_burst_quiet_rate) {
|
||||
adaptive_burst_loud_blocks = 0;
|
||||
++adaptive_burst_quiet_blocks;
|
||||
} else {
|
||||
|
|
@ -431,122 +445,128 @@ static void adaptive_burst_control_update()
|
|||
|
||||
if (adaptive_burst_loud_blocks >= Modes.adaptive_burst_loud_runlength) {
|
||||
// we need to reduce gain (further)
|
||||
if (!adaptive_burst_suppressing) {
|
||||
adaptive_burst_suppressing = true;
|
||||
adaptive_burst_orig_gain = sdrGetGain();
|
||||
gain_down = gain_not_up = true;
|
||||
gain_down_reason = "high rate of loud undecoded messages";
|
||||
|
||||
// if we're currently doing a downward scan, reducing gain further may confuse it;
|
||||
// stop that scan and restart it once we are no longer in a reduced-gain state
|
||||
if (adaptive_range_state == RANGE_SCAN_DOWN) {
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_rescan_timer = 0;
|
||||
}
|
||||
} else if (adaptive_burst_quiet_blocks < Modes.adaptive_burst_quiet_runlength) {
|
||||
// we're OK at the current gain, but should not increase it
|
||||
gain_not_up = true;
|
||||
} else if (current_gain < adaptive_range_gain_limit) {
|
||||
// we're OK at the current gain, and can increase gain to the previously discovered
|
||||
// dynamic range limit
|
||||
gain_up = true;
|
||||
gain_up_reason = "low loud message rate and gain below dynamic range limit";
|
||||
}
|
||||
}
|
||||
|
||||
if (Modes.adaptive_range_control && !adaptive_range_change_timer) {
|
||||
float available_range = -20 * log10(adaptive_range_smoothed / 65536.0);
|
||||
// allow the gain limit to increase if this gain setting is acceptable
|
||||
// (decreasing the limit is done separately in SCAN_UP / IDLE states when we decide to reduce gain)
|
||||
if (available_range >= Modes.adaptive_range_target && current_gain > adaptive_range_gain_limit)
|
||||
adaptive_range_gain_limit = current_gain;
|
||||
|
||||
switch (adaptive_range_state) {
|
||||
case RANGE_SCAN_UP:
|
||||
if (available_range < Modes.adaptive_range_target) {
|
||||
// Current gain fails to meet our target. Switch to downward scanning.
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) < required dynamic range (%.1fdB), switching to downward scan\n", available_range, Modes.adaptive_range_target);
|
||||
gain_down = gain_not_up = true;
|
||||
gain_down_reason = "probing dynamic range gain lower bound";
|
||||
adaptive_range_state = RANGE_SCAN_DOWN;
|
||||
if (adaptive_range_gain_limit >= current_gain)
|
||||
adaptive_range_gain_limit = current_gain - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
adaptive_decrease_gain("saw a noisy period with many undecoded loud messages");
|
||||
adaptive_burst_loud_blocks = 0;
|
||||
adaptive_burst_change_delay = Modes.adaptive_burst_change_delay;
|
||||
}
|
||||
if (sdrGetGain() >= adaptive_gain_max) {
|
||||
// We have reached our upper gain limit
|
||||
fprintf(stderr, "adaptive: reached upper gain limit, halting dynamic range scan here\n");
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
if (adaptive_burst_suppressing && adaptive_burst_quiet_blocks >= Modes.adaptive_burst_quiet_runlength) {
|
||||
// we can relax the gain restriction
|
||||
adaptive_increase_gain("saw a quiet period with few loud messages");
|
||||
adaptive_burst_quiet_blocks = 0;
|
||||
adaptive_burst_change_delay = Modes.adaptive_burst_change_delay;
|
||||
// This gain step is OK and we have more to try, try the next gain step up.
|
||||
// (But if burst detection has inhibited increasing gain, don't do anything yet, just try again next block)
|
||||
if (!gain_not_up) {
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) >= required dynamic range (%.1fdB), continuing upward scan\n", available_range, Modes.adaptive_range_target);
|
||||
gain_up = true;
|
||||
gain_up_reason = "probing dynamic range gain upper bound";
|
||||
}
|
||||
break;
|
||||
|
||||
if (sdrGetGain() >= adaptive_burst_orig_gain)
|
||||
adaptive_burst_suppressing = false;
|
||||
case RANGE_SCAN_DOWN:
|
||||
if (available_range >= Modes.adaptive_range_target) {
|
||||
// Current gain meets our target; we are done with the scan.
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) >= required dynamic range (%.1fdB), stopping downwards scan here\n", available_range, Modes.adaptive_range_target);
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sdrGetGain() <= adaptive_gain_min) {
|
||||
fprintf(stderr, "adaptive: reached lower gain limit, halting dynamic range scan here\n");
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
// This gain step is too loud and we have more to try, try the next gain step down
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) < required dynamic range (%.1fdB), continuing downwards scan\n", available_range, Modes.adaptive_range_target);
|
||||
gain_down = gain_not_up = true;
|
||||
gain_down_reason = "probing dynamic range gain lower bound";
|
||||
break;
|
||||
|
||||
case RANGE_SCAN_IDLE:
|
||||
// Look for increased noise that could be compensated for by decreasing gain.
|
||||
// Do this even if we're waiting to rescan or if burst control is also active
|
||||
if (available_range + adaptive_gain_down_db / 2 < Modes.adaptive_range_target && sdrGetGain() > adaptive_gain_min) {
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) + half gain step down (%.1fdB) < required dynamic range (%.1fdB), starting downward scan\n",
|
||||
available_range, Modes.adaptive_range_target, adaptive_gain_down_db);
|
||||
if (current_gain >= adaptive_range_gain_limit)
|
||||
adaptive_range_gain_limit = current_gain - 1;
|
||||
adaptive_range_state = RANGE_SCAN_DOWN;
|
||||
gain_down = gain_not_up = true;
|
||||
gain_down_reason = "dynamic range fell below target value";
|
||||
break;
|
||||
}
|
||||
|
||||
// Infrequently consider increasing gain to handle the case where we've selected a too-low gain where the noise floor is dominated by noise unrelated to the gain setting.
|
||||
// But don't do this while burst control is preventing gain increases.
|
||||
if (!adaptive_range_rescan_timer && !gain_not_up) {
|
||||
if (available_range >= Modes.adaptive_range_target && sdrGetGain() < adaptive_gain_max) {
|
||||
fprintf(stderr, "adaptive: start periodic scan for acceptable dynamic range at increased gain\n");
|
||||
gain_up = true;
|
||||
gain_up_reason = "periodic re-probing of dynamic range gain upper bound";
|
||||
adaptive_range_state = RANGE_SCAN_UP;
|
||||
break;
|
||||
}
|
||||
|
||||
// Nothing to do for a while.
|
||||
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "adaptive: in a weird state (%d), trying to fix it\n", adaptive_range_state);
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void adaptive_range_control_update()
|
||||
{
|
||||
if (!Modes.adaptive_range_control)
|
||||
return;
|
||||
|
||||
if (adaptive_range_delay > 0)
|
||||
--adaptive_range_delay;
|
||||
|
||||
float available_range = -20 * log10(adaptive_range_smoothed / 65536.0);
|
||||
|
||||
switch (adaptive_range_state) {
|
||||
case RANGE_SCAN_UP:
|
||||
if (adaptive_range_delay > 0)
|
||||
break;
|
||||
|
||||
if (available_range < Modes.adaptive_range_target) {
|
||||
// Current gain fails to meet our target. Switch to downward scanning.
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) < required dynamic range (%.1fdB), switching to downward scan\n", available_range, Modes.adaptive_range_target);
|
||||
adaptive_decrease_gain("downwards dynamic range gain scan");
|
||||
adaptive_range_state = RANGE_SCAN_DOWN;
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sdrGetGain() >= adaptive_gain_max) {
|
||||
// We have reached our upper gain limit
|
||||
fprintf(stderr, "adaptive: reached upper gain limit, halting dynamic range scan here\n");
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_delay = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
// This gain step is OK and we have more to try, try the next gain step up.
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) >= required dynamic range (%.1fdB), continuing upward scan\n", available_range, Modes.adaptive_range_target);
|
||||
adaptive_increase_gain("upwards dynamic range scan");
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
break;
|
||||
|
||||
case RANGE_SCAN_DOWN:
|
||||
if (adaptive_range_delay > 0)
|
||||
break;
|
||||
|
||||
if (available_range >= Modes.adaptive_range_target) {
|
||||
// Current gain meets our target; we are done with the scan.
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) >= required dynamic range (%.1fdB), stopping downwards scan here\n", available_range, Modes.adaptive_range_target);
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_delay = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sdrGetGain() <= adaptive_gain_min) {
|
||||
fprintf(stderr, "adaptive: reached lower gain limit, halting dynamic range scan here\n");
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_delay = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
// This gain step is too loud and we have more to try, try the next gain step down
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) < required dynamic range (%.1fdB), continuing downwards scan\n", available_range, Modes.adaptive_range_target);
|
||||
adaptive_decrease_gain("downwards dynamic range gain scan");
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
break;
|
||||
|
||||
case RANGE_SCAN_IDLE:
|
||||
// Look for increased noise that could be compensated for by decreasing gain.
|
||||
// Do this even if we're delaying.
|
||||
if (available_range + adaptive_gain_down_db / 2 < Modes.adaptive_range_target && sdrGetGain() > adaptive_gain_min) {
|
||||
fprintf(stderr, "adaptive: available dynamic range (%.1fdB) + half gain step down (%.1fdB) < required dynamic range (%.1fdB), starting downward scan\n",
|
||||
available_range, Modes.adaptive_range_target, adaptive_gain_down_db);
|
||||
adaptive_range_state = RANGE_SCAN_DOWN;
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
if (adaptive_range_delay > 0)
|
||||
break;
|
||||
|
||||
// Infrequently consider increasing gain to handle the case where we've selected a too-low gain where the noise floor is dominated by noise unrelated to the gain setting
|
||||
if (available_range >= Modes.adaptive_range_target && sdrGetGain() < adaptive_gain_max) {
|
||||
fprintf(stderr, "adaptive: start periodic scan for acceptable dynamic range at increased gain\n");
|
||||
adaptive_increase_gain("upwards dynamic range scan");
|
||||
adaptive_range_state = RANGE_SCAN_UP;
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
// Nothing to do for a while.
|
||||
adaptive_range_delay = Modes.adaptive_range_rescan_delay;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "adaptive: in a weird state (%d), trying to fix it\n", adaptive_range_state);
|
||||
adaptive_range_state = RANGE_SCAN_IDLE;
|
||||
adaptive_range_delay = Modes.adaptive_range_scan_delay;
|
||||
break;
|
||||
}
|
||||
|
||||
// now actually perform any gain changes
|
||||
|
||||
if (gain_down)
|
||||
adaptive_decrease_gain(gain_down_reason);
|
||||
else if (gain_up && !gain_not_up)
|
||||
adaptive_increase_gain(gain_up_reason);
|
||||
}
|
||||
|
|
|
|||
16
dump1090.c
16
dump1090.c
|
|
@ -128,7 +128,7 @@ static void modesInitConfig(void) {
|
|||
|
||||
Modes.adaptive_burst_control = false;
|
||||
Modes.adaptive_burst_alpha = 2.0 / (5 + 1);
|
||||
Modes.adaptive_burst_change_delay = 15;
|
||||
Modes.adaptive_burst_change_delay = 5;
|
||||
Modes.adaptive_burst_loud_runlength = 10;
|
||||
Modes.adaptive_burst_loud_rate = 5.0;
|
||||
Modes.adaptive_burst_quiet_runlength = 10;
|
||||
|
|
@ -137,7 +137,7 @@ static void modesInitConfig(void) {
|
|||
Modes.adaptive_range_control = false;
|
||||
Modes.adaptive_range_alpha = 2.0 / (5 + 1);
|
||||
Modes.adaptive_range_percentile = 40;
|
||||
Modes.adaptive_range_scan_delay = 15;
|
||||
Modes.adaptive_range_change_delay = 10;
|
||||
Modes.adaptive_range_rescan_delay = 900;
|
||||
|
||||
sdrInitConfig();
|
||||
|
|
@ -353,6 +353,8 @@ static void showHelp(void)
|
|||
" Adaptive gain\n"
|
||||
"\n"
|
||||
"--adaptive-burst Adjust gain for too-loud message bursts\n"
|
||||
"--adaptive-range-change-delay <s> Set delay after changing gain before\n"
|
||||
" resuming burst control (seconds)\n"
|
||||
"--adaptive-burst-alpha <a> Set burst rate smoothing factor\n"
|
||||
" (0..1, smaller=more smoothing)\n"
|
||||
"--adaptive-burst-loud-rate <r> Set burst rate for gain decrease\n"
|
||||
|
|
@ -364,8 +366,8 @@ static void showHelp(void)
|
|||
"--adaptive-range-alpha <a> Set dynamic range noise smoothing factor\n"
|
||||
" (0..1, smaller=more smoothing)\n"
|
||||
"--adaptive-range-percentile <p> Set dynamic range noise percentile\n"
|
||||
"--adaptive-range-scan-delay <s> Set data collection interval for dynamic\n"
|
||||
" range gain scanning (seconds)\n"
|
||||
"--adaptive-range-change-delay <s> Set delay after changing gain before\n"
|
||||
" resuming dynamic range control (seconds)\n"
|
||||
"--adaptive-range-rescan-delay <s> Set rescan interval for dynamic range\n"
|
||||
" gain scanning (seconds)\n"
|
||||
"--adaptive-min-gain <g> Set gain adjustment range lower limit (dB)\n"
|
||||
|
|
@ -765,7 +767,7 @@ int main(int argc, char **argv) {
|
|||
Modes.adaptive_burst_control = true;
|
||||
} else if (!strcmp(argv[j], "--adaptive-burst-alpha") && more) {
|
||||
Modes.adaptive_burst_alpha = atof(argv[++j]);
|
||||
} else if (!strcmp(argv[j], "--adaptive-burst-delay") && more) {
|
||||
} else if (!strcmp(argv[j], "--adaptive-burst-change-delay") && more) {
|
||||
Modes.adaptive_burst_change_delay = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j], "--adaptive-burst-loud-rate") && more) {
|
||||
Modes.adaptive_burst_loud_rate = atof(argv[++j]);
|
||||
|
|
@ -783,8 +785,8 @@ int main(int argc, char **argv) {
|
|||
Modes.adaptive_range_percentile = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j], "--adaptive-range-target") && more) {
|
||||
Modes.adaptive_range_target = atof(argv[++j]);
|
||||
} else if (!strcmp(argv[j], "--adaptive-range-scan-delay") && more) {
|
||||
Modes.adaptive_range_scan_delay = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j], "--adaptive-range-change-delay") && more) {
|
||||
Modes.adaptive_range_change_delay = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j], "--adaptive-range-rescan-delay") && more) {
|
||||
Modes.adaptive_range_rescan_delay = atoi(argv[++j]);
|
||||
} else if (sdrHandleOption(argc, argv, &j)) {
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ struct _Modes { // Internal state
|
|||
float adaptive_range_alpha;
|
||||
unsigned adaptive_range_percentile;
|
||||
float adaptive_range_target;
|
||||
unsigned adaptive_range_scan_delay;
|
||||
unsigned adaptive_range_change_delay;
|
||||
unsigned adaptive_range_rescan_delay;
|
||||
};
|
||||
|
||||
|
|
|
|||
6
net_io.c
6
net_io.c
|
|
@ -1863,13 +1863,15 @@ static char * appendStatsJson(char *p,
|
|||
p = safe_snprintf(p, end,
|
||||
",\"adaptive\":"
|
||||
"{\"gain_db\":%.1f"
|
||||
",\"gain_reduced_seconds\":%u"
|
||||
",\"dynamic_range_limit_db\":%.1f"
|
||||
",\"gain_changes\":%u"
|
||||
",\"loud_undecoded\":%u"
|
||||
",\"loud_decoded\":%u"
|
||||
",\"noise_dbfs\":%.1f"
|
||||
",\"gain_seconds\":[",
|
||||
sdrGetGainDb(st->adaptive_gain),
|
||||
st->adaptive_gain_reduced_seconds,
|
||||
sdrGetGainDb(st->adaptive_range_gain_limit),
|
||||
st->adaptive_gain_changes,
|
||||
st->adaptive_loud_undecoded,
|
||||
st->adaptive_loud_decoded,
|
||||
st->adaptive_noise_dbfs);
|
||||
|
|
|
|||
14
stats.c
14
stats.c
|
|
@ -120,11 +120,15 @@ void display_stats(struct stats *st) {
|
|||
" %5u loud undecoded bursts\n"
|
||||
" %5u loud decoded messages\n"
|
||||
" %5.1f dBFS current noise floor\n"
|
||||
" %5.1f dB current gain setting\n",
|
||||
" %5.1f dB current gain setting\n"
|
||||
" %5.1f dB current dynamic range gain upper limit\n"
|
||||
" %5u gain changes caused by adaptive gain control\n",
|
||||
st->adaptive_loud_undecoded,
|
||||
st->adaptive_loud_decoded,
|
||||
st->adaptive_noise_dbfs,
|
||||
sdrGetGainDb(st->adaptive_gain));
|
||||
sdrGetGainDb(st->adaptive_gain),
|
||||
sdrGetGainDb(st->adaptive_range_gain_limit),
|
||||
st->adaptive_gain_changes);
|
||||
|
||||
uint32_t total_seconds = 0;
|
||||
for (unsigned i = 0; i < STATS_GAIN_COUNT; ++i)
|
||||
|
|
@ -140,9 +144,6 @@ void display_stats(struct stats *st) {
|
|||
}
|
||||
}
|
||||
|
||||
printf(" %5u seconds (%5.1f%%) at reduced gain due to loud messages\n",
|
||||
st->adaptive_gain_reduced_seconds, 100.0 * st->adaptive_gain_reduced_seconds / total_seconds);
|
||||
|
||||
printf(" Gain histogram:\n");
|
||||
for (unsigned i = 0; i < STATS_GAIN_COUNT; ++i) {
|
||||
unsigned seconds = st->adaptive_gain_seconds[i];
|
||||
|
|
@ -408,8 +409,9 @@ void add_stats(const struct stats *st1, const struct stats *st2, struct stats *t
|
|||
target->adaptive_gain = adaptive_best->adaptive_gain;
|
||||
for (unsigned i = 0; i < STATS_GAIN_COUNT; ++i)
|
||||
target->adaptive_gain_seconds[i] = st1->adaptive_gain_seconds[i] + st2->adaptive_gain_seconds[i];
|
||||
target->adaptive_gain_reduced_seconds = st1->adaptive_gain_reduced_seconds + st2->adaptive_gain_reduced_seconds;
|
||||
target->adaptive_loud_undecoded = st1->adaptive_loud_undecoded + st2->adaptive_loud_undecoded;
|
||||
target->adaptive_loud_decoded = st1->adaptive_loud_decoded + st2->adaptive_loud_decoded;
|
||||
target->adaptive_gain_changes = st1->adaptive_gain_changes + st2->adaptive_gain_changes;
|
||||
target->adaptive_noise_dbfs = adaptive_best->adaptive_noise_dbfs;
|
||||
target->adaptive_range_gain_limit = adaptive_best->adaptive_range_gain_limit;
|
||||
}
|
||||
|
|
|
|||
3
stats.h
3
stats.h
|
|
@ -135,10 +135,11 @@ struct stats {
|
|||
bool adaptive_valid; // is the following data valid?
|
||||
int adaptive_gain; // Current gain step in use
|
||||
uint32_t adaptive_gain_seconds[STATS_GAIN_COUNT]; // Seconds spent at each gain step
|
||||
uint32_t adaptive_gain_reduced_seconds; // Seconds spent at a reduced gain due to burst detection
|
||||
uint32_t adaptive_loud_undecoded; // Total number of loud, undecoded bursts
|
||||
uint32_t adaptive_loud_decoded; // Total number of loud, decoded messages
|
||||
uint32_t adaptive_gain_changes; // Total number of gain changes caused by adaptive gain control
|
||||
double adaptive_noise_dbfs; // Current adaptive-dynamic-range smoothed noise measurement, dBFS
|
||||
int adaptive_range_gain_limit; // Current adaptive-dynamic-range gain step limit
|
||||
};
|
||||
|
||||
void add_stats(const struct stats *st1, const struct stats *st2, struct stats *target);
|
||||
|
|
|
|||
Loading…
Reference in New Issue