Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: DMBasicGUI DMSupport
GuiLibGraph.cpp
00001 #include "GuiLibGraph.h" 00002 00003 #include <cstring> 00004 00005 #include <float.h> 00006 00007 00008 #define USE_LED_FOR_DEBUGGING 00009 00010 #ifdef USE_LED_FOR_DEBUGGING 00011 00012 #include "gpio_api.h" 00013 #include "wait_api.h" 00014 #include "toolchain.h" 00015 #include "mbed_interface.h" 00016 00017 /* 00018 Draw a part of a profile - which will be a quadrilateral, vertical at left and right, horizontal at the bottom, but a sloping straight line at the top - 00019 by calling the EasyGUI GuiLib_VLine function multiple times. 00020 00021 Args: colour of the profile to the left of the boundary 00022 colour of the profile to the right of the boundary 00023 X coords of the left and right edges of the section 00024 X coord of the colour boundary 00025 Y coord of the bottom 00026 Y coords of the top left and top right 00027 00028 Returns true if OK, false if it failed (e.g. the coords were invalid). 00029 */ 00030 extern bool DrawProfileSectionUsingGuiLibVLine(GuiConst_INTCOLOR colour1, GuiConst_INTCOLOR colour2, GuiConst_INT16S xLeft, GuiConst_INT16S xRight, GuiConst_INT16S xColourBoundary, 00031 GuiConst_INT16S yBottom, GuiConst_INT16S yTopLeft, GuiConst_INT16S yTopRight); 00032 00033 00034 // Turn on LED 4 when we get an error (mismatched time/X coord) in the graph data (see below) 00035 static void SetLed4(bool turnLedOn) 00036 { 00037 gpio_t led_4; gpio_init_out(&led_4, LED4); 00038 00039 if(turnLedOn) { 00040 gpio_write(&led_4, 1); // one appears to mean "turn LED 4 on" 00041 } else { 00042 gpio_write(&led_4, 0); // zero appears to turn it off 00043 } 00044 } 00045 00046 #endif // USE_LED_FOR_DEBUGGING 00047 00048 00049 /* 00050 The GuiLibGraphDataSet class - encapsulates a GuiLib_GraphDataPoint array, 00051 making access and use of it more straightforward than if the caller 00052 had to do this directly. 00053 00054 We assume that X coordinates are time values, and Y coordinates are temperature, pressure, etc, values. 00055 ****************************************************************************************************** 00056 00057 We leave it to the caller to decide what the Y coordinate units are, but we let the caller specify 00058 the time (i.e. X axis) units. If the caller changes the units, we recalculate the X coordinates to match. 00059 The options are minutes and seconds - we default to minutes. 00060 00061 Note that the coord values are integers (as in the easyGUI Graph item). 00062 */ 00063 00064 /* 00065 Static member function, to provide a standard way of converting the floating point coordinates we store internally (for accuracy), 00066 to the integer values required by easyGUI graphs. We always store the time value (X coordinate) as minutes, in units of 0.1 minute, 00067 as does the GC itself. We convert to integer minutes or seconds as required by the caller. 00068 00069 We do not concern ourselves here with the units of the Y coordinate - we assume that the caller knows what they are. 00070 00071 Args: pointer to the GuiLib_GraphDataPoint object to receive the integer coordinates 00072 the floating point data to be converted 00073 the time unit (minutes or seconds) for the generated X coordinate 00074 00075 No return value (we assume the conversion will always succeed). 00076 */ 00077 void GuiLibGraphDataSet::ConvertFloatingDataPointToGuiLibGraphDataPoint(GuiLib_GraphDataPoint* graphDataPoint, FloatingDataPoint floatingDataPoint, TimeUnit timeUnit) 00078 { 00079 // The 'floor' mathematical function expects a double as its argument 00080 00081 double doubleX = (double) floatingDataPoint.X; 00082 if(timeUnit == SECONDS) { 00083 doubleX *= 60.0; 00084 } 00085 00086 double doubleY = (double) floatingDataPoint.Y; 00087 00088 // Round, don't truncate 00089 graphDataPoint->X = (GuiConst_INT32S) floor(doubleX + 0.5); 00090 graphDataPoint->Y = (GuiConst_INT32S) floor(doubleY + 0.5); 00091 } 00092 00093 GuiLibGraphDataSet::GuiLibGraphDataSet() 00094 { 00095 // No data yet... 00096 00097 theGraphDataSet = NULL; 00098 00099 graphDataSetActualLength = 0; 00100 graphDataSetSizeInIncrements = 0; 00101 00102 nonRoundedTotalMethodTime = 0.0f; 00103 } 00104 00105 GuiLibGraphDataSet::~GuiLibGraphDataSet() 00106 { 00107 if(theGraphDataSet != NULL) { 00108 delete [] theGraphDataSet; 00109 } 00110 } 00111 00112 00113 /* 00114 Clears (i.e. deletes, removes) all the existing data (if any) for this dataset 00115 */ 00116 void GuiLibGraphDataSet::ClearData(void) 00117 { 00118 if(theGraphDataSet != NULL) { 00119 delete [] theGraphDataSet; 00120 theGraphDataSet = NULL; 00121 00122 graphDataSetActualLength = 0; 00123 graphDataSetSizeInIncrements = 0; 00124 } 00125 } 00126 00127 00128 /* 00129 We extend the dataset array in steps of 'SIZE_INCREMENT'. This function does that, 00130 and copies the existing data to the extended array. 00131 00132 It returns true if it succeeded in extending the dataset array, false otherwise. 00133 */ 00134 bool GuiLibGraphDataSet::ExtendDataSetArray(void) 00135 { 00136 bool retVal = false; 00137 00138 if(theGraphDataSet != NULL) { 00139 FloatingDataPoint* newGraphDataSet = new FloatingDataPoint[(graphDataSetSizeInIncrements + 1) * SIZE_INCREMENT]; 00140 00141 if(newGraphDataSet != NULL) { 00142 std::memcpy(newGraphDataSet, theGraphDataSet, (graphDataSetSizeInIncrements * SIZE_INCREMENT * sizeof(FloatingDataPoint))); 00143 00144 delete [] theGraphDataSet; 00145 00146 theGraphDataSet = newGraphDataSet; 00147 00148 ++graphDataSetSizeInIncrements; 00149 00150 // Leave 'graphDataSetActualLength' with whatever value it currently has. 00151 // We have only extended the array, not added any data to it. 00152 00153 retVal = true; // Success 00154 } 00155 00156 } else { 00157 // This is the first 'extension' 00158 theGraphDataSet = new FloatingDataPoint[SIZE_INCREMENT]; 00159 00160 if(theGraphDataSet != NULL) { 00161 graphDataSetSizeInIncrements = 1; 00162 00163 // We assume 'graphDataSetActualLength' is set to zero - leave it with this value. 00164 // We have only allocated memory for the array, not put any data in it. 00165 00166 retVal = true; // Success 00167 } 00168 } 00169 00170 return retVal; 00171 } 00172 00173 00174 /* 00175 Adds a datapoint with the specified X and Y values to the dataset array, 00176 making sure to keep the array elements in ascending order of X coordinate. 00177 Other member functions of this class rely on this. 00178 ************************************************* 00179 00180 Returns true for success, false for failure. 00181 00182 Args: the X and Y values for the new datapoint 00183 00184 Note that this function takes no account of the units specified for the X (i.e. time) axis - 00185 we assume that the caller has already ensured that the X value is in those units. 00186 */ 00187 bool GuiLibGraphDataSet::AddDataPoint(float X, float Y) 00188 { 00189 // Check that we do not already have a point at the same X coordinate - 00190 // if so, return false 00191 for (int i = 0; i < graphDataSetActualLength; ++i) { 00192 if(theGraphDataSet[i].X == X) { 00193 return false; 00194 } 00195 00196 if(theGraphDataSet[i].X > X) { 00197 // We have now reached a point with a greater X coord, 00198 // without finding a point with the same X coord - safe to continue 00199 break; 00200 } 00201 } 00202 00203 if((graphDataSetActualLength + 1) > (graphDataSetSizeInIncrements * SIZE_INCREMENT)) { 00204 00205 // We need to extend the array before adding the new datapoint 00206 00207 if(!ExtendDataSetArray()) { 00208 return false; 00209 } 00210 } 00211 00212 // We now have room for the new datapoint 00213 // - make sure we keep the datapoints in ascending order of X coord 00214 int positionToAdd = graphDataSetActualLength; 00215 00216 if(graphDataSetActualLength > 0) { 00217 00218 while(theGraphDataSet[positionToAdd - 1].X > X) { 00219 --positionToAdd; 00220 00221 if(positionToAdd == 0) break; 00222 } 00223 00224 00225 // Move 'up' all the array elements 'above' positionToAdd 00226 00227 for (int j = graphDataSetActualLength; j > positionToAdd; --j) { 00228 theGraphDataSet[j] = theGraphDataSet[j - 1]; 00229 } 00230 } 00231 00232 // We are now ready to add the new datapoint 00233 00234 theGraphDataSet[positionToAdd].X = X; 00235 theGraphDataSet[positionToAdd].Y = Y; 00236 00237 ++graphDataSetActualLength; 00238 00239 return true; 00240 } 00241 00242 /* 00243 Returns the X and Y coordinates of the datapoint at the specified index in the dataset. 00244 00245 Returns true if we actually have a point at the index, false if not (does not set 00246 the destination variables in this case). 00247 00248 Args: the index of the point required 00249 pointers to variables to contain the X and Y values of the specified datapoint. 00250 (It is up to the caller to make sure these are valid.) 00251 */ 00252 bool GuiLibGraphDataSet::GetDataPoint(int dataPointIndex, float *X, float *Y) 00253 { 00254 if((dataPointIndex >= 0) && (dataPointIndex < graphDataSetActualLength)) { 00255 00256 *X = theGraphDataSet[dataPointIndex].X; 00257 *Y = theGraphDataSet[dataPointIndex].Y; 00258 00259 return true; 00260 } 00261 00262 // 'else' index not valid 00263 return false; 00264 } 00265 00266 00267 /* 00268 Returns the Y coordinate of a point on the graph at the specified X coordinate. 00269 This will in general mean interpolating between two adjacent points - we assume 00270 a straight line in this case. 00271 00272 Note that this function relies on the array elements being in ascending order 00273 of X coordinate. 00274 */ 00275 float GuiLibGraphDataSet::GetYCoordAtXCoord(float X) 00276 { 00277 if(graphDataSetActualLength > 0) { 00278 00279 if(graphDataSetActualLength > 1) { 00280 00281 // Deal with the extreme cases first 00282 if(theGraphDataSet[0].X > X) { 00283 00284 // Required point is before the start of our array 00285 00286 double X0 = (double) theGraphDataSet[0].X; 00287 double Y0 = (double) theGraphDataSet[0].Y; 00288 double X1 = (double) theGraphDataSet[1].X; 00289 double Y1 = (double) theGraphDataSet[1].Y; 00290 00291 double m = (Y1 - Y0) / (X1 - X0); 00292 double c = Y0 - (m * X0); 00293 00294 return (float)(m * (double) X) + c; 00295 } 00296 00297 // 'else' ... 00298 if(theGraphDataSet[graphDataSetActualLength - 1].X < X) { 00299 00300 // Required point is after the end of our array 00301 00302 double X0 = (double) theGraphDataSet[graphDataSetActualLength - 2].X; 00303 double Y0 = (double) theGraphDataSet[graphDataSetActualLength - 2].Y; 00304 double X1 = (double) theGraphDataSet[graphDataSetActualLength - 1].X; 00305 double Y1 = (double) theGraphDataSet[graphDataSetActualLength - 1].Y; 00306 00307 double m = (Y1 - Y0) / (X1 - X0); 00308 double c = Y0 - (m * X0); 00309 00310 return (float)(m * (double) X) + c; 00311 } 00312 00313 // 'else' - required data point must either be coincident with one of our data points, 00314 // or between two of them 00315 for (int i = 0; i < (graphDataSetActualLength - 1); ++i) { 00316 00317 if(theGraphDataSet[i].X == X) { 00318 // We already have a data point at the specified X coordinate 00319 00320 return theGraphDataSet[i].Y; 00321 } 00322 00323 // 'else' 00324 if(theGraphDataSet[i + 1].X == X) { 00325 // We already have a data point at the specified X coordinate 00326 00327 return theGraphDataSet[i + 1].Y; 00328 } 00329 00330 // 'else' 00331 if(theGraphDataSet[i + 1].X > X) { 00332 00333 // We must interpolate between two data points 00334 00335 double xValue = (double) X; 00336 00337 double X0 = (double) theGraphDataSet[i].X; 00338 double Y0 = (double) theGraphDataSet[i].Y; 00339 double X1 = (double) theGraphDataSet[i + 1].X; 00340 double Y1 = (double) theGraphDataSet[i + 1].Y; 00341 00342 double m = (Y1 - Y0) / (X1 - X0); 00343 double c = Y0 - (m * X0); 00344 00345 return (float)((m * xValue) + c); 00346 } 00347 } 00348 } 00349 00350 // 'else' we only have one datapoint - return its Y coord 00351 // (i.e. treat the 'graph' as a straight line) 00352 return theGraphDataSet[0].Y; 00353 } 00354 00355 // 'else' we have no existing data points to interpolate 00356 return 0.0f; 00357 } 00358 00359 /* 00360 Copy our current 'actual' floating point data array, 00361 to a GuiLib_GraphDataPoint array, as required by the easyGUI Graph. 00362 00363 The actual floating point data is always in minutes, but may well have a fractional part, 00364 since the GC measures time, in effect, in units of 0.1 minute. The GuiLib_GraphDataPoint 00365 structure, however, uses integers, so we copy the actual data to integer numbers 00366 either in minutes or seconds, as specified by the caller. We convert the original 00367 floating point values to integers by rounding them, using our static function 00368 'ConvertFloatingDataPointToGuiLibGraphDataPoint'. 00369 00370 If the caller wants minutes to be the time unit, we may well end up with multiple points 00371 at the same X (i.e. time) coordinate - guard against this by combining them into one point 00372 whose Y coordinate is the average of the duplicate points. If the caller wants seconds, 00373 we assume that we cannot possibly have multiple points at the same X coordinate. 00374 00375 Note that we pass a pointer to the GuiLib_GraphDataPoint array back to the caller. 00376 It is up to the caller to delete this array when it is no longer needed. 00377 ************************************************************************ 00378 00379 It is also up to the caller to check that the pointer we return is not NULL before using it. 00380 ******************************************************************************************** 00381 00382 00383 Args: the time unit (minutes or seconds) to be used in the copy 00384 a pointer to an unsigned integer to contain the number of points in the copied array 00385 00386 Return value: pointer to the array of GuiLib_GraphDataPoints containing the copied data 00387 00388 */ 00389 GuiLib_GraphDataPoint* GuiLibGraphDataSet::GetGraphDataPointCopy(TimeUnit timeUnit, GuiConst_INT16U *countOfCopiedPoints) 00390 { 00391 // Remember that our 'native' data points are in time (i.e. X coordinate) units of 0.1 minute 00392 00393 // First, a straight copy, rounding the X coordinate to the unit specified 00394 GuiLib_GraphDataPoint* graphDataSetCopy = new GuiLib_GraphDataPoint[graphDataSetActualLength]; 00395 00396 if(graphDataSetCopy == NULL) { 00397 00398 // Failed to allocate the memory we need 00399 00400 *countOfCopiedPoints = 0; 00401 return NULL; 00402 } 00403 00404 // 'else' memory allocated - continue 00405 00406 for(int index = 0; index < graphDataSetActualLength; ++index) { 00407 ConvertFloatingDataPointToGuiLibGraphDataPoint(&graphDataSetCopy[index], theGraphDataSet[index], timeUnit); 00408 } 00409 00410 if(timeUnit == SECONDS) { 00411 00412 // The simple case - since our 'native' data points are in 'units' of 0.1 minute, 00413 // we cannot have multiple points at the same number of seconds, i.e. with the same X coordinate - 00414 // so just return the duplicate array, with rounded X and Y coordinate values, that we created above 00415 00416 *countOfCopiedPoints = graphDataSetActualLength; // Tell caller how many points there are 00417 return graphDataSetCopy; 00418 } 00419 00420 00421 // 'else' must be minutes - now, we must guard against multiple points at the same X coordinate 00422 // ******************************************************************* 00423 00424 // Count how many distinct points - i.e. with different X coordinate values - there are 00425 int pointIndex; 00426 GuiConst_INT32S lastXCoord = graphDataSetCopy[0].X; 00427 int countOfDistinctPoints = 1; // We have already looked at the first point 00428 for(pointIndex = 1; pointIndex < graphDataSetActualLength; ++pointIndex) { 00429 if(graphDataSetCopy[pointIndex].X > lastXCoord) { 00430 ++countOfDistinctPoints; 00431 lastXCoord = graphDataSetCopy[pointIndex].X; 00432 } 00433 // 'else' this point has the same X coordinate as the previous one 00434 } 00435 00436 // Now allocate an array to contain that number of points 00437 GuiLib_GraphDataPoint* graphDataSetMinutesCopy = new GuiLib_GraphDataPoint[countOfDistinctPoints]; 00438 00439 if(graphDataSetMinutesCopy == NULL) { 00440 // Failed to allocate the memory we need 00441 delete [] graphDataSetCopy; 00442 00443 *countOfCopiedPoints = 0; 00444 return NULL; 00445 } 00446 00447 // 'else' memory allocated - continue 00448 00449 // When we find two or more points with the same X coordinate in the original dataset copy array, 00450 // we create one point in the 'minutes' array, whose Y coordinate is the average of the Y coordinates 00451 // of the original points 00452 00453 graphDataSetMinutesCopy[0] = graphDataSetCopy[0]; 00454 int minutesPointIndex = 0; 00455 lastXCoord = graphDataSetCopy[0].X; 00456 int multiPointCount = 1; // Count of the number of points at the current X coordinate 00457 for(pointIndex = 1; pointIndex < graphDataSetActualLength; ++pointIndex) { 00458 if(graphDataSetCopy[pointIndex].X > lastXCoord) { 00459 00460 if(multiPointCount > 1) { 00461 // Set the minutes copy to the average of the Y values we have accumulated 00462 graphDataSetMinutesCopy[minutesPointIndex].Y /= multiPointCount; 00463 multiPointCount = 1; 00464 } 00465 00466 ++minutesPointIndex; 00467 graphDataSetMinutesCopy[minutesPointIndex] = graphDataSetCopy[pointIndex]; 00468 00469 lastXCoord = graphDataSetCopy[pointIndex].X; 00470 00471 } else { 00472 00473 // This point must have the same X coordinate as the previous one - 00474 // start accumulating the Y values, ready to take the average 00475 graphDataSetMinutesCopy[minutesPointIndex].Y += graphDataSetCopy[pointIndex].Y; 00476 ++multiPointCount; 00477 } 00478 } 00479 00480 if(multiPointCount > 1) { 00481 graphDataSetMinutesCopy[minutesPointIndex].Y /= multiPointCount; 00482 } 00483 00484 delete [] graphDataSetCopy; // No longer needed 00485 00486 *countOfCopiedPoints = countOfDistinctPoints; // Tell caller how many points there are in the copied array 00487 return graphDataSetMinutesCopy; // Caller wants this array, not the original copy 00488 } 00489 00490 00491 /* 00492 Copy our current 'actual' floating point data array, 00493 to a GuiLib_GraphDataPoint array, as required by the easyGUI Graph. 00494 00495 The actual floating point data is always in minutes, but may well have a fractional part, 00496 since the GC measures time, in effect, in units of 0.1 minute - and we have time as the X axis. 00497 The GuiLib_GraphDataPoint structure, however, uses integers, so before copying the actual data 00498 to integer numbers, we multiply the X values by 10.0. 00499 00500 Note that we pass a pointer to the GuiLib_GraphDataPoint array back to the caller. 00501 It is up to the caller to delete this array when it is no longer needed. 00502 ************************************************************************ 00503 00504 It is also up to the caller to check that the pointer we return is not NULL before using it. 00505 ******************************************************************************************** 00506 00507 00508 Args: a pointer to an unsigned integer to contain the number of points in the copied array 00509 00510 Return value: pointer to the array of GuiLib_GraphDataPoints containing the copied data 00511 00512 */ 00513 GuiLib_GraphDataPoint* GuiLibGraphDataSet::GetGraphDataPointCopyInTenthsOfMinutes(float yAxisScaleFactor, GuiConst_INT16U *countOfCopiedPoints) 00514 { 00515 GuiLib_GraphDataPoint* graphDataSetCopy = new GuiLib_GraphDataPoint[graphDataSetActualLength]; 00516 00517 if(graphDataSetCopy == NULL) { 00518 00519 // Failed to allocate the memory we need 00520 00521 *countOfCopiedPoints = 0; 00522 return NULL; 00523 } 00524 00525 // 'else' memory allocated - continue... 00526 00527 // The 'floor' mathematical function expects a double as its argument 00528 double doubleX; 00529 double doubleY; 00530 00531 for(int index = 0; index < graphDataSetActualLength; ++index) { 00532 00533 // 'floor' math(s) function requires a double argument, not float 00534 doubleX = ((double)theGraphDataSet[index].X) * 10.0; 00535 doubleY = (double)(theGraphDataSet[index].Y * yAxisScaleFactor); 00536 00537 // Round, don't truncate 00538 graphDataSetCopy[index].X = (GuiConst_INT32S) floor(doubleX + 0.5); 00539 graphDataSetCopy[index].Y = (GuiConst_INT32S) floor(doubleY + 0.5); 00540 } 00541 00542 *countOfCopiedPoints = graphDataSetActualLength; // Tell caller how many points there are 00543 return graphDataSetCopy; 00544 } 00545 00546 00547 /* 00548 Sets another GuiLibGraphDataSet object to a 'partial copy' of this one. 00549 A 'partial copy' is a copy that begins at the specified X start coordinate, and ends at the specified X end coordinate. 00550 We copy all of our coordinate pairs that lie between those two X values to the destination. 00551 If we do not have a coordinate pair exactly at the specified X start coordinate, we generate one 00552 using our 'GetYCoordAtXCoord' member function. The same applies to the end X coordinate. 00553 00554 Args: the X coordinate at which to start the copy 00555 the X coordinate at which to end the copy 00556 a pointer to the GuiLibGraphDataSet object which is to be the copy destination 00557 */ 00558 void GuiLibGraphDataSet::MakePartialCopy(float startXCoord, float endXCoord, GuiLibGraphDataSet* copyDestination) 00559 { 00560 // First, clear the copy destination of any existing data 00561 copyDestination->ClearData(); 00562 00563 00564 // If the end coord is less than the start coord, do nothing 00565 // - leave data clear 00566 if(endXCoord < startXCoord) { 00567 return; 00568 } 00569 00570 if(graphDataSetActualLength > 0) { 00571 00572 int pointIndex; 00573 for (pointIndex = 0; pointIndex < graphDataSetActualLength; ++pointIndex) { 00574 00575 if(theGraphDataSet[pointIndex].X >= startXCoord) { 00576 break; 00577 } 00578 } 00579 00580 if(pointIndex < graphDataSetActualLength) { 00581 if (theGraphDataSet[pointIndex].X > startXCoord) { 00582 00583 // Need to add a start point at the specified start X coord 00584 copyDestination->AddDataPoint(startXCoord, GetYCoordAtXCoord(startXCoord)); 00585 00586 } // 'else' the start point already has the correct X coordinate - no need to add another 00587 00588 // pointIndex already has the correct start value 00589 for (; pointIndex < graphDataSetActualLength; ++pointIndex) { 00590 00591 if(theGraphDataSet[pointIndex].X <= endXCoord) { 00592 copyDestination->AddDataPoint(theGraphDataSet[pointIndex].X, theGraphDataSet[pointIndex].Y); 00593 } else { 00594 break; 00595 } 00596 } 00597 00598 if (theGraphDataSet[pointIndex - 1].X < endXCoord) { 00599 00600 // Need to add a final point at the end X coord 00601 copyDestination->AddDataPoint(endXCoord, GetYCoordAtXCoord(endXCoord)); 00602 00603 } // 'else' the final point already has the correct X coordinate - no need to add another 00604 00605 } // 'else' we have no points above the start X coordinate - nothing to copy 00606 00607 } // 'else' we have no points to copy 00608 } 00609 00610 /* 00611 Sets another GuiLibGraphDataSet object to an 'interpolated partial copy' of this one. 00612 This is a copy that starts at a specified X coordinate and finishes at another specified X coordinate, 00613 and has a specified interval between X coordinates. This will, in general, require interpolating 00614 between our existing data points. 00615 00616 Args: the X coordinate at which to start 00617 the X coordinate at which to finish 00618 the X interval - i.e. the interval between X coords 00619 a pointer to the GuiLibGraphDataSet object which is to be the copy destination 00620 */ 00621 void GuiLibGraphDataSet::MakeInterpolatedPartialCopy(float startXCoord, float endXCoord, float xInterval, GuiLibGraphDataSet* copyDestination) 00622 { 00623 // First, clear the copy destination of any existing data 00624 copyDestination->ClearData(); 00625 00626 // Let the for loop limits take care of whether the start and end X coordinates are in the correct order 00627 00628 // Now copy/generate the actual points 00629 float xCoord; 00630 float endLimit = endXCoord + (xInterval / 2.0f); // Should just be 'endXCoord' - but allow for floating point rounding, 00631 // otherwise we may omit the last point 00632 for (xCoord = startXCoord; xCoord <= endLimit; xCoord += xInterval) { 00633 copyDestination->AddDataPoint(xCoord, GetYCoordAtXCoord(xCoord)); 00634 } 00635 } 00636 00637 /* 00638 Sets another GuiLibGraphDataSet object to an 'interpolated partial copy' of this one. 00639 This is a copy that starts at a specified X coordinate and finishes at another specified X coordinate, 00640 and has a specified interval between X coordinates. This will, in general, require interpolating 00641 between our existing data points. 00642 00643 Unlike the original 'MakeInterpolatedPartialCopy' above, this guarantees to add a final point at the end 00644 of the copy range, whether this makes an exact interval or not. This means that, in a profile where 00645 the 'interpolated partial copy' is displayed as a bar chart underneath a line (created using the 00646 'MakePartialCopy' function), we are guaranteed to have a bar at the exact end of the line - 00647 it will not overhang empty space. 00648 00649 Args: the X coordinate at which to start 00650 the X coordinate at which to finish 00651 the X interval - i.e. the interval between X coords 00652 a pointer to the GuiLibGraphDataSet object which is to be the copy destination 00653 */ 00654 void GuiLibGraphDataSet::MakeInterpolatedPartialCopyWithFinalPoint(float startXCoord, float endXCoord, float xInterval, GuiLibGraphDataSet* copyDestination) 00655 { 00656 // First, clear the copy destination of any existing data 00657 copyDestination->ClearData(); 00658 00659 // Let the for loop limits take care of whether the start and end X coordinates are in the correct order 00660 00661 // Now copy/generate the actual points 00662 float xCoord; 00663 bool needFinalPoint = true; 00664 for (xCoord = startXCoord; xCoord <= endXCoord; xCoord += xInterval) { 00665 copyDestination->AddDataPoint(xCoord, GetYCoordAtXCoord(xCoord)); 00666 00667 if(xCoord == endXCoord) { 00668 needFinalPoint = false; 00669 } 00670 } 00671 00672 if(needFinalPoint) { 00673 // We need to add a final point at the exact end of the copy range 00674 copyDestination->AddDataPoint(endXCoord, GetYCoordAtXCoord(endXCoord)); 00675 } 00676 } 00677 00678 /* 00679 Gets the current temperature for a particular component. 00680 00681 Args: the GC command to get the temperature for that component 00682 the usbDevice and usbHostGC instances corresponding to the GC 00683 00684 Returns the current temperature of the component as a floating-point value, in degrees C. 00685 */ 00686 float GuiLibGraphDataSet::GetComponentTemperature(char *cmd, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00687 { 00688 // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 00689 // it is not re-entrant (and nor is the GC) 00690 while(usbHostGC->ExecutingSetDeviceReport()) {} 00691 00692 char response[50]; 00693 usbHostGC->SetDeviceReport(usbDevice, cmd, response); 00694 // We expect a response like this: "Dxxx1234" - temp in units of 1 deg c 00695 00696 float retVal; 00697 00698 // But check for "EPKT" first 00699 if(response[0] == 'E') { 00700 retVal = -1.0f; // ** Caller must check for this ** 00701 } else { 00702 sscanf(&response[4], "%f", &retVal); 00703 } 00704 00705 return retVal; 00706 } 00707 00708 /* 00709 Gets, and returns, the current column temperature. 00710 00711 Returns the current column temperature as a floating-point value, in degrees C. 00712 */ 00713 float GuiLibGraphDataSet::GetColumnTemperature(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00714 { 00715 //#define GCOL_VALUE_IN_TENTHS_OF_A_DEGREE 00716 #ifdef GCOL_VALUE_IN_TENTHS_OF_A_DEGREE 00717 // This value is actually in tenths of a degree, not whole degrees as stated above 00718 return ((GetComponentTemperature("GCOL", usbDevice, usbHostGC)) * 0.1f); 00719 #undef GCOL_VALUE_IN_TENTHS_OF_A_DEGREE 00720 #else 00721 return GetComponentTemperature("GCOL", usbDevice, usbHostGC); 00722 #endif 00723 } 00724 00725 /* 00726 Gets, and returns, the current injector temperature. 00727 00728 Returns the current injector temperature as a floating-point value, in degrees C. 00729 */ 00730 float GuiLibGraphDataSet::GetInjectorTemperature(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00731 { 00732 //#define GINJ_VALUE_IN_TENTHS_OF_A_DEGREE 00733 #ifdef GINJ_VALUE_IN_TENTHS_OF_A_DEGREE 00734 // This value is actually in tenths of a degree, not whole degrees as stated above 00735 return ((GetComponentTemperature("GINJ", usbDevice, usbHostGC)) * 0.1f); 00736 #undef GINJ_VALUE_IN_TENTHS_OF_A_DEGREE 00737 #else 00738 return GetComponentTemperature("GINJ", usbDevice, usbHostGC); 00739 #endif 00740 } 00741 00742 /* 00743 Gets, and returns, a time value, obtained using the command passed to it. 00744 We expect that this value will be returned by the GC in units of 0.1 minute. 00745 00746 Args: a pointer to a null-terminated string containing the command to use 00747 pointers to the USBDevice and USBHost instances corresponding to the GC 00748 00749 Note that this code is intended to be as efficient as possible. 00750 00751 Returns the required time as a floating-point value, in minutes - i.e. scaled from 00752 the value returned by the GC. 00753 */ 00754 float GuiLibGraphDataSet::GetTimeValue(char *cmd, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00755 { 00756 // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 00757 // it is not re-entrant (and nor is the GC) 00758 while(usbHostGC->ExecutingSetDeviceReport()) {} 00759 00760 char buff[10]; 00761 char response[50]; 00762 usbHostGC->SetDeviceReport(usbDevice, cmd, response); 00763 // We expect a response like this: "Dxxx1234" - time in units of 0.1 minute 00764 00765 float retVal; 00766 00767 // But check for "EPKT" first 00768 if(response[0] == 'E') { 00769 retVal = -1.0f; // ** Caller must check for this ** 00770 } else { 00771 buff[0] = response[4]; 00772 buff[1] = response[5]; 00773 buff[2] = response[6]; 00774 buff[3] = '.'; 00775 buff[4] = response[7]; 00776 00777 sscanf(buff, "%f", &retVal); 00778 } 00779 00780 return retVal; 00781 } 00782 00783 /* 00784 Gets, and returns, the initial hold time. 00785 00786 Returns the initial hold time as a floating-point value, in minutes. 00787 */ 00788 float GuiLibGraphDataSet::GetInitialHoldTime(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00789 { 00790 return GetTimeValue("GTIM", usbDevice, usbHostGC); 00791 } 00792 00793 /* 00794 Gets, and returns, the PTV/injector initial time. 00795 00796 Returns the initial time as a floating-point value, in minutes. 00797 */ 00798 float GuiLibGraphDataSet::GetPTVInitialTime(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00799 { 00800 return GetTimeValue("GIPT", usbDevice, usbHostGC); 00801 } 00802 00803 /* 00804 Gets, and returns, the initial pressure. 00805 00806 Note that this code is intended to be as efficient as possible. 00807 00808 Returns the initial pressure as a floating-point value, in minutes. 00809 */ 00810 float GuiLibGraphDataSet::GetInitialPressure(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00811 { 00812 // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 00813 // it is not re-entrant (and nor is the GC) 00814 while(usbHostGC->ExecutingSetDeviceReport()) {} 00815 00816 char buff[10]; 00817 char response[50]; 00818 usbHostGC->SetDeviceReport(usbDevice, "GPRS", response); 00819 // We expect a response like this: "DPRS1234" - pressure in units of 0.1 psi 00820 00821 float retVal; 00822 00823 // But check for "EPKT" first 00824 if(response[0] == 'E') { 00825 retVal = -1.0f; // ** Caller must check for this ** 00826 } else { 00827 buff[0] = response[4]; 00828 buff[1] = response[5]; 00829 buff[2] = response[6]; 00830 buff[3] = '.'; 00831 buff[4] = response[7]; 00832 00833 sscanf(buff, "%f", &retVal); 00834 } 00835 00836 return retVal; 00837 } 00838 00839 /* 00840 Gets a value from the GC for a particular ramp. 00841 00842 All commands that get ramp values from the GC have the form "Gxxr", where "G" means "get", 00843 the characters "xx" are specific to the command, and "r" is the ramp index, 0 through 9. 00844 The GC's response will have the form "Dxxrnnnn", where "nnnn" is a four digit value - 00845 this is the value we want. This function converts that value to a float, and returns it in that form. 00846 It is up to the caller to convert this into the correct units for the command, including scaling it if necessary. 00847 00848 Args: the command to get the required ramp value (the three "Gxx" characters) 00849 the ramp index (0 to 9 inclusive) 00850 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00851 00852 Returns the ramp value obtained from the GC, as an (unscaled) floating point value 00853 */ 00854 float GuiLibGraphDataSet::GetRampValue(char *cmd, int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00855 { 00856 // Check command length is valid 00857 if(strlen(cmd) != 3) { 00858 return 0.0f; 00859 } 00860 00861 // Now the ramp index 00862 if((rampIndex < 0) || (rampIndex > 9)) { 00863 return 0.0f; 00864 } 00865 00866 00867 char response[GC_MESSAGE_LENGTH+2]; 00868 00869 // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 00870 // it is not re-entrant (and nor is the GC) 00871 while(usbHostGC->ExecutingSetDeviceReport()) {} 00872 00873 char buff[100]; 00874 sprintf(buff, "%s%d", cmd, rampIndex); 00875 usbHostGC->SetDeviceReport(usbDevice, buff, response); 00876 00877 float rampValue; 00878 00879 // We expect a response of the form "DXXnrrrr", where "XX" is the second and third character of the command, 00880 // 'n' is the ramp index, and "rrrr" is the required ramp value 00881 00882 // But check for "EPKT" first... 00883 if(response[0] == 'E') { 00884 rampValue = 0.0f; 00885 } else { 00886 sscanf(&response[4], "%f", &rampValue); 00887 } 00888 00889 return rampValue; 00890 } 00891 00892 /* 00893 Gets the temperature ramp rate for a particular ramp. 00894 00895 Args: the ramp index (0 to 9 inclusive) 00896 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00897 00898 Returns the temperature ramp rate obtained from the GC, in degrees C per minute. 00899 */ 00900 float GuiLibGraphDataSet::GetTemperatureRampRate(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00901 { 00902 return GetRampValue("GRP", rampIndex, usbDevice, usbHostGC); 00903 } 00904 00905 /* 00906 Gets the PTV/injector temperature ramp rate for a particular ramp. 00907 00908 Args: the ramp index (0 to 9 inclusive) 00909 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00910 00911 Returns the temperature ramp rate obtained from the GC, in degrees C per minute. 00912 */ 00913 float GuiLibGraphDataSet::GetPTVTemperatureRampRate(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00914 { 00915 return GetRampValue("GIP", rampIndex, usbDevice, usbHostGC); 00916 } 00917 00918 /* 00919 Gets the upper temperature for a particular ramp. 00920 00921 Args: the ramp index (0 to 9 inclusive) 00922 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00923 00924 Returns the upper temperature obtained from the GC, in degrees C. 00925 */ 00926 float GuiLibGraphDataSet::GetRampUpperTemperature(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00927 { 00928 //#define GRC_VALUE_IN_TENTHS_OF_A_DEGREE 00929 #ifdef GRC_VALUE_IN_TENTHS_OF_A_DEGREE 00930 // This value is actually in tenths of a degree, not whole degrees as stated above 00931 return ((GetRampValue("GRC", rampIndex, usbDevice, usbHostGC)) * 0.1f); 00932 #undef GRC_VALUE_IN_TENTHS_OF_A_DEGREE 00933 #else 00934 return GetRampValue("GRC", rampIndex, usbDevice, usbHostGC); 00935 #endif 00936 } 00937 00938 /* 00939 Gets the upper temperature for a particular PTV/injector ramp. 00940 00941 Args: the ramp index (0 to 9 inclusive) 00942 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00943 00944 Returns the upper temperature obtained from the GC, in degrees C. 00945 */ 00946 float GuiLibGraphDataSet::GetPTVRampUpperTemperature(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00947 { 00948 return GetRampValue("GIC", rampIndex, usbDevice, usbHostGC); 00949 } 00950 00951 /* 00952 Gets the ramp time for a particular ramp. 00953 00954 Args: the ramp index (0 to 9 inclusive) 00955 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00956 00957 Returns the ramp time obtained from the GC, in minutes (actually the GC returns a value in units of 0.1 minute, 00958 but this function applies that scaling to the value before returning it). 00959 */ 00960 float GuiLibGraphDataSet::GetRampUpperTime(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00961 { 00962 return ((GetRampValue("GRS", rampIndex, usbDevice, usbHostGC)) * 0.1f); 00963 } 00964 00965 /* 00966 Gets the ramp time for a particular PTV/injector ramp. 00967 00968 Args: the ramp index (0 to 9 inclusive) 00969 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00970 00971 Returns the ramp time obtained from the GC, in minutes (actually the GC returns a value in units of 0.1 minute, 00972 but this function applies that scaling to the value before returning it). 00973 */ 00974 float GuiLibGraphDataSet::GetPTVRampUpperTime(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00975 { 00976 return ((GetRampValue("GIS", rampIndex, usbDevice, usbHostGC)) * 0.1f); 00977 } 00978 00979 /* 00980 Gets the pressure ramp rate for a particular ramp. 00981 00982 Args: the ramp index (0 to 9 inclusive) 00983 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00984 00985 Returns the pressure ramp rate obtained from the GC, in psi per minute (actually the GC returns a value 00986 in units of 0.01 psi/min, but this function applies that scaling to the value before returning it). 00987 */ 00988 float GuiLibGraphDataSet::GetPressureRampRate(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 00989 { 00990 return ((GetRampValue("GPR", rampIndex, usbDevice, usbHostGC)) * 0.01f); 00991 } 00992 00993 /* 00994 Gets the upper pressure for a particular ramp. 00995 00996 Args: the ramp index (0 to 9 inclusive) 00997 pointers to the USBDeviceConnected and USBHostGC instances that match the GC 00998 00999 Returns the upper pressure obtained from the GC, in psi (actually the GC returns a value 01000 in units of 0.1 psi, but this function applies that scaling to the value before returning it). 01001 */ 01002 float GuiLibGraphDataSet::GetRampUpperPressure(int rampIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 01003 { 01004 return ((GetRampValue("GPU", rampIndex, usbDevice, usbHostGC)) * 0.1f); 01005 } 01006 01007 /* 01008 Setup this dataset to match the column ramp temperature vs time values 01009 currently set up in the GC. 01010 01011 Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC 01012 01013 Returns the number of points in the updated dataset. 01014 */ 01015 int GuiLibGraphDataSet::SetupFromColumnTemperatureRampValues(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 01016 { 01017 // First, delete all existing data from this dataset 01018 ClearData(); 01019 01020 01021 // Now let the AddDataPoint function take care of extending the dataset array, etc 01022 01023 // First point will be at the current temperature, time zero. 01024 // Second point will be at the same temperature, after the initial time. 01025 01026 float initialTemperature = GetColumnTemperature(usbDevice, usbHostGC); 01027 float initialHoldTime = GetInitialHoldTime(usbDevice, usbHostGC); 01028 01029 AddDataPoint(0, initialTemperature); 01030 AddDataPoint(initialHoldTime, initialTemperature); 01031 int pointCount = 2; 01032 01033 // These values are specific to each ramp (index 0 through 9) 01034 float temperatureRampRate; // This is the rate at which the GC will increase the column temperature 01035 // (if this is zero, this ramp is not used, and nor are any after it - 01036 // i.e. the previous ramp was the final one) 01037 float rampUpperTemperature; // This is the upper (or "target") temperature 01038 float rampUpperTime; // Once the column reaches the upper/target temperature, the GC 01039 // will keep it at that temperature for this length of time - 01040 // at the end of this, the next ramp will start (unless this is the last one) 01041 float rampingTime; 01042 01043 float totalMethodTime = initialHoldTime; 01044 01045 float previousRampUpperTemperature = initialTemperature; 01046 01047 // These are the coordinates we add to the graph dataset 01048 float X1, X2; 01049 float Y1, Y2; 01050 01051 #ifdef USE_VERSION_102 // This starts at ramp index 0 - older versions start at 1 01052 for (int rampIndex = 0; rampIndex < 10; ++rampIndex) { 01053 #else 01054 for (int rampIndex = 1; rampIndex < 10; ++rampIndex) { 01055 #endif 01056 temperatureRampRate = GetTemperatureRampRate(rampIndex, usbDevice, usbHostGC); 01057 01058 if(temperatureRampRate <= FLT_MIN) { // Avoid rounding errors - do not test for equality with zero 01059 // No more ramps 01060 break; 01061 } 01062 01063 01064 rampUpperTemperature = GetRampUpperTemperature(rampIndex, usbDevice, usbHostGC); 01065 01066 // Sanity check 01067 if(rampUpperTemperature < previousRampUpperTemperature) { 01068 // This should never happen. We must have got our temperature units confused (or something) - give up 01069 break; 01070 } 01071 01072 rampingTime = (rampUpperTemperature - previousRampUpperTemperature) / temperatureRampRate; 01073 01074 totalMethodTime += rampingTime; 01075 01076 X1 = totalMethodTime; 01077 Y1 = rampUpperTemperature; 01078 01079 if(AddDataPoint(X1, Y1)) { 01080 ++pointCount; 01081 } 01082 01083 01084 rampUpperTime = GetRampUpperTime(rampIndex, usbDevice, usbHostGC); 01085 01086 if(rampUpperTime > FLT_MIN) { // i.e. > 0 (avoid floating point rounding errors) 01087 01088 totalMethodTime += rampUpperTime; 01089 01090 X2 = totalMethodTime; 01091 Y2 = rampUpperTemperature; 01092 01093 if(AddDataPoint(X2, Y2)) { 01094 ++pointCount; 01095 } 01096 } 01097 // 'Else' the ramp upper time is zero - we are not holding here - 01098 // so do not add a second point (AddDataPoint does not allow 01099 // two points at the same X coordinate anyway) 01100 // *** See comment [HOLDING INTERVAL] in SetupGasPressureProfileWithTimingsFromColumnMethod *** 01101 01102 previousRampUpperTemperature = rampUpperTemperature; 01103 } 01104 01105 nonRoundedTotalMethodTime = totalMethodTime; 01106 01107 return pointCount; 01108 } 01109 01110 /* 01111 Setup this dataset to match the ramp pressure vs time values currently set up in the GC. 01112 Compares the time values with those for a specified column method, and (assuming the time values 01113 should match those for the column method) turns on LED 3 if they do not. 01114 01115 Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC 01116 - pointer to a GuiLibGraphDataSet object containing the matching column method 01117 01118 Returns the number of points in the updated dataset. 01119 */ 01120 int GuiLibGraphDataSet::SetupFromPressureRampValues(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, GuiLibGraphDataSet* columnMethodDataSet) 01121 { 01122 // First, delete all existing data from this dataset 01123 ClearData(); 01124 01125 01126 // Now let the AddDataPoint function take care of extending the dataset array, etc 01127 01128 // First point will be at the initial pressure, time zero. 01129 // Second point will be at the same pressure, after the initial time. 01130 float initialPressure = (double) GetInitialPressure(usbDevice, usbHostGC); 01131 float initialHoldTime = (double) GetInitialHoldTime(usbDevice, usbHostGC); 01132 01133 AddDataPoint(0, initialPressure); 01134 AddDataPoint(initialHoldTime, initialPressure); 01135 int pointCount = 2; 01136 01137 // These values are specific to each ramp (index 0 through 9) 01138 float pressureRampRate; // This is the rate at which the GC will increase the gas pressure 01139 // (if this is zero, this ramp is not used, and nor are any after it - 01140 // i.e. the previous ramp was the final one) 01141 float rampUpperPressure; // This is the upper (or "target") pressure 01142 float rampUpperTime; // Once the column reaches the upper/target pressure, the GC 01143 // will keep it at that pressure for this length of time - 01144 // at the end of this, the next ramp will start (unless this is the last one) 01145 float rampingTime; 01146 01147 float totalMethodTime = initialHoldTime; 01148 01149 float previousRampUpperPressure = initialPressure; 01150 01151 // These are the coordinates we add to the graph dataset 01152 float X1, X2; 01153 float Y1, Y2; 01154 01155 // These are the corresponding coordinates for the column method 01156 float columnX1, columnX2; 01157 float columnY1, columnY2; 01158 01159 #ifdef USE_VERSION_102 // This starts at ramp index 0, older versions start at 1 01160 for (int rampIndex = 0; rampIndex < 10; ++rampIndex) { 01161 #else 01162 for (int rampIndex = 1; rampIndex < 10; ++rampIndex) { 01163 #endif 01164 pressureRampRate = GetPressureRampRate(rampIndex, usbDevice, usbHostGC); 01165 01166 if(pressureRampRate <= FLT_MIN) { // Avoid rounding errors - do not test for equality with zero 01167 // No more ramps 01168 break; 01169 } 01170 01171 01172 rampUpperPressure = GetRampUpperPressure(rampIndex, usbDevice, usbHostGC); 01173 01174 // Sanity check 01175 if(rampUpperPressure < previousRampUpperPressure) { 01176 // This should never happen. We must have got our pressure units confused (or something) - give up 01177 break; 01178 } 01179 01180 01181 rampingTime = (rampUpperPressure - previousRampUpperPressure) / pressureRampRate; 01182 01183 totalMethodTime += rampingTime; 01184 01185 // Round the floating point values to the nearest integer 01186 X1 = totalMethodTime; 01187 Y1 = rampUpperPressure; 01188 01189 if(columnMethodDataSet->GetDataPoint(pointCount, &columnX1, &columnY1)) { 01190 if(X1 != columnX1) { 01191 // Our time and the corresponding column method time do not match - 01192 // indicate error by turning on LED 3, and use the column method value 01193 // (the two methods must match) 01194 #ifdef USE_LED_FOR_DEBUGGING 01195 SetLed4(true); 01196 #endif 01197 X1 = columnX1; 01198 } 01199 } 01200 01201 if(AddDataPoint(X1, Y1)) { 01202 ++pointCount; 01203 } 01204 01205 01206 rampUpperTime = GetRampUpperTime(rampIndex, usbDevice, usbHostGC); 01207 01208 totalMethodTime += rampUpperTime; 01209 01210 X2 = totalMethodTime; 01211 Y2 = rampUpperPressure; 01212 01213 if(columnMethodDataSet->GetDataPoint(pointCount, &columnX2, &columnY2)) { 01214 if(X2 != columnX2) { 01215 // Our time and the corresponding column method time do not match - 01216 // indicate error by turning on LED 3, and use the column method value 01217 // (the two methods must match) 01218 #ifdef USE_LED_FOR_DEBUGGING 01219 SetLed4(true); 01220 #endif 01221 X2 = columnX2; 01222 } 01223 } 01224 01225 if(AddDataPoint(X2, Y2)) { 01226 ++pointCount; 01227 } 01228 01229 previousRampUpperPressure = rampUpperPressure; 01230 } 01231 01232 nonRoundedTotalMethodTime = totalMethodTime; 01233 01234 #ifdef USE_LED_FOR_DEBUGGING 01235 // Done with LED 4 01236 SetLed4(false); 01237 #endif 01238 return pointCount; 01239 } 01240 01241 /* 01242 Setup this dataset to give a injector temperature profile that matches a column method. 01243 This will show a constant injector temperature for the same length of time 01244 as the specified column method. 01245 01246 Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC 01247 - pointer to a GuiLibGraphDataSet object containing the matching column method 01248 01249 Returns the number of points in the updated dataset (this will always be 2). 01250 */ 01251 int GuiLibGraphDataSet::SetupInjectorTemperatureProfileToMatchColumnMethod(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, GuiLibGraphDataSet* columnMethodDataSet) 01252 { 01253 // First, delete all existing data from this dataset 01254 ClearData(); 01255 01256 01257 nonRoundedTotalMethodTime = columnMethodDataSet->nonRoundedTotalMethodTime; 01258 01259 // Now let the AddDataPoint function take care of extending the dataset array, etc 01260 01261 // First point will be at the current injector temperature, time zero. 01262 // Second point will be at the same temperature, at the end of the column method time. 01263 01264 float injectorTemperature = GetInjectorTemperature(usbDevice, usbHostGC); 01265 float methodDuration = columnMethodDataSet->GetTotalMethodTime(); 01266 01267 AddDataPoint(0, injectorTemperature); 01268 AddDataPoint(methodDuration, injectorTemperature); 01269 int pointCount = 2; 01270 01271 return pointCount; 01272 } 01273 01274 01275 /* 01276 Setup this dataset to an injector temperature profile derived purely from the 01277 injector/PTV settings in the GC, without reference to the current column method (if any). 01278 01279 Args: pointers to the USBDeviceConnected and USBHostGC instances that match the GC 01280 01281 Returns the number of points in the updated dataset. 01282 */ 01283 int GuiLibGraphDataSet::SetupFromPTVTemperatureRampValues(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) 01284 { 01285 // First, delete all existing data from this dataset 01286 ClearData(); 01287 01288 01289 // Now let the AddDataPoint function take care of extending the dataset array, etc 01290 01291 // First point will be at the current PTV temperature, time zero. 01292 // Second point will be at the same temperature, after the initial time. 01293 // Note that the words "injector" and "PTV" are effectively synonymous 01294 01295 float initialTemperature = GetInjectorTemperature(usbDevice, usbHostGC); 01296 float initialTime = GetPTVInitialTime(usbDevice, usbHostGC); 01297 01298 AddDataPoint(0, initialTemperature); 01299 AddDataPoint(initialTime, initialTemperature); 01300 int pointCount = 2; 01301 01302 // These values are specific to each ramp (index 0 through 9) 01303 float temperatureRampRate; // This is the rate at which the GC will increase the PTV temperature 01304 // (if this is zero, this ramp is not used, and nor are any after it - 01305 // i.e. the previous ramp was the final one) 01306 float rampUpperTemperature; // This is the upper (or "target") temperature 01307 float rampUpperTime; // Once the PTV reaches the upper/target temperature, the GC 01308 // will keep it at that temperature for this length of time - 01309 // at the end of this, the next ramp will start (unless this is the last one) 01310 float rampingTime; 01311 01312 float totalMethodTime = initialTime; 01313 01314 float previousRampUpperTemperature = initialTemperature; 01315 01316 // These are the coordinates we add to the graph dataset 01317 float X1, X2; 01318 float Y1, Y2; 01319 01320 #ifdef USE_VERSION_102 // This starts at ramp index 0 - older versions start at 1 01321 for (int rampIndex = 0; rampIndex < 10; ++rampIndex) { 01322 #else 01323 for (int rampIndex = 1; rampIndex < 10; ++rampIndex) { 01324 #endif 01325 temperatureRampRate = GetPTVTemperatureRampRate(rampIndex, usbDevice, usbHostGC); 01326 01327 if(temperatureRampRate <= FLT_MIN) { // Avoid rounding errors - do not test for equality with zero 01328 // No more ramps 01329 break; 01330 } 01331 01332 01333 rampUpperTemperature = GetPTVRampUpperTemperature(rampIndex, usbDevice, usbHostGC); 01334 01335 // Sanity check 01336 if(rampUpperTemperature < previousRampUpperTemperature) { 01337 // This should never happen. We must have got our temperature units confused (or something) - give up 01338 break; 01339 } 01340 01341 rampingTime = (rampUpperTemperature - previousRampUpperTemperature) / temperatureRampRate; 01342 01343 totalMethodTime += rampingTime; 01344 01345 X1 = totalMethodTime; 01346 Y1 = rampUpperTemperature; 01347 01348 if(AddDataPoint(X1, Y1)) { 01349 ++pointCount; 01350 } 01351 01352 01353 rampUpperTime = GetPTVRampUpperTime(rampIndex, usbDevice, usbHostGC); 01354 01355 if(rampUpperTime > FLT_MIN) { // i.e. > 0 (avoid floating point rounding errors) 01356 01357 totalMethodTime += rampUpperTime; 01358 01359 X2 = totalMethodTime; 01360 Y2 = rampUpperTemperature; 01361 01362 if(AddDataPoint(X2, Y2)) { 01363 ++pointCount; 01364 } 01365 } 01366 // 'Else' the ramp upper time is zero - we are not holding here - 01367 // so do not add a second point (AddDataPoint does not allow 01368 // two points at the same X coordinate anyway) 01369 01370 previousRampUpperTemperature = rampUpperTemperature; 01371 } 01372 01373 nonRoundedTotalMethodTime = totalMethodTime; 01374 01375 return pointCount; 01376 } 01377 01378 /* 01379 Setup this dataset with a gas pressure profile that matches the current column method. 01380 The timings will be taken from the matching column method - which *must* already be set up. 01381 In Constant Pressure mode (first gas ramp rate == 0), the same pressure 01382 will be maintained from start to end of the method, while in Programmed Pressure mode 01383 (first gas ramp rate > 0), the pressure ramps will each start and end at the same times 01384 as the corresponding temperature ramps in the column method. 01385 01386 In both cases, the total duration will be the same as the column method. 01387 01388 Args: - pointers to the USBDeviceConnected and USBHostGC instances that match the GC 01389 - pointer to a GuiLibGraphDataSet object containing the matching column method 01390 01391 Returns the number of points in the updated dataset. 01392 */ 01393 int GuiLibGraphDataSet::SetupGasPressureProfileWithTimingsFromColumnMethod(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, GuiLibGraphDataSet* columnMethodDataSet) 01394 { 01395 // First, delete all existing data from this dataset 01396 ClearData(); 01397 01398 // Now let the AddDataPoint function take care of extending the dataset array, etc 01399 01400 int pointCount; 01401 01402 if(GetPressureRampRate(0, usbDevice, usbHostGC) > FLT_MIN) { // Avoid rounding errors 01403 01404 // Programmed Pressure mode 01405 01406 // First point will be at the initial pressure, time zero. 01407 // Second point will be at the same pressure, after the initial time. 01408 float initialPressure = (double) GetInitialPressure(usbDevice, usbHostGC); 01409 float initialHoldTime = (double) GetInitialHoldTime(usbDevice, usbHostGC); 01410 01411 AddDataPoint(0, initialPressure); 01412 AddDataPoint(initialHoldTime, initialPressure); 01413 pointCount = 2; 01414 01415 // This value is specific to each ramp (index 0 through 9) 01416 float rampUpperPressure; // This is the upper (or "target") pressure 01417 01418 float totalMethodTime = initialHoldTime; 01419 01420 // These are the coordinates we add to the graph dataset. 01421 // X coordinates are time values, Y coordinates are temperatures 01422 // (in the column dataset) or pressures (in this dataset). 01423 // Note that we get our time values from the column dataset - 01424 // we do not calculate them here - we want to make sure they match 01425 float X1, Y1; 01426 float X2, Y2; 01427 01428 #ifdef USE_VERSION_102 // This starts at ramp index 0, older versions start at 1 01429 for (int rampIndex = 0; rampIndex < 10; ++rampIndex) { 01430 #else 01431 for (int rampIndex = 1; rampIndex < 10; ++rampIndex) { 01432 #endif 01433 rampUpperPressure = GetRampUpperPressure(rampIndex, usbDevice, usbHostGC); 01434 01435 if(!columnMethodDataSet->GetDataPoint(pointCount, &X1, &Y1)) { 01436 // We have run out of points in the column method 01437 break; 01438 } 01439 01440 AddDataPoint(X1, rampUpperPressure); 01441 totalMethodTime = X1; 01442 ++pointCount; 01443 01444 if(!columnMethodDataSet->GetDataPoint(pointCount, &X2, &Y2)) { 01445 // We have run out of points in the column method 01446 break; 01447 } 01448 01449 if(Y2 == Y1) { 01450 // Temperatures are the same - this is a non-zero holding interval 01451 AddDataPoint(X2, rampUpperPressure); 01452 totalMethodTime = X2; 01453 ++pointCount; 01454 } 01455 // 'else' we are not holding here - do not add a second point - 01456 // the second point here is actually the end of the next ramp, 01457 // so re-use it for the next ramp (don't increment pointCount, 01458 // so we get the same data point again 'next time round') 01459 // *** See comment [HOLDING INTERVAL] in SetupFromColumnTemperatureRampValues *** 01460 } 01461 01462 nonRoundedTotalMethodTime = totalMethodTime; 01463 01464 } else { 01465 01466 // Constant pressure mode 01467 01468 nonRoundedTotalMethodTime = columnMethodDataSet->nonRoundedTotalMethodTime; 01469 01470 // First point will be at the initial gas pressure, time zero. 01471 // Second point will be at the same pressure, at the end of the column method time. 01472 01473 float initialPressure = (double) GetInitialPressure(usbDevice, usbHostGC); 01474 float methodDuration = columnMethodDataSet->GetTotalMethodTime(); 01475 01476 AddDataPoint(0, initialPressure); 01477 AddDataPoint(methodDuration, initialPressure); 01478 pointCount = 2; 01479 } 01480 01481 return pointCount; 01482 } 01483 01484 /* 01485 Returns the total time expected to be taken by the current method. 01486 (This is effectively the same as the X coordinate of the final datapoint.) 01487 01488 Takes no arguments. 01489 */ 01490 float GuiLibGraphDataSet::GetTotalMethodTime(void) 01491 { 01492 if(graphDataSetActualLength <= 0) { 01493 return 0.0f; 01494 } 01495 01496 // 'else' we have some data 01497 float finalX; 01498 float finalY; 01499 if(GetDataPoint((graphDataSetActualLength - 1), &finalX, &finalY)) { 01500 return (finalX); 01501 } 01502 01503 // 'else' GetDataPoint failed for some reason 01504 return 0.0f; 01505 } 01506 01507 /* 01508 Returns the range of X coordinate values. 01509 01510 Args: pointers to the variables to contain the minimum and maximum X values 01511 (these are integers, as required for an easyGUI graph) 01512 the time unit to be used (minutes or seconds) 01513 01514 No return code. 01515 */ 01516 void GuiLibGraphDataSet::GetXAxisRange(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax, TimeUnit timeUnit) 01517 { 01518 if(graphDataSetActualLength <= 0) { 01519 // We have no data 01520 *xMin = 0; 01521 *xMax = 0; 01522 01523 return; 01524 } 01525 01526 // 'else' we have some data 01527 float X1; 01528 float Y1; 01529 float X2; 01530 float Y2; 01531 GetDataPoint(0, &X1, &Y1); 01532 GetDataPoint((graphDataSetActualLength - 1), &X2, &Y2); 01533 01534 if(timeUnit == SECONDS) { 01535 X1 *= 60.0f; 01536 X2 *= 60.0f; 01537 } 01538 01539 *xMin = (GuiConst_INT32S) floor((double)X1 + 0.5); 01540 *xMax = (GuiConst_INT32S) floor((double)X2 + 0.5); 01541 } 01542 01543 /* 01544 Returns the range of X coordinate values, in units of 0.1 minute. 01545 01546 Args: pointers to the variables to contain the minimum and maximum X values 01547 (these are integers, as required for an easyGUI graph) 01548 01549 No return code. 01550 */ 01551 void GuiLibGraphDataSet::GetXAxisRangeInTenthsOfMinutes(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax) 01552 { 01553 if(graphDataSetActualLength <= 0) { 01554 // We have no data 01555 *xMin = 0; 01556 *xMax = 0; 01557 01558 return; 01559 } 01560 01561 // 'else' we have some data 01562 float X1; 01563 float Y1; 01564 float X2; 01565 float Y2; 01566 GetDataPoint(0, &X1, &Y1); 01567 GetDataPoint((graphDataSetActualLength - 1), &X2, &Y2); 01568 01569 *xMin = (GuiConst_INT32S) (X1 * 10.0f); 01570 *xMax = (GuiConst_INT32S) (X2 * 10.0f); 01571 } 01572 01573 /* 01574 Returns the range of X coordinate values - but rounded to be a whole number of 'ticks' 01575 (i.e. the interval between marks on the X axis). 01576 01577 Args: pointers to the variables to contain the minimum and maximum X values 01578 the tick size 01579 the time unit 01580 01581 No return code. 01582 */ 01583 void GuiLibGraphDataSet::GetXAxisRange(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax, GuiConst_INT32S xTickSize, TimeUnit timeUnit) 01584 { 01585 GuiConst_INT32S rawXMin; 01586 GuiConst_INT32S rawXMax; 01587 01588 GetXAxisRange(&rawXMin, &rawXMax, timeUnit); 01589 01590 RoundAxisRangeByTickSize(xMin, xMax, rawXMin, rawXMax, xTickSize); 01591 } 01592 01593 /* 01594 Returns the range of X coordinate values, in units of 0.1 minute - but rounded to be a whole number of 'ticks' 01595 (i.e. the interval between marks on the X axis). 01596 01597 Args: pointers to the variables to contain the minimum and maximum X values 01598 the tick size 01599 01600 No return code. 01601 */ 01602 void GuiLibGraphDataSet::GetXAxisRangeInTenthsOfMinutes(GuiConst_INT32S *xMin, GuiConst_INT32S *xMax, GuiConst_INT32S xTickSize) 01603 { 01604 GuiConst_INT32S rawXMin; 01605 GuiConst_INT32S rawXMax; 01606 01607 GetXAxisRangeInTenthsOfMinutes(&rawXMin, &rawXMax); 01608 01609 RoundAxisRangeByTickSize(xMin, xMax, rawXMin, rawXMax, xTickSize); 01610 } 01611 01612 /* 01613 Returns the range of Y coordinate values. 01614 01615 Args: pointers to the variables to contain the minimum and maximum Y values 01616 (these are integers, as required for an easyGUI graph) 01617 01618 No return code. 01619 */ 01620 void GuiLibGraphDataSet::GetYAxisRange(GuiConst_INT32S *yMin, GuiConst_INT32S *yMax) 01621 { 01622 if(graphDataSetActualLength <= 0) { 01623 // We have no data 01624 *yMin = 0; 01625 *yMax = 0; 01626 01627 return; 01628 } 01629 01630 // 'else' we have some data 01631 float X; 01632 float Y; 01633 float minYSoFar; 01634 float maxYSoFar; 01635 01636 GetDataPoint(0, &X, &Y); 01637 minYSoFar = Y; 01638 maxYSoFar = Y; 01639 01640 for (int pointIndex = 1; pointIndex < graphDataSetActualLength; ++pointIndex) { 01641 GetDataPoint(pointIndex, &X, &Y); 01642 01643 if(Y < minYSoFar) { minYSoFar = Y; } 01644 if(Y > maxYSoFar) { maxYSoFar = Y; } 01645 } 01646 01647 *yMin = (GuiConst_INT32S) floor((double)minYSoFar + 0.5); 01648 *yMax = (GuiConst_INT32S) floor((double)maxYSoFar + 0.5); 01649 } 01650 01651 /* 01652 Returns the range of Y coordinate values - but rounded to be a whole number of 'ticks' 01653 (i.e. the interval between marks on the Y axis) 01654 01655 Args: pointers to the variables to contain the minimum and maximum X values 01656 the tick size 01657 01658 No return code. 01659 */ 01660 void GuiLibGraphDataSet::GetYAxisRange(GuiConst_INT32S *yMin, GuiConst_INT32S *yMax, GuiConst_INT32S yTickSize) 01661 { 01662 GuiConst_INT32S rawYMin; 01663 GuiConst_INT32S rawYMax; 01664 01665 GetYAxisRange(&rawYMin, &rawYMax); 01666 01667 RoundAxisRangeByTickSize(yMin, yMax, rawYMin, rawYMax, yTickSize); 01668 } 01669 01670 /* 01671 Rounds an axis range to be a whole number of 'ticks' - i.e. the range starts at the last tick below the minimum value, 01672 and ends at the first tick above the maximum value. 01673 01674 Args: pointers to the variables to contain the rounded minimum and maximum 01675 the 'raw' minimum and maximum values 01676 the tick size 01677 */ 01678 void GuiLibGraphDataSet::RoundAxisRangeByTickSize(GuiConst_INT32S *axisMin, GuiConst_INT32S *axisMax, GuiConst_INT32S rawAxisMin, GuiConst_INT32S rawAxisMax, GuiConst_INT32S axisTickSize) 01679 { 01680 if(axisTickSize > 0) { 01681 float fAxisTickSize = (float) axisTickSize; 01682 01683 float fRawAxisMin = (float) rawAxisMin; 01684 float axisTickCountMin = fRawAxisMin / fAxisTickSize; 01685 *axisMin = (GuiConst_INT32S) (fAxisTickSize * (floor (axisTickCountMin))); 01686 01687 float fRawAxisMax = (float) rawAxisMax; 01688 float axisTickCountMax = fRawAxisMax / fAxisTickSize; 01689 *axisMax = (GuiConst_INT32S) (fAxisTickSize * (ceil (axisTickCountMax))); 01690 } else { 01691 *axisMin = rawAxisMin; 01692 *axisMax = rawAxisMax; 01693 } 01694 } 01695 01696 01697 /* 01698 Draws the profile directly to the display, using the GuiLib_VLine function, via the DrawProfileSectionUsingGuiLibVLine function defined in main.cpp. 01699 In general, this will be two colours, with a boundary part way across, to represent how much of the method we have executed, and how much remains. 01700 01701 This is the private version of this function, which takes a boolean value stating whether or not 01702 to record the parameter values. One public version takes explicit values, and tells this function to record them, 01703 while the other takes no parameters, simply passing this function the previously recorded values 01704 (this is so that the caller does not have to keep re-calculating the same values, possibly in more than one place). 01705 01706 Args: the display X coordinate at which to start 01707 the display Y coordinate at which to start 01708 the X and Y scale factors - i.e. the values you have to multiply our X and Y coordinates by, to get display coordinates 01709 the colour of the first section 01710 the colour of the second section 01711 the X coordinate (internal, not display) at which the second section starts. If this is < 0, we draw the entire profile in the first colour 01712 flag saying whether or not to record the parameters 01713 01714 No return code 01715 */ 01716 void GuiLibGraphDataSet::DrawUsingGuiLibVLine(GuiConst_INT16S xLeft, GuiConst_INT16S yBottom, double xScaleFactor, double yScaleFactor, 01717 GuiConst_INTCOLOR firstColour, GuiConst_INTCOLOR secondColour, double xColourBoundary, bool recordParameters) 01718 { 01719 if(theGraphDataSet != NULL) { 01720 01721 GuiConst_INT16S profileSectionLeft; 01722 GuiConst_INT16S profileSectionRight; 01723 01724 GuiConst_INT16S profileSectionTopLeft; 01725 GuiConst_INT16S profileSectionTopRight; 01726 01727 GuiConst_INT16S profileColourBoundary = xLeft + (GuiConst_INT16S)floor(xColourBoundary * xScaleFactor); 01728 01729 for(int graphDataSetIndex = 0; graphDataSetIndex < (graphDataSetActualLength - 1); ++graphDataSetIndex) { 01730 01731 profileSectionLeft = xLeft + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex].X * xScaleFactor); 01732 profileSectionRight = xLeft + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex + 1].X * xScaleFactor); 01733 01734 profileSectionTopLeft = yBottom + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex].Y * yScaleFactor); 01735 profileSectionTopRight = yBottom + (GuiConst_INT16S)floor(theGraphDataSet[graphDataSetIndex + 1].Y * yScaleFactor); 01736 01737 DrawProfileSectionUsingGuiLibVLine(firstColour, secondColour, profileSectionLeft, profileSectionRight, profileColourBoundary, yBottom, profileSectionTopLeft, profileSectionTopRight); 01738 } 01739 } 01740 01741 if(recordParameters) { 01742 DrawUsingGuiLibVLineParameters.isSet = true; 01743 DrawUsingGuiLibVLineParameters.xLeft = xLeft; 01744 DrawUsingGuiLibVLineParameters.yBottom = yBottom; 01745 DrawUsingGuiLibVLineParameters.xScaleFactor = xScaleFactor; 01746 DrawUsingGuiLibVLineParameters.yScaleFactor = yScaleFactor; 01747 DrawUsingGuiLibVLineParameters.firstColour = firstColour; 01748 DrawUsingGuiLibVLineParameters.secondColour = secondColour; 01749 DrawUsingGuiLibVLineParameters.xColourBoundary = xColourBoundary; 01750 } 01751 //&&&&& 01752 } 01753 01754 /* 01755 Call the DrawUsingGuiLibVLine function (see above) with newly-calculated parameter values. 01756 01757 Args: the display X coordinate at which to start 01758 the display Y coordinate at which to start 01759 the X and Y scale factors - i.e. the values you have to multiply our X and Y coordinates by, to get display coordinates 01760 the colour of the first section 01761 the colour of the second section 01762 the X coordinate (internal, not display) at which the second section starts. If this is < 0, we draw the entire profile in the first colour 01763 01764 No return code. 01765 */ 01766 void GuiLibGraphDataSet::DrawUsingGuiLibVLine(GuiConst_INT16S xLeft, GuiConst_INT16S yBottom, double xScaleFactor, double yScaleFactor, GuiConst_INTCOLOR firstColour, GuiConst_INTCOLOR secondColour, double xColourBoundary) 01767 { 01768 DrawUsingGuiLibVLine(xLeft, 01769 yBottom, 01770 xScaleFactor, 01771 yScaleFactor, 01772 firstColour, 01773 secondColour, 01774 xColourBoundary, 01775 true ); 01776 } 01777 01778 /* 01779 Repeats the last call to the version of DrawUsingGuiLibVLine with parameters, 01780 using the same parameter values (if available). This is so that the caller 01781 does not have to repeatedly calculate the same values in more than one place. 01782 */ 01783 void GuiLibGraphDataSet::DrawUsingGuiLibVLine(void) 01784 { 01785 if(DrawUsingGuiLibVLineParameters.isSet) { 01786 DrawUsingGuiLibVLine(DrawUsingGuiLibVLineParameters.xLeft, 01787 DrawUsingGuiLibVLineParameters.yBottom, 01788 DrawUsingGuiLibVLineParameters.xScaleFactor, 01789 DrawUsingGuiLibVLineParameters.yScaleFactor, 01790 DrawUsingGuiLibVLineParameters.firstColour, 01791 DrawUsingGuiLibVLineParameters.secondColour, 01792 DrawUsingGuiLibVLineParameters.xColourBoundary, 01793 false ); 01794 } 01795 // else no values available - do nothing 01796 } 01797 01798 01799 01800 /* 01801 GuiLibGraph constructor - the graph index must be specified by the caller, 01802 and must match the index number specified for the graph in easyGUI 01803 ****************************************************************** 01804 */ 01805 GuiLibGraph::GuiLibGraph(GuiConst_INT8U graphIndex) 01806 { 01807 GuiLib_GraphIndex = graphIndex; 01808 01809 xAxisUnits = MINUTES; 01810 01811 for(int i = 0; i < DATASET_INDEX_COUNT; ++i) { 01812 dataSetDataPtr[i] = NULL; 01813 } 01814 01815 xAxisLabelData.isSet = false; 01816 } 01817 01818 GuiLibGraph::~GuiLibGraph() 01819 { 01820 for(int i = 0; i < DATASET_INDEX_COUNT; ++i) { 01821 if(dataSetDataPtr[i] != NULL) { 01822 delete [] dataSetDataPtr[i]; 01823 } 01824 } 01825 } 01826 01827 01828 void GuiLibGraph::SetXAxisUnits(TimeUnit newXAxisUnits) 01829 { 01830 xAxisUnits = newXAxisUnits; 01831 } 01832 01833 TimeUnit GuiLibGraph::GetXAxisUnits(void) 01834 { 01835 return xAxisUnits; 01836 } 01837 01838 01839 /* 01840 Draws the axes for this graph. 01841 01842 Encapsulates the easyGUI 'GuiLib_Graph_DrawAxes' function, 01843 passing it the index number specified for this graph 01844 01845 Args: None (we already know the index number for this graph) 01846 01847 */ 01848 GuiConst_INT8U GuiLibGraph::DrawAxes(void) 01849 { 01850 if(xAxisUnits == SECONDS) { 01851 GuiLib_Graph_HideXAxis(GuiLib_GraphIndex, MINUTES_XAXIS_INDEX); 01852 GuiLib_Graph_ShowXAxis(GuiLib_GraphIndex, SECONDS_XAXIS_INDEX); 01853 } else { 01854 GuiLib_Graph_ShowXAxis(GuiLib_GraphIndex, MINUTES_XAXIS_INDEX); 01855 GuiLib_Graph_HideXAxis(GuiLib_GraphIndex, SECONDS_XAXIS_INDEX); 01856 } 01857 01858 return GuiLib_Graph_DrawAxes(GuiLib_GraphIndex); 01859 } 01860 01861 /* 01862 Since the units we are using for the time values (0.1 minute) cannot be displayed by the easyGUI graph object, 01863 which works only in integers, we are multiplying our x coordinates by 10 before passing them to the graph. 01864 This means that we cannot let the easyGUI graph label its own X axis, since the values will look incorrect 01865 to the user. We must therefore draw the values ourselves - which is what this function does. 01866 01867 This is the private version of this function, which takes a boolean value stating whether or not 01868 to record the parameter values. One public version takes explicit values, and tells this function to record them, 01869 while the other takes no parameters, simply passing this function the previously recorded values 01870 (this is so that the caller does not have to keep re-calculating the same values, possibly in more than one place). 01871 01872 Args: the start and end X axis values 01873 the tick size (i.e. the interval between successive values 01874 the X and Y coordinates of the graph 01875 the width of the graph 01876 boolean stating whether or not to record the parameter values 01877 */ 01878 void GuiLibGraph::DrawXAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 01879 GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphW, bool recordParameters) 01880 { 01881 int intervalCount = (maxValue - minValue) / tickSize; 01882 01883 GuiConst_INT16S xCoord = graphX + 10; // 10 is empirical - relative to the graph X coordinate, it puts the labels in the correct place 01884 GuiConst_INT16S xCoordInterval = graphW / intervalCount; 01885 01886 GuiConst_INT16S yCoord = graphY + 15; // 15 is also empirical - relative to the graph Y coordinate, it puts the labels in the correct place 01887 01888 const GuiConst_INT16U fontNo = GuiFont_Helv20Bold; 01889 char text[100]; 01890 01891 for(GuiConst_INT32S xValue = minValue; xValue <= maxValue; xValue += tickSize) { 01892 sprintf(text, "%d", xValue); 01893 01894 GuiLib_DrawStr( 01895 xCoord, //GuiConst_INT16S X, 01896 yCoord, //GuiConst_INT16S Y, 01897 fontNo, //GuiConst_INT16U FontNo, 01898 text, //GuiConst_TEXT PrefixLocate *String, 01899 GuiLib_ALIGN_CENTER, //GuiConst_INT8U Alignment, 01900 GuiLib_PS_ON, //GuiConst_INT8U PsWriting, 01901 GuiLib_TRANSPARENT_ON, //GuiConst_INT8U Transparent, 01902 GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 01903 0, //GuiConst_INT16S BackBoxSizeX, 01904 0, //GuiConst_INT16S BackBoxSizeY1, 01905 0, //GuiConst_INT16S BackBoxSizeY2, 01906 GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, 01907 0, //GuiConst_INTCOLOR ForeColor, 01908 0xFFFF //GuiConst_INTCOLOR BackColor 01909 ); 01910 01911 xCoord += xCoordInterval; 01912 } 01913 01914 if(recordParameters) { 01915 xAxisLabelData.isSet = true; 01916 xAxisLabelData.minValue = minValue; 01917 xAxisLabelData.maxValue = maxValue; 01918 xAxisLabelData.tickSize = tickSize; 01919 xAxisLabelData.graphX = graphX; 01920 xAxisLabelData.graphY = graphY; 01921 xAxisLabelData.graphW = graphW; 01922 } 01923 } 01924 01925 /* 01926 Call the DrawXAxisLabels function (see above) with newly-calculated parameter values. 01927 01928 Args: the start and end X axis values 01929 the tick size (i.e. the interval between successive values 01930 the X and Y coordinates of the graph 01931 the width of the graph 01932 */ 01933 void GuiLibGraph::DrawXAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 01934 GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphW) 01935 { 01936 DrawXAxisLabels(minValue, 01937 maxValue, 01938 tickSize, 01939 graphX, 01940 graphY, 01941 graphW, 01942 true ); 01943 } 01944 01945 /* 01946 Repeats the last call to the version of DrawXAxisLabels with parameters, 01947 using the same parameter values (if available). This is so that the caller 01948 does not have to repeatedly calculate the same values in more than one place. 01949 */ 01950 void GuiLibGraph::DrawXAxisLabels(void) 01951 { 01952 if(xAxisLabelData.isSet) { 01953 DrawXAxisLabels(xAxisLabelData.minValue, 01954 xAxisLabelData.maxValue, 01955 xAxisLabelData.tickSize, 01956 xAxisLabelData.graphX, 01957 xAxisLabelData.graphY, 01958 xAxisLabelData.graphW, 01959 false ); 01960 } 01961 // else no values available - do nothing 01962 } 01963 01964 01965 /* 01966 Similarly to the X axis, we deliberately pass values that are larger than the real values to the Y axis. 01967 This is to avoid the 'stepped' appearance that the profile can have for methods with a small range of values in Y. 01968 This means that we cannot let the easyGUI graph label its own Y axis, since the values will look incorrect 01969 to the user. We must therefore draw the values ourselves - which is what this function does. 01970 01971 This is the private version of this function, which takes a boolean value stating whether or not 01972 to record the parameter values. One public version takes explicit values, and tells this function to record them, 01973 while the other takes no parameters, simply passing this function the previously recorded values 01974 (this is so that the caller does not have to keep re-calculating the same values, possibly in more than one place). 01975 01976 Args: the start and end Y axis values 01977 the tick size (i.e. the interval between successive values 01978 the X and Y coordinates of the graph 01979 the height of the graph 01980 boolean stating whether or not to record the parameter values 01981 */ 01982 void GuiLibGraph::DrawYAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 01983 GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphH, bool recordParameters) 01984 { 01985 int intervalCount = (maxValue - minValue) / tickSize; 01986 01987 GuiConst_INT16S xCoord = graphX; // GuiLib_ALIGN_RIGHT means that the text ends at the X coordinate 01988 01989 GuiConst_INT16S yCoord = graphY - 2; // 2 is also empirical - relative to the graph Y coordinate, it puts the labels in the correct place 01990 GuiConst_INT16S yCoordInterval = (graphH / intervalCount) - 2; // 2 is also empirical - otherwise, the labels get out of step with the Y axis ticks as they go up the graph 01991 01992 const GuiConst_INT16U fontNo = GuiFont_Helv20Bold; 01993 char text[100]; 01994 01995 for(GuiConst_INT32S xValue = minValue; xValue <= maxValue; xValue += tickSize) { 01996 sprintf(text, "%d", xValue); 01997 01998 GuiLib_DrawStr( 01999 xCoord, //GuiConst_INT16S X, 02000 yCoord, //GuiConst_INT16S Y, 02001 fontNo, //GuiConst_INT16U FontNo, 02002 text, //GuiConst_TEXT PrefixLocate *String, 02003 GuiLib_ALIGN_RIGHT, //GuiConst_INT8U Alignment, 02004 GuiLib_PS_ON, //GuiConst_INT8U PsWriting, 02005 GuiLib_TRANSPARENT_ON, //GuiConst_INT8U Transparent, 02006 GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 02007 0, //GuiConst_INT16S BackBoxSizeX, 02008 0, //GuiConst_INT16S BackBoxSizeY1, 02009 0, //GuiConst_INT16S BackBoxSizeY2, 02010 GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, 02011 0, //GuiConst_INTCOLOR ForeColor, 02012 0xFFFF //GuiConst_INTCOLOR BackColor 02013 ); 02014 02015 yCoord -= yCoordInterval; 02016 } 02017 02018 if(recordParameters) { 02019 yAxisLabelData.isSet = true; 02020 yAxisLabelData.minValue = minValue; 02021 yAxisLabelData.maxValue = maxValue; 02022 yAxisLabelData.tickSize = tickSize; 02023 yAxisLabelData.graphX = graphX; 02024 yAxisLabelData.graphY = graphY; 02025 yAxisLabelData.graphH = graphH; 02026 } 02027 } 02028 02029 /* 02030 Call the DrawYAxisLabels function (see above) with newly-calculated parameter values. 02031 02032 Args: the start and end Y axis values 02033 the tick size (i.e. the interval between successive values 02034 the X and Y coordinates of the graph 02035 the height of the graph 02036 */ 02037 void GuiLibGraph::DrawYAxisLabels(GuiConst_INT32S minValue, GuiConst_INT32S maxValue, GuiConst_INT32S tickSize, 02038 GuiConst_INT16S graphX, GuiConst_INT16S graphY, GuiConst_INT16S graphH) 02039 { 02040 DrawYAxisLabels(minValue, 02041 maxValue, 02042 tickSize, 02043 graphX, 02044 graphY, 02045 graphH, 02046 true ); 02047 } 02048 02049 /* 02050 Repeats the last call to the version of DrawYAxisLabels with parameters, 02051 using the same parameter values (if available). This is so that the caller 02052 does not have to repeatedly calculate the same values in more than one place. 02053 */ 02054 void GuiLibGraph::DrawYAxisLabels(void) 02055 { 02056 if(yAxisLabelData.isSet) { 02057 DrawYAxisLabels(yAxisLabelData.minValue, 02058 yAxisLabelData.maxValue, 02059 yAxisLabelData.tickSize, 02060 yAxisLabelData.graphX, 02061 yAxisLabelData.graphY, 02062 yAxisLabelData.graphH, 02063 false ); 02064 } 02065 // else no values available - do nothing 02066 } 02067 02068 02069 /* 02070 Draws the specified data set for this graph. 02071 02072 Encapsulates the easyGUI 'GuiLib_Graph_DrawDataSet' function, 02073 passing it the index number specified for this graph, 02074 and the data set index number given by the caller. 02075 02076 Note that a data set with that index must already have been created 02077 in easyGUI for this graph. 02078 02079 Args: index number of the data set 02080 02081 */ 02082 GuiConst_INT8U GuiLibGraph::DrawDataSet(GuiConst_INT8U dataSetIndex) 02083 { 02084 return GuiLib_Graph_DrawDataSet(GuiLib_GraphIndex, dataSetIndex); 02085 } 02086 02087 /* 02088 Draws the specified data point in the specified data set for this graph. 02089 02090 Encapsulates the easyGUI 'GuiLib_Graph_DrawDataPoint' function, 02091 passing it the index number specified for this graph, 02092 and the data set and data point index numbers given by the caller. 02093 02094 Note that a data set with that index must already have been created 02095 in easyGUI for this graph. 02096 02097 Args: index number of the data set 02098 index number of the data point in that set 02099 02100 */ 02101 GuiConst_INT8U GuiLibGraph::DrawDataPoint(GuiConst_INT8U dataSetIndex, GuiConst_INT16U dataPointIndex) 02102 { 02103 return GuiLib_Graph_DrawDataPoint(GuiLib_GraphIndex, dataSetIndex, dataPointIndex); 02104 } 02105 02106 /* 02107 Shows the specified data set for this graph. 02108 02109 Encapsulates the easyGUI 'GuiLib_Graph_ShowDataSet' function, 02110 passing it the index number specified for this graph, 02111 and the data set index number given by the caller. 02112 02113 Note that a data set with that index must already have been created 02114 in easyGUI for this graph. 02115 02116 Args: index number of the data set 02117 02118 */ 02119 GuiConst_INT8U GuiLibGraph::ShowDataSet(GuiConst_INT8U dataSetIndex) 02120 { 02121 return GuiLib_Graph_ShowDataSet(GuiLib_GraphIndex, dataSetIndex); 02122 } 02123 02124 /* 02125 Hides the specified data set for this graph. 02126 02127 Encapsulates the easyGUI 'GuiLib_Graph_HideDataSet' function, 02128 passing it the index number specified for this graph, 02129 and the data set index number given by the caller. 02130 02131 Note that a data set with that index must already have been created 02132 in easyGUI for this graph. 02133 02134 Args: index number of the data set 02135 02136 */ 02137 GuiConst_INT8U GuiLibGraph::HideDataSet(GuiConst_INT8U dataSetIndex) 02138 { 02139 return GuiLib_Graph_HideDataSet(GuiLib_GraphIndex, dataSetIndex); 02140 } 02141 02142 /* 02143 Sets the range of the X axis for this graph. 02144 02145 Encapsulates the easyGUI 'GuiLib_Graph_SetXAxisRange' function, 02146 passing it the index number specified for this graph, 02147 the X axis index number (this depends on the selected units), 02148 and the minimum and maximum values given by the caller. 02149 02150 Args: minimum value for the range 02151 maximum value for the range 02152 02153 */ 02154 GuiConst_INT8U GuiLibGraph::SetXAxisRange(GuiConst_INT32S minValue, GuiConst_INT32S maxValue) 02155 { 02156 #define BOTH_AT_ONCE 02157 #ifdef BOTH_AT_ONCE 02158 // The graph coordinates seem to relate to X axis zero, regardless of which one we are actually displaying - 02159 // so set both at once 02160 if(GuiLib_Graph_SetXAxisRange(GuiLib_GraphIndex, MINUTES_XAXIS_INDEX, minValue, maxValue) == 0) { 02161 // Failed 02162 return 0; 02163 } 02164 02165 // 'else' OK 02166 return GuiLib_Graph_SetXAxisRange(GuiLib_GraphIndex, SECONDS_XAXIS_INDEX, minValue, maxValue); 02167 #else 02168 GuiConst_INT8U xAxisIndex = (xAxisUnits == SECONDS) ? SECONDS_XAXIS_INDEX : MINUTES_XAXIS_INDEX; 02169 02170 return GuiLib_Graph_SetXAxisRange(GuiLib_GraphIndex, xAxisIndex, minValue, maxValue); 02171 #endif 02172 } 02173 02174 /* 02175 Sets the range of the Y axis for this graph. 02176 02177 Encapsulates the easyGUI 'GuiLib_Graph_SetYAxisRange' function, 02178 passing it the index number specified for this graph, 02179 the Y axis index number (currently always zero), 02180 and the minimum and maximum values for its range given by the caller. 02181 02182 Args: minimum value for the range 02183 maximum value for the range 02184 02185 */ 02186 GuiConst_INT8U GuiLibGraph::SetYAxisRange(GuiConst_INT32S minValue, GuiConst_INT32S maxValue) 02187 { 02188 GuiConst_INT8U yAxisIndex = YAXIS_INDEX; 02189 02190 return GuiLib_Graph_SetYAxisRange(GuiLib_GraphIndex, yAxisIndex, minValue, maxValue); 02191 } 02192 02193 /* 02194 Sets the data for the specified data set. 02195 02196 Args: the dataset index (this must already have been created in easyGUI) 02197 (a pointer to) a GuiLibGraphDataSet object containing the new data 02198 the time unit (minutes or seconds) to be used 02199 02200 Note that we reject dataset index values outside the range 0-9 02201 (since we need to keep a pointer to the memory used, and currently 02202 we only allow a fixed number of them). 02203 02204 */ 02205 GuiConst_INT8U GuiLibGraph::SetDataForGraphDataSet(GuiConst_INT8U dataSetIndex, GuiLibGraphDataSet* dataSet, TimeUnit timeUnit) 02206 { 02207 /* 02208 This is a copy of the declaration of 'GuiLib_Graph_AddDataSet' in GuiLib.h: 02209 02210 extern GuiConst_INT8U GuiLib_Graph_AddDataSet( 02211 GuiConst_INT8U GraphIndex, 02212 GuiConst_INT8U DataSetIndex, 02213 GuiConst_INT8U XAxisIndex, 02214 GuiConst_INT8U YAxisIndex, 02215 GuiLib_GraphDataPoint *DataPtr, 02216 GuiConst_INT16U DataSize, 02217 GuiConst_INT16U DataCount, 02218 GuiConst_INT16U DataFirst); 02219 02220 We supply either default values for these parameters, 02221 or get them from the GuiLibGraphDataSet object. 02222 */ 02223 02224 if(dataSetIndex < DATASET_INDEX_COUNT) { 02225 // Make sure this data set, on this graph, has only the data we are about to 'add' to it - 02226 // get rid of any existing data 02227 GuiLib_Graph_RemoveDataSet(GuiLib_GraphIndex, dataSetIndex); 02228 02229 if(dataSetDataPtr[dataSetIndex] != NULL) { 02230 delete [] dataSetDataPtr[dataSetIndex]; 02231 } 02232 02233 GuiConst_INT16U dataSize; 02234 GuiLib_GraphDataPoint* dataCopy = dataSet->GetGraphDataPointCopy(timeUnit, &dataSize); 02235 02236 if(dataCopy != NULL) { 02237 // Although the data is passed to us in an array allocated by the GuiLibGraphDataSet object, 02238 // we keep a pointer to it in our own array, so that we can delete it when it is no longer required. 02239 dataSetDataPtr[dataSetIndex] = dataCopy; 02240 02241 // I think this easyGUI function has a misleading name - you cannot 'add' a dataset with this function - 02242 // you can only specify the data for a dataset that already exists 02243 return GuiLib_Graph_AddDataSet(GuiLib_GraphIndex, dataSetIndex, 0, 0, dataSetDataPtr[dataSetIndex], dataSize, dataSize, 0); 02244 } 02245 // 'else' copy failed - drop through to error return 02246 } 02247 02248 // 'else' dataset index out of range, or copy failed (see above) 02249 return 0; 02250 } 02251 02252 02253 /* 02254 Sets the data for the specified data set, using tenths of minutes as the X axis unit 02255 02256 Args: the dataset index (this must already have been created in easyGUI) 02257 (a pointer to) a GuiLibGraphDataSet object containing the new data 02258 02259 Note that we reject dataset index values outside the range 0-9 02260 (since we need to keep a pointer to the memory used, and currently 02261 we only allow a fixed number of them). 02262 02263 */ 02264 GuiConst_INT8U GuiLibGraph::SetDataForGraphDataSetInTenthsOfMinutes(GuiConst_INT8U dataSetIndex, float yAxisScaleFactor, GuiLibGraphDataSet* dataSet) 02265 { 02266 /* 02267 This is a copy of the declaration of 'GuiLib_Graph_AddDataSet' in GuiLib.h: 02268 02269 extern GuiConst_INT8U GuiLib_Graph_AddDataSet( 02270 GuiConst_INT8U GraphIndex, 02271 GuiConst_INT8U DataSetIndex, 02272 GuiConst_INT8U XAxisIndex, 02273 GuiConst_INT8U YAxisIndex, 02274 GuiLib_GraphDataPoint *DataPtr, 02275 GuiConst_INT16U DataSize, 02276 GuiConst_INT16U DataCount, 02277 GuiConst_INT16U DataFirst); 02278 02279 We supply either default values for these parameters, 02280 or get them from the GuiLibGraphDataSet object. 02281 */ 02282 02283 if(dataSetIndex < DATASET_INDEX_COUNT) { 02284 // Make sure this data set, on this graph, has only the data we are about to 'add' to it - 02285 // get rid of any existing data 02286 GuiLib_Graph_RemoveDataSet(GuiLib_GraphIndex, dataSetIndex); 02287 02288 if(dataSetDataPtr[dataSetIndex] != NULL) { 02289 delete [] dataSetDataPtr[dataSetIndex]; 02290 } 02291 02292 GuiConst_INT16U dataSize; 02293 GuiLib_GraphDataPoint* dataCopy = dataSet->GetGraphDataPointCopyInTenthsOfMinutes(yAxisScaleFactor, &dataSize); 02294 02295 if(dataCopy != NULL) { 02296 // Although the data is passed to us in an array allocated by the GuiLibGraphDataSet object, 02297 // we keep a pointer to it in our own array, so that we can delete it when it is no longer required. 02298 dataSetDataPtr[dataSetIndex] = dataCopy; 02299 02300 // I think this easyGUI function has a misleading name - you cannot 'add' a dataset with this function - 02301 // you can only specify the data for a dataset that already exists 02302 return GuiLib_Graph_AddDataSet(GuiLib_GraphIndex, dataSetIndex, 0, 0, dataSetDataPtr[dataSetIndex], dataSize, dataSize, 0); 02303 } 02304 // 'else' copy failed - drop through to error return 02305 } 02306 02307 // 'else' dataset index out of range, or copy failed (see above) 02308 return 0; 02309 } 02310 02311 02312 /* 02313 Sets the specified data set to consist of a single point. 02314 02315 Args: the dataset index (this must already have been created in easyGUI) 02316 the X coordinate of the point 02317 the Y coordinate of the point 02318 */ 02319 GuiConst_INT8U GuiLibGraph::SetSinglePointForGraphDataSet(GuiConst_INT8U dataSetIndex, GuiConst_INT32S X, GuiConst_INT32S Y) 02320 { 02321 // return GuiLib_Graph_AddDataPoint(GuiLib_GraphIndex, dataSetIndex, X, Y); 02322 02323 /* 02324 This is a copy of the declaration of 'GuiLib_Graph_AddDataSet' in GuiLib.h: 02325 02326 extern GuiConst_INT8U GuiLib_Graph_AddDataSet( 02327 GuiConst_INT8U GraphIndex, 02328 GuiConst_INT8U DataSetIndex, 02329 GuiConst_INT8U XAxisIndex, 02330 GuiConst_INT8U YAxisIndex, 02331 GuiLib_GraphDataPoint *DataPtr, 02332 GuiConst_INT16U DataSize, 02333 GuiConst_INT16U DataCount, 02334 GuiConst_INT16U DataFirst); 02335 02336 We supply either default values for these parameters, 02337 or get them from the values passed to us. 02338 */ 02339 GuiLib_GraphDataPoint dataPoint; 02340 dataPoint.X = X; 02341 dataPoint.Y = Y; 02342 02343 // I think this easyGUI function has a misleading name - you cannot 'add' a dataset with this function - 02344 // you can only specify the data for a dataset that already exists 02345 return GuiLib_Graph_AddDataSet(GuiLib_GraphIndex, dataSetIndex, 0, 0, &dataPoint, 10, 1, 0); 02346 } 02347 02348 /* 02349 Redraws this graph (by calling the easyGUI GuiLib_Graph_Redraw function). 02350 This redraws the entire graph, including background, axes and data sets. 02351 */ 02352 GuiConst_INT8U GuiLibGraph::Redraw(void) 02353 { 02354 return GuiLib_Graph_Redraw(GuiLib_GraphIndex); 02355 }
Generated on Tue Jul 19 2022 00:31:07 by
1.7.2