From 3bc8898039e8fa89129c653a57d58c00943edd99 Mon Sep 17 00:00:00 2001 From: Fisch Date: Sun, 21 Mar 2021 15:31:05 +0100 Subject: [PATCH] copy hoverbrettvisualizer for osd starting point --- logdata_visualization/Visualization.pde | 419 ++++++++++++++++++ .../logdata_visualization.pde | 164 +++++++ 2 files changed, 583 insertions(+) create mode 100644 logdata_visualization/Visualization.pde create mode 100644 logdata_visualization/logdata_visualization.pde diff --git a/logdata_visualization/Visualization.pde b/logdata_visualization/Visualization.pde new file mode 100644 index 0000000..d31caac --- /dev/null +++ b/logdata_visualization/Visualization.pde @@ -0,0 +1,419 @@ + +abstract class Visualization +{ + float value=0; + float value2=1; + + PVector posOrigin = new PVector(100,100); + float valueMin=0; + float valueMax=100; + float value2Min=0; //value 2 for twodimensional visualization + float value2Max=1; + + 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(0,0,0); + color cscale = color(100,100,100); + color cborder = color(204,104,0); + color cmin = color(0,150,0); + color cmax = color(150,0,0); + color ctext = color(0,0,0); + + int textsize=12; + float textWidthScale=1.0/2*this.textsize/2; //*text.length()* + int showdecimals=2; + + String title=""; + + + 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.valuethis.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.value2this.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 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 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 + 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); + + //Title + text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5); + + } +} + +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 + 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); + + + //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 Tacho extends Visualization { + int size; + + + public Tacho(int px, int py, int psize, float pvmin, float pvmax) { + super.valueMin=pvmin; + super.valueMax=pvmax; + 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 (!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 + 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); + + //Title + text(super.title, super.posOrigin.x-super.title.length()*super.textWidthScale, super.posOrigin.y+super.textsize*1.5); + + } +} + + +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 + 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); + if (super.value21000){ //show counting up if update too long ago + _delay=millis()-lastReceive; + } + 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); + } +} + +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