MotionCal/gui.cpp

441 lines
13 KiB
C++
Raw Normal View History

2016-02-16 17:19:44 -08:00
#include "gui.h"
#include "imuread.h"
2016-02-18 01:31:30 -08:00
2016-03-15 15:32:15 -07:00
wxString port_name;
2016-02-18 01:31:30 -08:00
wxBEGIN_EVENT_TABLE(MyCanvas, wxGLCanvas)
2016-03-15 15:32:15 -07:00
EVT_SIZE(MyCanvas::OnSize)
EVT_PAINT(MyCanvas::OnPaint)
//EVT_CHAR(MyCanvas::OnChar)
//EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
2016-02-18 01:31:30 -08:00
wxEND_EVENT_TABLE()
MyCanvas::MyCanvas(wxWindow *parent, wxWindowID id, int* gl_attrib)
: wxGLCanvas(parent, id, gl_attrib)
{
//m_xrot = 0;
//m_yrot = 0;
//m_numverts = 0;
// Explicitly create a new rendering context instance for this canvas.
m_glRC = new wxGLContext(this);
}
MyCanvas::~MyCanvas()
{
delete m_glRC;
}
void MyCanvas::OnSize(wxSizeEvent& event)
{
//printf("OnSize\n");
if (!IsShownOnScreen()) return;
SetCurrent(*m_glRC);
resize_callback(event.GetSize().x, event.GetSize().y);
}
void MyCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
{
//printf("OnPaint\n");
wxPaintDC dc(this);
SetCurrent(*m_glRC);
display_callback();
SwapBuffers();
}
void MyCanvas::InitGL()
{
//printf("Init\n");
SetCurrent(*m_glRC);
visualize_init();
}
/*****************************************************************************/
2016-02-18 01:31:30 -08:00
2016-02-16 17:19:44 -08:00
BEGIN_EVENT_TABLE(MyFrame,wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
EVT_MENU(ID_SENDCAL_MENU, MyFrame::OnSendCal)
EVT_BUTTON(ID_CLEAR_BUTTON, MyFrame::OnClear)
EVT_BUTTON(ID_SENDCAL_BUTTON, MyFrame::OnSendCal)
2016-02-18 01:31:30 -08:00
EVT_TIMER(ID_TIMER, MyFrame::OnTimer)
2016-04-03 07:04:18 -07:00
EVT_MENU_RANGE(9000, 9999, MyFrame::OnPortMenu)
EVT_MENU_OPEN(MyFrame::OnShowMenu)
2016-04-03 07:04:18 -07:00
EVT_COMBOBOX(ID_PORTLIST, MyFrame::OnPortList)
EVT_COMBOBOX_DROPDOWN(ID_PORTLIST, MyFrame::OnShowPortList)
2016-02-16 17:19:44 -08:00
END_EVENT_TABLE()
2016-02-16 17:19:44 -08:00
MyFrame::MyFrame(wxWindow *parent, wxWindowID id, const wxString &title,
const wxPoint &position, const wxSize& size, long style) :
wxFrame( parent, id, title, position, size, style )
{
2016-02-18 01:31:30 -08:00
wxMenuBar *menuBar;
wxMenu *menu;
wxSizer *hsizer, *vsizer, *calsizer;
wxStaticText *text;
int i, j;
2016-02-18 01:31:30 -08:00
menuBar = new wxMenuBar;
menu = new wxMenu;
menu->Append(ID_SENDCAL_MENU, wxT("Send Calibration"));
m_sendcal_menu = menu;
m_sendcal_menu->Enable(ID_SENDCAL_MENU, false);
2016-02-18 01:31:30 -08:00
menu->Append(wxID_EXIT, wxT("Quit"));
menuBar->Append(menu, wxT("&File"));
2016-03-15 15:32:15 -07:00
menu = new wxMenu;
menuBar->Append(menu, "Port");
m_port_menu = menu;
2016-03-15 15:32:15 -07:00
2016-02-18 01:31:30 -08:00
menu = new wxMenu;
menu->Append(wxID_ABOUT, wxT("About"));
menuBar->Append(menu, wxT("&Help"));
SetMenuBar(menuBar);
wxBoxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *leftsizer = new wxStaticBoxSizer(wxVERTICAL, this, "Communication");
wxBoxSizer *middlesizer = new wxStaticBoxSizer(wxVERTICAL, this, "Magnetometer");
wxBoxSizer *rightsizer = new wxStaticBoxSizer(wxVERTICAL, this, "Calibration");
topsizer->Add(leftsizer, 0, wxALL | wxEXPAND | wxALIGN_TOP, 5);
topsizer->Add(middlesizer, 1, wxALL | wxEXPAND, 5);
topsizer->Add(rightsizer, 0, wxALL | wxEXPAND | wxALIGN_TOP, 5);
vsizer = new wxBoxSizer(wxVERTICAL);
leftsizer->Add(vsizer, 0, wxALL, 8);
text = new wxStaticText(this, wxID_ANY, "Port");
vsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
2016-04-03 07:04:18 -07:00
m_port_list = new wxComboBox(this, ID_PORTLIST, "",
wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
m_port_list->Append("(none)");
2016-04-03 07:04:18 -07:00
m_port_list->Append(SAMPLE_PORT_NAME); // never seen, only for initial size
m_port_list->SetSelection(0);
vsizer->Add(m_port_list, 1, wxEXPAND, 0);
vsizer->AddSpacer(8);
text = new wxStaticText(this, wxID_ANY, "Actions");
vsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
m_button_clear = new wxButton(this, ID_CLEAR_BUTTON, "Clear");
m_button_clear->Enable(false);
vsizer->Add(m_button_clear, 1, wxEXPAND, 0);
m_button_sendcal = new wxButton(this, ID_SENDCAL_BUTTON, "Send Cal");
vsizer->Add(m_button_sendcal, 1, wxEXPAND, 0);
m_button_sendcal->Enable(false);
vsizer = new wxBoxSizer(wxVERTICAL);
middlesizer->Add(vsizer, 1, wxEXPAND | wxALL, 8);
text = new wxStaticText(this, wxID_ANY, "");
text->SetLabelMarkup("<small><i>Ideal calibration is a perfectly centered sphere</i></small>");
vsizer->Add(text, 0, wxALIGN_CENTER_HORIZONTAL, 0);
int gl_attrib[20] = { WX_GL_RGBA, WX_GL_MIN_RED, 1, WX_GL_MIN_GREEN, 1,
WX_GL_MIN_BLUE, 1, WX_GL_DEPTH_SIZE, 1, WX_GL_DOUBLEBUFFER, 0};
2016-02-18 01:31:30 -08:00
m_canvas = new MyCanvas(this, wxID_ANY, gl_attrib);
2016-03-16 01:30:33 -07:00
m_canvas->SetMinSize(wxSize(400,400));
vsizer->Add(m_canvas, 1, wxEXPAND | wxALL, 0);
hsizer = new wxGridSizer(4, 0, 15);
middlesizer->Add(hsizer, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, 5);
vsizer = new wxBoxSizer(wxVERTICAL);
hsizer->Add(vsizer, 1, wxALIGN_CENTER_HORIZONTAL);
text = new wxStaticText(this, wxID_ANY, "Gaps");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_coverage = new wxStaticText(this, wxID_ANY, "100.0%");
vsizer->Add(m_err_coverage, 1, wxALIGN_CENTER_HORIZONTAL);
vsizer = new wxBoxSizer(wxVERTICAL);
hsizer->Add(vsizer, 1, wxALIGN_CENTER_HORIZONTAL);
text = new wxStaticText(this, wxID_ANY, "Variance");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_variance = new wxStaticText(this, wxID_ANY, "100.0%");
vsizer->Add(m_err_variance, 1, wxALIGN_CENTER_HORIZONTAL);
vsizer = new wxBoxSizer(wxVERTICAL);
hsizer->Add(vsizer, 1, wxALIGN_CENTER_HORIZONTAL);
text = new wxStaticText(this, wxID_ANY, "Wobble");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_wobble = new wxStaticText(this, wxID_ANY, "100.0%");
vsizer->Add(m_err_wobble, 1, wxALIGN_CENTER_HORIZONTAL);
vsizer = new wxBoxSizer(wxVERTICAL);
hsizer->Add(vsizer, 1, wxALIGN_CENTER_HORIZONTAL);
text = new wxStaticText(this, wxID_ANY, "Fit Error");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_fit = new wxStaticText(this, wxID_ANY, "100.0%");
vsizer->Add(m_err_fit, 1, wxALIGN_CENTER_HORIZONTAL);
calsizer = new wxBoxSizer(wxVERTICAL);
rightsizer->Add(calsizer, 0, wxALL, 8);
text = new wxStaticText(this, wxID_ANY, "Magnetic Offset");
calsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
vsizer = new wxGridSizer(1, 0, 0);
calsizer->Add(vsizer, 1, wxLEFT, 20);
for (i=0; i < 3; i++) {
m_mag_offset[i] = new wxStaticText(this, wxID_ANY, "0.00");
vsizer->Add(m_mag_offset[i], 1);
}
text = new wxStaticText(this, wxID_ANY, "Magnetic Mapping");
calsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
vsizer = new wxGridSizer(3, 0, 12);
calsizer->Add(vsizer, 1, wxLEFT, 20);
for (i=0; i < 3; i++) {
for (j=0; j < 3; j++) {
m_mag_mapping[i][j] = new wxStaticText(this, wxID_ANY,
((i == j) ? "+1.000" : "+0.000"));
vsizer->Add(m_mag_mapping[i][j], 1);
}
}
text = new wxStaticText(this, wxID_ANY, "Magnetic Field");
calsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
m_mag_field = new wxStaticText(this, wxID_ANY, "0.00");
calsizer->Add(m_mag_field, 0, wxLEFT, 20);
text = new wxStaticText(this, wxID_ANY, "Accelerometer");
calsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
vsizer = new wxGridSizer(1, 0, 0);
calsizer->Add(vsizer, 1, wxLEFT, 20);
for (i=0; i < 3; i++) {
m_accel[i] = new wxStaticText(this, wxID_ANY, "0.000");
vsizer->Add(m_accel[i], 1);
}
text = new wxStaticText(this, wxID_ANY, "Gyroscope");
calsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
vsizer = new wxGridSizer(1, 0, 0);
calsizer->Add(vsizer, 1, wxLEFT, 20);
for (i=0; i < 3; i++) {
m_gyro[i] = new wxStaticText(this, wxID_ANY, "0.000");
vsizer->Add(m_gyro[i], 1);
}
calsizer->AddSpacer(8);
text = new wxStaticText(this, wxID_ANY, "");
text->SetLabelMarkup("<small>Calibration should be performed\n<b>after</b> final installation. Presence\nof magnets and ferrous metals\ncan alter magnetic calibration.\nMechanical stress during\nassembly can alter accelerometer\nand gyroscope calibration.</small>");
//text->Wrap(200);
//calsizer->Add(text, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL, 0);
calsizer->Add(text, 0, wxALIGN_CENTER_HORIZONTAL, 0);
topsizer->SetSizeHints(this);
SetSizerAndFit(topsizer);
2016-02-18 01:31:30 -08:00
Show(true);
Raise();
m_canvas->InitGL();
2016-03-15 15:32:15 -07:00
raw_data_reset();
//open_port(PORT);
2016-02-18 01:31:30 -08:00
m_timer = new wxTimer(this, ID_TIMER);
m_timer->Start(14, wxTIMER_CONTINUOUS);
2016-02-18 01:31:30 -08:00
}
void MyFrame::OnTimer(wxTimerEvent &event)
{
float gaps, variance, wobble, fiterror;
char buf[32];
int i, j;
2016-02-18 01:31:30 -08:00
//printf("OnTimer\n");
2016-03-15 16:04:11 -07:00
if (port_is_open()) {
read_serial_data();
m_canvas->Refresh();
gaps = quality_surface_gap_error();
variance = quality_magnitude_variance_error();
wobble = quality_wobble_error();
fiterror = quality_spherical_fit_error();
if (gaps < 15.0f && variance < 4.5f && wobble < 4.0f && fiterror < 5.0f) {
m_sendcal_menu->Enable(ID_SENDCAL_MENU, true);
2016-04-03 07:04:18 -07:00
m_button_sendcal->Enable(true);
} else if (gaps > 20.0f && variance > 5.0f && wobble > 5.0f && fiterror > 6.0f) {
m_sendcal_menu->Enable(ID_SENDCAL_MENU, false);
2016-04-03 07:04:18 -07:00
m_button_sendcal->Enable(false);
2016-03-15 16:04:11 -07:00
}
snprintf(buf, sizeof(buf), "%.1f%%", quality_surface_gap_error());
m_err_coverage->SetLabelText(buf);
snprintf(buf, sizeof(buf), "%.1f%%", quality_magnitude_variance_error());
m_err_variance->SetLabelText(buf);
snprintf(buf, sizeof(buf), "%.1f%%", quality_wobble_error());
m_err_wobble->SetLabelText(buf);
snprintf(buf, sizeof(buf), "%.1f%%", quality_spherical_fit_error());
m_err_fit->SetLabelText(buf);
for (i=0; i < 3; i++) {
snprintf(buf, sizeof(buf), "%.2f", magcal.V[i]);
m_mag_offset[i]->SetLabelText(buf);
}
for (i=0; i < 3; i++) {
for (j=0; j < 3; j++) {
snprintf(buf, sizeof(buf), "%+.3f", magcal.invW[i][j]);
m_mag_mapping[i][j]->SetLabelText(buf);
}
}
snprintf(buf, sizeof(buf), "%.2f", magcal.B);
m_mag_field->SetLabelText(buf);
for (i=0; i < 3; i++) {
snprintf(buf, sizeof(buf), "%.3f", 0.0f); // TODO...
m_accel[i]->SetLabelText(buf);
}
for (i=0; i < 3; i++) {
snprintf(buf, sizeof(buf), "%.3f", 0.0f); // TODO...
m_gyro[i]->SetLabelText(buf);
}
2016-03-15 16:04:11 -07:00
} else {
2016-04-03 07:04:18 -07:00
if (!port_name.IsEmpty()) {
//printf("port has closed, updating stuff\n");
m_sendcal_menu->Enable(ID_SENDCAL_MENU, false);
m_button_clear->Enable(false);
m_button_sendcal->Enable(false);
m_port_list->Clear();
m_port_list->Append("(none)");
m_port_list->SetSelection(0);
port_name = "";
}
2016-03-15 16:04:11 -07:00
}
2016-02-16 17:19:44 -08:00
}
void MyFrame::OnClear(wxCommandEvent &event)
{
2016-04-03 07:04:18 -07:00
//printf("OnClear\n");
raw_data_reset();
}
2016-03-15 16:04:11 -07:00
void MyFrame::OnSendCal(wxCommandEvent &event)
{
2016-04-03 07:04:18 -07:00
/*printf("OnSendCal\n");
2016-03-15 16:04:11 -07:00
printf("Magnetic Calibration: (%.1f%% fit error)\n", magcal.FitError);
printf(" %7.2f %6.3f %6.3f %6.3f\n",
magcal.V[0], magcal.invW[0][0], magcal.invW[0][1], magcal.invW[0][2]);
printf(" %7.2f %6.3f %6.3f %6.3f\n",
magcal.V[1], magcal.invW[1][0], magcal.invW[1][1], magcal.invW[1][2]);
printf(" %7.2f %6.3f %6.3f %6.3f\n",
magcal.V[2], magcal.invW[2][0], magcal.invW[2][1], magcal.invW[2][2]);
2016-04-03 07:04:18 -07:00
*/
2016-03-15 16:04:11 -07:00
send_calibration();
}
2016-03-15 15:32:15 -07:00
2016-04-03 07:04:18 -07:00
void MyFrame::OnShowMenu(wxMenuEvent &event)
{
2016-04-03 07:04:18 -07:00
wxMenu *menu = event.GetMenu();
if (menu != m_port_menu) return;
2016-04-03 07:04:18 -07:00
//printf("OnShow Port Menu, %s\n", (const char *)menu->GetTitle());
while (menu->GetMenuItemCount() > 0) {
menu->Delete(menu->GetMenuItems()[0]);
}
menu->AppendRadioItem(9000, " (none)");
bool isopen = port_is_open();
2016-04-03 07:04:18 -07:00
if (!isopen) menu->Check(9000, true);
wxArrayString list = serial_port_list();
int num = list.GetCount();
for (int i=0; i < num; i++) {
menu->AppendRadioItem(9001 + i, list[i]);
if (isopen && port_name.IsSameAs(list[i])) {
menu->Check(9001 + i, true);
}
}
menu->UpdateUI();
}
2016-04-03 07:04:18 -07:00
void MyFrame::OnShowPortList(wxCommandEvent& event)
{
2016-04-03 07:04:18 -07:00
//printf("OnShowPortList\n");
m_port_list->Clear();
m_port_list->Append("(none)");
wxArrayString list = serial_port_list();
int num = list.GetCount();
for (int i=0; i < num; i++) {
m_port_list->Append(list[i]);
}
}
2016-04-03 07:04:18 -07:00
void MyFrame::OnPortMenu(wxCommandEvent &event)
2016-03-15 15:32:15 -07:00
{
int id = event.GetId();
wxString name = m_port_menu->FindItem(id)->GetItemLabelText();
2016-03-15 15:32:15 -07:00
close_port();
2016-04-03 07:04:18 -07:00
//printf("OnPortMenu, id = %d, name = %s\n", id, (const char *)name);
2016-03-15 15:32:15 -07:00
port_name = name;
2016-04-03 07:04:18 -07:00
m_port_list->Clear();
m_port_list->Append(port_name);
m_port_list->SetSelection(0);
2016-03-15 15:32:15 -07:00
if (id == 9000) return;
raw_data_reset();
open_port((const char *)name);
2016-04-03 07:04:18 -07:00
m_button_clear->Enable(true);
2016-03-15 15:32:15 -07:00
}
2016-04-03 07:04:18 -07:00
void MyFrame::OnPortList(wxCommandEvent& event)
{
int selected = m_port_list->GetSelection();
if (selected == wxNOT_FOUND) return;
wxString name = m_port_list->GetString(selected);
//printf("OnPortList, %s\n", (const char *)name);
close_port();
port_name = name;
if (name == "(none)") return;
raw_data_reset();
open_port((const char *)name);
m_button_clear->Enable(true);
}
2016-03-15 15:32:15 -07:00
2016-02-16 17:19:44 -08:00
void MyFrame::OnAbout(wxCommandEvent &event)
{
wxMessageDialog dialog(this,
2016-04-01 05:37:52 -07:00
"MotionCal - Motion Sensor Calibration Tool\n\n"
"Paul Stoffregen <paul@pjrc.com>\n"
"http://www.pjrc.com/store/prop_shield.html\n"
"https://github.com/PaulStoffregen/MotionCal\n\n"
"Copyright 2016, PJRC.COM, LLC.",
"About MotionCal", wxOK|wxICON_INFORMATION|wxCENTER);
2016-02-16 17:19:44 -08:00
dialog.ShowModal();
}
void MyFrame::OnQuit( wxCommandEvent &event )
{
Close(true);
}
MyFrame::~MyFrame(void)
{
2016-04-01 00:58:50 -07:00
m_timer->Stop();
2016-02-18 01:31:30 -08:00
close_port();
2016-02-16 17:19:44 -08:00
}
2016-03-15 15:32:15 -07:00
/*****************************************************************************/
2016-02-16 17:19:44 -08:00
IMPLEMENT_APP(MyApp)
MyApp::MyApp()
{
}
bool MyApp::OnInit()
{
// make sure we exit properly on macosx
SetExitOnFrameDelete(true);
wxPoint pos(100, 100);
2016-04-01 05:37:52 -07:00
MyFrame *frame = new MyFrame(NULL, -1, "Motion Sensor Calibration Tool",
pos, wxSize(1120,760), wxDEFAULT_FRAME_STYLE);
2016-04-02 12:43:57 -07:00
#ifdef WINDOWS
frame->SetIcon(wxIcon("MotionCal"));
#endif
2016-02-16 17:19:44 -08:00
frame->Show( true );
return true;
}
int MyApp::OnExit()
{
return 0;
}
2016-03-15 15:32:15 -07:00