diff --git a/Makefile b/Makefile
index 2153aaf..83a8d7a 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/checkempty.png b/checkempty.png
new file mode 100644
index 0000000..350179e
Binary files /dev/null and b/checkempty.png differ
diff --git a/checkemptygray.png b/checkemptygray.png
new file mode 100644
index 0000000..38da865
Binary files /dev/null and b/checkemptygray.png differ
diff --git a/checkgreen.png b/checkgreen.png
new file mode 100644
index 0000000..f888858
Binary files /dev/null and b/checkgreen.png differ
diff --git a/gui.cpp b/gui.cpp
index f471fb3..f39f6a7 100644
--- a/gui.cpp
+++ b/gui.cpp
@@ -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("Ideal calibration is a perfectly centered sphere");
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("Calibration should be performed\nafter final installation. Presence\nof magnets and ferrous metals\ncan alter magnetic calibration.\nMechanical stress during\nassembly can alter accelerometer\nand gyroscope calibration.");
//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 \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)
diff --git a/gui.h b/gui.h
index 2d82427..64af8ac 100644
--- a/gui.h
+++ b/gui.h
@@ -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
diff --git a/imuread.c b/imuread.c
index 68412b2..445afc9 100644
--- a/imuread.c
+++ b/imuread.c
@@ -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();
diff --git a/imuread.h b/imuread.h
index 6bcae6f..9be6ce1 100644
--- a/imuread.h
+++ b/imuread.h
@@ -15,8 +15,8 @@
#if defined(LINUX)
#include
#include
- #include
- #include
+ #include // sudo apt install mesa-common-dev
+ #include // sudo apt install libglu1-mesa-dev freeglut3-dev
#elif defined(WINDOWS)
#include
#include
@@ -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);
diff --git a/magcal.c b/magcal.c
index b3496b9..3e80c4b 100644
--- a/magcal.c
+++ b/magcal.c
@@ -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) {
diff --git a/png2c.pl b/png2c.pl
new file mode 100755
index 0000000..d622546
--- /dev/null
+++ b/png2c.pl
@@ -0,0 +1,95 @@
+#! /usr/bin/perl
+
+binmode IN, ":bytes";
+$file = $ARGV[0];
+
+print "#include \n";
+print "#include \n";
+print "#include \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 <= $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
+
+
+
+
+
+
+
+
+
+
+
diff --git a/quality.c b/quality.c
index 3a3ee85..7e44354 100644
--- a/quality.c
+++ b/quality.c
@@ -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;
diff --git a/rawdata.c b/rawdata.c
index 0547248..caaf063 100644
--- a/rawdata.c
+++ b/rawdata.c
@@ -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]);
diff --git a/resource.rs b/resource.rc
similarity index 52%
rename from resource.rs
rename to resource.rc
index 6d3784b..ae193a5 100644
--- a/resource.rs
+++ b/resource.rc
@@ -1 +1,2 @@
MotionCal ICON "icon.ico"
+#include "wx/msw/wx.rc"
diff --git a/serialdata.c b/serialdata.c
index faeb071..bdd2695 100644
--- a/serialdata.c
+++ b/serialdata.c
@@ -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;
}
diff --git a/visualize.c b/visualize.c
index 1141686..7fc11ab 100644
--- a/visualize.c
+++ b/visualize.c
@@ -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, ¤t_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,