Pi Firmware: Autocal optimization pass uses stored data, so much faster

Change-Id: I1807b7872c7677d7e37fca32294464da30feed14
pull/11/head
David Banks 2018-11-06 12:45:12 +00:00
rodzic 430a2a161a
commit 632d739db8
2 zmienionych plików z 72 dodań i 33 usunięć

Wyświetl plik

@ -2,6 +2,7 @@
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
#include "defs.h"
#include "cpld.h"
#include "osd.h"
@ -207,6 +208,10 @@ static int cpld_get_version() {
return cpld_version;
}
static int sum_channels(int *rgb) {
return rgb[CHAN_RED] + rgb[CHAN_GREEN] + rgb[CHAN_BLUE];
}
static void cpld_calibrate(int elk, int chars_per_line) {
int min_i = 0;
int metric; // this is a point value (at one sample offset)
@ -219,6 +224,8 @@ static void cpld_calibrate(int elk, int chars_per_line) {
int *metrics;
int *errors;
int rgb_metrics[8][NUM_OFFSETS * NUM_CHANNELS];
if (mode7) {
log_info("Calibrating mode 7");
range = 8;
@ -231,33 +238,41 @@ static void cpld_calibrate(int elk, int chars_per_line) {
errors = &errors_default;
}
// Measure the error metrics at all possible offset values
min_metric = INT_MAX;
config->half_px_delay = 0;
config->full_px_delay = 0;
for (int i = 0; i < range; i++) {
for (int j = 0; j < NUM_OFFSETS; j++) {
config->sp_offset[j] = i;
printf("INFO: ");
for (int i = 0; i < NUM_OFFSETS; i++) {
printf("%6c", 'A' + i);
}
printf(" total\r\n");
for (int value = 0; value < range; value++) {
for (int i = 0; i < NUM_OFFSETS; i++) {
config->sp_offset[i] = value;
}
write_config(config);
rgb_metric = diff_N_frames_by_sample(NUM_CAL_FRAMES, mode7, elk, chars_per_line);
metric = 0;
printf("INFO: offset = %d: metrics = ", i);
for (int j = 0; j < NUM_OFFSETS; j++) {
printf("INFO: value = %d: metrics = ", value);
for (int i = 0; i < NUM_OFFSETS; i++) {
int per_sample = 0;
for (int c = 0; c < NUM_CHANNELS; c++) {
per_sample += rgb_metric[j * NUM_CHANNELS + c];
for (int j = 0; j < NUM_CHANNELS; j++) {
per_sample += rgb_metric[i * NUM_CHANNELS + j];
}
metric += per_sample;
printf("%6d", per_sample);
}
printf("; total = %7d\r\n", metric);
metrics[i] = metric;
printf("%8d\r\n", metric);
metrics[value] = metric;
osd_sp(config, 1, metric);
if (metric < min_metric) {
min_metric = metric;
}
// Save the metrics in case the second phase is needed
memcpy(rgb_metrics[value], rgb_metric, NUM_CHANNELS * NUM_OFFSETS * sizeof(int));
}
// Use a 3 sample window to find the minimum and maximum
min_win_metric = INT_MAX;
for (int i = 0; i < range; i++) {
@ -271,62 +286,66 @@ static void cpld_calibrate(int elk, int chars_per_line) {
}
}
}
// If the min metric is at the limit, make use of the half pixel delay
if (mode7 && min_metric > 0 && (min_i <= 1 || min_i >= 6)) {
log_info("Enabling half pixel delay");
config->half_px_delay = 1;
min_i ^= 4;
// Swap the metrics as well
for (int i = 0; i < range; i++) {
for (int j = 0; j < NUM_OFFSETS * NUM_CHANNELS; j++) {
int tmp = rgb_metrics[i][j];
rgb_metrics[i][j] = rgb_metrics[i ^ 4][j];
rgb_metrics[i ^ 4][j] = tmp;
}
}
}
// In all modes, start with the min metric
for (int i = 0; i < NUM_OFFSETS; i++) {
config->sp_offset[i] = min_i;
}
log_sp(config);
// If the metric is non zero, there is scope for further optimization in mode7
if (mode7 && min_metric > 0) {
int ref = min_metric;
log_info("Optimizing calibration");
log_sp(config);
log_debug("ref = %d", ref);
for (int i = 0; i < NUM_OFFSETS; i++) {
// Start with current value of the sample point i
int value = config->sp_offset[i];
// Look up the current metric for this value
int ref = sum_channels(rgb_metrics[value] + i * NUM_CHANNELS);
// Loop up the metric if we decrease this by one
int left = INT_MAX;
if (value > 0) {
left = sum_channels(rgb_metrics[value - 1] + i * NUM_CHANNELS);
}
// Look up the metric if we increase this by one
int right = INT_MAX;
if (config->sp_offset[i] > 0) {
config->sp_offset[i]--;
write_config(config);
rgb_metric = diff_N_frames(NUM_CAL_FRAMES, mode7, elk, chars_per_line);
left = rgb_metric[CHAN_RED] + rgb_metric[CHAN_GREEN] + rgb_metric[CHAN_BLUE];
osd_sp(config, 1, left);
config->sp_offset[i]++;
}
if (config->sp_offset[i] < 7) {
config->sp_offset[i]++;
write_config(config);
rgb_metric = diff_N_frames(NUM_CAL_FRAMES, mode7, elk, chars_per_line);
right = rgb_metric[CHAN_RED] + rgb_metric[CHAN_GREEN] + rgb_metric[CHAN_BLUE];
osd_sp(config, 1, right);
config->sp_offset[i]--;
if (value < 7) {
right = sum_channels(rgb_metrics[value + 1] + i * NUM_CHANNELS);
}
// Make the actual decision
if (left < right && left < ref) {
config->sp_offset[i]--;
ref = left;
log_info("nudged %d left, metric = %d", i, ref);
} else if (right < left && right < ref) {
config->sp_offset[i]++;
ref = right;
log_info("nudged %d right, metric = %d", i, ref);
}
}
log_sp(config);
}
// Determine mode 7 alignment
if (mode7 && supports_delay) {
write_config(config);
config->full_px_delay = analyze_mode7_alignment();
}
// Perform a final test of errors
write_config(config);
rgb_metric = diff_N_frames(NUM_CAL_FRAMES, mode7, elk, chars_per_line);
*errors = rgb_metric[CHAN_RED] + rgb_metric[CHAN_GREEN] + rgb_metric[CHAN_BLUE];
*errors = sum_channels(rgb_metric);
osd_sp(config, 1, *errors);
log_info("Calibration complete");
log_sp(config);

Wyświetl plik

@ -633,6 +633,26 @@ int *diff_N_frames_by_sample(int n, int mode7, int elk, int chars_per_line) {
#ifdef INSTRUMENT_CAL
t_compare += _get_cycle_counter() - t;
#endif
// At this point the diffs correspond to the sample points in
// an unusual order: A F C B E D
//
// This happens for three reasons:
// - the CPLD starts with sample point B, so you get B C D E F A
// - the firmware skips the first quad, so you get F A B C D E
// - the frame buffer swaps odd and even pixels, so you get A F C B E D
//
// Mutate the result to correctly order the sample points:
// A F C B E D => A B C D E F
//
// Then the downstream algorithms don't have to worry
for (int j = 0; j < NUM_CHANNELS; j++) {
int f = diff[j + 1 * NUM_CHANNELS];
int b = diff[j + 3 * NUM_CHANNELS];
int d = diff[j + 5 * NUM_CHANNELS];
diff[j + 1 * NUM_CHANNELS] = b;
diff[j + 3 * NUM_CHANNELS] = d;
diff[j + 5 * NUM_CHANNELS] = f;
}
// Accumulate the result
for (int j = 0; j < NUM_CHANNELS * NUM_OFFSETS; j++) {