You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SimpleSkyCam/posctl.cc

936 lines
31 KiB

/***************************************************************************************
*
* posctl.cc is part of SimpleSkyCam.
*
*****************************************************************************************/
#include <list>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include "gui.h"
#include "pid.h"
#include "filter.h"
#include "detect.h"
#include "configuration.h"
#include "video.h"
#include "videodev.h"
#include "histogram.h"
#include "error.h"
extern PosCtl posctl;
extern Simulation simulation;
extern GtkBuilder *_builder_; // work around for threads
GtkWidget *posctl_rot_da = NULL;
GtkWidget *posctl_axis1_da = NULL;
GtkWidget *posctl_axis2_da = NULL;
double linedash1[] = { 1.0, 4.0 };
/*
* axis history chart.
*/
#define AXIS_HISTORY_CNT 400
struct AxisHistory {
double diff;
double out;
struct timeval ts;
};
struct AxisHistory axis_history[2][AXIS_HISTORY_CNT];
int axis_history_pos[2] = { 0, 0 };
void axis_history_add(int axis, double diff, double out) {
if (axis > 1 || axis < 0) return;
axis_history_pos[axis]++;
if (axis_history_pos[axis] < 0) axis_history_pos[axis] = 0;
if (axis_history_pos[axis] >= AXIS_HISTORY_CNT) axis_history_pos[axis] = 0;
gettimeofday(&axis_history[axis][axis_history_pos[axis]].ts, NULL);
axis_history[axis][axis_history_pos[axis]].diff = diff;
axis_history[axis][axis_history_pos[axis]].out = out;
}
#define CALIB_MAXSPEED 1.0
#define CALIB_DURATION_DELTA 10.0
#define CALIB_DURATION_AXIS (CALIB_DURATION_DELTA / CALIB_MAXSPEED)
void posctl_gui_update();
void cb_posctl_show_window (GtkWidget *widget, gpointer data) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
GtkWidget *wnd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "window-posctl"));
gtk_widget_show(wnd);
}
void cb_posctl_btncalib (GtkWidget *widget, gpointer data) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
// GtkWidget *btn = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_calib"));
// gtk_widget_set_sensitive(btn, false);
posctl.StartCalibration();
posctl_gui_update();
}
void cb_posctl_btnstop (GtkWidget *widget, gpointer data) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
posctl.Stop();
posctl_gui_update();
}
void cb_posctl_btncontrol (GtkWidget *widget, gpointer data) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
posctl.StartControl();
posctl_gui_update();
}
void cb_posctl_show (GtkWidget *widget, gpointer data) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
posctl_gui_update();
}
void cb_posctl_btn_axismove (GtkWidget *widget, gpointer data) {
GtkWidget *btn_a1min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a1_min"));
GtkWidget *btn_a1center = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a1_center"));
GtkWidget *btn_a1max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a1_max"));
GtkWidget *btn_a2min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a2_min"));
GtkWidget *btn_a2center = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a2_center"));
GtkWidget *btn_a2max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a2_max"));
double a1min, a2min, a1max, a2max;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
posctl.GetAxisParam(0, &a1min, &a1max, NULL, NULL, NULL);
posctl.GetAxisParam(1, &a2min, &a2max, NULL, NULL, NULL);
if (widget == btn_a1min) posctl.OutputWriteValue(0, a1min);
else if (widget == btn_a1center) posctl.OutputWriteValue(0, (a1max-a1min)/2.0+a1min);
else if (widget == btn_a1max) posctl.OutputWriteValue(0, a1max);
else if (widget == btn_a2min) posctl.OutputWriteValue(1, a1min);
else if (widget == btn_a2center) posctl.OutputWriteValue(1, (a1max-a1min)/2.0+a1min);
else if (widget == btn_a2max) posctl.OutputWriteValue(1, a1max);
posctl_gui_update();
}
void cb_posctl_change_entry (GtkWidget *widget, gpointer data) {
double a1min, a1max, a1p, a1i, a1d;
double a2min, a2max, a2p, a2i, a2d;
GtkWidget *e_posdevice = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_device"));
GtkWidget *a1_min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_min"));
GtkWidget *a1_max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_max"));
GtkWidget *a1_kp = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_kp"));
GtkWidget *a1_ki = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_ki"));
GtkWidget *a1_kd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_kd"));
GtkWidget *a2_min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_min"));
GtkWidget *a2_max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_max"));
GtkWidget *a2_kp = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_kp"));
GtkWidget *a2_ki = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_ki"));
GtkWidget *a2_kd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_kd"));
const char *s;
posctl.GetAxisParam(0, &a1min, &a1max, &a1p, &a1i, &a1d);
posctl.GetAxisParam(1, &a2min, &a2max, &a2p, &a2i, &a2d);
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
s = gtk_entry_get_text(GTK_ENTRY(widget));
posctl.LockMutex();
if (e_posdevice == widget) posctl.SetDevice(s);
else if (a1_min == widget) a1min = atof(s);
else if (a1_max == widget) a1max = atof(s);
else if (a1_kp == widget) a1p = atof(s);
else if (a1_ki == widget) a1i = atof(s);
else if (a1_kd == widget) a1d = atof(s);
else if (a2_min == widget) a2min = atof(s);
else if (a2_max == widget) a2max = atof(s);
else if (a2_kp == widget) a2p = atof(s);
else if (a2_ki == widget) a2i = atof(s);
else if (a2_kd == widget) a2d = atof(s);
posctl.SetAxisParam(0, a1min, a1max, a1p, a1i, a1d);
posctl.SetAxisParam(1, a2min, a2max, a2p, a2i, a2d);
posctl.UnLockMutex();
}
void cb_posctl_angles_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) {
int clientw, clienth;
position_f_2d lpos[5];
float lmax;
position_2d center;
//
// rotation da
GtkWidget *entry_rot_angle = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_rotangle"));
GtkWidget *entry_rot_len = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_rotlen"));
GtkWidget *entry_axis1_angle = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1angle"));
GtkWidget *entry_axis1_len = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1len"));
GtkWidget *entry_axis2_angle = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2angle"));
GtkWidget *entry_axis2_len = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2len"));
float rotangle = atof(gtk_entry_get_text(GTK_ENTRY(entry_rot_angle))) * (M_PI / 180);
float rotlen = atof(gtk_entry_get_text(GTK_ENTRY(entry_rot_len)));
float a1_angle = atof(gtk_entry_get_text(GTK_ENTRY(entry_axis1_angle))) * (M_PI / 180);
float a1_len = atof(gtk_entry_get_text(GTK_ENTRY(entry_axis1_len)));
float a2_angle = atof(gtk_entry_get_text(GTK_ENTRY(entry_axis2_angle))) * (M_PI / 180);
float a2_len = atof(gtk_entry_get_text(GTK_ENTRY(entry_axis2_len)));
if (posctl_rot_da == NULL) // should only be called once
posctl_rot_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_rotation"));
if (posctl_axis1_da == NULL) // should only be called once
posctl_axis1_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_rotation"));
if (posctl_axis2_da == NULL) // should only be called once
posctl_axis2_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_rotation"));
clienth = gtk_widget_get_allocated_height(posctl_rot_da);
clientw = gtk_widget_get_allocated_width(posctl_rot_da);
center.x = clientw >> 1;
center.y = clienth >> 1;
//
// draw the background
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_paint(cr);
cairo_fill (cr);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
cairo_rectangle (cr, 1, 1, clientw - 2, clienth - 2);
cairo_fill (cr);
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_set_dash(cr, linedash1, 2, 0);
cairo_move_to(cr, center.x, center.y - center.y * 0.8);
cairo_line_to(cr, center.x, center.y + center.y * 0.8);
cairo_stroke(cr);
cairo_move_to(cr, center.x - center.x * 0.8, center.y);
cairo_line_to(cr, center.x + center.x * 0.8, center.y);
cairo_stroke(cr);
cairo_set_dash(cr, NULL, 0, 0);
//
// calculate angles
lpos[0].x = sin(rotangle) * rotlen;
lpos[0].y = cos(rotangle) * rotlen;
lpos[1].x = lpos[0].x + sin(a1_angle) * a1_len;
lpos[1].y = lpos[0].y + cos(a1_angle) * a1_len;
lpos[2].x = lpos[0].x;
lpos[2].y = lpos[0].y;
lpos[3].x = lpos[0].x + sin(a2_angle) * a2_len;
lpos[3].y = lpos[0].y + cos(a2_angle) * a2_len;
lpos[4].x = lpos[0].x;
lpos[4].y = lpos[0].y;
// find maximum
for (int i = 0; i < 5; i++) {
if (i == 0) {
lmax = fabs (lpos[0].x);
if (fabs(lpos[i].y) > lmax) lmax = fabs(lpos[i].y);
} else {
if (fabs(lpos[i].x) > lmax) lmax = fabs(lpos[i].x);
if (fabs(lpos[i].y) > lmax) lmax = fabs(lpos[i].y);
}
}
//
// draw
cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, 11);
cairo_set_line_width(cr, 4.0);
// rot
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_move_to(cr, center.x, center.y);
cairo_line_to(cr, center.x + float (lpos[0].x * center.x * 0.8 / lmax),
center.y - float (lpos[0].y * center.y * 0.8 / lmax));
cairo_stroke(cr);
cairo_move_to(cr, center.x + float (lpos[0].x * center.x * 0.9 / lmax),
center.y - float (lpos[0].y * center.y * 0.9 / lmax));
cairo_show_text(cr, (char *)"E");
// axis1
cairo_set_source_rgb(cr, 0.0, 1.0, 0.5);
cairo_move_to(cr, center.x + float (lpos[1].x * center.x * 0.8 / lmax),
center.y - float (lpos[1].y * center.y * 0.8 / lmax));
cairo_line_to(cr, center.x + float (lpos[2].x * center.x * 0.8 / lmax),
center.y - float (lpos[2].y * center.y * 0.8 / lmax));
cairo_stroke(cr);
cairo_move_to(cr, center.x + float (lpos[1].x * center.x * 0.9 / lmax),
center.y - float (lpos[1].y * center.y * 0.9 / lmax));
cairo_show_text(cr, (char *)"1");
// axis2
cairo_set_source_rgb(cr, 0.0, 0.5, 1.0);
cairo_move_to(cr, center.x + float (lpos[3].x * center.x * 0.8 / lmax),
center.y - float (lpos[3].y * center.y * 0.8 / lmax));
cairo_line_to(cr, center.x + float (lpos[4].x * center.x * 0.8 / lmax),
center.y - float (lpos[4].y * center.y * 0.8 / lmax));
cairo_stroke(cr);
cairo_move_to(cr, center.x + float (lpos[3].x * center.x * 0.9 / lmax),
center.y - float (lpos[3].y * center.y * 0.9 / lmax));
cairo_show_text(cr, (char *)"2");
};
void cb_posctl_entryanglelen (GtkWidget *widget, gpointer data) {
if (posctl_rot_da == NULL) // should only be called once
posctl_rot_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_rotation"));
gdk_window_invalidate_rect(gtk_widget_get_window(posctl_rot_da), NULL, true);
};
#define AXIS_DIFF_MIN -10.0
#define AXIS_DIFF_MAX 10.0
void cb_posctl_axis_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) {
GtkWidget *da1 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_axis1"));
GtkWidget *da2 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_axis2"));
position_2d center;
int axis = 0 , i, cnt;
double dx, dy;
double aoutmin[2];
double aoutmax[2];
int clienth = gtk_widget_get_allocated_height(area);
int clientw = gtk_widget_get_allocated_width(area);
center.x = clientw >> 1;
center.y = clienth >> 1;
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_paint(cr);
cairo_fill (cr);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.8);
cairo_rectangle (cr, 1, 1, clientw - 2, clienth - 2);
cairo_fill (cr);
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_set_dash(cr, linedash1, 2, 0);
cairo_move_to(cr, center.x, center.y - center.y * 0.8);
cairo_line_to(cr, center.x, clienth);
cairo_stroke(cr);
cairo_set_dash(cr, NULL, 0, 0);
if (da1 == area) axis = 0;
else if (da2 == area) axis = 1;
else {
cairo_move_to(cr, 30, 0);
cairo_show_text(cr, (char *)"unknown");
return;
}
posctl.GetAxisParam(axis, &aoutmin[axis], &aoutmax[axis], NULL, NULL, NULL);
// draw diff
cairo_set_source_rgb(cr, 1.0, 0.5, 0.5);
cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, 10);
cairo_move_to(cr, 00, 10);
cairo_show_text(cr, (char *)"diff");
// draw elements diff
i = axis_history_pos[axis];
cnt = 0;
do {
dx = ((double) clientw) * (axis_history[axis][i].diff - AXIS_DIFF_MIN) /
(AXIS_DIFF_MAX-AXIS_DIFF_MIN);
dy = ((double) clienth) * ((double)cnt) / ((double) AXIS_HISTORY_CNT);
if (dx >= clientw) dx = clientw-1;
if (dy >= clienth) dy = clienth-1;
if (dx < 0) dx = 0;
if (dy < 0) dy = 0;
if (cnt == 0) cairo_move_to(cr, (int)dx, int(dy));
else cairo_line_to(cr, (int)dx, int(dy));
cnt++;
i--;
if (i < 0) i = AXIS_HISTORY_CNT-1;
} while (i != axis_history_pos[axis]);
cairo_stroke(cr);
cairo_set_source_rgb(cr, 0.0, 1.0, 0.5);
cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size (cr, 10);
cairo_move_to(cr, 40, 10);
cairo_show_text(cr, (char *)"out");
// draw elements diff
i = axis_history_pos[axis];
cnt = 0;
do {
dx = ((double) clientw) * (axis_history[axis][i].out - aoutmin[axis]) /
(aoutmax[axis]-aoutmin[axis]);
dy = ((double) clienth) * ((double)cnt) / ((double) AXIS_HISTORY_CNT);
if (dx >= clientw) dx = clientw-1;
if (dy >= clienth) dy = clienth-1;
if (dx < 0) dx = 0;
if (dy < 0) dy = 0;
if (cnt == 0) cairo_move_to(cr, (int)dx, int(dy));
else cairo_line_to(cr, (int)dx, int(dy));
cnt++;
i--;
if (i < 0) i = AXIS_HISTORY_CNT-1;
} while (i != axis_history_pos[axis]);
cairo_stroke(cr);
// draw chart
};
/*
* posctl gui update
*/
void posctl_gui_update() {
char txt[255];
double kp, ki, kd, mi, ma;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
GtkWidget *caliblabel = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_calib_label"));
GtkWidget *btnclib = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_calib"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_stop"));
GtkWidget *btncontrol = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_control"));
GtkWidget *e_cal_rotangle = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_rotangle"));
GtkWidget *e_cal_rotlen = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_rotlen"));
GtkWidget *e_cal_a1angle = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1angle"));
GtkWidget *e_cal_a1len = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1len"));
GtkWidget *e_cal_a2angle = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2angle"));
GtkWidget *e_cal_a2len = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2len"));
GtkWidget *e_posdevice = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_device"));
GtkWidget *btn_a1min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a1_min"));
GtkWidget *btn_a1center = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a1_center"));
GtkWidget *btn_a1max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a1_max"));
GtkWidget *btn_a2min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a2_min"));
GtkWidget *btn_a2center = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a2_center"));
GtkWidget *btn_a2max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_btn_a2_max"));
GtkWidget *a1_min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_min"));
GtkWidget *a1_max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_max"));
GtkWidget *a1_kp = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_kp"));
GtkWidget *a1_ki = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_ki"));
GtkWidget *a1_kd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a1_kd"));
GtkWidget *a1_out = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_lb_outx"));
GtkWidget *a1_pv = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_lb_dx"));
GtkWidget *a2_min = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_min"));
GtkWidget *a2_max = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_max"));
GtkWidget *a2_kp = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_kp"));
GtkWidget *a2_ki = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_ki"));
GtkWidget *a2_kd = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_entry_a2_kd"));
GtkWidget *a2_out = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_lb_outy"));
GtkWidget *a2_pv = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_lb_dy"));
posctl.LockMutex();
int m = posctl.GetMode();
if (m == POSCTL_MODE_OFF) {
gtk_widget_set_sensitive(btnclib, true);
gtk_widget_set_sensitive(btncontrol, true);
gtk_widget_set_sensitive(btnstop, false);
gtk_widget_set_sensitive(btn_a1min, true);
gtk_widget_set_sensitive(btn_a1center, true);
gtk_widget_set_sensitive(btn_a1max, true);
gtk_widget_set_sensitive(btn_a2min, true);
gtk_widget_set_sensitive(btn_a2center, true);
gtk_widget_set_sensitive(btn_a2max, true);
gtk_widget_set_sensitive(e_posdevice, true);
gtk_label_set_label(GTK_LABEL(caliblabel), (char *) "");
}
else {
if (m == POSCTL_MODE_CALIB) {
gtk_label_set_label(GTK_LABEL(caliblabel), (char *) "running");
}
gtk_widget_set_sensitive(btnclib, false);
gtk_widget_set_sensitive(btncontrol, false);
gtk_widget_set_sensitive(btnstop, true);
gtk_widget_set_sensitive(btn_a1min, false);
gtk_widget_set_sensitive(btn_a1center, false);
gtk_widget_set_sensitive(btn_a1max, false);
gtk_widget_set_sensitive(btn_a2min, false);
gtk_widget_set_sensitive(btn_a2center, false);
gtk_widget_set_sensitive(btn_a2max, false);
gtk_widget_set_sensitive(e_posdevice, false);
}
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.calib_rot.a);
gtk_entry_set_text (GTK_ENTRY(e_cal_rotangle), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.calib_rot.l);
gtk_entry_set_text (GTK_ENTRY(e_cal_rotlen), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.calib_axis1.a);
gtk_entry_set_text (GTK_ENTRY(e_cal_a1angle), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.calib_axis1.l);
gtk_entry_set_text (GTK_ENTRY(e_cal_a1len), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.calib_axis2.a);
gtk_entry_set_text (GTK_ENTRY(e_cal_a2angle), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.calib_axis2.l);
gtk_entry_set_text (GTK_ENTRY(e_cal_a2len), txt);
gtk_entry_set_text (GTK_ENTRY(e_posdevice), posctl.GetDevice().c_str());
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.axis_op[0]);
gtk_label_set_text(GTK_LABEL(a1_out), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.axis_op[1]);
gtk_label_set_text(GTK_LABEL(a2_out), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.axis_pv[0]);
gtk_label_set_text(GTK_LABEL(a1_pv), txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", posctl.axis_pv[1]);
gtk_label_set_text(GTK_LABEL(a2_pv), txt);
posctl.GetAxisParam(0, &mi, &ma, &kp, &ki, &kd);
strfromd (txt, sizeof(txt-1), (const char *)"%f", mi);
gtk_entry_set_text_nofocus (a1_min, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", ma);
gtk_entry_set_text_nofocus (a1_max, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", kp);
gtk_entry_set_text_nofocus (a1_kp, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", ki);
gtk_entry_set_text_nofocus (a1_ki, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", kd);
gtk_entry_set_text_nofocus (a1_kd, txt);
posctl.GetAxisParam(1, &mi, &ma, &kp, &ki, &kd);
strfromd (txt, sizeof(txt-1), (const char *)"%f", mi);
gtk_entry_set_text_nofocus (a2_min, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", ma);
gtk_entry_set_text_nofocus (a2_max, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", kp);
gtk_entry_set_text_nofocus (a2_kp, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", ki);
gtk_entry_set_text_nofocus (a2_ki, txt);
strfromd (txt, sizeof(txt-1), (const char *)"%f", kd);
gtk_entry_set_text_nofocus (a2_kd, txt);
posctl.UnLockMutex();
}
/*
* callback from the detect thread.
* the gtk/gui updates must and will be processed in a separate gui thread
*/
gboolean cb_thread_posctl (gpointer data) {
GtkWidget *da1 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_axis1"));
GtkWidget *da2 = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "posctl_da_axis2"));
posctl_gui_update();
gtk_widget_queue_draw(da1);
gtk_widget_queue_draw(da2);
if (posctl_axis1_da) gdk_window_invalidate_rect(gtk_widget_get_window(posctl_axis1_da), NULL, true);
if (posctl_axis2_da) gdk_window_invalidate_rect(gtk_widget_get_window(posctl_axis2_da), NULL, true);
return false;
};
PosCtl::PosCtl() {
mode = POSCTL_MODE_OFF;
calib_mode = POSCTL_CALIB_MODE_OFF;
device_fd = -1;
device = "";
device_type = POSCTL_DEVTYPE_TTY;
};
/*
* stop the control or the calibration
*/
void PosCtl::Stop() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
mode = POSCTL_MODE_OFF;
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
void PosCtl::StartCalibration() {
printf ("%s:%d\n", __FILE__, __LINE__);
if (mode != POSCTL_MODE_OFF) {
printf ("%s:%d mode is not off, can't start calibration.\n", __FILE__, __LINE__);
return;
}
mode = POSCTL_MODE_CALIB;
calib_mode = POSCTL_CALIB_MODE_START;
gettimeofday (&calib_timestamp, NULL);
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
void PosCtl::StartControl() {
if (mode != POSCTL_MODE_OFF) {
printf ("%s:%d mode is not off, can't start control.\n", __FILE__, __LINE__);
return;
}
printf ("%s:%d %s start controlling Target:%f , %f\n", __FILE__, __LINE__, __FUNCTION__,
target_pos.x, target_pos.y);
pid_axis[0].Start();
pid_axis[1].Start();
mode = POSCTL_MODE_CONTROL;
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
/*
* get and set PID parameter, no mutex lock
* the access to the data should be already locked by the curren tthread
*/
void PosCtl::SetAxisParam ( int axis, double min, double max,
double k, double i, double d) {
if (axis < 0 || axis > 1) return;
pid_axis[axis].SetParam(min, max, k, i, d);
};
void PosCtl::GetAxisParam ( int axis, double *min, double *max,
double *k, double *i, double *d) {
if (axis < 0 || axis > 1) return;
pid_axis[axis].GetParam(min, max, k, i, d);
};
/*
* calibration functions
*/
void PosCtl::CalibModeStart(int x, int y) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
calib_pos.x = x;
calib_pos.y = y;
calib_mode = POSCTL_CALIB_MODE_DELTA;
OutputWriteStop();
gettimeofday (&calib_timestamp, NULL);
gdk_threads_add_idle(cb_thread_posctl, NULL);
};
void PosCtl::CalibModeDelta(int x, int y) {
struct timeval tv;
float timediff;
double a1min, a1max;
gettimeofday (&tv, NULL);
timediff = (float)(tv.tv_sec - calib_timestamp.tv_sec) + ((tv.tv_usec - calib_timestamp.tv_usec) / 1000000.0);
if (timediff > CALIB_DURATION_DELTA) {
position_f_2d fp;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
pid_axis[0].GetParam(&a1min, &a1max, NULL, NULL, NULL);
fp.x = (x - calib_pos.x) / (float)timediff;
fp.y = -(y - calib_pos.y) / (float)timediff;
LockMutex();
calib_rot_v = fp;
calc_vec2anglelen(&fp, &calib_rot);
UnLockMutex();
calib_mode = POSCTL_CALIB_MODE_AXIS1;
calib_pos.x = x;
calib_pos.y = y;
OutputWriteValue(0, CALIB_MAXSPEED);
gettimeofday (&calib_timestamp, NULL);
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
};
void PosCtl::CalibModeAxis(int x, int y) {
struct timeval tv;
float timediff;
position_f_2d fp;
gettimeofday (&tv, NULL);
timediff = (float)(tv.tv_sec - calib_timestamp.tv_sec) + ((tv.tv_usec - calib_timestamp.tv_usec) / 1000000.0);
if (timediff > CALIB_DURATION_AXIS) {
printf ("%s:%d %s calib_mode: %d\n", __FILE__, __LINE__, __FUNCTION__, calib_mode);
fp.x = +(x - calib_pos.x) / timediff;
fp.y = -(y - calib_pos.y) / timediff;
if (calib_mode == POSCTL_CALIB_MODE_AXIS1) {
calib_axis1_v = fp - calib_rot_v;
calc_vec2anglelen(&calib_axis1_v, &calib_axis1);
OutputWriteStop();
OutputWriteValue(1, CALIB_MAXSPEED);
}
else if (calib_mode == POSCTL_CALIB_MODE_AXIS2) {
calib_axis2_v = fp - calib_rot_v;
calc_vec2anglelen(&calib_axis2_v, &calib_axis2);
OutputWriteStart();
}
calib_mode++;
calib_pos.x = x;
calib_pos.y = y;
gettimeofday (&calib_timestamp, NULL);
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
};
void PosCtl::CalibModeFinish() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
mode = POSCTL_MODE_OFF;
calib_mode = POSCTL_CALIB_MODE_OFF;
gettimeofday (&calib_timestamp, NULL);
gdk_threads_add_idle(cb_thread_posctl, NULL);
};
/*
* Loop, if new data is aviable
*/
void PosCtl::Loop (int posx, int posy) {
//
// calibration mode?
if (mode == POSCTL_MODE_CALIB) {
switch (calib_mode) {
case (POSCTL_CALIB_MODE_START):
CalibModeStart(posx, posy);
break;
case (POSCTL_CALIB_MODE_DELTA):
CalibModeDelta(posx, posy);
break;
case (POSCTL_CALIB_MODE_AXIS1):
case (POSCTL_CALIB_MODE_AXIS2):
CalibModeAxis(posx, posy);
break;
case (POSCTL_CALIB_MODE_FINISH):
CalibModeFinish();
break;
default:
calib_mode = POSCTL_CALIB_MODE_OFF;
mode = POSCTL_MODE_OFF;
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
}
//
// dist_axis1+axis2 should contain the lot distance to the
// of the calibrated axis vector with the starting point at target_pos
// if the current position is below the axis line, the distance must be negative.
//
// this distance must be controlled by the PID axis controller.
// dist_axis1 --> will control pid_axis2
// dist_axis2 --> will control pis_axis1
else if (mode == POSCTL_MODE_CONTROL) {
position_f_2d p;
double d;
LockMutex();
// calculate
// dist_axis < 0.0 pid should increase the output.
// > 0.0 pid should decrease the output
p.x = 0.0; p.y = 0.0;
d = calib_axis1_v.perpendicular(calib_axis2_v, p);
p.x = posx; p.y = posy;
axis_pv[0] = calib_axis2_v.perpendicular(p, target_pos);
axis_pv[1] = calib_axis1_v.perpendicular(p, target_pos);
axis_op[0] = pid_axis[0].Update(0.0, axis_pv[0]);
axis_op[1] = pid_axis[1].Update(0.0, axis_pv[1]);
printf ("%s:%d %s", __FILE__, __LINE__, __FUNCTION__);
printf (" d: %+6.3f ", d);
printf (" Axis1 [dist:%+6.3f out1:%+6.3f] ", axis_pv[0], axis_op[0]);
printf (" Axis2 [dist:%+6.3f out2:%+6.3f]\n", axis_pv[1], axis_op[1]);
OutputWriteValue(0, axis_op[0]);
OutputWriteValue(1, axis_op[1]);
axis_history_add(0, axis_pv[0], axis_op[0]);
axis_history_add(1, axis_pv[1], axis_op[1]);
UnLockMutex();
gdk_threads_add_idle(cb_thread_posctl, NULL);
}
else {
LockMutex();
target_pos.x = posx;
target_pos.y = posy;
UnLockMutex();
}
};
void PosCtl::SetDevice (std::string d) {
printf ("%s:%d %s new device:%s\n", __FILE__, __LINE__, __FUNCTION__, d.c_str());
OutputClose();
device = d;
if (d.compare ("SIMULATION") == 0) device_type = POSCTL_DEVTYPE_SIM;
else device_type = POSCTL_DEVTYPE_TTY;
};
int PosCtl::OutputClose() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (device_fd > 0) close(device_fd);
device_fd = -1;
return 0;
};
int PosCtl::OutputOpen() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (device_fd > 0) return 0;
device_fd = open (device.c_str(), O_RDWR);
if (device_fd < 0) {
printf ("%s:%d could not open device:%s Error:%s\n", __FILE__, __LINE__,
device.c_str(), strerror(errno));
errormessage_display ((char *)"window-posctl", (char *)"OutputOpen",
(char*)"%s:%d could not open device:%s Error:%s\n", __FILE__, __LINE__, device.c_str(), strerror(errno));
return -1;
}
return 0;
};
int PosCtl::WriteTTY (char * outbuf) {
ssize_t len;
printf ("%s:%d %s send: '%s'\n", __FILE__, __LINE__, __FUNCTION__, outbuf);
len = write (device_fd, outbuf, strlen(outbuf));
if ((size_t) len != strlen(outbuf) || len < 0) {
printf ("%s:%d could not write data to device:%s Error:%s\n", __FILE__, __LINE__,
device.c_str(), strerror(errno));
errormessage_display ((char *)"window-posctl", (char *)"WriteTTY",
(char *) "%s:%d could not write data to device:%s Error:%s\n", __FILE__, __LINE__,
device.c_str(), strerror(errno));
}
return 0;
}
int PosCtl::ReadTTY (char * inbuf, int length) {
ssize_t len;
// make device non-blocking
fcntl(device_fd, F_SETFL, fcntl(device_fd, F_GETFL) | O_NONBLOCK);
len = read (device_fd, inbuf, length);
// somehow the first read sometimes fails, no idea why
if (len < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
usleep(100000);
len = read (device_fd, inbuf, length);
}
if (len < 0) {
printf ("%s:%d could not read data from device:%s Error:%s\n", __FILE__, __LINE__,
device.c_str(), strerror(errno));
errormessage_display ((char *)"window-posctl", (char *)"ReadTTY",
(char *) "%s:%d could not read data from device:%s Error:%s\n", __FILE__, __LINE__,
device.c_str(), strerror(errno));
inbuf[0] = 0;
}
else {
inbuf[len] = 0;
printf ("%s:%d %s receive: '%s'\n", __FILE__, __LINE__, __FUNCTION__, inbuf);
}
// make device blocking
fcntl(device_fd, F_SETFL, fcntl(device_fd, F_GETFL) & ~O_NONBLOCK);
return 0;
}
int PosCtl::OutputWriteValue (int axis, double value) {
char outbuf[255];
// printf ("%s:%d %s Axis %d Value:%f\n", __FILE__, __LINE__, __FUNCTION__, axis, value);
if (device_type == POSCTL_DEVTYPE_SIM) {
simulation.AxisSetValue(axis, value);
return 0;
}
if (device_fd <= 0) if (OutputOpen() != 0) return -1;
//
// save language setting and set to plain C
std::string s = setlocale(LC_ALL, NULL);
setlocale (LC_ALL, "C");
snprintf(outbuf, 254, ":R%c%+09.4f#", (axis == 0 ? 'D' : 'R'), value);
outbuf[254] = 0;
// reset language setting to default
setlocale (LC_ALL, s.c_str());
WriteTTY(outbuf);
ReadTTY(outbuf, sizeof(outbuf) - 1);
return 0;
};
int PosCtl::OutputWriteStop () {
char outbuf[255];
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (device_type == POSCTL_DEVTYPE_SIM) {
simulation.AxisStop();
return 0;
}
snprintf (outbuf, 255, ":Q#");
if (device_fd <= 0) if (OutputOpen() != 0) return -1;
WriteTTY(outbuf);
return 0;
}
int PosCtl::OutputWriteStart () {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
OutputWriteValue(0, 0.0);
OutputWriteValue(1, 0.0);
return 0;
}