NxWM::CCalibration: Add logic to collect and average samples, optionally discarding the most extreem values

This commit is contained in:
Gregory Nutt 2013-10-14 18:35:23 -06:00
parent 1393518ba7
commit 31456e2e6a
5 changed files with 386 additions and 85 deletions

View file

@ -384,4 +384,7 @@
* NxWM::CCalibration.cxx/hxx: If CONFIG_NXWM_CALIBRATION_MESSAGES is
defined then CCalibration will provide some instructions in the center
of the display (2013-10-14).
* NxWM::CCalibration.cxx/hxx: Add options to collect and average
multiple samples at each location. Also logic to discard the most
extreme samples before averaging (2013-10-14).

32
Kconfig
View file

@ -791,8 +791,36 @@ config NXWM_CALIBRATION_FONTID
Use this default NxWidgets font ID instead of the system font ID
(NXFONT_DEFAULT). Default: 0
endif
endif
endif # NXWM_CALIBRATION_CUSTOM_FONTID
config NXWM_CALIBRATION_AVERAGE
bool "Average Samples"
default n
---help---
Collect multiple samples at each calibration position and use the
average of the samples. NOTE that is option is not available if we are
not providing instructions on the display. That is because it would
be impossible to know what to do if we are collecting multiple samples
at each position.
if NXWM_CALIBRATION_AVERAGE
config NXWM_CALIBRATION_NSAMPLES
int "Number of Samples in Average"
default 4
range 2 255
---help---
This is the number of samples to use in the average.
config NXWM_CALIBRATION_DISCARD_MINMAX
bool "Discard minimum and maximum values"
default n
---help---
Discard the largest and smallest values before averaging. This
setting is ignored if NXWM_CALIBRATION_NSAMPLES < 3.
endif # NXWM_CALIBRATION_AVERAGE
endif # NXWM_CALIBRATION_MESSAGES
config NXWM_CUSTOM_CALIBRATION_ICON
bool "Custom Calibration Icon"

View file

@ -160,6 +160,10 @@ namespace NxWM
bool m_stop; /**< True: We have been asked to stop the calibration */
bool m_touched; /**< True: The screen is touched */
uint8_t m_touchId; /**< The ID of the touch */
#if CONFIG_NXWM_CALIBRATION_AVERAGE
uint8_t m_nsamples; /**< Number of samples collected so far at this position */
struct nxgl_point_s m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES];
#endif
struct nxgl_point_s m_calibData[CALIB_DATA_POINTS];
/**
@ -170,6 +174,22 @@ namespace NxWM
void touchscreenInput(struct touch_sample_s &sample);
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
/**
* Create widgets need by the calibration thread.
*
* @return True if the widgets were successfully created.
*/
bool createWidgets(void);
/**
* Destroy widgets created for the calibration thread.
*/
void destroyWidgets(void);
#endif
/**
* Start the calibration thread.
*
@ -229,6 +249,17 @@ namespace NxWM
static FAR void *calibration(FAR void *arg);
/**
* Accumulate and average touch sample data
*
* @param average. When the averaged data is available, return it here
* @return True: Average data is available; False: Need to collect more samples
*/
#if CONFIG_NXWM_CALIBRATION_AVERAGE
bool averageSamples(struct nxgl_point_s &average);
#endif
/**
* This is the calibration state machine. It is called initially and then
* as new touchscreen data is received.

View file

@ -526,6 +526,36 @@
# define CONFIG_NXWM_CALIBRATION_MARGIN 40
#endif
// Calibration sample averaging
#ifndef CONFIG_NXWM_CALIBRATION_AVERAGE
# undef CONFIG_NXWM_CALIBRATION_AVERAGE
# undef CONFIG_NXWM_CALIBRATION_NSAMPLES
# define CONFIG_NXWM_CALIBRATION_NSAMPLES 1
# undef CONFIG_NXWM_CALIBRATION_DISCARD_MINMAX
#endif
#if !defined(CONFIG_NXWM_CALIBRATION_NSAMPLES) || CONFIG_NXWM_CALIBRATION_NSAMPLES < 2
# undef CONFIG_NXWM_CALIBRATION_AVERAGE
# undef CONFIG_NXWM_CALIBRATION_NSAMPLES
# define CONFIG_NXWM_CALIBRATION_NSAMPLES 1
# undef CONFIG_NXWM_CALIBRATION_DISCARD_MINMAX
#endif
#if CONFIG_NXWM_CALIBRATION_NSAMPLES < 3
# undef CONFIG_NXWM_CALIBRATION_DISCARD_MINMAX
#endif
#if CONFIG_NXWM_CALIBRATION_NSAMPLES > 255
# define CONFIG_NXWM_CALIBRATION_NSAMPLES 255
#endif
#ifdef CONFIG_NXWM_CALIBRATION_DISCARD_MINMAX
# define NXWM_CALIBRATION_NAVERAGE (CONFIG_NXWM_CALIBRATION_NSAMPLES - 2)
#else
# define NXWM_CALIBRATION_NAVERAGE CONFIG_NXWM_CALIBRATION_NSAMPLES
#endif
/* Hexcalculator applications ***********************************************/
/**
* Calibration display settings:

View file

@ -41,6 +41,7 @@
#include <cerrno>
#include <sched.h>
#include <limits.h>
#include <assert.h>
#include <debug.h>
@ -79,9 +80,9 @@
using namespace NxWM;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
static const char g_touchmsg[] = "Touch";
static const char g_againmsg[] = "Again";
static const char g_okmsg[] = "OK";
static const char g_touchMsg[] = "Touch";
static const char g_againMsg[] = "Again";
static const char g_okMsg[] = "OK";
#endif
/****************************************************************************
@ -352,7 +353,7 @@ void CCalibration::touchscreenInput(struct touch_sample_s &sample)
{
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_TOUCHEDCOLOR;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->setText(g_okmsg);
m_text->setText(g_okMsg);
#endif
showCalibration();
m_touched = true;
@ -402,6 +403,113 @@ void CCalibration::touchscreenInput(struct touch_sample_s &sample)
}
}
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
/**
* Create widgets need by the calibration thread.
*
* @return True if the widgets were successfully created.
*/
bool CCalibration::createWidgets(void)
{
// Select a font for the calculator
m_font = new NXWidgets::
CNxFont((nx_fontid_e)CONFIG_NXWM_CALIBRATION_FONTID,
CONFIG_NXWM_DEFAULT_FONTCOLOR, CONFIG_NXWM_TRANSPARENT_COLOR);
if (!m_font)
{
gdbg("ERROR failed to create font\n");
return false;
}
// Recover the window instance contained in the application window
NXWidgets::INxWindow *window = m_window->getWindow();
// Get the size of the window
struct nxgl_size_s windowSize;
if (!window->getSize(&windowSize))
{
gdbg("ERROR: Failed to get window size\n");
delete m_font;
m_font = (NXWidgets::CNxFont *)0;
return false;
}
// How big is the biggest string we might display?
nxgl_coord_t maxStringWidth = m_font->getStringWidth(g_touchMsg);
nxgl_coord_t altStringWidth = m_font->getStringWidth(g_againMsg);
if (altStringWidth > maxStringWidth)
{
maxStringWidth = altStringWidth;
}
// How big can the label be?
struct nxgl_size_s labelSize;
labelSize.w = maxStringWidth + 2*4;
labelSize.h = m_font->getHeight() + 2*4;
// Where should the label be?
struct nxgl_point_s labelPos;
labelPos.x = ((windowSize.w - labelSize.w) / 2);
labelPos.y = ((windowSize.h - labelSize.h) / 2);
// Get the widget control associated with the application window
NXWidgets::CWidgetControl *control = m_window->getWidgetControl();
// Create a label to show the calibration message.
m_text = new NXWidgets::
CLabel(control, labelPos.x, labelPos.y, labelSize.w, labelSize.h, "");
if (!m_text)
{
gdbg("ERROR: Failed to create CLabel\n");
delete m_font;
m_font = (NXWidgets::CNxFont *)0;
return false;
}
// No border
m_text->setBorderless(true);
// Center text
m_text->setTextAlignmentHoriz(NXWidgets::CLabel::TEXT_ALIGNMENT_HORIZ_CENTER);
// Disable drawing and events until we are asked to redraw the window
m_text->disableDrawing();
m_text->setRaisesEvents(false);
// Select the font
m_text->setFont(m_font);
return true;
}
/**
* Destroy widgets created for the calibration thread.
*/
void CCalibration::destroyWidgets(void)
{
delete m_text;
m_text = (NXWidgets::CLabel *)0;
delete m_font;
m_font = (NXWidgets::CNxFont *)0;
}
#endif
/**
* Start the calibration thread.
*
@ -463,77 +571,16 @@ FAR void *CCalibration::calibration(FAR void *arg)
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
// Create widgets that will be used in the calibration display
// Select a font for the calculator
This->m_font = new NXWidgets::
CNxFont((nx_fontid_e)CONFIG_NXWM_CALIBRATION_FONTID,
CONFIG_NXWM_DEFAULT_FONTCOLOR, CONFIG_NXWM_TRANSPARENT_COLOR);
if (!This->m_font)
if (!This->createWidgets())
{
gdbg("ERROR failed to create font\n");
gdbg("ERROR failed to create widgets\n");
return false;
}
// Recover the window instance contained in the application window
// No samples have yet been collected
NXWidgets::INxWindow *window = This->m_window->getWindow();
// Get the size of the window
struct nxgl_size_s windowSize;
if (!window->getSize(&windowSize))
{
gdbg("ERROR: Failed to get window size\n");
delete This->m_font;
This->m_font = (NXWidgets::CNxFont *)0;
return false;
}
// How big can the label be?
struct nxgl_size_s labelSize;
labelSize.w = (windowSize.w - 2*CONFIG_NXWM_CALIBRATION_MARGIN - 20);
labelSize.h = This->m_font->getHeight() + 2*4;
// Where should the label be?
struct nxgl_point_s labelPos;
labelPos.x = ((windowSize.w - labelSize.w) / 2);
labelPos.y = ((windowSize.h - labelSize.h) / 2);
// Get the widget control associated with the application window
NXWidgets::CWidgetControl *control = This->m_window->getWidgetControl();
// Create a label to show the calibration message.
This->m_text = new NXWidgets::
CLabel(control, labelPos.x, labelPos.y, labelSize.w, labelSize.h, "");
if (!This->m_text)
{
gdbg("ERROR: Failed to create CLabel\n");
delete This->m_font;
This->m_font = (NXWidgets::CNxFont *)0;
return false;
}
// No border
This->m_text->setBorderless(true);
// Center text
This->m_text->setTextAlignmentHoriz(NXWidgets::CLabel::TEXT_ALIGNMENT_HORIZ_CENTER);
// Disable drawing and events until we are asked to redraw the window
This->m_text->disableDrawing();
This->m_text->setRaisesEvents(false);
// Select the font
This->m_text->setFont(This->m_font);
This->m_nsamples = 0;
#endif
// The calibration thread is now running
@ -611,20 +658,125 @@ FAR void *CCalibration::calibration(FAR void *arg)
This->finishCalibration();
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
// Destroy widgets
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
delete This->m_text;
This->m_text = (NXWidgets::CLabel *)0;
delete This->m_font;
This->m_font = (NXWidgets::CNxFont *)0;
This->destroyWidgets();
#endif
gvdbg("Terminated: m_calthread=%d\n", (int)This->m_calthread);
return (FAR void *)0;
}
/**
* Accumulate and average touch sample data
*
* @param average. When the averaged data is available, return it here
* @return True: Average data is available; False: Need to collect more samples
*/
#if CONFIG_NXWM_CALIBRATION_AVERAGE
bool CCalibration::averageSamples(struct nxgl_point_s &average)
{
// Have we started collecting sample data? */
// Save the sample data
gvdbg("Sample %d: Touch x: %d y: %d\n", m_nsamples+1, m_touchPos.x, m_touchPos.y);
m_sampleData[m_nsamples].x = m_touchPos.x;
m_sampleData[m_nsamples].y = m_touchPos.y;
m_nsamples++;
// Have all of the samples been collected?
if (m_nsamples < CONFIG_NXWM_CALIBRATION_NSAMPLES)
{
// Not yet... return false
return false;
}
// Yes.. we have all of the samples
#ifdef CONFIG_NXWM_CALIBRATION_NSAMPLES
// Discard the smallest X and Y values
int xValue = INT_MAX;
int xIndex = -1;
int yValue = INT_MAX;
int yIndex = -1;
for (int i = 0; i < CONFIG_NXWM_CALIBRATION_NSAMPLES; i++)
{
if ((int)m_sampleData[i].x < xValue)
{
xValue = (int)m_sampleData[i].x;
xIndex = i;
}
if ((int)m_sampleData[i].y < yValue)
{
yValue = (int)m_sampleData[i].y;
yIndex = i;
}
}
m_sampleData[xIndex].x = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-1].x;
m_sampleData[yIndex].y = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-1].y;
// Discard the largest X and Y values
xValue = -1;
xIndex = -1;
yValue = -1;
yIndex = -1;
for (int i = 0; i < CONFIG_NXWM_CALIBRATION_NSAMPLES-1; i++)
{
if ((int)m_sampleData[i].x > xValue)
{
xValue = (int)m_sampleData[i].x;
xIndex = i;
}
if ((int)m_sampleData[i].y > yValue)
{
yValue = (int)m_sampleData[i].y;
yIndex = i;
}
}
m_sampleData[xIndex].x = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-2].x;
m_sampleData[yIndex].y = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-2].y;
#endif
#if NXWM_CALIBRATION_NAVERAGE > 1
// Calculate the average of the remaining values
long xaccum = 0;
long yaccum = 0;
for (int i = 0; i < NXWM_CALIBRATION_NAVERAGE; i++)
{
xaccum += m_sampleData[i].x;
yaccum += m_sampleData[i].y;
}
average.x = xaccum / NXWM_CALIBRATION_NAVERAGE;
average.y = yaccum / NXWM_CALIBRATION_NAVERAGE;
#else
average.x = m_sampleData[0].x;
average.y = m_sampleData[0].y;
#endif
gvdbg("Average: Touch x: %d y: %d\n", average.x, average.y);
m_nsamples = 0;
return true;
}
#endif
/**
* This is the calibration state machine. It is called initially and then
* as new touchscreen data is received.
@ -634,6 +786,43 @@ void CCalibration::stateMachine(void)
{
gvdbg("Old m_calphase=%d\n", m_calphase);
#if CONFIG_NXWM_CALIBRATION_AVERAGE
// Are we collecting samples?
struct nxgl_point_s average;
switch (m_calphase)
{
case CALPHASE_UPPER_LEFT:
case CALPHASE_UPPER_RIGHT:
case CALPHASE_LOWER_RIGHT:
case CALPHASE_LOWER_LEFT:
{
// Yes... Have all of the samples been collected?
if (!averageSamples(average))
{
// Not yet... Show the calibration screen again with the circle
// in the normal color and an instruction to touch the circle again.
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
m_text->setText(g_againMsg);
showCalibration();
// And wait for the next touch
return;
}
}
break;
// No... we are not collecting data now
default:
break;
}
#endif
// Recover the window instance contained in the full screen window
NXWidgets::INxWindow *window = m_window->getWindow();
@ -672,7 +861,7 @@ void CCalibration::stateMachine(void)
m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->setText(g_touchmsg);
m_text->setText(g_touchMsg);
#endif
showCalibration();
@ -687,8 +876,13 @@ void CCalibration::stateMachine(void)
// A touch has been received while in the CALPHASE_UPPER_LEFT state.
// Save the touch data and set up the next calibration display
#if CONFIG_NXWM_CALIBRATION_AVERAGE
m_calibData[CALIB_UPPER_LEFT_INDEX].x = average.x;
m_calibData[CALIB_UPPER_LEFT_INDEX].y = average.y;
#else
m_calibData[CALIB_UPPER_LEFT_INDEX].x = m_touchPos.x;
m_calibData[CALIB_UPPER_LEFT_INDEX].y = m_touchPos.y;
#endif
// Clear the previous screen by re-drawing it using the background
// color. That is much faster than clearing the whole display
@ -704,7 +898,7 @@ void CCalibration::stateMachine(void)
m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->setText(g_touchmsg);
m_text->setText(g_touchMsg);
#endif
showCalibration();
@ -719,8 +913,13 @@ void CCalibration::stateMachine(void)
// A touch has been received while in the CALPHASE_UPPER_RIGHT state.
// Save the touch data and set up the next calibration display
#if CONFIG_NXWM_CALIBRATION_AVERAGE
m_calibData[CALIB_UPPER_RIGHT_INDEX].x = average.x;
m_calibData[CALIB_UPPER_RIGHT_INDEX].y = average.y;
#else
m_calibData[CALIB_UPPER_RIGHT_INDEX].x = m_touchPos.x;
m_calibData[CALIB_UPPER_RIGHT_INDEX].y = m_touchPos.y;
#endif
// Clear the previous screen by re-drawing it using the backgro9und
// color. That is much faster than clearing the whole display
@ -736,7 +935,7 @@ void CCalibration::stateMachine(void)
m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->setText(g_touchmsg);
m_text->setText(g_touchMsg);
#endif
showCalibration();
@ -751,8 +950,13 @@ void CCalibration::stateMachine(void)
// A touch has been received while in the CALPHASE_LOWER_RIGHT state.
// Save the touch data and set up the next calibration display
#if CONFIG_NXWM_CALIBRATION_AVERAGE
m_calibData[CALIB_LOWER_RIGHT_INDEX].x = average.x;
m_calibData[CALIB_LOWER_RIGHT_INDEX].y = average.y;
#else
m_calibData[CALIB_LOWER_RIGHT_INDEX].x = m_touchPos.x;
m_calibData[CALIB_LOWER_RIGHT_INDEX].y = m_touchPos.y;
#endif
// Clear the previous screen by re-drawing it using the backgro9und
// color. That is much faster than clearing the whole display
@ -768,7 +972,7 @@ void CCalibration::stateMachine(void)
m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->setText(g_touchmsg);
m_text->setText(g_touchMsg);
#endif
showCalibration();
@ -783,8 +987,13 @@ void CCalibration::stateMachine(void)
// A touch has been received while in the CALPHASE_LOWER_LEFT state.
// Save the touch data and set up the next calibration display
#if CONFIG_NXWM_CALIBRATION_AVERAGE
m_calibData[CALIB_LOWER_LEFT_INDEX].x = average.x;
m_calibData[CALIB_LOWER_LEFT_INDEX].y = average.y;
#else
m_calibData[CALIB_LOWER_LEFT_INDEX].x = m_touchPos.x;
m_calibData[CALIB_LOWER_LEFT_INDEX].y = m_touchPos.y;
#endif
// Clear the previous screen by re-drawing it using the backgro9und
// color. That is much faster than clearing the whole display
@ -792,7 +1001,7 @@ void CCalibration::stateMachine(void)
m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->setText(g_touchmsg);
m_text->setText(g_touchMsg);
#endif
showCalibration();
@ -844,17 +1053,17 @@ void CCalibration::showCalibration(void)
port->drawFilledCircle(&m_screenInfo.pos, CALIBRATION_CIRCLE_RADIUS,
m_screenInfo.circleFillColor);
/* Draw horizontal line */
// Draw horizontal line
port->drawFilledRect(0, m_screenInfo.pos.y, windowSize.w, CALIBRATION_LINE_THICKNESS,
m_screenInfo.lineColor);
/* Draw vertical line */
// Draw vertical line
port->drawFilledRect(m_screenInfo.pos.x, 0, CALIBRATION_LINE_THICKNESS, windowSize.h,
m_screenInfo.lineColor);
/* Show the touchscreen message */
// Show the touchscreen message
#ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
m_text->enableDrawing();