diff --git a/logdata_visualization/README.md b/logdata_visualization/README.md new file mode 100644 index 0000000..8d5e348 --- /dev/null +++ b/logdata_visualization/README.md @@ -0,0 +1,8 @@ +# Bobbycar OSD + +This processing software can display log information from the bobbycar in realime. + +It is used alongside the controller_teensy firmware. +Log information is constantly printed out on one serial interface and can be written either to an sd card or transmitted via rf or bluetooth. + +![screenshot](Screenshot.png "Screenshot") \ No newline at end of file diff --git a/logdata_visualization/Screenshot.png b/logdata_visualization/Screenshot.png new file mode 100644 index 0000000..cda66e1 Binary files /dev/null and b/logdata_visualization/Screenshot.png differ diff --git a/logdata_visualization/Visualization.pde b/logdata_visualization/Visualization.pde index d31caac..16c2ae5 100644 --- a/logdata_visualization/Visualization.pde +++ b/logdata_visualization/Visualization.pde @@ -17,18 +17,19 @@ abstract class Visualization boolean showMinMax=false; //default colors (not all used by every implementation) - color cmain = color(0,0,0); + color cmain = color(255,255,255); color cscale = color(100,100,100); - color cborder = color(204,104,0); + color cborder = color(200,200,200); color cmin = color(0,150,0); color cmax = color(150,0,0); - color ctext = color(0,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 valueUnit=""; public abstract void drawVis(); @@ -105,6 +106,10 @@ abstract class Visualization this.title=pt; } + public void setValueUnit(String pt) { + this.valueUnit=pt; + } + public void setshowdecimals(int pd) { this.showdecimals=pd; } @@ -151,16 +156,72 @@ public class BarV extends Visualization { //text 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); - text(super.getFormattedValue(super.value),super.posOrigin.x+this.size.x+1,super.posOrigin.y-this.size.y/2+super.textsize/2); + 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 - text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5); + 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 + 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); @@ -192,9 +253,9 @@ public class BarH extends Visualization { //text 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),super.posOrigin.x+this.size.x/2-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y-this.size.y-1); + 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 @@ -203,6 +264,53 @@ public class BarH extends Visualization { } } + + + +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 + 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; @@ -244,7 +352,7 @@ public class Tacho extends Visualization { } //text - text(super.getFormattedValue(super.value),super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize/2-this.size*0.3); + 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); //Title text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5); @@ -314,9 +422,9 @@ public class Direction extends Visualization { //text fill(super.ctext); - text(super.getFormattedValue(super.value),super.posOrigin.x-super.getFormattedValue(super.value).length()*super.textWidthScale,super.posOrigin.y+super.textsize*1.5*1); + 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=nextTime){ + TableRow row = logdata.getRow(nextID); + lastTimeData=nextTime; + nextTime=(long)(row.getFloat("time")*1000); //get time and convert from seconds to ms + + 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"); + 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"); + + if (loopmillis-nextTime>1000) {//too much behind + long _timestep=nextTime-lastTimeData; //approximated time step + nextID+=(loopmillis-nextTime)/_timestep* 0.9; //fast forward estimated time steps + } + nextID++; + nextID=nextID%logdata.getRowCount(); + } - //visVoltage.drawVis(); - //visCurrent.drawVis(); - visSpeedl.drawVis(); - visSpeedr.drawVis(); - visYaw.drawVis(); - visGT.drawVis(); - visGT_Vertical.drawVis(); - fill(color(0,0,0)); + + 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_speed_FrontL.setValue(speed_FrontL); vis_speed_FrontL.drawVis(); + vis_speed_FrontR.setValue(speed_FrontR); vis_speed_FrontR.drawVis(); + vis_speed_RearL.setValue(speed_RearL); vis_speed_RearL.drawVis(); + vis_speed_RearR.setValue(speed_RearR); vis_speed_RearR.drawVis(); + + vis_current_FrontL.setValue(-current_FrontL); vis_current_FrontL.drawVis(); //TODO: remove negative sign + vis_current_FrontR.setValue(-current_FrontR); vis_current_FrontR.drawVis(); //TODO: remove negative sign + vis_current_RearL.setValue(-current_RearL); vis_current_RearL.drawVis(); //TODO: remove negative sign + vis_current_RearR.setValue(-current_RearR); vis_current_RearR.drawVis(); //TODO: remove negative sign + + vis_throttle.setValue(throttle); vis_throttle.drawVis(); + vis_brake.setValue(-brake); vis_brake.drawVis(); + + int speed_mean=int((speed_FrontL+speed_FrontR+speed_RearL+speed_RearR)/4.0); + vis_c_speed_mean.setValue(speed_mean); vis_c_speed_mean.drawVis(); + + vis_currentAll.setValue(-currentAll); vis_currentAll.drawVis(); //TODO: remove negative sign + + vis_graph_speed_mean.setValue(speed_mean); vis_graph_speed_mean.drawVis(); + vis_graph_currentAll.setValue(-currentAll); vis_graph_currentAll.drawVis(); + + + + //Temperature + PVector pos_temperature = new PVector(220,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/100.0)+"V", pos_voltage.x,pos_voltage.y); //TODO: remove /100 + fill(color(map(vbat_Rear,12*3,12*4.2,0,120),50,100)); + text("vbat_Rear="+(vbat_Rear/100.0)+"V", pos_voltage.x,pos_voltage.y+12); //TODO: remove /100 + + + colorMode(RGB, 255, 255, 255); + fill(color(200,200,200)); + textAlign(LEFT); textSize(12); - long _delay=lastDelay; - if (millis()-lastReceive>1000){ //show counting up if update too long ago - _delay=millis()-lastReceive; + text("d="+(nextTime-lastTimeData)+"ms", 5+70,12); + if (loopmillis-lastTimeData>(nextTime-lastTimeData)*10) { //deviation too high + text("ff="+(loopmillis-lastTimeData)+"ms", 5+70+70,12); //show warning } - text("Delay="+(_delay), 5,12); -} - - -public void receive() -{ - boolean received=false; - byte[] inBuffer = new byte[17]; - while(serialport.available()>0) { - - inBuffer = serialport.readBytes(); - serialport.readBytes(inBuffer); - if (inBuffer != null && inBuffer.length==17) { - received=true; - int _address=0; - speedl = extract_int16_t(inBuffer,_address); _address+=2; - speedr = extract_int16_t(inBuffer,_address); _address+=2; - booleanvalues = extract_uint8_t(inBuffer,_address); _address+=1; - voltage = extract_float(inBuffer, _address); _address+=4; - //current = extract_float(inBuffer,_address);_address+=4; - yaw = extract_float(inBuffer,_address);_address+=4; - gt_length = extract_uint16_t(inBuffer,_address); _address+=2; - gt_horizontal = extract_int8_t(inBuffer,_address); _address+=1; - gt_vertical = extract_int8_t(inBuffer,_address); _address+=1; - //println("yaw="+yaw); - //println("gt_horizontal="+gt_horizontal); - - boolean motorenabled=boolean(booleanvalues & (1<<0)>>0); //check bit 0 - int controlmode=(booleanvalues & (3<<1))>>1; //check bit 1 and 2 - - println("motorenabled="+motorenabled+" controlmode="+controlmode); - } - - } - if (received){ - lastDelay=millis()-lastReceive; - lastReceive=millis(); - } -} -public int extract_int8_t(byte array[], int startbyte) { - if ( ((int)array[startbyte] & 0x80) == 0x00 ) { //2's complement, not negative - return ((int)array[startbyte] & 0xff); - }else{ - return -128 + ( (int)array[startbyte] & 0x7f); - } + text("t="+(loopmillis/1000.0)+"s", 5,12); } - -public int extract_uint8_t(byte array[], int startbyte) { - return ((int)array[startbyte] & 0xff); -} - -public int extract_uint16_t(byte array[], int startbyte) { - return ((int)array[startbyte] & 0xff) | ((int)array[startbyte+1] & 0xff)<<8; -} - -public int extract_int16_t(byte array[], int startbyte) { - if ( ((int)array[startbyte+1] & 0x80) == 0x00 ) { //2's complement, not negative - return ( ((int)array[startbyte] & 0xff) | ((int)array[startbyte+1] & 0x7f)<<8 ); - }else{ //value is negative - return -32768 + ( ((int)array[startbyte] & 0xff) | ((int)array[startbyte+1] & 0x7f)<<8 ); - } -} - -public float extract_float(byte array[], int startbyte) { - int MASK = 0xff; - int bits = 0; - int i = startbyte+3; //+3 because goes backwards and has 4 bytes - for (int shifter = 3; shifter >= 0; shifter--) { - bits |= ((int) array[i] & MASK) << (shifter * 8); - i--; - } - return Float.intBitsToFloat(bits); -} \ No newline at end of file