re add log tools
This commit is contained in:
parent
590447d855
commit
1b3d85bcb9
6 changed files with 1974 additions and 0 deletions
168
logdata/Timeline.pde
Normal file
168
logdata/Timeline.pde
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
|
||||||
|
public class Timeline
|
||||||
|
{
|
||||||
|
PVector posOrigin;
|
||||||
|
PVector size;
|
||||||
|
|
||||||
|
int textsize=12;
|
||||||
|
|
||||||
|
color cborder = color(200,200,200);
|
||||||
|
color ctimeslider = color(255,255,255);
|
||||||
|
|
||||||
|
private float time_start; //in seconds
|
||||||
|
private float time_end;
|
||||||
|
|
||||||
|
float[] preview_cmd;
|
||||||
|
float[] preview_throttle;
|
||||||
|
float[] preview_brake;
|
||||||
|
float[] preview_currentAll;
|
||||||
|
|
||||||
|
public Timeline(int px, int py, int pw, int ph) {
|
||||||
|
this.posOrigin= new PVector(px,py); //lower left corner
|
||||||
|
this.size = new PVector(pw,ph); //to the right and up
|
||||||
|
|
||||||
|
preview_cmd=new float[(int)this.size.x];
|
||||||
|
preview_throttle=new float[(int)this.size.x];
|
||||||
|
preview_brake=new float[(int)this.size.x];
|
||||||
|
preview_currentAll=new float[(int)this.size.x];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void generatePreview(Table logdata) {
|
||||||
|
int cID=0; //current ID for generating preview in datalog
|
||||||
|
for (int x=0;x<this.size.x;x++) //for every width pixel of timeline
|
||||||
|
{
|
||||||
|
float t1=xPosToTimeSeconds(x); //time start for this pixel
|
||||||
|
float t2=xPosToTimeSeconds(x+1); //time end for this pixel
|
||||||
|
while ((cID+1 < logdata.getRowCount()) && ( t1 > (logdata.getRow(cID+1).getFloat("time")))) { //while starttime not reached
|
||||||
|
cID++; //next
|
||||||
|
}
|
||||||
|
int preview_mean_counter=0;
|
||||||
|
float preview_mean_cmd=0;
|
||||||
|
float preview_mean_brake=0;
|
||||||
|
float preview_mean_throttle=0;
|
||||||
|
float preview_mean_currentAll=0;
|
||||||
|
|
||||||
|
|
||||||
|
while ((cID+1 < logdata.getRowCount()) && ( t2 > (logdata.getRow(cID+1).getFloat("time")))) { //while endtime not reached
|
||||||
|
TableRow row = logdata.getRow(cID);
|
||||||
|
nextTimeData=(int)(logdata.getRow(cID+1).getFloat("time")*1000); //get time and convert from seconds to ms
|
||||||
|
|
||||||
|
lastTimeMillis=nextTimeMillis;
|
||||||
|
|
||||||
|
|
||||||
|
preview_mean_cmd += (row.getInt("cmd_FrontL")+row.getInt("cmd_FrontR")+row.getInt("cmd_RearL")+row.getInt("cmd_RearR"))/4.0;
|
||||||
|
preview_mean_brake += row.getInt("brake");
|
||||||
|
preview_mean_throttle += row.getInt("throttle");
|
||||||
|
preview_mean_currentAll += row.getFloat("currentAll");
|
||||||
|
/*
|
||||||
|
current_FrontL=row.getFloat("current_FrontL");
|
||||||
|
current_FrontR=row.getFloat("current_FrontR");
|
||||||
|
current_RearL=row.getFloat("current_RearL");
|
||||||
|
current_RearR=row.getFloat("current_RearR");
|
||||||
|
speed_FrontL=row.getInt("speed_FrontL");
|
||||||
|
speed_FrontR=row.getInt("speed_FrontR");
|
||||||
|
speed_RearL=row.getInt("speed_RearL");
|
||||||
|
speed_RearR=row.getInt("speed_RearR");
|
||||||
|
temp_Front=row.getFloat("temp_Front");
|
||||||
|
temp_Rear=row.getFloat("temp_Rear");
|
||||||
|
vbat_Front=row.getFloat("vbat_Front");
|
||||||
|
vbat_Rear=row.getFloat("vbat_Rear");
|
||||||
|
currentAll=row.getFloat("currentAll");
|
||||||
|
throttle=row.getInt("throttle");
|
||||||
|
brake=row.getInt("brake");*/
|
||||||
|
|
||||||
|
preview_mean_counter++;
|
||||||
|
|
||||||
|
cID++; //next
|
||||||
|
}
|
||||||
|
preview_mean_cmd/=preview_mean_counter;
|
||||||
|
preview_mean_brake/=preview_mean_counter;
|
||||||
|
preview_mean_throttle/=preview_mean_counter;
|
||||||
|
preview_mean_currentAll/=preview_mean_counter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
preview_cmd[x]=preview_mean_cmd;
|
||||||
|
preview_brake[x]=preview_mean_brake;
|
||||||
|
preview_throttle[x]=preview_mean_throttle;
|
||||||
|
preview_currentAll[x]=preview_mean_currentAll;
|
||||||
|
}//next pixel
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimes(float _start,float _end) {
|
||||||
|
this.time_start=_start;
|
||||||
|
this.time_end=_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void drawTL(float _time) {
|
||||||
|
//Preview visualization
|
||||||
|
for (int x=0;x<this.size.x;x++) //for every width pixel of timeline
|
||||||
|
{
|
||||||
|
if (!Float.isNaN(preview_cmd[x])) {
|
||||||
|
float preview_cmd_scaled=constrain(map(preview_cmd[x],0,1000,0.0,1.0), 0.0,1.0);
|
||||||
|
preview_cmd_scaled=sqrt(sqrt(preview_cmd_scaled))+0.1;//more contrast
|
||||||
|
|
||||||
|
float preview_currentAll_scaled=constrain(map(preview_currentAll[x],0,10,0.0,1.0), 0.0,1.0);
|
||||||
|
preview_currentAll_scaled=sqrt(sqrt(preview_currentAll_scaled));//more contrast
|
||||||
|
|
||||||
|
colorMode(HSB, 360, 100, 100);
|
||||||
|
stroke(color(200-preview_currentAll_scaled*200,100,preview_cmd_scaled*100));
|
||||||
|
line(this.posOrigin.x+x,this.posOrigin.y,this.posOrigin.x+x,this.posOrigin.y+this.size.y); //color gradient background
|
||||||
|
|
||||||
|
float preview_throttle_scaled=constrain(map(preview_throttle[x],0,1000,0.0,1.0), 0.0,1.0);
|
||||||
|
colorMode(HSB, 360, 100, 100);
|
||||||
|
stroke(color(240,100,100));
|
||||||
|
line(this.posOrigin.x+x,this.posOrigin.y+this.size.y,this.posOrigin.x+x,this.posOrigin.y+this.size.y*(1-preview_throttle_scaled/2)); //line bottom up
|
||||||
|
|
||||||
|
float preview_brake_scaled=constrain(map(preview_brake[x],0,1000,0.0,1.0), 0.0,1.0);
|
||||||
|
colorMode(HSB, 360, 100, 100);
|
||||||
|
stroke(color(0,100,100));
|
||||||
|
line(this.posOrigin.x+x,this.posOrigin.y,this.posOrigin.x+x,this.posOrigin.y+this.size.y*preview_brake_scaled/2); //line top down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Boarder
|
||||||
|
rectMode(CORNERS);
|
||||||
|
noFill();
|
||||||
|
stroke(this.cborder);
|
||||||
|
rect(this.posOrigin.x,this.posOrigin.y,this.posOrigin.x+this.size.x,this.posOrigin.y+this.size.y); //border
|
||||||
|
|
||||||
|
//current time
|
||||||
|
int timeslider_xpos = (int)( this.posOrigin.x+timeSecondsToXPos(_time) );
|
||||||
|
stroke(this.ctimeslider);
|
||||||
|
line(timeslider_xpos,this.posOrigin.y-3,timeslider_xpos,this.posOrigin.y+this.size.y+3);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
textSize(this.textsize);
|
||||||
|
textAlign(CENTER);
|
||||||
|
text(int(this.time_start+0.5)+"s", this.posOrigin.x, this.posOrigin.y-1); //starttime
|
||||||
|
text(int(this.time_end+0.5)+"s", this.posOrigin.x+this.size.x, this.posOrigin.y-1); //endtime
|
||||||
|
}
|
||||||
|
|
||||||
|
public float checkMouse(int mx, int my)
|
||||||
|
{
|
||||||
|
if (mx>=this.posOrigin.x && mx <=this.posOrigin.x+this.size.x && my>=this.posOrigin.y && my<=this.posOrigin.y+this.size.y) { //mouse whithin timeline boarder
|
||||||
|
float mousetime=xPosToTimeSeconds((int)(mx-this.posOrigin.x));
|
||||||
|
//float mousetimesliderpercent=(mx-this.posOrigin.x)/this.size.x;
|
||||||
|
//float mousetime=mousetimesliderpercent*(this.time_end-this.time_start)+this.time_start;
|
||||||
|
return millis()/1000.0-mousetime;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int timeSecondsToXPos(float _time) {
|
||||||
|
float timesliderpercent = (_time-this.time_start)/(this.time_end-this.time_start); //0<= x <=1
|
||||||
|
return (int)(timesliderpercent*this.size.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float xPosToTimeSeconds(int _x) {
|
||||||
|
float mousetimesliderpercent=_x/this.size.x;
|
||||||
|
return mousetimesliderpercent*(this.time_end-this.time_start)+this.time_start;
|
||||||
|
}
|
||||||
|
}
|
691
logdata/Visualization.pde
Normal file
691
logdata/Visualization.pde
Normal file
|
@ -0,0 +1,691 @@
|
||||||
|
|
||||||
|
abstract class Visualization
|
||||||
|
{
|
||||||
|
float value=0;
|
||||||
|
float value2=-1000;
|
||||||
|
|
||||||
|
PVector posOrigin = new PVector(100,100);
|
||||||
|
float valueMin=0;
|
||||||
|
float valueMax=0;
|
||||||
|
float value2Min=0; //value 2 for twodimensional visualization or second displayed value
|
||||||
|
float value2Max=0;
|
||||||
|
|
||||||
|
float valueMinRecord = Float.NaN; //nan means no value here
|
||||||
|
float valueMaxRecord = Float.NaN;
|
||||||
|
float value2MinRecord = Float.NaN; //nan means no value here
|
||||||
|
float value2MaxRecord = Float.NaN;
|
||||||
|
boolean showMinMax=false;
|
||||||
|
|
||||||
|
//default colors (not all used by every implementation)
|
||||||
|
|
||||||
|
|
||||||
|
color cmain = color(255,255,255);
|
||||||
|
color cmain2 = color(255,255,255);
|
||||||
|
color cscale = color(100,100,100);
|
||||||
|
color cborder = color(200,200,200);
|
||||||
|
color cmin = color(0,150,0);
|
||||||
|
color cmax = color(150,0,0);
|
||||||
|
color ctext = color(255,255,255);
|
||||||
|
|
||||||
|
int textsize=12;
|
||||||
|
float textWidthScale=1.0/2*this.textsize/2; //*text.length()*
|
||||||
|
int showdecimals=2;
|
||||||
|
|
||||||
|
String title="";
|
||||||
|
String title2="";
|
||||||
|
String valueUnit="";
|
||||||
|
String value2Unit="";
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void drawVis();
|
||||||
|
public void setValue(float pv){
|
||||||
|
//this.value=constrain(pv,valueMin,valueMax);
|
||||||
|
this.value=pv;
|
||||||
|
if (this.showMinMax){
|
||||||
|
if (Float.isNaN(this.valueMinRecord) || this.value<this.valueMinRecord){
|
||||||
|
this.valueMinRecord=this.value;
|
||||||
|
}
|
||||||
|
if (Float.isNaN(this.valueMaxRecord) || this.value>this.valueMaxRecord){
|
||||||
|
this.valueMaxRecord=this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue2(float pv){
|
||||||
|
//this.value2=constrain(pv,valueMin,valueMax);
|
||||||
|
this.value2=pv;
|
||||||
|
if (this.showMinMax){
|
||||||
|
if (Float.isNaN(this.value2MinRecord) || this.value2<this.value2MinRecord){
|
||||||
|
this.value2MinRecord=this.value2;
|
||||||
|
}
|
||||||
|
if (Float.isNaN(this.value2MaxRecord) || this.value2>this.value2MaxRecord){
|
||||||
|
this.value2MaxRecord=this.value2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getValueNormalized() {
|
||||||
|
return (constrain(this.value,this.valueMin,this.valueMax)-this.valueMin)/(this.valueMax-this.valueMin);
|
||||||
|
}
|
||||||
|
public float getValueMinNormalized() {
|
||||||
|
return (constrain(this.valueMinRecord,this.valueMin,this.valueMax)-this.valueMin)/(this.valueMax-this.valueMin);
|
||||||
|
}
|
||||||
|
public float getValueMaxNormalized() {
|
||||||
|
return (constrain(this.valueMaxRecord,this.valueMin,this.valueMax)-this.valueMin)/(this.valueMax-this.valueMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getValue2Normalized() {
|
||||||
|
return (constrain(this.value2,this.value2Min,this.value2Max)-this.value2Min)/(this.value2Max-this.value2Min);
|
||||||
|
}
|
||||||
|
public float getValue2MinNormalized() {
|
||||||
|
return (constrain(this.value2MinRecord,this.value2Min,this.value2Max)-this.value2Min)/(this.value2Max-this.value2Min);
|
||||||
|
}
|
||||||
|
public float getValue2MaxNormalized() {
|
||||||
|
return (constrain(this.value2MaxRecord,this.value2Min,this.value2Max)-this.value2Min)/(this.value2Max-this.value2Min);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowMinMax(boolean pshowMinMax){
|
||||||
|
this.showMinMax = pshowMinMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setcmain(color pc){
|
||||||
|
this.cmain=pc;
|
||||||
|
}
|
||||||
|
public void setcmain2(color pc){
|
||||||
|
this.cmain2=pc;
|
||||||
|
}
|
||||||
|
public void setcscale(color pc){
|
||||||
|
this.cscale=pc;
|
||||||
|
}
|
||||||
|
public void setcborder(color pc){
|
||||||
|
this.cborder=pc;
|
||||||
|
}
|
||||||
|
public void setcmin(color pc){
|
||||||
|
this.cmin=pc;
|
||||||
|
}
|
||||||
|
public void setcmax(color pc){
|
||||||
|
this.cmax=pc;
|
||||||
|
}
|
||||||
|
public void setctext(color pc){
|
||||||
|
this.ctext=pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String pt) {
|
||||||
|
this.title=pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle2(String pt) {
|
||||||
|
this.title2=pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueUnit(String pt) {
|
||||||
|
this.valueUnit=pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue2Unit(String pt) {
|
||||||
|
this.value2Unit=pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setshowdecimals(int pd) {
|
||||||
|
this.showdecimals=pd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormattedValue(double pv){
|
||||||
|
if( ( (int)(pv*100)/100.0 )%1==0){
|
||||||
|
return ""+(int)pv;
|
||||||
|
}else{ //has decimals
|
||||||
|
return String.format("%."+this.showdecimals+"f", pv); //limit decimal places
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BarV extends Visualization {
|
||||||
|
PVector size = new PVector(10,100);
|
||||||
|
|
||||||
|
public BarV(int px, int py, int pw, int ph, float pvmin, float pvmax) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //lower left corner
|
||||||
|
this.size = new PVector(pw,ph); //to the right and up
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
fill(super.cmain); noStroke();
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueNormalized()) );
|
||||||
|
|
||||||
|
|
||||||
|
if (!Float.isNaN(super.valueMinRecord)){
|
||||||
|
stroke(super.cmin);
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) ,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) );
|
||||||
|
}
|
||||||
|
if (!Float.isNaN(super.valueMaxRecord)){
|
||||||
|
stroke(super.cmax);
|
||||||
|
line(super.posOrigin.x, super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()), super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
noFill(); stroke(this.cborder);
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
text(super.getFormattedValue(super.valueMin)+valueUnit,super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2);
|
||||||
|
text(super.getFormattedValue(super.valueMax)+valueUnit,super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y+super.textsize/2);
|
||||||
|
textAlign(LEFT);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2-this.size.y/2); //display value
|
||||||
|
|
||||||
|
//Title
|
||||||
|
textAlign(CENTER);
|
||||||
|
text(super.title, super.posOrigin.x+this.size.x/2, super.posOrigin.y-this.size.y-1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class BarV_cmd extends Visualization {
|
||||||
|
PVector size = new PVector(10,100);
|
||||||
|
|
||||||
|
public BarV_cmd(int px, int py, int pw, int ph, float pvmin, float pvmax) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //lower left corner
|
||||||
|
this.size = new PVector(pw,ph); //to the right and up
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
fill(super.cmain); noStroke();
|
||||||
|
int zeroy=(int)map(0,super.valueMin,super.valueMax,0,this.size.y);
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueNormalized()) );
|
||||||
|
|
||||||
|
|
||||||
|
if (!Float.isNaN(super.valueMinRecord)){
|
||||||
|
stroke(super.cmin);
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) ,super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMinNormalized()) );
|
||||||
|
}
|
||||||
|
if (!Float.isNaN(super.valueMaxRecord)){
|
||||||
|
stroke(super.cmax);
|
||||||
|
line(super.posOrigin.x, super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()), super.posOrigin.x+this.size.x,super.posOrigin.y-( this.size.y*super.getValueMaxNormalized()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
noFill(); stroke(this.cborder);
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-zeroy); //zero line
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
//text(super.getFormattedValue(super.valueMin),super.posOrigin.x+this.size.x+1,super.posOrigin.y+super.textsize/2);
|
||||||
|
//text(super.getFormattedValue(super.valueMax),super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y+super.textsize/2);
|
||||||
|
textAlign(RIGHT);
|
||||||
|
text(super.getFormattedValue(super.value),super.posOrigin.x+this.size.x/2+super.textWidthScale*3,super.posOrigin.y+super.textsize); //display value
|
||||||
|
textAlign(LEFT);
|
||||||
|
text(valueUnit,super.posOrigin.x+this.size.x/2+super.textWidthScale*3,super.posOrigin.y+super.textsize); //display unit on the right without disturbing alignment
|
||||||
|
|
||||||
|
|
||||||
|
//Title
|
||||||
|
textAlign(LEFT);
|
||||||
|
text(super.title, super.posOrigin.x, super.posOrigin.y-this.size.y-1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class BarH extends Visualization {
|
||||||
|
PVector size = new PVector(10,100);
|
||||||
|
|
||||||
|
public BarH(int px, int py, int pw, int ph,float pvmin, float pvmax) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //lower left corner
|
||||||
|
this.size = new PVector(pw,ph); //to the right and up
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
fill(super.cmain); noStroke();
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueNormalized()),super.posOrigin.y- this.size.y );
|
||||||
|
|
||||||
|
if (!Float.isNaN(super.valueMinRecord)){
|
||||||
|
stroke(super.cmin);
|
||||||
|
line(super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y- this.size.y );
|
||||||
|
}
|
||||||
|
if (!Float.isNaN(super.valueMaxRecord)){
|
||||||
|
stroke(super.cmax);
|
||||||
|
line(super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y- this.size.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
noFill(); stroke(super.cborder);
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
text(super.getFormattedValue(super.valueMin)+valueUnit,super.posOrigin.x-super.getFormattedValue(super.valueMin).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
|
||||||
|
text(super.getFormattedValue(super.valueMax)+valueUnit,super.posOrigin.x+this.size.x-super.getFormattedValue(super.valueMax).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x+this.size.x/2-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
|
||||||
|
|
||||||
|
|
||||||
|
//Title
|
||||||
|
text(super.title, super.posOrigin.x+this.size.x/2-super.title.length()*super.textWidthScale,super.posOrigin.y+this.size.y+1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class BarH_cmd extends Visualization {
|
||||||
|
PVector size = new PVector(10,100);
|
||||||
|
|
||||||
|
public BarH_cmd(int px, int py, int pw, int ph,float pvmin, float pvmax) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //lower left corner
|
||||||
|
this.size = new PVector(pw,ph); //to the right and up
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
int zerox=(int)map(0,super.valueMin,super.valueMax,0,this.size.x);
|
||||||
|
|
||||||
|
fill(super.cmain); noStroke();
|
||||||
|
rect(super.posOrigin.x+zerox,super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueNormalized()),super.posOrigin.y- this.size.y );
|
||||||
|
|
||||||
|
if (!Float.isNaN(super.valueMinRecord)){
|
||||||
|
stroke(super.cmin);
|
||||||
|
line(super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMinNormalized()),super.posOrigin.y- this.size.y );
|
||||||
|
}
|
||||||
|
if (!Float.isNaN(super.valueMaxRecord)){
|
||||||
|
stroke(super.cmax);
|
||||||
|
line(super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y,super.posOrigin.x+( this.size.x*super.getValueMaxNormalized()),super.posOrigin.y- this.size.y );
|
||||||
|
}
|
||||||
|
|
||||||
|
noFill(); stroke(super.cborder);
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y- this.size.y );
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
//text(super.getFormattedValue(super.valueMin),super.posOrigin.x-super.getFormattedValue(super.valueMin).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
|
||||||
|
//text(super.getFormattedValue(super.valueMax),super.posOrigin.x+this.size.x-super.getFormattedValue(super.valueMax).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x+this.size.x/2-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1);
|
||||||
|
|
||||||
|
|
||||||
|
//Title
|
||||||
|
text(super.title, super.posOrigin.x+this.size.x/2-super.title.length()*super.textWidthScale,super.posOrigin.y+this.textsize+1);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Tacho extends Visualization {
|
||||||
|
int size;
|
||||||
|
|
||||||
|
|
||||||
|
public Tacho(int px, int py, int psize, float pvmin, float pvmax) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.value2Min=pvmin; //same scale
|
||||||
|
super.value2Max=pvmax; //same scale
|
||||||
|
super.posOrigin= new PVector(px,py); //circle center
|
||||||
|
this.size = psize; //radius from the center
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
stroke(super.cmain);
|
||||||
|
float angle=PI-super.getValueNormalized()*PI; //0=right, positive=CCW
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle)*this.size*0.8 ,super.posOrigin.y-sin(angle)*this.size*0.8); //draw tacho needle
|
||||||
|
|
||||||
|
if (super.value2!=-1000) {
|
||||||
|
stroke(super.cmain2);
|
||||||
|
float angle2=PI-super.getValue2Normalized()*PI; //0=right, positive=CCW
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle2)*this.size*0.8 ,super.posOrigin.y-sin(angle2)*this.size*0.8); //draw tacho needle
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Float.isNaN(super.valueMinRecord)){
|
||||||
|
stroke(this.cmin);
|
||||||
|
float angleMin=PI-super.getValueMinNormalized()*PI; //0=right, positive=CCW
|
||||||
|
line(super.posOrigin.x+cos(angleMin)*this.size*0.65 ,super.posOrigin.y-sin(angleMin)*this.size*0.65,super.posOrigin.x+cos(angleMin)*this.size*0.75 ,super.posOrigin.y-sin(angleMin)*this.size*0.75); //draw tacho needle min
|
||||||
|
}
|
||||||
|
if (!Float.isNaN(super.valueMaxRecord)){
|
||||||
|
stroke(this.cmax);
|
||||||
|
float angleMax=PI-super.getValueMaxNormalized()*PI; //0=right, positive=CCW
|
||||||
|
line(super.posOrigin.x+cos(angleMax)*this.size*0.65 ,super.posOrigin.y-sin(angleMax)*this.size*0.65,super.posOrigin.x+cos(angleMax)*this.size*0.75 ,super.posOrigin.y-sin(angleMax)*this.size*0.75); //draw tacho needle max
|
||||||
|
}
|
||||||
|
|
||||||
|
stroke(this.cscale);
|
||||||
|
fill(super.ctext);
|
||||||
|
float _steps=0.1;
|
||||||
|
for (float i=0;i<=1+_steps; i+=_steps){
|
||||||
|
float a=PI-PI*i;
|
||||||
|
line(super.posOrigin.x+cos(a)*this.size*0.85 ,super.posOrigin.y-sin(a)*this.size*0.85 ,super.posOrigin.x+cos(a)*this.size ,super.posOrigin.y-sin(a)*this.size);
|
||||||
|
String _text=super.getFormattedValue(super.valueMin+(super.valueMax-super.valueMin)*i);
|
||||||
|
text(_text,super.posOrigin.x+cos(a)*this.size*1.1-_text.length()*super.textWidthScale-1 ,super.posOrigin.y-sin(a)*this.size*1.1+super.textsize/2-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//text
|
||||||
|
fill(super.cmain);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2-this.size*0.3);
|
||||||
|
if (super.value2!=-1000) { //value2 is used
|
||||||
|
fill(super.cmain2);
|
||||||
|
text(super.getFormattedValue(super.value2)+value2Unit,super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2*4-this.size*0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Title
|
||||||
|
textAlign(CENTER);
|
||||||
|
fill(super.ctext);
|
||||||
|
text(super.title, super.posOrigin.x, super.posOrigin.y+super.textsize*1.5);
|
||||||
|
if (super.title2!="") {
|
||||||
|
text(super.title2, super.posOrigin.x, super.posOrigin.y+super.textsize*1.5*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class Direction extends Visualization {
|
||||||
|
int size;
|
||||||
|
float angleoffset;
|
||||||
|
float minvisiblevalue;
|
||||||
|
float maxvisiblevalue;
|
||||||
|
|
||||||
|
public Direction(int px, int py, int psize,float pvmin, float pvmax, float pvlmin, float pvlmax, float pangleoffset) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //center
|
||||||
|
this.size = psize; //radius from the center
|
||||||
|
|
||||||
|
super.value2Min=pvlmin;
|
||||||
|
super.value2Max=pvlmax;
|
||||||
|
|
||||||
|
this.angleoffset=pangleoffset;
|
||||||
|
|
||||||
|
this.minvisiblevalue=valueMin;
|
||||||
|
this.maxvisiblevalue=valueMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction(int px, int py, int psize,float pvmin, float pvmax, float pvlmin, float pvlmax, float pangleoffset, float pminvisiblevalue, float pmaxvisiblevalue) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //center
|
||||||
|
this.size = psize; //radius from the center
|
||||||
|
|
||||||
|
super.value2Min=pvlmin;
|
||||||
|
super.value2Max=pvlmax;
|
||||||
|
|
||||||
|
this.angleoffset=pangleoffset;
|
||||||
|
|
||||||
|
this.minvisiblevalue=pminvisiblevalue;
|
||||||
|
this.maxvisiblevalue=pmaxvisiblevalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
stroke(super.cborder); noFill();
|
||||||
|
if (this.minvisiblevalue==super.valueMin & this.maxvisiblevalue==super.valueMax) {
|
||||||
|
ellipseMode(RADIUS); //centerx, centery, width,height for ellipse
|
||||||
|
ellipse(super.posOrigin.x, super.posOrigin.y, this.size,this.size);
|
||||||
|
}else{
|
||||||
|
arc(super.posOrigin.x, super.posOrigin.y, this.size*2,this.size*2, this.angleoffset+2*PI -this.minvisiblevalue/super.valueMin*PI, 2*PI+this.angleoffset +this.maxvisiblevalue/super.valueMax*PI, PIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
stroke(super.cmain);
|
||||||
|
float angle=map(super.getValueNormalized(),0,1,0,2*PI)+this.angleoffset;
|
||||||
|
//float _vecsize=this.size*( (super.value2-super.value2Min)/(super.value2Max-super.value2Min));
|
||||||
|
float _vecsize=this.size*super.getValue2Normalized();
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize);
|
||||||
|
|
||||||
|
line(super.posOrigin.x+cos(angle-0.1)*_vecsize*0.9,super.posOrigin.y-sin(angle-0.1)*_vecsize*0.9,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize); //arrow
|
||||||
|
line(super.posOrigin.x+cos(angle+0.1)*_vecsize*0.9,super.posOrigin.y-sin(angle+0.1)*_vecsize*0.9,super.posOrigin.x+cos(angle)*_vecsize,super.posOrigin.y-sin(angle)*_vecsize);
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit,super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize*1.5*1);
|
||||||
|
if (super.value2<super.value2Max){ //display only if in use
|
||||||
|
text("l="+super.getFormattedValue(super.value2)+valueUnit,super.posOrigin.x-("l="+super.getFormattedValue(super.value2)).length()*super.textWidthScale,super.posOrigin.y+super.textsize*1.5*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Title
|
||||||
|
text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5*3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class GraphRoll extends Visualization {
|
||||||
|
PVector size;
|
||||||
|
float[] valuearray;
|
||||||
|
float[] valueMinarray;
|
||||||
|
float[] valueMaxarray;
|
||||||
|
int arraypos;
|
||||||
|
int recordevery=1; //minimum value =1, the higher the slower
|
||||||
|
int recordevery_counter=0;
|
||||||
|
|
||||||
|
public GraphRoll(int px, int py, int psx, int psy,float pvmin, float pvmax,int precordevery) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //center
|
||||||
|
this.size = new PVector(psx,psy);
|
||||||
|
|
||||||
|
this.recordevery= precordevery;
|
||||||
|
|
||||||
|
this.valuearray = new float[psx]; //array size equals window width
|
||||||
|
this.arraypos=0; //points to position to write to next (current value is at arraypos-1)
|
||||||
|
this.valueMinarray = new float[psx];
|
||||||
|
this.valueMaxarray = new float[psx];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
//update history
|
||||||
|
this.recordevery_counter++;
|
||||||
|
if (this.recordevery_counter>=this.recordevery){
|
||||||
|
this.valuearray[this.arraypos]=super.getValueNormalized();
|
||||||
|
this.arraypos++;
|
||||||
|
this.arraypos%=this.valuearray.length;
|
||||||
|
this.recordevery_counter=0;
|
||||||
|
|
||||||
|
if (super.showMinMax) {
|
||||||
|
this.valueMinarray[this.arraypos]=super.getValueMinNormalized();
|
||||||
|
this.valueMaxarray[this.arraypos]=super.getValueMaxNormalized();
|
||||||
|
super.valueMinRecord=Float.NaN;
|
||||||
|
super.valueMaxRecord=Float.NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
stroke(super.cborder);
|
||||||
|
noFill();
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-this.size.y);
|
||||||
|
int zeroy=(int)map(0,super.valueMin,super.valueMax,0,this.size.y);
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-zeroy);
|
||||||
|
|
||||||
|
noFill();
|
||||||
|
int _cpos=this.arraypos;
|
||||||
|
int _x=0; //position of _ya
|
||||||
|
while (_cpos!=((this.arraypos-1+this.valuearray.length)%this.valuearray.length)) { //go trough all values starting at oldest value
|
||||||
|
float _ya=this.valuearray[_cpos];
|
||||||
|
float _yb=this.valuearray[(_cpos+1)%this.valuearray.length];
|
||||||
|
|
||||||
|
//float _yaMin=this.valueMinarray[_cpos];
|
||||||
|
float _ybMin=this.valueMinarray[(_cpos+1)%this.valuearray.length];
|
||||||
|
float _ybMax=this.valueMaxarray[(_cpos+1)%this.valuearray.length];
|
||||||
|
|
||||||
|
|
||||||
|
if (super.showMinMax) {
|
||||||
|
stroke(super.cmin);
|
||||||
|
//line(super.posOrigin.x+_x,super.posOrigin.y-_yaMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y);
|
||||||
|
line(super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
|
||||||
|
stroke(super.cmax);
|
||||||
|
line(super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMax*this.size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
stroke(super.cmain);
|
||||||
|
line(super.posOrigin.x+_x,super.posOrigin.y-_ya*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_x++;
|
||||||
|
_cpos++;
|
||||||
|
_cpos%=this.valuearray.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit, super.posOrigin.x+this.size.x-50, super.posOrigin.y+super.textsize*1.5);
|
||||||
|
text(super.getFormattedValue(super.valueMin)+valueUnit, super.posOrigin.x+this.size.x+1, super.posOrigin.y+super.textsize*0.5-1);
|
||||||
|
text(super.getFormattedValue(super.valueMax)+valueUnit, super.posOrigin.x+this.size.x+1, super.posOrigin.y-this.size.y+super.textsize*0.5-1);
|
||||||
|
//Title
|
||||||
|
text(super.title, super.posOrigin.x, super.posOrigin.y+super.textsize*1.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class GraphRoll_minimal extends Visualization {
|
||||||
|
PVector size;
|
||||||
|
float[] valuearray;
|
||||||
|
float[] valueMinarray;
|
||||||
|
float[] valueMaxarray;
|
||||||
|
int arraypos;
|
||||||
|
int recordevery=1; //minimum value =1, the higher the slower
|
||||||
|
int recordevery_counter=0;
|
||||||
|
|
||||||
|
public GraphRoll_minimal(int px, int py, int psx, int psy,float pvmin, float pvmax,int precordevery) {
|
||||||
|
super.valueMin=pvmin;
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //center
|
||||||
|
this.size = new PVector(psx,psy);
|
||||||
|
|
||||||
|
this.recordevery= precordevery;
|
||||||
|
|
||||||
|
this.valuearray = new float[psx]; //array size equals window width
|
||||||
|
this.arraypos=0; //points to position to write to next (current value is at arraypos-1)
|
||||||
|
this.valueMinarray = new float[psx];
|
||||||
|
this.valueMaxarray = new float[psx];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
//update history
|
||||||
|
this.recordevery_counter++;
|
||||||
|
if (this.recordevery_counter>=this.recordevery){
|
||||||
|
this.valuearray[this.arraypos]=super.getValueNormalized();
|
||||||
|
this.arraypos++;
|
||||||
|
this.arraypos%=this.valuearray.length;
|
||||||
|
this.recordevery_counter=0;
|
||||||
|
|
||||||
|
if (super.showMinMax) {
|
||||||
|
this.valueMinarray[this.arraypos]=super.getValueMinNormalized();
|
||||||
|
this.valueMaxarray[this.arraypos]=super.getValueMaxNormalized();
|
||||||
|
super.valueMinRecord=Float.NaN;
|
||||||
|
super.valueMaxRecord=Float.NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
stroke(super.cborder);
|
||||||
|
noFill();
|
||||||
|
rect(super.posOrigin.x,super.posOrigin.y,super.posOrigin.x+this.size.x,super.posOrigin.y-this.size.y);
|
||||||
|
int zeroy=(int)map(0,super.valueMin,super.valueMax,0,this.size.y);
|
||||||
|
int dotlength=10;
|
||||||
|
float dotlengthpercentage=0.2; //0 to 1. the lower the less dot length
|
||||||
|
for(int _xdot=(int)super.posOrigin.x; _xdot+dotlength<=super.posOrigin.x+this.size.x; _xdot+=dotlength) {
|
||||||
|
//line(super.posOrigin.x,super.posOrigin.y-zeroy,super.posOrigin.x+this.size.x,super.posOrigin.y-zeroy); //zero line
|
||||||
|
line(_xdot,super.posOrigin.y-zeroy,_xdot+dotlength*dotlengthpercentage,super.posOrigin.y-zeroy); //zero line
|
||||||
|
}
|
||||||
|
|
||||||
|
noFill();
|
||||||
|
int _cpos=this.arraypos;
|
||||||
|
int _x=0; //position of _ya
|
||||||
|
while (_cpos!=((this.arraypos-1+this.valuearray.length)%this.valuearray.length)) { //go trough all values starting at oldest value
|
||||||
|
float _ya=this.valuearray[_cpos];
|
||||||
|
float _yb=this.valuearray[(_cpos+1)%this.valuearray.length];
|
||||||
|
|
||||||
|
//float _yaMin=this.valueMinarray[_cpos];
|
||||||
|
float _ybMin=this.valueMinarray[(_cpos+1)%this.valuearray.length];
|
||||||
|
float _ybMax=this.valueMaxarray[(_cpos+1)%this.valuearray.length];
|
||||||
|
|
||||||
|
|
||||||
|
if (super.showMinMax) {
|
||||||
|
stroke(super.cmin);
|
||||||
|
//line(super.posOrigin.x+_x,super.posOrigin.y-_yaMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y);
|
||||||
|
line(super.posOrigin.x+_x+1,super.posOrigin.y-_ybMin*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
|
||||||
|
stroke(super.cmax);
|
||||||
|
line(super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_ybMax*this.size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
stroke(super.cmain);
|
||||||
|
line(super.posOrigin.x+_x,super.posOrigin.y-_ya*this.size.y, super.posOrigin.x+_x+1,super.posOrigin.y-_yb*this.size.y);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_x++;
|
||||||
|
_cpos++;
|
||||||
|
_cpos%=this.valuearray.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
//text
|
||||||
|
textAlign(LEFT);
|
||||||
|
fill(super.ctext);
|
||||||
|
text(super.getFormattedValue(super.value)+valueUnit, super.posOrigin.x+this.size.x+1, super.posOrigin.y-this.valuearray[(_cpos)%this.valuearray.length]*this.size.y);
|
||||||
|
//text(super.getFormattedValue(super.valueMin), super.posOrigin.x+this.size.x+1, super.posOrigin.y+super.textsize*0.5-1);
|
||||||
|
//text(super.getFormattedValue(super.valueMax), super.posOrigin.x+this.size.x+1, super.posOrigin.y-this.size.y+super.textsize*0.5-1);
|
||||||
|
//Title
|
||||||
|
text(super.title, super.posOrigin.x, super.posOrigin.y-this.size.y+super.textsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VectorDisplay extends Visualization {
|
||||||
|
int size = 100;
|
||||||
|
|
||||||
|
public VectorDisplay(int px, int py, int psize, float pvmax) {
|
||||||
|
super.valueMax=pvmax;
|
||||||
|
super.posOrigin= new PVector(px,py); //center
|
||||||
|
this.size=psize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawVis() {
|
||||||
|
rectMode(CORNERS);
|
||||||
|
textSize(super.textsize);
|
||||||
|
|
||||||
|
stroke(super.cmain);
|
||||||
|
strokeWeight(4);
|
||||||
|
float _x=super.posOrigin.x+this.value/super.valueMax*this.size;
|
||||||
|
float _y=super.posOrigin.y+this.value2/super.valueMax*this.size;
|
||||||
|
line(super.posOrigin.x,super.posOrigin.y,_x,_y);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
114
logdata/analyze.py
Normal file
114
logdata/analyze.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
#import csv
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
def mapRange(value, inMin, inMax, outMin, outMax,constrain=False):
|
||||||
|
if constrain:
|
||||||
|
return max(outMin, min(outMax, outMin + (((value - inMin) / (inMax - inMin)) * (outMax - outMin))))
|
||||||
|
else:
|
||||||
|
return outMin + (((value - inMin) / (inMax - inMin)) * (outMax - outMin))
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Analyzes fixed csv logs from bobbycar')
|
||||||
|
parser.add_argument('-i', '--input', type=argparse.FileType('r'), required=True, help="input csv log file")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
df = pd.read_csv(args.input.name)
|
||||||
|
|
||||||
|
x = df['timestamp']
|
||||||
|
x = [i-x[0] for i in x] #offset time by starttime
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def template():
|
||||||
|
scattersize=1
|
||||||
|
scatteralpha=0.1
|
||||||
|
|
||||||
|
|
||||||
|
fig, ax1 = plt.subplots()
|
||||||
|
|
||||||
|
ax2 = ax1.twinx()
|
||||||
|
|
||||||
|
|
||||||
|
#plt.scatter(x,df['rpm_FrontL'], s=scattersize, alpha=scatteralpha, label="rpm_FrontL")
|
||||||
|
ax1.plot(x,np.array(df['temp_Front']), c='#0000ff', alpha=0.5, label="temp_Front")
|
||||||
|
ax1.plot(x,np.array(df['temp_Rear']), c='#ff0000', alpha=0.5, label="temp_Rear")
|
||||||
|
ax1.plot(x,np.array(df['temp_Air']), c='#00ff00', alpha=0.5, label="temp_Air")
|
||||||
|
#ax2.plot(x,np.array(df['throttle']), c='g', alpha=0.5, label="throttle")
|
||||||
|
|
||||||
|
|
||||||
|
#confidence
|
||||||
|
#confidence_FrontL=[abs(x-df['cmd_FrontL'][i-5 if i>4 else 0])+abs(x-df['cmd_FrontL'][i-10 if i>9 else 0])+abs(x-df['cmd_FrontL'][i-20 if i>19 else 0]) for i,x in enumerate(df['cmd_FrontL'])]
|
||||||
|
#confidence_FrontL=[mapRange(x,0,50,1.0,0.0,True) for x in confidence_FrontL]
|
||||||
|
|
||||||
|
|
||||||
|
#ax1.scatter(df['cmd_FrontL'],df['rpm_FrontL'], s=scattersize, alpha=scatteralpha, label="FrontL")
|
||||||
|
#ax1.scatter(df['cmd_FrontR'],df['rpm_FrontR'], s=scattersize, alpha=scatteralpha, label="FrontR")
|
||||||
|
#ax1.scatter(df['cmd_RearL'],df['rpm_RearL'], s=scattersize, alpha=scatteralpha, label="RearL")
|
||||||
|
#ax1.scatter(df['cmd_RearR'],df['rpm_RearR'], s=scattersize, alpha=scatteralpha, label="RearR")
|
||||||
|
|
||||||
|
ax1.set_xlabel('timestamp')
|
||||||
|
#plt.ylabel('data')
|
||||||
|
ax1.set_ylabel('first axis')
|
||||||
|
ax2.set_ylabel('second axis')
|
||||||
|
#plt.title('')
|
||||||
|
ax1.legend(loc='upper left')
|
||||||
|
ax2.legend(loc='upper right')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def rpmDifference():
|
||||||
|
scattersize=1
|
||||||
|
scatteralpha=0.1
|
||||||
|
|
||||||
|
|
||||||
|
fig, ax1 = plt.subplots()
|
||||||
|
|
||||||
|
#confidence_FrontL=[abs(x-df['cmd_FrontL'][i-5 if i>4 else 0])+abs(x-df['cmd_FrontL'][i-10 if i>9 else 0])+abs(x-df['cmd_FrontL'][i-20 if i>19 else 0]) for i,x in enumerate(df['cmd_FrontL'])]
|
||||||
|
frontRPMdiff=np.convolve(np.array(df['rpm_FrontL'])-np.array(df['rpm_FrontR']), np.ones(10)/10, mode='same')
|
||||||
|
rearRPMdiff=np.convolve(np.array(df['rpm_RearL'])-np.array(df['rpm_RearR']), np.ones(10)/10, mode='same')
|
||||||
|
|
||||||
|
ax1.plot(x,frontRPMdiff, c='#0000ff', alpha=0.5, label="rpm Difference Front")
|
||||||
|
ax1.plot(x,rearRPMdiff, c='#00ff00', alpha=0.5, label="rpm Difference Rear")
|
||||||
|
|
||||||
|
ax1.set_xlabel('timestamp')
|
||||||
|
ax1.set_ylabel('rpm Difference')
|
||||||
|
ax1.legend(loc='upper left')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def plot_rpmVsCurrent():
|
||||||
|
scattersize=2
|
||||||
|
scatteralpha=0.5
|
||||||
|
|
||||||
|
#confidence
|
||||||
|
#confidence_FrontL=[abs(x-df['cmd_FrontL'][i-5 if i>4 else 0])+abs(x-df['cmd_FrontL'][i-10 if i>9 else 0])+abs(x-df['cmd_FrontL'][i-20 if i>19 else 0]) for i,x in enumerate(df['cmd_FrontL'])]
|
||||||
|
#confidence_FrontL=[mapRange(x,0,50,1.0,0.0,True) for x in confidence_FrontL]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fig, ax1 = plt.subplots()
|
||||||
|
ax1.scatter(df['rpm_FrontL'],df['current_FrontL'], s=5, alpha=scatteralpha, label="FrontL")
|
||||||
|
ax1.scatter(df['rpm_FrontR'],df['current_FrontR'], s=5, alpha=scatteralpha, label="FrontR")
|
||||||
|
#ax1.scatter(df['cmd_FrontR'],df['rpm_FrontR'], s=scattersize, alpha=scatteralpha, label="FrontR")
|
||||||
|
#ax1.scatter(df['cmd_RearL'],df['rpm_RearL'], s=scattersize, alpha=scatteralpha, label="RearL")
|
||||||
|
#ax1.scatter(df['cmd_RearR'],df['rpm_RearR'], s=scattersize, alpha=scatteralpha, label="RearR")
|
||||||
|
|
||||||
|
ax1.set_xlabel('rpm')
|
||||||
|
ax1.set_ylabel('current (A)')
|
||||||
|
plt.title('rpm vs current')
|
||||||
|
ax1.legend(loc='upper left')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
#plot_rpmVsCurrent()
|
||||||
|
rpmDifference()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
exit()
|
232
logdata/copyLogsFromBobbycar.py
Normal file
232
logdata/copyLogsFromBobbycar.py
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
|
||||||
|
import serial
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
chunksize=4096
|
||||||
|
|
||||||
|
#LOG0002
|
||||||
|
#32 = 124 kbps
|
||||||
|
#128 = 263 kbps
|
||||||
|
#4096 = 416 kbps
|
||||||
|
#32768 = 427 kbps
|
||||||
|
#65536 = failed
|
||||||
|
|
||||||
|
serialport = serial.Serial(port='/dev/ttyACM0', baudrate=115200, timeout=1)
|
||||||
|
|
||||||
|
def establish_connection():
|
||||||
|
|
||||||
|
serialport.write("\n".encode())
|
||||||
|
|
||||||
|
|
||||||
|
serialport.write("echo off\n".encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
while len(serialport.readline())>0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
serialport.write("test\n".encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
response = serialport.readline()
|
||||||
|
hrresponse=response.rstrip().decode('ascii')
|
||||||
|
if hrresponse != "OK":
|
||||||
|
print("Unexpected test response:"+str(response))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
def get_filenames():
|
||||||
|
|
||||||
|
filenames=[]
|
||||||
|
|
||||||
|
serialport.write("ls\n".encode())
|
||||||
|
while True:
|
||||||
|
response = serialport.readline()
|
||||||
|
hrresponse=response.rstrip().decode('ascii')
|
||||||
|
if(len(response))>0:
|
||||||
|
#print(hrresponse)
|
||||||
|
filenames.append(hrresponse)
|
||||||
|
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return filenames
|
||||||
|
|
||||||
|
|
||||||
|
def get_filesize(filename):
|
||||||
|
|
||||||
|
filesize=0
|
||||||
|
|
||||||
|
serialport.write(("sizeof "+str(filename)+"\n").encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
response = serialport.readline()
|
||||||
|
hrresponse=response.rstrip().decode('ascii')
|
||||||
|
if(len(response))>0:
|
||||||
|
filesize=int(hrresponse)
|
||||||
|
|
||||||
|
return filesize
|
||||||
|
|
||||||
|
|
||||||
|
def copy_file(source,destination,expectedsize):
|
||||||
|
os.makedirs(os.path.dirname(writefilename), exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
transferstarttime=time.time()
|
||||||
|
chunkstarttime=time.time()
|
||||||
|
last_print=time.time()
|
||||||
|
|
||||||
|
with open(writefilename, 'wb') as writer:
|
||||||
|
serialport.write(("chunksize "+str(chunksize)+"\n").encode())
|
||||||
|
serialport.write(("get "+filename+"\n").encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
acc_datalen=0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
'''
|
||||||
|
response = serialport.readline()
|
||||||
|
hrresponse=response.rstrip().decode('ascii')
|
||||||
|
if(len(response))>0:
|
||||||
|
#print(hrresponse)
|
||||||
|
writer.write(response)
|
||||||
|
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
data=serialport.read(chunksize)
|
||||||
|
|
||||||
|
if(len(data))>0: #data received
|
||||||
|
#print("received "+str(len(data))+" bytes")
|
||||||
|
#hrresponse=data.rstrip().decode('ascii')
|
||||||
|
#print(hrresponse)
|
||||||
|
|
||||||
|
acc_datalen+=len(data)
|
||||||
|
|
||||||
|
checksum=(sum(data) & 0xFF)
|
||||||
|
checksumarray=bytearray([checksum])
|
||||||
|
|
||||||
|
writer.write(data)
|
||||||
|
serialport.write(checksumarray) #request next chunk by sending checksum of last chunk
|
||||||
|
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
if (time.time()-last_print>0.5):
|
||||||
|
last_print=time.time()
|
||||||
|
chunkduration=time.time()-chunkstarttime
|
||||||
|
chunkstarttime=time.time()
|
||||||
|
progress=0
|
||||||
|
if expectedsize>0:
|
||||||
|
progress=acc_datalen/expectedsize
|
||||||
|
print(str(round(progress*100,0))+"% \t"+str(round(chunkduration*1000,3))+" ms for "+str(len(data))+" Byte \t = "+str(round((len(data)/chunkduration)/1000,3))+" kB/s")
|
||||||
|
|
||||||
|
|
||||||
|
fileduration=time.time()-transferstarttime
|
||||||
|
|
||||||
|
file_stats=os.stat(writefilename)
|
||||||
|
print("Finished transfer of "+str(acc_datalen)+" B or "+str(file_stats.st_size)+" (os) Byte in "+str(fileduration)+" s \t = "+str(round(file_stats.st_size/fileduration/1000,3))+" kB/s")
|
||||||
|
|
||||||
|
|
||||||
|
return acc_datalen
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def log_off():
|
||||||
|
serialport.write(("log off\n").encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
response = serialport.readline()
|
||||||
|
hrresponse=response.rstrip().decode('ascii')
|
||||||
|
if (not hrresponse.startswith("Log disabled")):
|
||||||
|
print("Unexpected response:"+str(response))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
def delete_file(filename):
|
||||||
|
|
||||||
|
|
||||||
|
serialport.write(("rm "+filename+"\n").encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
response = serialport.readline()
|
||||||
|
hrresponse=response.rstrip().decode('ascii')
|
||||||
|
if hrresponse != "OK":
|
||||||
|
print("Unexpected response:"+str(response))
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Transfers log files from bobbycar over serial')
|
||||||
|
parser.add_argument('-o', '--output', nargs='?', help="output directory")
|
||||||
|
parser.add_argument('-d','--delete', action="store_true", help="delete files after transfer")
|
||||||
|
args = parser.parse_args()
|
||||||
|
outputfolder="sdcard"
|
||||||
|
if (args.output is not None):
|
||||||
|
outputfolder = args.output
|
||||||
|
if(outputfolder[-1]=='/'):
|
||||||
|
outputfolder=outputfolder[:-1] #remove last slash
|
||||||
|
print("Outputfolder:"+outputfolder)
|
||||||
|
|
||||||
|
|
||||||
|
if serialport.isOpen():
|
||||||
|
|
||||||
|
establish_connection()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Get File List
|
||||||
|
filenames=get_filenames()
|
||||||
|
|
||||||
|
|
||||||
|
#Copy all Files
|
||||||
|
|
||||||
|
failed=0
|
||||||
|
|
||||||
|
for filename in filenames:
|
||||||
|
print("Reading file "+filename)
|
||||||
|
expectedsize=get_filesize(filename)
|
||||||
|
print("Expecting "+str(expectedsize)+" Byte")
|
||||||
|
|
||||||
|
writefilename=outputfolder+'/'+filename
|
||||||
|
receivedsize=copy_file(filename,writefilename,expectedsize)
|
||||||
|
|
||||||
|
if (expectedsize!=receivedsize):
|
||||||
|
print("Warning: Filesize does not match!")
|
||||||
|
failed+=1
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
print(str(len(filenames))+" Files copied with "+str(failed)+" failed")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Delete all files
|
||||||
|
if (args.delete):
|
||||||
|
if (failed>0):
|
||||||
|
print("Copy not successful. Files won't be deleted!")
|
||||||
|
exit()
|
||||||
|
deletecheckstring=''.join(random.choices(string.ascii_lowercase, k=3))
|
||||||
|
deletecheck=input("Enter "+deletecheckstring+" to confirm deletion of "+str(len(filenames))+" files on bobbycar:")
|
||||||
|
|
||||||
|
if (deletecheck==deletecheckstring):
|
||||||
|
log_off()
|
||||||
|
for filename in filenames:
|
||||||
|
print("Deleting "+filename)
|
||||||
|
delete_file(filename)
|
||||||
|
else:
|
||||||
|
print("Verification failed!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
serialport.write("echo on\n".encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
serialport.close()
|
||||||
|
|
532
logdata/logdata_visualization.pde
Normal file
532
logdata/logdata_visualization.pde
Normal file
|
@ -0,0 +1,532 @@
|
||||||
|
import processing.serial.*;
|
||||||
|
|
||||||
|
int vis_textsize=12; //copy from Visualization class
|
||||||
|
|
||||||
|
String logfile_name="/media/fisch/HDD/Projects/bobbycar/bobbycar_repo/logdata_visualization/sonstige_fahrten/20220828_154604.csv";
|
||||||
|
int columnCount=20;
|
||||||
|
|
||||||
|
boolean useSerial=false; //false=read from csv log, true=read from serial port
|
||||||
|
//String serial_port="COM3";
|
||||||
|
String serial_port="/dev/ttyUSB0";
|
||||||
|
Serial serial;
|
||||||
|
String serialString=""; //last read string
|
||||||
|
int serial_endchar=10; //10=ASCII Linefeed
|
||||||
|
|
||||||
|
float minCurrentDisplayed=-5;
|
||||||
|
float maxCurrentDisplayed=20;
|
||||||
|
|
||||||
|
float minRPMDisplayed=-100;
|
||||||
|
float maxRPMDisplayed=1200;
|
||||||
|
|
||||||
|
//timeline
|
||||||
|
float logdata_start_time=0;
|
||||||
|
float logdata_end_time=0;
|
||||||
|
|
||||||
|
Visualization vis_cmd_FrontL;
|
||||||
|
Visualization vis_cmd_FrontR;
|
||||||
|
Visualization vis_cmd_RearL;
|
||||||
|
Visualization vis_cmd_RearR;
|
||||||
|
|
||||||
|
Visualization vis_current_FrontL;
|
||||||
|
Visualization vis_current_FrontR;
|
||||||
|
Visualization vis_current_RearL;
|
||||||
|
Visualization vis_current_RearR;
|
||||||
|
|
||||||
|
Visualization vis_rpm_FrontL;
|
||||||
|
Visualization vis_rpm_FrontR;
|
||||||
|
Visualization vis_rpm_RearL;
|
||||||
|
Visualization vis_rpm_RearR;
|
||||||
|
|
||||||
|
Visualization vis_throttle;
|
||||||
|
Visualization vis_brake;
|
||||||
|
|
||||||
|
Visualization vis_currentAll;
|
||||||
|
|
||||||
|
Visualization vis_c_speed;
|
||||||
|
|
||||||
|
//vis_c means calculated value, not raw value from log
|
||||||
|
Visualization vis_c_rpm_mean;
|
||||||
|
|
||||||
|
Visualization vis_graph_currentAll;
|
||||||
|
Visualization vis_graph_speed_mean;
|
||||||
|
|
||||||
|
|
||||||
|
Visualization vis_c_graph_receivedelay;
|
||||||
|
|
||||||
|
Visualization vis_c_rpmvector;
|
||||||
|
Visualization vis_c_currentvector;
|
||||||
|
|
||||||
|
boolean showTimeline=!useSerial;
|
||||||
|
Timeline tl;
|
||||||
|
int timeoffset=0; //for moving timeslider
|
||||||
|
|
||||||
|
|
||||||
|
Table logdata;
|
||||||
|
int nextID=0; //next row number to be displayed
|
||||||
|
long lastTimeData=0; //last time data received
|
||||||
|
int nextTimeData=0; //time of nextID row
|
||||||
|
int lastTimeMillis=0; //local time
|
||||||
|
int nextTimeMillis=0; //local time
|
||||||
|
|
||||||
|
boolean newdataforced=true;
|
||||||
|
|
||||||
|
int dataErrorCount=0;
|
||||||
|
|
||||||
|
boolean running=true;
|
||||||
|
int timePaused=0;
|
||||||
|
|
||||||
|
//Data from log
|
||||||
|
int cmd_FrontL;
|
||||||
|
int cmd_FrontR;
|
||||||
|
int cmd_RearL;
|
||||||
|
int cmd_RearR;
|
||||||
|
float current_FrontL;
|
||||||
|
float current_FrontR;
|
||||||
|
float current_RearL;
|
||||||
|
float current_RearR;
|
||||||
|
int rpm_FrontL;
|
||||||
|
int rpm_FrontR;
|
||||||
|
int rpm_RearL;
|
||||||
|
int rpm_RearR;
|
||||||
|
float temp_Front;
|
||||||
|
float temp_Rear;
|
||||||
|
float vbat_Front;
|
||||||
|
float vbat_Rear;
|
||||||
|
float currentAll;
|
||||||
|
int throttle;
|
||||||
|
int brake;
|
||||||
|
float speed;
|
||||||
|
float trip;
|
||||||
|
float currentConsumed;
|
||||||
|
|
||||||
|
color bg=color(0);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
//size(1920, 1080); //Full HD
|
||||||
|
size(1000, 800); //Laptop Preview
|
||||||
|
frameRate(100);
|
||||||
|
|
||||||
|
if (useSerial) {
|
||||||
|
printArray(Serial.list());
|
||||||
|
// Open the port you are using at the rate you want:
|
||||||
|
serial = new Serial(this, serial_port, 115200);
|
||||||
|
|
||||||
|
serial.clear();
|
||||||
|
// Throw out the first reading, in case we started reading
|
||||||
|
// in the middle of a string from the sender.
|
||||||
|
println("readUntil");
|
||||||
|
serialString = serial.readStringUntil(serial_endchar);
|
||||||
|
println("read:"+serialString);
|
||||||
|
serialString = null;
|
||||||
|
}else{
|
||||||
|
logdata = loadTable(logfile_name, "header, csv");
|
||||||
|
float _checkTimeLast=-100;
|
||||||
|
|
||||||
|
|
||||||
|
for (int i=0; i < logdata.getRowCount();i++) {
|
||||||
|
float _checkTimeCurrent=logdata.getRow(i).getFloat("time")*1000;
|
||||||
|
|
||||||
|
if (logdata.getRow(i).getString(0).charAt(0)=='#') { //check if row starts with # (comment)
|
||||||
|
print("removed comment:");
|
||||||
|
for (int is=0;is< logdata.getRow(i).getColumnCount() && logdata.getRow(i).getString(is) != null; is++)
|
||||||
|
{
|
||||||
|
print(", "+logdata.getRow(i).getString(is));
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
logdata.removeRow(i); i--;
|
||||||
|
}else if (Float.isNaN(_checkTimeCurrent) || _checkTimeCurrent <= _checkTimeLast) { //check if time is plausible
|
||||||
|
print("removed unplausible time:");
|
||||||
|
for (int is=0;is< logdata.getRow(i).getColumnCount() && logdata.getRow(i).getString(is) != null; is++)
|
||||||
|
{
|
||||||
|
print(", "+logdata.getRow(i).getString(is));
|
||||||
|
}
|
||||||
|
println();
|
||||||
|
logdata.removeRow(i); i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logdata_start_time=logdata.getRow(0).getFloat("time");
|
||||||
|
logdata_end_time = logdata.getRow(logdata.getRowCount()-1).getFloat("time");
|
||||||
|
println("loaded "+logdata.getRowCount()+" lines. Starttime: "+logdata_start_time+"s , Endtime: "+logdata_end_time+"s");
|
||||||
|
}
|
||||||
|
|
||||||
|
PVector pos_vis_cmd = new PVector(100,200);
|
||||||
|
PVector size_vis_cmd = new PVector(10,100);
|
||||||
|
PVector dist_vis_cmd = new PVector(80,150);
|
||||||
|
|
||||||
|
colorMode(RGB, 255, 255, 255);
|
||||||
|
|
||||||
|
//cmd
|
||||||
|
color c_cmd=color(255,50,0);
|
||||||
|
vis_cmd_FrontL = new BarV_cmd((int)pos_vis_cmd.x,(int)pos_vis_cmd.y,(int)size_vis_cmd.x,(int)size_vis_cmd.y,-1000,1000);
|
||||||
|
|
||||||
|
vis_cmd_FrontR = new BarV_cmd((int)(pos_vis_cmd.x+dist_vis_cmd.x),(int)pos_vis_cmd.y,(int)size_vis_cmd.x,(int)size_vis_cmd.y,-1000,1000);
|
||||||
|
vis_cmd_FrontR.setTitle("cmd");
|
||||||
|
|
||||||
|
vis_cmd_RearL = new BarV_cmd((int)pos_vis_cmd.x,(int)(pos_vis_cmd.y+dist_vis_cmd.y),(int)size_vis_cmd.x,(int)size_vis_cmd.y,-1000,1000);
|
||||||
|
|
||||||
|
vis_cmd_RearR = new BarV_cmd((int)(pos_vis_cmd.x+dist_vis_cmd.x),(int)(pos_vis_cmd.y+dist_vis_cmd.y),(int)size_vis_cmd.x,(int)size_vis_cmd.y,-1000,1000);
|
||||||
|
|
||||||
|
vis_cmd_FrontL.setcmain(c_cmd);
|
||||||
|
vis_cmd_FrontR.setcmain(c_cmd);
|
||||||
|
vis_cmd_RearL.setcmain(c_cmd);
|
||||||
|
vis_cmd_RearR.setcmain(c_cmd);
|
||||||
|
|
||||||
|
// Speed
|
||||||
|
color c_speed=color(50,50,255);
|
||||||
|
vis_rpm_FrontL = new BarV_cmd((int)(pos_vis_cmd.x-size_vis_cmd.x*2),(int)pos_vis_cmd.y+vis_textsize,(int)size_vis_cmd.x,(int)size_vis_cmd.y,minRPMDisplayed,maxRPMDisplayed);
|
||||||
|
|
||||||
|
vis_rpm_FrontR = new BarV_cmd((int)(pos_vis_cmd.x+dist_vis_cmd.x+size_vis_cmd.x*2),(int)pos_vis_cmd.y+vis_textsize,(int)size_vis_cmd.x,(int)size_vis_cmd.y,minRPMDisplayed,maxRPMDisplayed);
|
||||||
|
vis_rpm_FrontR.setTitle("speed");
|
||||||
|
|
||||||
|
vis_rpm_RearL = new BarV_cmd((int)(pos_vis_cmd.x-size_vis_cmd.x*2),(int)(pos_vis_cmd.y+vis_textsize+dist_vis_cmd.y),(int)size_vis_cmd.x,(int)size_vis_cmd.y,minRPMDisplayed,maxRPMDisplayed);
|
||||||
|
vis_rpm_RearL.setValueUnit("rpm");
|
||||||
|
|
||||||
|
vis_rpm_RearR = new BarV_cmd((int)(pos_vis_cmd.x+dist_vis_cmd.x+size_vis_cmd.x*2),(int)(pos_vis_cmd.y+vis_textsize+dist_vis_cmd.y),(int)size_vis_cmd.x,(int)size_vis_cmd.y,minRPMDisplayed,maxRPMDisplayed);
|
||||||
|
|
||||||
|
vis_rpm_FrontL.setcmain(c_speed);
|
||||||
|
vis_rpm_FrontR.setcmain(c_speed);
|
||||||
|
vis_rpm_RearL.setcmain(c_speed);
|
||||||
|
vis_rpm_RearR.setcmain(c_speed);
|
||||||
|
|
||||||
|
// Current
|
||||||
|
color c_current=color(255,200,50);
|
||||||
|
vis_current_FrontL = new BarV_cmd((int)(pos_vis_cmd.x-size_vis_cmd.x*2*2),(int)pos_vis_cmd.y+vis_textsize*2,(int)size_vis_cmd.x,(int)size_vis_cmd.y,minCurrentDisplayed,maxCurrentDisplayed);
|
||||||
|
|
||||||
|
vis_current_FrontR = new BarV_cmd((int)(pos_vis_cmd.x+dist_vis_cmd.x+size_vis_cmd.x*2*2),(int)pos_vis_cmd.y+vis_textsize*2,(int)size_vis_cmd.x,(int)size_vis_cmd.y,minCurrentDisplayed,maxCurrentDisplayed);
|
||||||
|
vis_current_FrontR.setTitle("current");
|
||||||
|
|
||||||
|
vis_current_RearL = new BarV_cmd((int)(pos_vis_cmd.x-size_vis_cmd.x*2*2),(int)(pos_vis_cmd.y+vis_textsize*2+dist_vis_cmd.y),(int)size_vis_cmd.x,(int)size_vis_cmd.y,minCurrentDisplayed,maxCurrentDisplayed);
|
||||||
|
vis_current_RearL.setValueUnit("A");
|
||||||
|
|
||||||
|
vis_current_RearR = new BarV_cmd((int)(pos_vis_cmd.x+dist_vis_cmd.x+size_vis_cmd.x*2*2),(int)(pos_vis_cmd.y+vis_textsize*2+dist_vis_cmd.y),(int)size_vis_cmd.x,(int)size_vis_cmd.y,minCurrentDisplayed,maxCurrentDisplayed);
|
||||||
|
|
||||||
|
vis_current_FrontL.setcmain(c_current);
|
||||||
|
vis_current_FrontR.setcmain(c_current);
|
||||||
|
vis_current_RearL.setcmain(c_current);
|
||||||
|
vis_current_RearR.setcmain(c_current);
|
||||||
|
|
||||||
|
//Inputs
|
||||||
|
PVector pos_vis_inputs = new PVector(width/2,height-50); //will be center for x
|
||||||
|
PVector size_vis_inputs = new PVector(200,40);
|
||||||
|
|
||||||
|
color c_throttle=color(255,150,50);
|
||||||
|
vis_throttle = new BarH_cmd((int)pos_vis_inputs.x,(int)pos_vis_inputs.y,(int)size_vis_inputs.x,(int)size_vis_inputs.y,0,1000);
|
||||||
|
vis_throttle.setcmain(c_throttle);
|
||||||
|
vis_throttle.setTitle("Throttle");
|
||||||
|
color c_brake=color(200,200,50);
|
||||||
|
vis_brake = new BarH_cmd((int)(pos_vis_inputs.x-size_vis_inputs.x),(int)pos_vis_inputs.y,(int)size_vis_inputs.x,(int)size_vis_inputs.y,0,1000);
|
||||||
|
vis_brake.setcmain(c_brake);
|
||||||
|
vis_brake.setTitle("Brake");
|
||||||
|
|
||||||
|
//Speed
|
||||||
|
vis_c_speed = new Tacho(width/2-150,height-150,100,-10,50);
|
||||||
|
vis_c_speed.setTitle("Speed");
|
||||||
|
vis_c_speed.setValueUnit("km/h");
|
||||||
|
vis_c_speed.setShowMinMax(true);
|
||||||
|
|
||||||
|
//RPM Man
|
||||||
|
vis_c_rpm_mean = new Tacho(width/2-400,height-150,75,minRPMDisplayed,maxRPMDisplayed);
|
||||||
|
vis_c_rpm_mean.setTitle("RPM");
|
||||||
|
vis_c_rpm_mean.setValueUnit("rpm");
|
||||||
|
vis_c_rpm_mean.setShowMinMax(true);
|
||||||
|
|
||||||
|
//Current
|
||||||
|
color c_currentall=color(240,255,50);
|
||||||
|
vis_currentAll = new Tacho(width/2+150,height-150,100,minCurrentDisplayed,maxCurrentDisplayed);
|
||||||
|
vis_currentAll.setTitle("currentAll");
|
||||||
|
vis_currentAll.setValueUnit("A");
|
||||||
|
vis_currentAll.setcmain(c_currentall); //currentAll color
|
||||||
|
vis_currentAll.setShowMinMax(true);
|
||||||
|
vis_currentAll.setValue2Unit("A");
|
||||||
|
vis_currentAll.setTitle2("avgCurrent/wheel");
|
||||||
|
vis_currentAll.setcmain2(c_current);
|
||||||
|
|
||||||
|
//Graph
|
||||||
|
PVector size_vis_graph1= new PVector(400,200);
|
||||||
|
PVector pos_vis_graph1= new PVector(width-size_vis_graph1.x-60,250);
|
||||||
|
|
||||||
|
vis_graph_speed_mean = new GraphRoll_minimal(int(pos_vis_graph1.x), int(pos_vis_graph1.y), int(size_vis_graph1.x),int(size_vis_graph1.y),minRPMDisplayed,maxRPMDisplayed,1);
|
||||||
|
vis_graph_speed_mean.setcborder(c_speed);
|
||||||
|
vis_graph_speed_mean.setcmain(c_speed);
|
||||||
|
vis_graph_speed_mean.setValueUnit("rpm");
|
||||||
|
|
||||||
|
vis_graph_currentAll = new GraphRoll_minimal(int(pos_vis_graph1.x), int(pos_vis_graph1.y), int(size_vis_graph1.x),int(size_vis_graph1.y),minCurrentDisplayed,maxCurrentDisplayed,1);
|
||||||
|
vis_graph_currentAll.setcborder(c_currentall);
|
||||||
|
vis_graph_currentAll.setcmain(c_currentall);
|
||||||
|
vis_graph_currentAll.setValueUnit("A");
|
||||||
|
|
||||||
|
color c_receivedelay=color(150,150,150);
|
||||||
|
vis_c_graph_receivedelay = new GraphRoll_minimal(5, vis_textsize*2+40, 200,40,0,1000,1);
|
||||||
|
vis_c_graph_receivedelay.setcborder(c_receivedelay);
|
||||||
|
vis_c_graph_receivedelay.setcmain(c_receivedelay);
|
||||||
|
vis_c_graph_receivedelay.setValueUnit("ms");
|
||||||
|
vis_c_graph_receivedelay.setTitle("receivedelay");
|
||||||
|
|
||||||
|
|
||||||
|
vis_c_rpmvector = new VectorDisplay(int(pos_vis_cmd.x+dist_vis_cmd.x/2+size_vis_cmd.x/2), int(pos_vis_cmd.y+dist_vis_cmd.y/2), 50, maxRPMDisplayed/20);
|
||||||
|
vis_c_rpmvector.setcmain(c_speed);
|
||||||
|
|
||||||
|
vis_c_currentvector = new VectorDisplay(int(pos_vis_cmd.x+dist_vis_cmd.x/2+size_vis_cmd.x/2), int(pos_vis_cmd.y+dist_vis_cmd.y/2+50), 50, maxCurrentDisplayed/20);
|
||||||
|
vis_c_currentvector.setcmain(c_current);
|
||||||
|
|
||||||
|
|
||||||
|
if (showTimeline) {
|
||||||
|
println("Preparing Timeline");
|
||||||
|
tl = new Timeline(30,height-30, width-30*2, 28);
|
||||||
|
tl.setTimes(logdata_start_time,logdata_end_time);
|
||||||
|
tl.generatePreview(logdata);
|
||||||
|
println("Timeline prepared");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() {
|
||||||
|
int loopmillis=0;
|
||||||
|
if (running) {
|
||||||
|
loopmillis=millis()-timeoffset;
|
||||||
|
}else{ //paused
|
||||||
|
loopmillis=timePaused-timeoffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useSerial) {
|
||||||
|
if (serial.available() > 0) {
|
||||||
|
serialString = serial.readStringUntil(serial_endchar);
|
||||||
|
//println("read:"+serialString);
|
||||||
|
if (serialString != null) {
|
||||||
|
println(serialString);
|
||||||
|
String[] list = split(serialString, ',');
|
||||||
|
|
||||||
|
if (list.length==20) { //data ok
|
||||||
|
lastTimeMillis=nextTimeMillis;
|
||||||
|
nextTimeMillis=loopmillis;
|
||||||
|
|
||||||
|
lastTimeData=nextTimeData;
|
||||||
|
nextTimeData=int(parseFloat(list[0])*1000);
|
||||||
|
cmd_FrontL=parseInt(list[1]);
|
||||||
|
cmd_FrontR=parseInt(list[2]);
|
||||||
|
cmd_RearL=parseInt(list[3]);
|
||||||
|
cmd_RearR=parseInt(list[4]);
|
||||||
|
current_FrontL=parseFloat(list[5]);
|
||||||
|
current_FrontR=parseFloat(list[6]);
|
||||||
|
current_RearL=parseFloat(list[7]);
|
||||||
|
current_RearR=parseFloat(list[8]);
|
||||||
|
rpm_FrontL=parseInt(list[9]);
|
||||||
|
rpm_FrontR=parseInt(list[10]);
|
||||||
|
rpm_RearL=parseInt(list[11]);
|
||||||
|
rpm_RearR=parseInt(list[12]);
|
||||||
|
temp_Front=parseFloat(list[13]);
|
||||||
|
temp_Rear=parseFloat(list[14]);
|
||||||
|
vbat_Front=parseFloat(list[15]);
|
||||||
|
vbat_Rear=parseFloat(list[16]);
|
||||||
|
currentAll=parseFloat(list[17]);
|
||||||
|
throttle=parseInt(list[18]);
|
||||||
|
brake=parseInt(list[19]);
|
||||||
|
speed=parseFloat(list[20]);
|
||||||
|
trip=parseFloat(list[21]);
|
||||||
|
currentConsumed=parseFloat(trim(list[22]));
|
||||||
|
}else{ //data missing or too much
|
||||||
|
dataErrorCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
while (newdataforced || loopmillis>=nextTimeData && nextID+1<logdata.getRowCount()){ //New Data
|
||||||
|
newdataforced=false; //reset flag
|
||||||
|
if ((nextID+1 < logdata.getRowCount()) && (nextID >= 0)) { //valid row
|
||||||
|
TableRow row = logdata.getRow(nextID);
|
||||||
|
|
||||||
|
lastTimeData=nextTimeData;
|
||||||
|
nextTimeData=(int)(logdata.getRow(nextID+1).getFloat("time")*1000); //get time and convert from seconds to ms
|
||||||
|
|
||||||
|
lastTimeMillis=nextTimeMillis;
|
||||||
|
nextTimeMillis=loopmillis;
|
||||||
|
|
||||||
|
cmd_FrontL=row.getInt("cmd_FrontL");
|
||||||
|
cmd_FrontR=row.getInt("cmd_FrontR");
|
||||||
|
cmd_RearL=row.getInt("cmd_RearL");
|
||||||
|
cmd_RearR=row.getInt("cmd_RearR");
|
||||||
|
current_FrontL=row.getFloat("current_FrontL");
|
||||||
|
current_FrontR=row.getFloat("current_FrontR");
|
||||||
|
current_RearL=row.getFloat("current_RearL");
|
||||||
|
current_RearR=row.getFloat("current_RearR");
|
||||||
|
rpm_FrontL=row.getInt("rpm_FrontL");
|
||||||
|
rpm_FrontR=row.getInt("rpm_FrontR");
|
||||||
|
rpm_RearL=row.getInt("rpm_RearL");
|
||||||
|
rpm_RearR=row.getInt("rpm_RearR");
|
||||||
|
temp_Front=row.getFloat("temp_Front");
|
||||||
|
temp_Rear=row.getFloat("temp_Rear");
|
||||||
|
vbat_Front=row.getFloat("vbat_Front");
|
||||||
|
vbat_Rear=row.getFloat("vbat_Rear");
|
||||||
|
currentAll=row.getFloat("currentAll");
|
||||||
|
throttle=row.getInt("throttle");
|
||||||
|
brake=row.getInt("brake");
|
||||||
|
speed=row.getFloat("speed");
|
||||||
|
trip=row.getFloat("trip");
|
||||||
|
currentConsumed=row.getFloat("currentConsumed");
|
||||||
|
|
||||||
|
if (loopmillis-nextTimeData>1000 && nextTimeData>lastTimeData) {//too much behind
|
||||||
|
long _timestep=nextTimeData-lastTimeData; //approximated time step
|
||||||
|
nextID+=(loopmillis-nextTimeData)/_timestep* 0.9; //fast forward estimated time steps
|
||||||
|
}
|
||||||
|
nextID++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
background(bg);
|
||||||
|
|
||||||
|
vis_cmd_FrontL.setValue(cmd_FrontL); vis_cmd_FrontL.drawVis();
|
||||||
|
vis_cmd_FrontR.setValue(cmd_FrontR); vis_cmd_FrontR.drawVis();
|
||||||
|
vis_cmd_RearL.setValue(cmd_RearL); vis_cmd_RearL.drawVis();
|
||||||
|
vis_cmd_RearR.setValue(cmd_RearR); vis_cmd_RearR.drawVis();
|
||||||
|
|
||||||
|
vis_rpm_FrontL.setValue(rpm_FrontL); vis_rpm_FrontL.drawVis();
|
||||||
|
vis_rpm_FrontR.setValue(rpm_FrontR); vis_rpm_FrontR.drawVis();
|
||||||
|
vis_rpm_RearL.setValue(rpm_RearL); vis_rpm_RearL.drawVis();
|
||||||
|
vis_rpm_RearR.setValue(rpm_RearR); vis_rpm_RearR.drawVis();
|
||||||
|
|
||||||
|
vis_current_FrontL.setValue(current_FrontL); vis_current_FrontL.drawVis();
|
||||||
|
vis_current_FrontR.setValue(current_FrontR); vis_current_FrontR.drawVis();
|
||||||
|
vis_current_RearL.setValue(current_RearL); vis_current_RearL.drawVis();
|
||||||
|
vis_current_RearR.setValue(current_RearR); vis_current_RearR.drawVis();
|
||||||
|
|
||||||
|
vis_throttle.setValue(throttle); vis_throttle.drawVis();
|
||||||
|
vis_brake.setValue(brake); vis_brake.drawVis();
|
||||||
|
|
||||||
|
vis_c_speed.setValue(speed*3.6); vis_c_speed.drawVis(); //from m/s in km/h
|
||||||
|
|
||||||
|
textAlign(LEFT);
|
||||||
|
textSize(vis_textsize);
|
||||||
|
text(trip+" m", width/2-10,height-110); //trip
|
||||||
|
text(currentConsumed+" Ah", width/2-10,height-110+vis_textsize); //consumed Current
|
||||||
|
|
||||||
|
int speed_mean=int((rpm_FrontL+rpm_FrontR+rpm_RearL+rpm_RearR)/4.0);
|
||||||
|
vis_c_rpm_mean.setValue(speed_mean); vis_c_rpm_mean.drawVis();
|
||||||
|
|
||||||
|
|
||||||
|
vis_currentAll.setValue(currentAll);
|
||||||
|
float current_mean=(current_FrontL+current_FrontR+current_RearL+current_RearR)/4.0;
|
||||||
|
vis_currentAll.setValue2(current_mean); vis_currentAll.drawVis();
|
||||||
|
|
||||||
|
vis_graph_speed_mean.setValue(speed_mean); vis_graph_speed_mean.drawVis();
|
||||||
|
vis_graph_currentAll.setValue(currentAll); vis_graph_currentAll.drawVis();
|
||||||
|
|
||||||
|
|
||||||
|
vis_c_graph_receivedelay.setValue(loopmillis-lastTimeMillis); vis_c_graph_receivedelay.drawVis();
|
||||||
|
|
||||||
|
vis_c_rpmvector.setValue2((-rpm_FrontL-rpm_FrontR+rpm_RearL+rpm_RearR)/4.0/sqrt(2)); //y component
|
||||||
|
vis_c_rpmvector.setValue((-rpm_FrontL+rpm_FrontR-rpm_RearL+rpm_RearR)/4.0/sqrt(2)); //x component
|
||||||
|
vis_c_rpmvector.drawVis();
|
||||||
|
|
||||||
|
vis_c_currentvector.setValue2((-current_FrontL-current_FrontR+current_RearL+current_RearR)/4.0/sqrt(2)); //y component
|
||||||
|
vis_c_currentvector.setValue((-current_FrontL+current_FrontR-current_RearL+current_RearR)/4.0/sqrt(2)); //x component
|
||||||
|
vis_c_currentvector.drawVis();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Temperature
|
||||||
|
PVector pos_temperature = new PVector(500,12);
|
||||||
|
colorMode(HSB, 360, 100, 100);
|
||||||
|
fill(color(map(temp_Front,16,50,180,360),50,100));
|
||||||
|
text("temp_Front="+(temp_Front)+"°C", pos_temperature.x,pos_temperature.y);
|
||||||
|
fill(color(map(temp_Rear,16,50,180,360),50,100));
|
||||||
|
text("temp_Rear="+(temp_Rear)+"°C", pos_temperature.x,pos_temperature.y+12);
|
||||||
|
//Voltage
|
||||||
|
PVector pos_voltage = new PVector(pos_temperature.x+150,12);
|
||||||
|
colorMode(HSB, 360, 100, 100);
|
||||||
|
fill(color(map(vbat_Front,12*3,12*4.2,0,120),50,100));
|
||||||
|
text("vbat_Front="+(vbat_Front)+"V", pos_voltage.x,pos_voltage.y);
|
||||||
|
fill(color(map(vbat_Rear,12*3,12*4.2,0,120),50,100));
|
||||||
|
text("vbat_Rear="+(vbat_Rear)+"V", pos_voltage.x,pos_voltage.y+12);
|
||||||
|
|
||||||
|
|
||||||
|
colorMode(RGB, 255, 255, 255);
|
||||||
|
fill(color(200,200,200));
|
||||||
|
textAlign(LEFT);
|
||||||
|
textSize(vis_textsize);
|
||||||
|
text("d="+(nextTimeData-lastTimeData)+"ms", 5+75,12);
|
||||||
|
if (!useSerial && loopmillis-lastTimeData>(nextTimeData-lastTimeData)*10) { //deviation too high when reading from file
|
||||||
|
text("ff="+(loopmillis-lastTimeData)+"ms", 5+75*2,12); //show warning
|
||||||
|
}
|
||||||
|
if (!running) {
|
||||||
|
fill(color(255,100,100));
|
||||||
|
}
|
||||||
|
text("t="+(loopmillis/1000.0)+"s", 5,12);
|
||||||
|
text("nextID="+nextID, 5,12+12);
|
||||||
|
fill(color(200,200,200));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
text(""+(dataErrorCount)+" errors", 5+70*3,12);
|
||||||
|
|
||||||
|
|
||||||
|
if(showTimeline) {
|
||||||
|
tl.drawTL(loopmillis/1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyPressed() {
|
||||||
|
if (key == CODED) {
|
||||||
|
if (!running) { //paused
|
||||||
|
if (keyCode == LEFT) {
|
||||||
|
|
||||||
|
} else if (keyCode == RIGHT) {
|
||||||
|
timeoffset=timePaused-nextTimeData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//println("key="+keyCode);
|
||||||
|
if (keyCode==82) { //82=r
|
||||||
|
println("restart");
|
||||||
|
timeoffset=millis(); //rest to time 0
|
||||||
|
nextID=0;
|
||||||
|
newdataforced=true;
|
||||||
|
}
|
||||||
|
if (keyCode == 32) {
|
||||||
|
if (running) { //switching from running to pause
|
||||||
|
timePaused=millis();
|
||||||
|
}else{ //unpause
|
||||||
|
timeoffset=+millis()-timePaused+timeoffset;
|
||||||
|
}
|
||||||
|
running=!running;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseClicked()
|
||||||
|
{
|
||||||
|
if (showTimeline)
|
||||||
|
{
|
||||||
|
int temp_timeoffset=(int)(tl.checkMouse(mouseX,mouseY)*1000);
|
||||||
|
if (temp_timeoffset!=0) {
|
||||||
|
|
||||||
|
if (!(nextID+1 < logdata.getRowCount()) && (nextID >= 0) || nextID<0) { //nextID not valid
|
||||||
|
nextID=0; //rest to good value
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Jump nextID from "+nextID);
|
||||||
|
//find nextID to jump to
|
||||||
|
while ((nextID+1 < logdata.getRowCount()) && (nextID >= 0) && (millis()-temp_timeoffset > (long)(logdata.getRow(nextID+1).getFloat("time")*1000))) { //jumped forward
|
||||||
|
nextID++;
|
||||||
|
}
|
||||||
|
print(" over "+nextID);
|
||||||
|
while ((nextID+1 < logdata.getRowCount()) && (nextID > 0) && (millis()-temp_timeoffset < (long)(logdata.getRow(nextID+1).getFloat("time")*1000))) { //jumped backward
|
||||||
|
nextID--;
|
||||||
|
}
|
||||||
|
println(" to "+nextID);
|
||||||
|
|
||||||
|
timeoffset=temp_timeoffset;
|
||||||
|
newdataforced=true; //force read line for nextID
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
237
logdata/logfix.py
Normal file
237
logdata/logfix.py
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
import numpy as np
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import pytz
|
||||||
|
import time
|
||||||
|
import argparse
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Copys, renames and fixes logfiles written by bobbycar sd logger.')
|
||||||
|
parser.add_argument('-i', '--input', type=argparse.FileType('r'), nargs='+', required=True, help="list of input log files")
|
||||||
|
parser.add_argument('-o', '--output', nargs='?', type=str, help="output filename")
|
||||||
|
parser.add_argument('-c','--consecutive', action="store_true", help="add consecutive files to input. If the input file ends with a number the following logfiles will be added.")
|
||||||
|
parser.add_argument("-t", "--time", nargs='?', type=int, help="Create new csv file after time without data. In Minutes.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ok=True
|
||||||
|
|
||||||
|
|
||||||
|
def getTimestamp(plines):
|
||||||
|
timestampline=-1
|
||||||
|
timestampfound=False
|
||||||
|
while not timestampfound:
|
||||||
|
timestampline+=1
|
||||||
|
timestampfound = (plines[timestampline].find('TIMESTAMP:')!=-1)
|
||||||
|
|
||||||
|
timestamp=int(plines[timestampline].split('TIMESTAMP:')[1]) #timestamp when file was created
|
||||||
|
|
||||||
|
if (timestampline==-1):
|
||||||
|
print("Error: Timestamp not found!")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
|
def filterLines(plines,plinesStarttime=None):
|
||||||
|
|
||||||
|
plines = [x.rstrip("\n") for x in plines] #remove \n
|
||||||
|
pcommentlinesMask = [True if x.startswith('#') else False for x in plines] #generate mask for lines with comments
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
plines=np.array(plines)
|
||||||
|
pcommentlinesMask=np.array(pcommentlinesMask)
|
||||||
|
|
||||||
|
if (plinesStarttime is not None): #if starttimelist given, match with pdatalinesOK
|
||||||
|
plinesStarttime = plinesStarttime[pcommentlinesMask==False] #get lines with data
|
||||||
|
|
||||||
|
pdatalines = plines[pcommentlinesMask==False] #get lines with data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pheader = pdatalines[0] #header is the first non comment line
|
||||||
|
|
||||||
|
pheaderSize = len(pheader.split(',')) #how many elements are expected per line
|
||||||
|
pdatalinesSize = [len(x.split(',')) for x in pdatalines] #count arraysize for every dataline
|
||||||
|
|
||||||
|
if (plinesStarttime is not None): #if starttimelist given, match with pdatalinesOK
|
||||||
|
plinesStarttime=plinesStarttime[np.array(pdatalinesSize)==pheaderSize]
|
||||||
|
|
||||||
|
pdatalinesOK = pdatalines[np.array(pdatalinesSize)==pheaderSize]
|
||||||
|
|
||||||
|
if (plinesStarttime is not None): #if starttimelist given, match with pdatalinesOK
|
||||||
|
plinesStarttime = [plinesStarttime[i] for i,x in enumerate(pdatalinesOK) if x != pheader]
|
||||||
|
|
||||||
|
pdatalinesOK = [x for x in pdatalinesOK if x != pheader] #exclude header from data lines
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pdatalinesFail = pdatalines[np.array(pdatalinesSize)!=pheaderSize]
|
||||||
|
|
||||||
|
|
||||||
|
plinesSize = [len(x.split(',')) for x in plines] #count arraysize for every dataline
|
||||||
|
plinesOK = np.array(plinesSize)==pheaderSize #mask for okay lines (valid for data lines)
|
||||||
|
|
||||||
|
return plines,pheader,pcommentlinesMask,pdatalines,pdatalinesFail,pdatalinesOK,pheaderSize,plinesOK,plinesStarttime
|
||||||
|
|
||||||
|
|
||||||
|
inputFilenames=[]
|
||||||
|
|
||||||
|
|
||||||
|
if (args.consecutive):
|
||||||
|
if(len(args.input)!=1):
|
||||||
|
parser.error("in consequtive mode exactly one input file is required")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
nextFilename=args.input[0].name
|
||||||
|
while os.path.isfile(nextFilename):
|
||||||
|
print(nextFilename+" exists")
|
||||||
|
inputFilenames.append(nextFilename)
|
||||||
|
|
||||||
|
digitStartpos=len(nextFilename)-1
|
||||||
|
digitEndpos=len(nextFilename)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
while (not nextFilename[digitStartpos:digitEndpos].isdigit() and digitStartpos>0 and digitEndpos>0):
|
||||||
|
digitStartpos-=1
|
||||||
|
digitEndpos-=1
|
||||||
|
|
||||||
|
while (nextFilename[digitStartpos:digitEndpos].isdigit() and digitStartpos>0 and digitEndpos>0):
|
||||||
|
digitStartpos-=1
|
||||||
|
|
||||||
|
digitStartpos+=1
|
||||||
|
|
||||||
|
|
||||||
|
number=int(nextFilename[digitStartpos:digitEndpos])+1
|
||||||
|
nextFilename=nextFilename[0:digitStartpos]+str(number).zfill(digitEndpos-digitStartpos)+nextFilename[digitEndpos:]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
inputFilenames=[x.name for x in args.input]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lines=[]
|
||||||
|
linesStarttime=[] #offset for every line with timestamp. will be combined to new column
|
||||||
|
header=""
|
||||||
|
for inputFilename in inputFilenames:
|
||||||
|
print("Reading "+str(inputFilename))
|
||||||
|
inputlines=[]
|
||||||
|
with open(inputFilename, 'r') as reader:
|
||||||
|
inputlines = reader.readlines()
|
||||||
|
|
||||||
|
lines+=inputlines
|
||||||
|
|
||||||
|
#Check Headers
|
||||||
|
_lines,_header,_,_,_,_,_,_,_=filterLines(inputlines)
|
||||||
|
|
||||||
|
if (header==""): #is first header
|
||||||
|
header=_header
|
||||||
|
|
||||||
|
assert header==_header, "Header is different!"
|
||||||
|
|
||||||
|
_timestamp=getTimestamp(_lines)
|
||||||
|
print("Timestamp="+str(_timestamp))
|
||||||
|
_linesStarttime=[_timestamp for x in inputlines] #create as many entries with start timestamp as there are lines in the current file
|
||||||
|
|
||||||
|
linesStarttime+=_linesStarttime
|
||||||
|
|
||||||
|
print("Lines in file="+str(len(inputlines)))
|
||||||
|
|
||||||
|
assert len(lines)==len(linesStarttime), "Length of lines and linesStarttime does not match"
|
||||||
|
|
||||||
|
linesStarttime=np.array(linesStarttime)
|
||||||
|
lines,header,commentlinesMask,datalines,datalinesFail,datalinesOK,headerSize,linesOK,linesStarttime=filterLines(lines,linesStarttime)
|
||||||
|
|
||||||
|
print("Found "+str(len(lines))+" lines")
|
||||||
|
print(str(np.sum(commentlinesMask))+" comments")
|
||||||
|
print(str(len(datalinesFail))+" Datalines Failed")
|
||||||
|
print(str(len(datalinesOK))+" Datalines OK")
|
||||||
|
print("Header Size is "+str(headerSize))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
timestamp=getTimestamp(lines) #get first timestamp
|
||||||
|
|
||||||
|
# Divide in sets for separate files based on off time between logs
|
||||||
|
last_timestamp=timestamp
|
||||||
|
next_timestamp=timestamp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
set_datalinesOK=[]
|
||||||
|
set_linesStarttime=[]
|
||||||
|
|
||||||
|
last_idata=0
|
||||||
|
|
||||||
|
if args.time is not None:
|
||||||
|
newfile_timestamp_difference=args.time
|
||||||
|
for idata,data in enumerate(datalinesOK):
|
||||||
|
last_timestamp=next_timestamp
|
||||||
|
next_timestamp=linesStarttime[idata]+float(datalinesOK[idata].split(',')[0])
|
||||||
|
if next_timestamp-last_timestamp>newfile_timestamp_difference: #diff too high, create new file
|
||||||
|
set_datalinesOK.append(datalinesOK[last_idata:idata])
|
||||||
|
set_linesStarttime.append(linesStarttime[last_idata:idata])
|
||||||
|
last_idata=idata
|
||||||
|
|
||||||
|
set_datalinesOK.append(datalinesOK[last_idata:]) #append last set
|
||||||
|
set_linesStarttime.append(linesStarttime[last_idata:]) #append last set
|
||||||
|
|
||||||
|
|
||||||
|
for iset,current_datalinesOK in enumerate(set_datalinesOK):
|
||||||
|
print("Creating Output for set "+str(iset))
|
||||||
|
current_linesStarttime=set_linesStarttime[iset]
|
||||||
|
|
||||||
|
timestamp=current_linesStarttime[0] #first line is start timestamp for this file
|
||||||
|
|
||||||
|
#filetime = datetime.fromtimestamp(timestamp).astimezone(pytz.timezone('Europe/Berlin')).strftime('%Y%m%d_%H%M%S')
|
||||||
|
filetime = datetime.utcfromtimestamp(timestamp).astimezone(pytz.timezone('Europe/Berlin')).strftime('%Y%m%d_%H%M%S') #Check if this still works in dst
|
||||||
|
#filetime = datetime.fromtimestamp(timestamp, tz=timezone.utc).astimezone(pytz.timezone('Europe/Berlin')).strftime('%Y%m%d_%H%M%S') #is +2h at least in summer
|
||||||
|
|
||||||
|
|
||||||
|
outputFilename=""
|
||||||
|
if args.output is not None:
|
||||||
|
outputFilename="_"+args.output
|
||||||
|
|
||||||
|
outputFilename = filetime+outputFilename+".csv"
|
||||||
|
|
||||||
|
#is_dst(datetime(2019, 4, 1), timezone="US/Pacific")
|
||||||
|
print("Timestamp:"+str(timestamp)+" -> "+str(filetime))
|
||||||
|
print("UTC: "+ datetime.utcfromtimestamp(timestamp).strftime('%A, %Y-%m-%d %H:%M:%S'))
|
||||||
|
#print("Local Time:"+time.strftime('%A, %Y-%m-%d %H:%M:%S', time.localtime(timestamp)))
|
||||||
|
|
||||||
|
|
||||||
|
print("Writing to: "+str(outputFilename))
|
||||||
|
|
||||||
|
|
||||||
|
header="timestamp,"+header #add timestamp column
|
||||||
|
|
||||||
|
writelines = [str(current_linesStarttime[i]+float(x.split(',')[0]))+","+x for i,x in enumerate(current_datalinesOK)] #add file timestamp to line time and add column to data
|
||||||
|
|
||||||
|
linesWritten = 0
|
||||||
|
if ok:
|
||||||
|
with open(outputFilename, 'w') as writer:
|
||||||
|
writer.write(header+"\n") #write header
|
||||||
|
for i,line in enumerate(writelines):
|
||||||
|
writer.write(line+"\n")
|
||||||
|
linesWritten+=1
|
||||||
|
|
||||||
|
print(str(linesWritten)+" lines written to "+str(outputFilename))
|
||||||
|
else:
|
||||||
|
print("Failed!")
|
||||||
|
|
Loading…
Add table
Reference in a new issue