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:
Oliver Jowett 2021-07-07 20:57:34 +08:00
parent fd8f2d77e1
commit ac97423249
6 changed files with 215 additions and 188 deletions

View File

@ -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;
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)
--adaptive_burst_change_delay;
int current_gain = sdrGetGain();
if (!adaptive_burst_change_delay) {
if (adaptive_burst_smoothed > Modes.adaptive_burst_loud_rate) {
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,49 +445,43 @@ 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;
}
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 (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;
if (sdrGetGain() >= adaptive_burst_orig_gain)
adaptive_burst_suppressing = false;
} 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";
}
}
}
static void adaptive_range_control_update()
{
if (!Modes.adaptive_range_control)
return;
if (adaptive_range_delay > 0)
--adaptive_range_delay;
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 (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");
gain_down = gain_not_up = true;
gain_down_reason = "probing dynamic range gain lower bound";
adaptive_range_state = RANGE_SCAN_DOWN;
adaptive_range_delay = Modes.adaptive_range_scan_delay;
if (adaptive_range_gain_limit >= current_gain)
adaptive_range_gain_limit = current_gain - 1;
break;
}
@ -481,72 +489,84 @@ static void adaptive_range_control_update()
// 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;
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_delay;
break;
}
// 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);
adaptive_increase_gain("upwards dynamic range scan");
adaptive_range_delay = Modes.adaptive_range_scan_delay;
gain_up = true;
gain_up_reason = "probing dynamic range gain upper bound";
}
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;
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_delay = Modes.adaptive_range_rescan_delay;
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);
adaptive_decrease_gain("downwards dynamic range gain scan");
adaptive_range_delay = Modes.adaptive_range_scan_delay;
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 delaying.
// 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;
adaptive_range_delay = Modes.adaptive_range_scan_delay;
gain_down = gain_not_up = true;
gain_down_reason = "dynamic range fell below target value";
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
// 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");
adaptive_increase_gain("upwards dynamic range scan");
gain_up = true;
gain_up_reason = "periodic re-probing of dynamic range gain upper bound";
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;
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_delay = Modes.adaptive_range_scan_delay;
adaptive_range_rescan_timer = Modes.adaptive_range_rescan_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);
}

View File

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

View File

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

View File

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

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

View File

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