Compare commits

...

10 commits

Author SHA1 Message Date
PaulStoffregen
c24740cd8f Add link to info about quality metric algorithms 2020-02-13 17:31:17 -08:00
PaulStoffregen
3e24361d18 Show calibration confirmation in GUI 2018-01-23 08:47:41 -08:00
PaulStoffregen
a68ad5a3c3 Check sent calibration with incoming cal messages 2018-01-23 07:06:44 -08:00
PaulStoffregen
8ee5e0bf55 Experimental (and ultimately ineffective) fiddling with axes 2018-01-23 06:14:51 -08:00
PaulStoffregen
dabaaece7d Fix use of uninitialized data in fUpdateCalibration10EIG 2018-01-22 18:59:52 -08:00
PaulStoffregen
f8f279563f Fix rawdata z axis 2018-01-22 16:10:39 -08:00
PaulStoffregen
d579c0dcf5 Detect USB errors on Windows, fixes #3 2016-04-03 14:20:27 -07:00
PaulStoffregen
447bd9e429 Improve appearance, especially on Windows 2016-04-03 13:32:15 -07:00
PaulStoffregen
a3a4b7e504 Make new GUI stuff (mostly) work 2016-04-03 07:04:18 -07:00
PaulStoffregen
1a6a6f48ee Add more GUI controls (not functional yet...) 2016-04-02 17:12:53 -07:00
15 changed files with 525 additions and 147 deletions

View file

@ -8,10 +8,11 @@ ALL = MotionCal imuread
CC = gcc
CXX = g++
CFLAGS = -O2 -Wall -D$(OS)
WXCONFIG = ~/wxwidgets/3.0.2.gtk2-opengl/bin/wx-config
WXFLAGS = `$(WXCONFIG) --cppflags`
CXXFLAGS = $(CFLAGS) `$(WXCONFIG) --cppflags`
LDFLAGS =
SFLAG = -s
WXCONFIG = ~/wxwidgets/3.0.2.gtk2-opengl/bin/wx-config
CLILIBS = -lglut -lGLU -lGL -lm
MAKEFLAGS = --jobs=12
@ -20,8 +21,9 @@ ALL = MotionCal.dmg
CC = gcc-4.2
CXX = g++-4.2
CFLAGS = -O2 -Wall -D$(OS)
CXXFLAGS = $(CFLAGS) `$(WXCONFIG) --cppflags`
WXCONFIG = ~/wxwidgets/3.0.2.mac-opengl/bin/wx-config
WXFLAGS = `$(WXCONFIG) --cppflags`
CXXFLAGS = $(CFLAGS) `$(WXCONFIG) --cppflags`
SFLAG = -s
CLILIBS = -lglut -lGLU -lGL -lm
VERSION = 0.01
@ -32,6 +34,7 @@ CC = /usr/bin/clang
CXX = /usr/bin/clang++
CFLAGS = -O2 -Wall -DMACOSX
WXCONFIG = wx-config
WXFLAGS = `$(WXCONFIG) --cppflags`
CXXFLAGS = $(CFLAGS) `$(WXCONFIG) --cppflags`
SFLAG =
CLILIBS = -lglut -lGLU -lGL -lm
@ -39,33 +42,42 @@ VERSION = 0.01
else ifeq ($(OS), WINDOWS)
ALL = MotionCal.exe
CC = i686-w64-mingw32-gcc
CXX = i686-w64-mingw32-g++
WINDRES = i686-w64-mingw32-windres
#MINGW_TOOLCHAIN = i586-mingw32msvc
MINGW_TOOLCHAIN = i686-w64-mingw32
CC = $(MINGW_TOOLCHAIN)-gcc
CXX = $(MINGW_TOOLCHAIN)-g++
WINDRES = $(MINGW_TOOLCHAIN)-windres
CFLAGS = -O2 -Wall -D$(OS)
CXXFLAGS = $(CFLAGS) `$(WXCONFIG) --cppflags`
WXFLAGS = `$(WXCONFIG) --cppflags`
CXXFLAGS = $(CFLAGS) $(WXFLAGS)
LDFLAGS = -static -static-libgcc
SFLAG = -s
WXCONFIG = ~/wxwidgets/3.0.2.mingw-opengl/bin/wx-config
#WXCONFIG = ~/wxwidgets/3.0.2.mingw-opengl-i586/bin/wx-config
#WXCONFIG = ~/wxwidgets/3.0.2.mingw-opengl/bin/wx-config
WXCONFIG = ~/wxwidgets/3.1.0.mingw-opengl/bin/wx-config
CLILIBS = -lglut32 -lglu32 -lopengl32 -lm
MAKEFLAGS = --jobs=12
endif
OBJS = visualize.o serialdata.o rawdata.o magcal.o matrix.o fusion.o quality.o mahony.o
IMGS = checkgreen.png checkempty.png checkemptygray.png
all: $(ALL)
MotionCal: gui.o portlist.o $(OBJS)
MotionCal: gui.o portlist.o images.o $(OBJS)
$(CXX) $(SFLAG) $(CFLAGS) $(LDFLAGS) -o $@ $^ `$(WXCONFIG) --libs all,opengl`
MotionCal.exe: resource.o gui.o portlist.o $(OBJS)
MotionCal.exe: resource.o gui.o portlist.o images.o $(OBJS)
$(CXX) $(SFLAG) $(CFLAGS) $(LDFLAGS) -o $@ $^ `$(WXCONFIG) --libs all,opengl`
-pjrcwinsigntool $@
-./cp_windows.sh $@
resource.o: resource.rs icon.ico
$(WINDRES) -o resource.o resource.rs
resource.o: resource.rc icon.ico
$(WINDRES) $(WXFLAGS) -o resource.o resource.rc
images.cpp: $(IMGS) png2c.pl
perl png2c.pl $(IMGS) > images.cpp
MotionCal.app: MotionCal Info.plist icon.icns
mkdir -p $@/Contents/MacOS
@ -86,7 +98,7 @@ imuread: imuread.o $(OBJS)
$(CC) -s $(CFLAGS) $(LDFLAGS) -o $@ $^ $(CLILIBS)
clean:
rm -f gui MotionCal imuread *.o *.exe *.sign?
rm -f gui MotionCal imuread *.o *.exe *.sign? images.cpp
rm -rf MotionCal.app MotionCal.dmg .DS_Store dmg_tmpdir
gui.o: gui.cpp gui.h imuread.h Makefile

BIN
checkempty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

BIN
checkemptygray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

BIN
checkgreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

277
gui.cpp
View file

@ -2,10 +2,8 @@
#include "imuread.h"
wxMenu *port_menu;
wxMenu *sendcal_menu;
wxString port_name;
static bool show_calibration_confirmed = false;
wxBEGIN_EVENT_TABLE(MyCanvas, wxGLCanvas)
@ -49,9 +47,10 @@ void MyCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
void MyCanvas::InitGL()
{
//printf("Init\n");
SetCurrent(*m_glRC);
visualize_init();
wxSizeEvent e = wxSizeEvent(GetSize());
OnSize(e);
}
@ -61,144 +60,179 @@ void MyCanvas::InitGL()
BEGIN_EVENT_TABLE(MyFrame,wxFrame)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
EVT_MENU(ID_SENDCAL, MyFrame::OnSendCal)
EVT_MENU(ID_SENDCAL_MENU, MyFrame::OnSendCal)
EVT_BUTTON(ID_CLEAR_BUTTON, MyFrame::OnClear)
EVT_BUTTON(ID_SENDCAL_BUTTON, MyFrame::OnSendCal)
EVT_TIMER(ID_TIMER, MyFrame::OnTimer)
EVT_MENU_RANGE(9000, 9999, MyFrame::OnPort)
EVT_MENU_OPEN(MyMenu::OnShowPortList)
EVT_MENU_HIGHLIGHT(-1, MyMenu::OnHighlight)
EVT_MENU_RANGE(9000, 9999, MyFrame::OnPortMenu)
EVT_MENU_OPEN(MyFrame::OnShowMenu)
EVT_COMBOBOX(ID_PORTLIST, MyFrame::OnPortList)
EVT_COMBOBOX_DROPDOWN(ID_PORTLIST, MyFrame::OnShowPortList)
END_EVENT_TABLE()
MyFrame::MyFrame(wxWindow *parent, wxWindowID id, const wxString &title,
const wxPoint &position, const wxSize& size, long style) :
wxFrame( parent, id, title, position, size, style )
{
wxPanel *panel;
wxMenuBar *menuBar;
wxMenu *menu;
wxSizer *topsizer;
wxSizer *leftsizer, *middlesizer, *rightsizer;
wxSizer *hsizer, *vsizer, *calsizer;
wxStaticText *text;
//wxMenuItem *item;
int i, j;
topsizer = new wxBoxSizer(wxHORIZONTAL);
panel = new wxPanel(this);
menuBar = new wxMenuBar;
menu = new wxMenu;
menu->Append(ID_SENDCAL, wxT("Send Calibration"));
sendcal_menu = menu;
sendcal_menu->Enable(ID_SENDCAL, false);
menu->Append(ID_SENDCAL_MENU, wxT("Send Calibration"));
m_sendcal_menu = menu;
m_sendcal_menu->Enable(ID_SENDCAL_MENU, false);
menu->Append(wxID_EXIT, wxT("Quit"));
menuBar->Append(menu, wxT("&File"));
menu = new wxMenu;
menuBar->Append(menu, "Port");
port_menu = menu;
m_port_menu = menu;
menu = new wxMenu;
//item = new wxMenuItem(menu, ID_ABOUT, "About");
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");
leftsizer = new wxStaticBoxSizer(wxVERTICAL, panel, "Communication");
middlesizer = new wxStaticBoxSizer(wxVERTICAL, panel, "Magnetometer");
rightsizer = new wxStaticBoxSizer(wxVERTICAL, panel, "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(panel, wxID_ANY, "Port");
vsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
m_port_list = new wxComboBox(panel, ID_PORTLIST, "",
wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY);
m_port_list->Append("(none)");
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(panel, wxID_ANY, "Actions");
vsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
m_button_clear = new wxButton(panel, ID_CLEAR_BUTTON, "Clear");
m_button_clear->Enable(false);
vsizer->Add(m_button_clear, 1, wxEXPAND, 0);
m_button_sendcal = new wxButton(panel, ID_SENDCAL_BUTTON, "Send Cal");
vsizer->Add(m_button_sendcal, 1, wxEXPAND, 0);
m_button_sendcal->Enable(false);
vsizer->AddSpacer(16);
text = new wxStaticText(panel, wxID_ANY, "Status");
vsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
wxImage::AddHandler(new wxPNGHandler);
//m_confirm_icon = new wxStaticBitmap(panel, ID_CONFIRM_ICON, MyBitmap("checkgreen.png"));
m_confirm_icon = new wxStaticBitmap(panel, wxID_ANY, MyBitmap("checkemptygray.png"));
vsizer->Add(m_confirm_icon, 0, wxALL | wxALIGN_CENTER_HORIZONTAL, 0);
vsizer = new wxBoxSizer(wxVERTICAL);
middlesizer->Add(vsizer, 1, wxEXPAND | wxALL, 8);
text = new wxStaticText(this, wxID_ANY, "");
text = new wxStaticText(panel, 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};
m_canvas = new MyCanvas(this, wxID_ANY, gl_attrib);
m_canvas->SetMinSize(wxSize(400,400));
m_canvas = new MyCanvas(panel, wxID_ANY, gl_attrib);
m_canvas->SetMinSize(wxSize(480,480));
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");
text = new wxStaticText(panel, wxID_ANY, "Gaps");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_coverage = new wxStaticText(this, wxID_ANY, "100.0%");
m_err_coverage = new wxStaticText(panel, 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");
text = new wxStaticText(panel, wxID_ANY, "Variance");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_variance = new wxStaticText(this, wxID_ANY, "100.0%");
m_err_variance = new wxStaticText(panel, 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");
text = new wxStaticText(panel, wxID_ANY, "Wobble");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_wobble = new wxStaticText(this, wxID_ANY, "100.0%");
m_err_wobble = new wxStaticText(panel, 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");
text = new wxStaticText(panel, wxID_ANY, "Fit Error");
vsizer->Add(text, 1, wxALIGN_CENTER_HORIZONTAL);
m_err_fit = new wxStaticText(this, wxID_ANY, "100.0%");
m_err_fit = new wxStaticText(panel, 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");
text = new wxStaticText(panel, 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");
m_mag_offset[i] = new wxStaticText(panel, wxID_ANY, "0.00");
vsizer->Add(m_mag_offset[i], 1);
}
text = new wxStaticText(this, wxID_ANY, "Magnetic Mapping");
text = new wxStaticText(panel, 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,
m_mag_mapping[i][j] = new wxStaticText(panel, wxID_ANY,
((i == j) ? "+1.000" : "+0.000"));
vsizer->Add(m_mag_mapping[i][j], 1);
}
}
text = new wxStaticText(this, wxID_ANY, "Magnetic Field");
text = new wxStaticText(panel, wxID_ANY, "Magnetic Field");
calsizer->Add(text, 0, wxTOP|wxBOTTOM, 4);
m_mag_field = new wxStaticText(this, wxID_ANY, "0.00");
m_mag_field = new wxStaticText(panel, wxID_ANY, "0.00");
calsizer->Add(m_mag_field, 0, wxLEFT, 20);
text = new wxStaticText(this, wxID_ANY, "Accelerometer");
text = new wxStaticText(panel, 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");
m_accel[i] = new wxStaticText(panel, wxID_ANY, "0.000");
vsizer->Add(m_accel[i], 1);
}
text = new wxStaticText(this, wxID_ANY, "Gyroscope");
text = new wxStaticText(panel, 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");
m_gyro[i] = new wxStaticText(panel, wxID_ANY, "0.000");
vsizer->Add(m_gyro[i], 1);
}
calsizer->AddSpacer(8);
text = new wxStaticText(this, wxID_ANY, "");
text = new wxStaticText(panel, 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);
panel->SetSizer(topsizer);
topsizer->SetSizeHints(panel);
Fit();
Show(true);
Raise();
@ -211,6 +245,7 @@ MyFrame::MyFrame(wxWindow *parent, wxWindowID id, const wxString &title,
void MyFrame::OnTimer(wxTimerEvent &event)
{
static int firstrun=1;
float gaps, variance, wobble, fiterror;
char buf[32];
int i, j;
@ -218,15 +253,29 @@ void MyFrame::OnTimer(wxTimerEvent &event)
//printf("OnTimer\n");
if (port_is_open()) {
read_serial_data();
if (firstrun && m_canvas->IsShown()) {
//int h, w;
//m_canvas->GetSize(&w, &h);
//printf("Canvas initial size = %d, %d\n", w, h);
firstrun = 0;
}
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) {
sendcal_menu->Enable(ID_SENDCAL, true);
if (!m_sendcal_menu->IsEnabled(ID_SENDCAL_MENU) || !m_button_sendcal->IsEnabled()) {
m_sendcal_menu->Enable(ID_SENDCAL_MENU, true);
m_button_sendcal->Enable(true);
m_confirm_icon->SetBitmap(MyBitmap("checkempty.png"));
}
} else if (gaps > 20.0f && variance > 5.0f && wobble > 5.0f && fiterror > 6.0f) {
sendcal_menu->Enable(ID_SENDCAL, false);
if (m_sendcal_menu->IsEnabled(ID_SENDCAL_MENU) || m_button_sendcal->IsEnabled()) {
m_sendcal_menu->Enable(ID_SENDCAL_MENU, false);
m_button_sendcal->Enable(false);
m_confirm_icon->SetBitmap(MyBitmap("checkemptygray.png"));
}
}
snprintf(buf, sizeof(buf), "%.1f%%", quality_surface_gap_error());
m_err_coverage->SetLabelText(buf);
@ -257,13 +306,33 @@ void MyFrame::OnTimer(wxTimerEvent &event)
m_gyro[i]->SetLabelText(buf);
}
} else {
sendcal_menu->Enable(ID_SENDCAL, false);
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_confirm_icon->SetBitmap(MyBitmap("checkemptygray.png"));
m_port_list->Clear();
m_port_list->Append("(none)");
m_port_list->SetSelection(0);
port_name = "";
}
}
if (show_calibration_confirmed) {
m_confirm_icon->SetBitmap(MyBitmap("checkgreen.png"));
show_calibration_confirmed = false;
}
}
void MyFrame::OnClear(wxCommandEvent &event)
{
//printf("OnClear\n");
raw_data_reset();
}
void MyFrame::OnSendCal(wxCommandEvent &event)
{
printf("OnSendCal\n");
/*printf("OnSendCal\n");
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]);
@ -271,23 +340,85 @@ void MyFrame::OnSendCal(wxCommandEvent &event)
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]);
*/
m_confirm_icon->SetBitmap(MyBitmap("checkempty.png"));
send_calibration();
}
void MyFrame::OnPort(wxCommandEvent &event)
void calibration_confirmed(void)
{
show_calibration_confirmed = true;
}
void MyFrame::OnShowMenu(wxMenuEvent &event)
{
wxMenu *menu = event.GetMenu();
if (menu != m_port_menu) return;
//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();
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();
}
void MyFrame::OnShowPortList(wxCommandEvent& event)
{
//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]);
}
}
void MyFrame::OnPortMenu(wxCommandEvent &event)
{
int id = event.GetId();
wxString name = port_menu->FindItem(id)->GetItemLabelText();
wxString name = m_port_menu->FindItem(id)->GetItemLabelText();
close_port();
//printf("OnPort, id = %d, name = %s\n", id, (const char *)name);
sendcal_menu->Enable(ID_SENDCAL, false);
//printf("OnPortMenu, id = %d, name = %s\n", id, (const char *)name);
port_name = name;
m_port_list->Clear();
m_port_list->Append(port_name);
m_port_list->SetSelection(0);
if (id == 9000) return;
raw_data_reset();
open_port((const char *)name);
m_button_clear->Enable(true);
}
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);
}
void MyFrame::OnAbout(wxCommandEvent &event)
{
@ -296,7 +427,7 @@ void MyFrame::OnAbout(wxCommandEvent &event)
"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.",
"Copyright 2018, PJRC.COM, LLC.",
"About MotionCal", wxOK|wxICON_INFORMATION|wxCENTER);
dialog.ShowModal();
}
@ -313,48 +444,6 @@ MyFrame::~MyFrame(void)
}
/*****************************************************************************/
// Port Menu
MyMenu::MyMenu(const wxString& title, long style) : wxMenu(title, style)
{
}
void MyMenu::OnShowPortList(wxMenuEvent &event)
{
wxMenu *menu;
int any=0;
int num;
menu = event.GetMenu();
//printf("OnShowPortList, %s\n", (const char *)menu->GetTitle());
if (menu != port_menu) return;
while (menu->GetMenuItemCount() > 0) {
menu->Delete(menu->GetMenuItems()[0]);
}
menu->AppendRadioItem(9000, " (none)");
wxArrayString list = serial_port_list();
num = list.GetCount();
bool isopen = port_is_open();
for (int i=0; i < num; i++) {
//printf("%d: port %s\n", i, (const char *)list[i]);
menu->AppendRadioItem(9001 + i, list[i]);
if (isopen && port_name.IsSameAs(list[i])) {
menu->Check(9001 + i, true);
any = 1;
}
}
if (!any) menu->Check(9000, true);
menu->UpdateUI();
}
void MyMenu::OnHighlight(wxMenuEvent &event)
{
//printf("OnHighlight\n");
}
/*****************************************************************************/
IMPLEMENT_APP(MyApp)

43
gui.h
View file

@ -24,8 +24,10 @@
#define ID_TIMER 10000
#define ID_SENDCAL 10001
#define ID_SENDCAL_MENU 10001
#define ID_CLEAR_BUTTON 10002
#define ID_SENDCAL_BUTTON 10003
#define ID_PORTLIST 10004
class MyCanvas : public wxGLCanvas
{
@ -61,9 +63,6 @@ public:
const wxSize &size = wxDefaultSize,
long style = wxDEFAULT_FRAME_STYLE);
~MyFrame(void);
void InitGL();
void OnPort(wxCommandEvent &event);
void OnSendCal(wxCommandEvent &event);
private:
wxStaticText *m_err_coverage;
wxStaticText *m_err_variance;
@ -78,20 +77,24 @@ private:
MyCanvas *m_canvas;
wxTimer *m_timer;
wxButton *m_button_clear;
wxButton *m_button_sendcal;
wxStaticBitmap *m_confirm_icon;
wxMenu *m_port_menu;
wxComboBox *m_port_list;
wxMenu *m_sendcal_menu;
void OnSendCal(wxCommandEvent &event);
void OnClear(wxCommandEvent &event);
void OnShowMenu(wxMenuEvent &event);
void OnShowPortList(wxCommandEvent &event);
void OnPortList(wxCommandEvent& event);
void OnPortMenu(wxCommandEvent &event);
void OnTimer(wxTimerEvent &event);
void OnAbout(wxCommandEvent &event);
void OnQuit(wxCommandEvent &event);
void OnTimer(wxTimerEvent &event);
DECLARE_EVENT_TABLE()
};
class MyMenu: public wxMenu
{
public:
MyMenu(const wxString& title = "", long style = 0);
void OnShowPortList(wxMenuEvent &event);
void OnHighlight(wxMenuEvent &event);
};
class MyApp: public wxApp
{
@ -106,5 +109,17 @@ private:
// portlist.cpp
wxArrayString serial_port_list();
// images.cpp
wxBitmap MyBitmap(const char *name);
// sample port name, for initial sizing of left panel
#if defined(LINUX)
#define SAMPLE_PORT_NAME "/dev/ttyACM5."
#elif defined(WINDOWS)
#define SAMPLE_PORT_NAME "COM22:."
#elif defined(MACOSX)
#define SAMPLE_PORT_NAME "/dev/cu.usbmodem2457891..."
#endif
#endif

View file

@ -21,8 +21,67 @@ static void glut_display_callback(void)
glutSwapBuffers();
}
extern int invert_q0;
extern int invert_q1;
extern int invert_q2;
extern int invert_q3;
extern int invert_x;
extern int invert_y;
extern int invert_z;
static void print_invert_state(void)
{
printf("Invert: %s %s %s %s %s %s %s\n",
(invert_q0 ? "Q0" : " "),
(invert_q1 ? "Q1" : " "),
(invert_q2 ? "Q2" : " "),
(invert_q3 ? "Q3" : " "),
(invert_x ? "x'" : " "),
(invert_y ? "y'" : " "),
(invert_z ? "z'" : " ")
);
}
static void glut_keystroke_callback(unsigned char ch, int x, int y)
{
if (ch == '0') {
invert_q0 ^= 1;
print_invert_state();
return;
}
if (ch == '1') {
invert_q1 ^= 1;
print_invert_state();
return;
}
if (ch == '2') {
invert_q2 ^= 1;
print_invert_state();
return;
}
if (ch == '3') {
invert_q3 ^= 1;
print_invert_state();
return;
}
if (ch == 'x') {
invert_x ^= 1;
print_invert_state();
return;
}
if (ch == 'y') {
invert_y ^= 1;
print_invert_state();
return;
}
if (ch == 'z') {
invert_z ^= 1;
print_invert_state();
return;
}
if (magcal.FitError > 9.0) {
printf("Poor Calibration: ");
printf("soft iron fit error = %.1f%%\n", magcal.FitError);
@ -38,6 +97,11 @@ static void glut_keystroke_callback(unsigned char ch, int x, int y)
send_calibration();
}
void calibration_confirmed(void)
{
printf("Calibration confirmed!\n");
}
int main(int argc, char *argv[])
{
raw_data_reset();

View file

@ -15,8 +15,8 @@
#if defined(LINUX)
#include <termios.h>
#include <unistd.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/gl.h> // sudo apt install mesa-common-dev
#include <GL/glu.h> // sudo apt install libglu1-mesa-dev freeglut3-dev
#elif defined(WINDOWS)
#include <windows.h>
#include <GL/gl.h>
@ -69,6 +69,7 @@ extern void close_port(void);
void raw_data_reset(void);
void cal1_data(const float *data);
void cal2_data(const float *data);
void calibration_confirmed(void);
void raw_data(const int16_t *data);
int send_calibration(void);
void visualize_init(void);

View file

@ -453,7 +453,7 @@ static void fUpdateCalibration10EIG(MagCalibration_t *MagCal)
// sum between MINEQUATIONS to MAXEQUATIONS entries into the 10x10 product matrix matA
iCount = 0;
for (j = 0; j < MAGBUFFSIZE; j++) {
if (MagCal->valid[j] != -1) {
if (MagCal->valid[j]) {
// use first valid magnetic buffer entry as estimate for offset
// to help solution (bit counts)
if (iCount == 0) {

95
png2c.pl Executable file
View file

@ -0,0 +1,95 @@
#! /usr/bin/perl
binmode IN, ":bytes";
$file = $ARGV[0];
print "#include <wx/bitmap.h>\n";
print "#include <wx/image.h>\n";
print "#include <wx/mstream.h>\n";
print "\n";
foreach $file (@ARGV) {
open(IN, $file) or die "Can't open $file: $!\n";
$count = 0;
$len = -s $file;
$filelist[$numfiles] = $file;
$file =~ /^([-_A-Za-z0-9\/]+)/;
$name = "png_$1";
$name =~ s/\//_/g;
$name =~ s/-/_/g;
$size[$numfiles] = $len;
$list[$numfiles++] = $name;
print "static unsigned char ${name}[$len] = \{\n";
while (read(IN, $byte, 1) == 1) {
$n = ord($byte);
printf '0x%02X', $n;
print ',' if $count < $len - 1;
print "\n" if $count++ % 12 == 11;
}
print "\};\n";
close(IN);
}
print "\nstatic const unsigned char *png_image_list[] = {\n";
for ($i=0; $i<$numfiles; $i++) {
print "$list[$i]";
print ',' if $i < $numfiles - 1;
print "\n";
}
print "\};\n";
print "\nstatic const char *png_image_name[] = {\n";
for ($i=0; $i<$numfiles; $i++) {
print "\"$filelist[$i]\"";
print ',' if $i < $numfiles - 1;
print "\n";
}
print "\};\n";
print "\nstatic const unsigned int png_image_size[] = {\n";
for ($i=0; $i<$numfiles; $i++) {
print "$size[$i]";
print ',' if $i < $numfiles - 1;
print "\n";
}
print "\};\n";
print "\nstatic wxBitmap * image_list[] = {\n";
for ($i=0; $i<$numfiles; $i++) {
print "NULL";
print ',' if $i < $numfiles - 1;
print "\n";
}
print "\};\n\n";
print <<EOT;
wxBitmap MyBitmap(size_t index)
{
if (index >= $numfiles || index < 0) return wxNullBitmap;
wxBitmap *p = image_list[index];
if (p) return *p;
wxMemoryInputStream istream(png_image_list[index], png_image_size[index]);
wxImage img(istream, wxBITMAP_TYPE_PNG);
p = new wxBitmap(img);
return *p;
}
wxBitmap MyBitmap(const char *name)
{
for (size_t i=0; i < $numfiles; i++) {
if (strcmp(name, png_image_name[i]) == 0) return MyBitmap(i);
}
return wxNullBitmap;
}
EOT

View file

@ -1,5 +1,7 @@
#include "imuread.h"
// Discussion of what these 4 quality metrics really do
// https://forum.pjrc.com/threads/59277-Motion-Sensor-Calibration-Tool-Parameter-Understanding
//static int countdown=1000;
//static int pr=0;

View file

@ -6,6 +6,9 @@ static AccelSensor_t accel;
static MagSensor_t mag;
static GyroSensor_t gyro;
static float cal_data_sent[19];
static int cal_confirm_needed=0;
void raw_data_reset(void)
{
rawcount = OVERSAMPLE_RATIO;
@ -25,7 +28,7 @@ static int choose_discard_magcal(void)
int32_t rawx, rawy, rawz;
int32_t dx, dy, dz;
float x, y, z;
uint64_t distsq, minsum=0xFFFFFFFFFFFFFFFF;
uint64_t distsq, minsum=0xFFFFFFFFFFFFFFFFull;
static int runcount=0;
int i, j, minindex=0;
Point_t point;
@ -120,28 +123,72 @@ static void add_magcal_data(const int16_t *data)
magcal.valid[i] = 1;
}
static int is_float_ok(float actual, float expected)
{
float err, maxerr;
err = fabsf(actual - expected);
maxerr = 0.0001f + fabsf(expected) * 0.00003f;
if (err <= maxerr) return 1;
return 0;
}
void cal1_data(const float *data)
{
#if 0
int i;
int i, ok;
printf("got cal1_data:\n");
for (i=0; i<10; i++) {
printf(" %.5f\n", data[i]);
if (cal_confirm_needed) {
#if 0
printf("expected cal1: ");
for (i=0; i<10; i++) {
printf(" %.5f,", cal_data_sent[i]);
}
printf("\ngot cal1_data: ");
for (i=0; i<10; i++) {
printf(" %.5f,", data[i]);
}
printf("\n");
#endif
ok = 1;
for (i=0; i<10; i++) {
if (!is_float_ok(data[i], cal_data_sent[i])) ok = 0;
}
if (ok) {
cal_confirm_needed &= ~1; // got cal1 confirm
if (cal_confirm_needed == 0) {
calibration_confirmed();
}
}
}
#endif
}
void cal2_data(const float *data)
{
#if 0
int i;
int i, ok;
printf("got cal2_data:\n");
for (i=0; i<9; i++) {
printf(" %.5f\n", data[i]);
if (cal_confirm_needed) {
#if 0
printf("expected cal2: ");
for (i=0; i<9; i++) {
printf(" %.5f,", cal_data_sent[i+10]);
}
printf("\ngot cal2_data: ");
for (i=0; i<9; i++) {
printf(" %.5f,", data[i]);
}
printf("\n");
#endif
ok = 1;
for (i=0; i<9; i++) {
if (!is_float_ok(data[i], cal_data_sent[i+10])) ok = 0;
}
if (ok) {
cal_confirm_needed &= ~2; // got cal2 confirm
if (cal_confirm_needed == 0) {
calibration_confirmed();
}
}
}
#endif
}
void raw_data(const int16_t *data)
@ -186,10 +233,10 @@ void raw_data(const int16_t *data)
z = (float)data[2] * G_PER_COUNT;
accel.GpFast[0] = x;
accel.GpFast[1] = y;
accel.GpFast[2] = y;
accel.GpFast[2] = z;
accel.Gp[0] += x;
accel.Gp[1] += y;
accel.Gp[2] += y;
accel.Gp[2] += z;
x = (float)data[3] * DEG_PER_SEC_PER_COUNT;
y = (float)data[4] * DEG_PER_SEC_PER_COUNT;
@ -267,12 +314,15 @@ int send_calibration(void)
*p++ = 84;
for (i=0; i < 3; i++) {
p = copy_lsb_first(p, 0.0f); // accelerometer offsets
cal_data_sent[0+i] = 0.0f;
}
for (i=0; i < 3; i++) {
p = copy_lsb_first(p, 0.0f); // gyroscope offsets
cal_data_sent[3+i] = 0.0f;
}
for (i=0; i < 3; i++) {
p = copy_lsb_first(p, magcal.V[i]); // 12 bytes offset/hardiron
cal_data_sent[6+i] = magcal.V[i];
}
p = copy_lsb_first(p, magcal.B); // field strength
p = copy_lsb_first(p, magcal.invW[0][0]); //10
@ -281,6 +331,17 @@ int send_calibration(void)
p = copy_lsb_first(p, magcal.invW[0][1]); //13
p = copy_lsb_first(p, magcal.invW[0][2]); //14
p = copy_lsb_first(p, magcal.invW[1][2]); //15
cal_data_sent[9] = magcal.B;
cal_data_sent[10] = magcal.invW[0][0];
cal_data_sent[11] = magcal.invW[0][1];
cal_data_sent[12] = magcal.invW[0][2];
cal_data_sent[13] = magcal.invW[1][0];
cal_data_sent[14] = magcal.invW[1][1];
cal_data_sent[15] = magcal.invW[1][2];
cal_data_sent[16] = magcal.invW[2][0];
cal_data_sent[17] = magcal.invW[2][1];
cal_data_sent[18] = magcal.invW[2][2];
cal_confirm_needed = 3;
crc = 0xFFFF;
for (i=0; i < 66; i++) {
crc = crc16(crc, buf[i]);

View file

@ -1 +1,2 @@
MotionCal ICON "icon.ico"
#include "wx/msw/wx.rc"

View file

@ -75,7 +75,7 @@ static int packet_magnetic_cal(const unsigned char *data)
magcal.BpFast[1][n] = y;
magcal.BpFast[2][n] = z;
magcal.valid[n] = 1;
printf("mag cal, n=%3d: %5d %5d %5d\n", n, x, y, z);
//printf("mag cal, n=%3d: %5d %5d %5d\n", n, x, y, z);
}
return 1;
}
@ -340,7 +340,7 @@ static int ascii_parse(const unsigned char *data, int len)
}
return ret;
fail:
printf("ascii FAIL\n");
//printf("ascii FAIL\n");
ascii_state = ASCII_STATE_WORD;
ascii_raw_data_count = 0;
ascii_num = 0;
@ -491,6 +491,7 @@ int open_port(const char *name)
len = sizeof(COMMCONFIG);
if (!GetCommConfig(port_handle, &port_cfg, &len)) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
return 0;
}
port_cfg.dcb.BaudRate = 115200;
@ -512,10 +513,12 @@ int open_port(const char *name)
port_cfg.dcb.StopBits = ONESTOPBIT;
if (!SetCommConfig(port_handle, &port_cfg, sizeof(COMMCONFIG))) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
return 0;
}
if (!EscapeCommFunction(port_handle, CLRDTR | CLRRTS)) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
return 0;
}
timeouts.ReadIntervalTimeout = MAXDWORD;
@ -525,10 +528,12 @@ int open_port(const char *name)
timeouts.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(port_handle, &timeouts)) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
return 0;
}
if (!EscapeCommFunction(port_handle, SETDTR)) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
return 0;
}
return 1;
@ -542,10 +547,17 @@ int read_serial_data(void)
unsigned char buf[256];
int r;
if (port_handle == INVALID_HANDLE_VALUE) return -1;
while (1) {
if (!ClearCommError(port_handle, &errmask, &st)) return -1;
if (!ClearCommError(port_handle, &errmask, &st)) {
r = -1;
break;
}
//printf("Read, %d requested, %lu buffered\n", count, st.cbInQue);
if (st.cbInQue <= 0) return 0;
if (st.cbInQue <= 0) {
r = 0;
break;
}
// now do a ReadFile, now that we know how much we can read
// a blocking (non-overlapped) read would be simple, but win32
// is all-or-nothing on async I/O and we must have it enabled
@ -585,6 +597,10 @@ int read_serial_data(void)
if (r <= 0) break;
newdata(buf, r);
}
if (r < 0) {
CloseHandle(port_handle);
port_handle = INVALID_HANDLE_VALUE;
}
return r;
}
@ -622,7 +638,7 @@ int write_serial_data(const void *ptr, int len)
void close_port(void)
{
CloseHandle(port_handle);
port_handle = NULL;
port_handle = INVALID_HANDLE_VALUE;
}

View file

@ -46,6 +46,14 @@ static void rotate(const Point_t *in, Point_t *out, const float *rmatrix)
static GLuint spherelist;
static GLuint spherelowreslist;
int invert_q0=0;
int invert_q1=0;
int invert_q2=0;
int invert_q3=1;
int invert_x=0;
int invert_y=0;
int invert_z=0;
void display_callback(void)
{
int i;
@ -76,11 +84,22 @@ void display_callback(void)
memcpy(&orientation, &current_orientation, sizeof(orientation));
// TODO: this almost but doesn't perfectly seems to get the
// real & screen axes in sync....
//orientation.q0 *= -1.0f;
//orientation.q1 *= -1.0f;
//orientation.q2 *= -1.0f;
orientation.q3 *= -1.0f;
if (invert_q0) orientation.q0 *= -1.0f;
if (invert_q1) orientation.q1 *= -1.0f;
if (invert_q2) orientation.q2 *= -1.0f;
if (invert_q3) orientation.q3 *= -1.0f;
quad_to_rotation(&orientation, rotation);
//rotation[0] *= -1.0f;
//rotation[1] *= -1.0f;
//rotation[2] *= -1.0f;
//rotation[3] *= -1.0f;
//rotation[4] *= -1.0f;
//rotation[5] *= -1.0f;
//rotation[6] *= -1.0f;
//rotation[7] *= -1.0f;
//rotation[8] *= -1.0f;
for (i=0; i < MAGBUFFSIZE; i++) {
if (magcal.valid[i]) {
apply_calibration(magcal.BpFast[0][i], magcal.BpFast[1][i],
@ -91,6 +110,9 @@ void display_callback(void)
quality_update(&point);
rotate(&point, &draw, rotation);
glPushMatrix();
if (invert_x) draw.x *= -1.0f;
if (invert_y) draw.y *= -1.0f;
if (invert_z) draw.z *= -1.0f;
glTranslatef(
draw.x * xscale + xoff,
draw.z * yscale + yoff,