ASCIIGraph draws graphs to serial console using the "Terminal" lib. The graph scales automatically, shows minimum and maximum. Custom designs can be applied through setting several characters.
ASCIIGraph.cpp@1:0f0b1ad6f3ac, 2013-07-12 (annotated)
- Committer:
- tknapp
- Date:
- Fri Jul 12 08:57:45 2013 +0000
- Revision:
- 1:0f0b1ad6f3ac
- Parent:
- 0:6885118d9d3f
Initial commit.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
tknapp | 1:0f0b1ad6f3ac | 1 | /* |
tknapp | 1:0f0b1ad6f3ac | 2 | * mbed library to draw graphs to serial console |
tknapp | 1:0f0b1ad6f3ac | 3 | * Copyright (c) 2013 Tobias Knapp |
tknapp | 1:0f0b1ad6f3ac | 4 | * Released under the MIT License: http://mbed.org/license/mit |
tknapp | 1:0f0b1ad6f3ac | 5 | */ |
tknapp | 1:0f0b1ad6f3ac | 6 | |
tknapp | 0:6885118d9d3f | 7 | #include "ASCIIGraph.h" |
tknapp | 0:6885118d9d3f | 8 | |
tknapp | 0:6885118d9d3f | 9 | ASCIIGraph::ASCIIGraph(Terminal * terminal, int startX, int startY, int width, int height, int labelXoffset){ |
tknapp | 0:6885118d9d3f | 10 | term = terminal; |
tknapp | 0:6885118d9d3f | 11 | START_X = startX >= 0 ? startX : 0; |
tknapp | 0:6885118d9d3f | 12 | START_Y = startY >= 0 ? startY : 0; |
tknapp | 0:6885118d9d3f | 13 | HEIGHT = height > 2 ? height : 8; |
tknapp | 0:6885118d9d3f | 14 | WIDTH = width > 2 ? width : 20; |
tknapp | 0:6885118d9d3f | 15 | LABEL_X_OFFSET = labelXoffset >= 7 ? labelXoffset : 7; |
tknapp | 0:6885118d9d3f | 16 | |
tknapp | 0:6885118d9d3f | 17 | //Set default drawing chars |
tknapp | 0:6885118d9d3f | 18 | xAxisChar = '-'; |
tknapp | 0:6885118d9d3f | 19 | zeroChar = '+'; |
tknapp | 0:6885118d9d3f | 20 | yAxisChar = '|'; |
tknapp | 0:6885118d9d3f | 21 | filledChar = '#'; |
tknapp | 0:6885118d9d3f | 22 | plainChar = ' '; |
tknapp | 0:6885118d9d3f | 23 | |
tknapp | 0:6885118d9d3f | 24 | //Init data and fill with 0 |
tknapp | 0:6885118d9d3f | 25 | pData = new float[WIDTH]; |
tknapp | 0:6885118d9d3f | 26 | for(int i = 0; i < WIDTH; ++i){ |
tknapp | 0:6885118d9d3f | 27 | pData[i] = 0.0; |
tknapp | 0:6885118d9d3f | 28 | } |
tknapp | 0:6885118d9d3f | 29 | |
tknapp | 0:6885118d9d3f | 30 | //Init line |
tknapp | 0:6885118d9d3f | 31 | pLine = new char[WIDTH+1]; |
tknapp | 0:6885118d9d3f | 32 | pLine[WIDTH] = '\0'; // end-char |
tknapp | 0:6885118d9d3f | 33 | |
tknapp | 0:6885118d9d3f | 34 | //Set first true |
tknapp | 0:6885118d9d3f | 35 | first = true; |
tknapp | 0:6885118d9d3f | 36 | |
tknapp | 0:6885118d9d3f | 37 | //Clear screen |
tknapp | 0:6885118d9d3f | 38 | //term->cls(); |
tknapp | 0:6885118d9d3f | 39 | |
tknapp | 0:6885118d9d3f | 40 | //Set coord sys bases |
tknapp | 0:6885118d9d3f | 41 | BASE_X = START_X + LABEL_X_OFFSET + 1; |
tknapp | 0:6885118d9d3f | 42 | BASE_Y = START_Y + HEIGHT - 1; |
tknapp | 0:6885118d9d3f | 43 | |
tknapp | 0:6885118d9d3f | 44 | } |
tknapp | 0:6885118d9d3f | 45 | |
tknapp | 0:6885118d9d3f | 46 | void ASCIIGraph::initGraph(){ |
tknapp | 0:6885118d9d3f | 47 | |
tknapp | 0:6885118d9d3f | 48 | //Clear Screen |
tknapp | 0:6885118d9d3f | 49 | term->cls(); |
tknapp | 0:6885118d9d3f | 50 | |
tknapp | 0:6885118d9d3f | 51 | //Draw y-axis |
tknapp | 0:6885118d9d3f | 52 | for(int i = 0; i < HEIGHT; ++i){ |
tknapp | 0:6885118d9d3f | 53 | term->locate(START_X + LABEL_X_OFFSET ,START_Y+i); |
tknapp | 0:6885118d9d3f | 54 | term->putc(yAxisChar); //166 //179 //186 |
tknapp | 0:6885118d9d3f | 55 | } |
tknapp | 0:6885118d9d3f | 56 | |
tknapp | 0:6885118d9d3f | 57 | //Draw (0|0) |
tknapp | 0:6885118d9d3f | 58 | term->locate(START_X + LABEL_X_OFFSET, START_Y + HEIGHT); |
tknapp | 0:6885118d9d3f | 59 | term->putc(zeroChar); //43 //192 //200 |
tknapp | 0:6885118d9d3f | 60 | |
tknapp | 0:6885118d9d3f | 61 | //Draw x-axis |
tknapp | 0:6885118d9d3f | 62 | for(int i = 0; i < WIDTH; ++i){ |
tknapp | 0:6885118d9d3f | 63 | term->locate(START_X+LABEL_X_OFFSET+1+i, START_Y + HEIGHT); |
tknapp | 0:6885118d9d3f | 64 | term->putc(xAxisChar); //45 //196 //205 |
tknapp | 0:6885118d9d3f | 65 | } |
tknapp | 0:6885118d9d3f | 66 | |
tknapp | 0:6885118d9d3f | 67 | //Gimmic: Print Hello if large enough |
tknapp | 0:6885118d9d3f | 68 | if(WIDTH > 48 && HEIGHT > 6){ |
tknapp | 0:6885118d9d3f | 69 | int x = (WIDTH - 47)/2; |
tknapp | 0:6885118d9d3f | 70 | int y = (HEIGHT - 7)/2; |
tknapp | 0:6885118d9d3f | 71 | |
tknapp | 0:6885118d9d3f | 72 | int boxX = START_X + LABEL_X_OFFSET + 1 + x; |
tknapp | 0:6885118d9d3f | 73 | int boxY = START_Y + 1 + y; |
tknapp | 0:6885118d9d3f | 74 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 75 | term->printf(" ## ### ### ### ### ### # "); |
tknapp | 0:6885118d9d3f | 76 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 77 | term->printf("# # # # # # # # "); |
tknapp | 0:6885118d9d3f | 78 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 79 | term->printf("#### ### # # # # ## ### ## ### ### "); |
tknapp | 0:6885118d9d3f | 80 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 81 | term->printf("# # # # # # # # # # # # # # #"); |
tknapp | 0:6885118d9d3f | 82 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 83 | term->printf("# # #### ### ### ### ### # ### ### # # "); |
tknapp | 0:6885118d9d3f | 84 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 85 | term->printf(" # "); |
tknapp | 0:6885118d9d3f | 86 | term->locate(boxX, boxY++); |
tknapp | 0:6885118d9d3f | 87 | term->printf(" # "); |
tknapp | 0:6885118d9d3f | 88 | wait(3); |
tknapp | 0:6885118d9d3f | 89 | } |
tknapp | 0:6885118d9d3f | 90 | |
tknapp | 0:6885118d9d3f | 91 | } |
tknapp | 0:6885118d9d3f | 92 | |
tknapp | 0:6885118d9d3f | 93 | ASCIIGraph::~ASCIIGraph(){ |
tknapp | 0:6885118d9d3f | 94 | delete[] pData; |
tknapp | 0:6885118d9d3f | 95 | delete[] pLine; |
tknapp | 0:6885118d9d3f | 96 | } |
tknapp | 0:6885118d9d3f | 97 | |
tknapp | 0:6885118d9d3f | 98 | void ASCIIGraph::pushDataPoint(float dataPoint){ |
tknapp | 0:6885118d9d3f | 99 | |
tknapp | 0:6885118d9d3f | 100 | if(!first){ |
tknapp | 0:6885118d9d3f | 101 | //Shift data to left |
tknapp | 0:6885118d9d3f | 102 | for(int i = 1; i < WIDTH; ++i) |
tknapp | 0:6885118d9d3f | 103 | pData[i-1] = pData[i]; |
tknapp | 0:6885118d9d3f | 104 | |
tknapp | 0:6885118d9d3f | 105 | //Set new dataPoint at the end |
tknapp | 0:6885118d9d3f | 106 | pData[WIDTH-1] = dataPoint; |
tknapp | 0:6885118d9d3f | 107 | |
tknapp | 0:6885118d9d3f | 108 | } else { |
tknapp | 0:6885118d9d3f | 109 | //Fill entire array with first dataPoint if array is empty |
tknapp | 0:6885118d9d3f | 110 | for(int i = 0; i < WIDTH; ++i){ |
tknapp | 0:6885118d9d3f | 111 | pData[i] = dataPoint; |
tknapp | 0:6885118d9d3f | 112 | } |
tknapp | 0:6885118d9d3f | 113 | first = false; |
tknapp | 0:6885118d9d3f | 114 | } |
tknapp | 0:6885118d9d3f | 115 | } |
tknapp | 0:6885118d9d3f | 116 | |
tknapp | 0:6885118d9d3f | 117 | //Draws Graph |
tknapp | 0:6885118d9d3f | 118 | void ASCIIGraph::drawGraph(){ |
tknapp | 0:6885118d9d3f | 119 | |
tknapp | 0:6885118d9d3f | 120 | float min; |
tknapp | 0:6885118d9d3f | 121 | float max; |
tknapp | 0:6885118d9d3f | 122 | float diff; |
tknapp | 0:6885118d9d3f | 123 | float valOfChar; |
tknapp | 0:6885118d9d3f | 124 | |
tknapp | 0:6885118d9d3f | 125 | //Find min and max |
tknapp | 0:6885118d9d3f | 126 | min = pData[0]; |
tknapp | 0:6885118d9d3f | 127 | max = pData[0]; |
tknapp | 0:6885118d9d3f | 128 | for(int i = 1; i < WIDTH; ++i){ |
tknapp | 0:6885118d9d3f | 129 | if(pData[i] < min) |
tknapp | 0:6885118d9d3f | 130 | min = pData[i]; |
tknapp | 0:6885118d9d3f | 131 | if(pData[i] > max) |
tknapp | 0:6885118d9d3f | 132 | max = pData[i]; |
tknapp | 0:6885118d9d3f | 133 | } |
tknapp | 0:6885118d9d3f | 134 | |
tknapp | 0:6885118d9d3f | 135 | //Calculate diff |
tknapp | 0:6885118d9d3f | 136 | diff = max - min; |
tknapp | 0:6885118d9d3f | 137 | |
tknapp | 0:6885118d9d3f | 138 | /* Calculate y-value of one cell in terminal |
tknapp | 0:6885118d9d3f | 139 | * e.g. diff = 5, HEIGHT in terminal is 10 |
tknapp | 0:6885118d9d3f | 140 | * => valOfChar = 5 / 10 = 0.5 |
tknapp | 0:6885118d9d3f | 141 | * => The y-value of each vertical cell is 0.5 |
tknapp | 0:6885118d9d3f | 142 | * => Two set (filled) cells represent the value 1.0 |
tknapp | 0:6885118d9d3f | 143 | */ |
tknapp | 0:6885118d9d3f | 144 | valOfChar = (diff / (float)HEIGHT); |
tknapp | 0:6885118d9d3f | 145 | term->locate(BASE_X - 1, BASE_Y + 3); |
tknapp | 0:6885118d9d3f | 146 | term->printf("Min: %f Max: %f Diff: %f Val: %f", min, max, diff, valOfChar); |
tknapp | 0:6885118d9d3f | 147 | |
tknapp | 0:6885118d9d3f | 148 | //Update scale |
tknapp | 0:6885118d9d3f | 149 | term->locate(START_X, START_Y); |
tknapp | 0:6885118d9d3f | 150 | term->printf("%4.1f", max); |
tknapp | 0:6885118d9d3f | 151 | |
tknapp | 0:6885118d9d3f | 152 | term->locate(START_X, BASE_Y + 1); |
tknapp | 0:6885118d9d3f | 153 | term->printf("%4.1f", min); |
tknapp | 0:6885118d9d3f | 154 | |
tknapp | 0:6885118d9d3f | 155 | /* |
tknapp | 0:6885118d9d3f | 156 | //Draw cell-by-cell (slow) |
tknapp | 0:6885118d9d3f | 157 | for(int i = 0; i < WIDTH; ++i){ |
tknapp | 0:6885118d9d3f | 158 | for(int j = 0; j < HEIGHT; ++j){ |
tknapp | 0:6885118d9d3f | 159 | term->locate(BASE_X + i, BASE_Y - j); |
tknapp | 0:6885118d9d3f | 160 | if( (j*valOfChar) < (pData[i]-min) ) |
tknapp | 0:6885118d9d3f | 161 | term->putc((int)'#'); |
tknapp | 0:6885118d9d3f | 162 | else |
tknapp | 0:6885118d9d3f | 163 | term->putc((int)' '); |
tknapp | 0:6885118d9d3f | 164 | } |
tknapp | 0:6885118d9d3f | 165 | } |
tknapp | 0:6885118d9d3f | 166 | */ |
tknapp | 0:6885118d9d3f | 167 | |
tknapp | 0:6885118d9d3f | 168 | //Draw line-by-line (fast) |
tknapp | 0:6885118d9d3f | 169 | |
tknapp | 0:6885118d9d3f | 170 | for(int i = 0; i < HEIGHT; ++i){ |
tknapp | 0:6885118d9d3f | 171 | for(int j = 0; j < WIDTH; ++j){ |
tknapp | 0:6885118d9d3f | 172 | pLine[j] = ((i*valOfChar) < (pData[j]-min)) ? filledChar : plainChar; |
tknapp | 0:6885118d9d3f | 173 | } |
tknapp | 0:6885118d9d3f | 174 | term->locate(BASE_X, BASE_Y - i); |
tknapp | 0:6885118d9d3f | 175 | term->printf(pLine); |
tknapp | 0:6885118d9d3f | 176 | } |
tknapp | 0:6885118d9d3f | 177 | |
tknapp | 0:6885118d9d3f | 178 | //TODO: Create one array holding ALL cells and print them at once! |
tknapp | 0:6885118d9d3f | 179 | } |
tknapp | 0:6885118d9d3f | 180 | |
tknapp | 0:6885118d9d3f | 181 | void ASCIIGraph::reset(){ |
tknapp | 0:6885118d9d3f | 182 | first = true; |
tknapp | 0:6885118d9d3f | 183 | term->locate(START_X, START_Y); |
tknapp | 0:6885118d9d3f | 184 | term->printf(" "); |
tknapp | 0:6885118d9d3f | 185 | term->locate(START_X, BASE_Y + 1); |
tknapp | 0:6885118d9d3f | 186 | term->printf(" "); |
tknapp | 0:6885118d9d3f | 187 | } |