Monday, September 15, 2014

FTC Software Toolbox - Rise Edge / Fall Edge

What is your favorite software tool? This post describes some of the tried and true software tools that I have found useful. I would love to hear from you about tools you find useful. Please put a comment at the bottom of this post with a description or a pointer to tools you find useful.

Today we will look at a utility that is very useful to filter button pushes and state machine events. This utility finds the rising edge or a falling edge on a signal.

// Story: As a software designer I want to find the rising edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEqF is initialized equal to false
//   bool globalBoolEqF = false; //global
//   outBool = RiseEdge( inBool, globalBoolEqF );
// Author: David Lempia at GeekMyBot.Blogspot.com
bool RiseEdge(bool in, bool &z1)
{
  bool out;

  out=false;
  if ((in==true) && (z1==false)) out=true;
  z1=in;

  return(out);
}

#define TEST_RISE_EDGE
#ifdef TEST_RISE_EDGE //-------------------------------------------------
// Add this code to a while loop in main
// Unit Test
// [x] Rise Edge of input results in a true output for 1 frame
// [x] Fall Edge of input results in no change to the output
// [x] True input for more than one frame results in a false output
// [x] False input results in a false output
#include "i_debug.c"

task main(){
  bool z=false;
  bool out,in=false;

  for (int i=0; i<10; i++){

    if (i<2) in=false;
    else if (i<4) in=true;
    else if (i<6) in=false;
    else if (i<8) in=true;

    out=RiseEdge(in, z); //Code to test

    DebugBool("In",in);
    DebugBool("Out",out);
    DebugPrint();
  }
}
#endif


Here is the watch window (tabular format) for the tests I ran on a RiseEdge function:



Here is the plot version for the tests I ran on a RiseEdge function:



Whenever the input signal transitions from a false to a true, the output goes true for one frame. This is a great way to trigger an event.
Likewise, here is the software for a falling edge:

// Story: As a software designer I want to find the falling edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEq0 is initialized equal to false
// outBool = RiseEdge( inBool, globalBoolEqF );
// Author: David Lempia at GeekMyBot.Blogspot.com
bool FallEdge(bool in, bool &z1)
{
 bool out;

 out=false;
  if ((in==false) && (z1==true)) out=true;
  z1=in;

 return(out);
}
#define TEST_FALL_EDGE
#ifdef TEST_FALL_EDGE //-------------------------------------------------
// Add this code to a while loop in main
// Unit Test
// [x] Fall Edge of input results in a true output for 1 frame
// [x] Rise Edge of input results in no change to the output
// [x] True input for more than one frame results in a false output
// [x] False input results in a false output
#include "i_debug.c"

task main(){
  bool z=false;
  bool out,in=false;

  for (int i=0; i<10; i++){

    if (i<2) in=false;
    else if (i<4) in=true;
    else if (i<6) in=false;
    else if (i<8) in=true;

    out=FallEdge(in, z); //Code to test

    DebugBool("In",in);
    DebugBool("Out",out);
    DebugPrint();
  }
}
#endif


Here is the resulting output
Let me know if you find this function useful.

Thursday, September 4, 2014

FTC Software Toolbox - Debug

What is your favorite software tool? This post describes some of the tried and true software tools that I have found useful. I would love to hear from you about tools you find useful. Please put a comment at the bottom of this post with a description or a pointer to tools you find useful.

Today we will look at my favorite module, Debug. Of course, setting breakpoints and debugging in Robot C is amazing. Sometimes, however, it is helpful to look at how the program is running using something less intrusive than Robot C debug and more friendly than a print statement. This debug program runs in the background. Results can be viewed in tabular format and as a time-history plot when the robot is reconnected to the PC and Robot C.

#pragma DebuggerWindows("debugstream")
// Story - As a software debugger, I want to see a descriptive string and a variable so that I can understand
//    what my software is doing.
// Usage - To use this software,
//   1. DebugSkip(4); Print out every 5th value. Use any number you want. The default is 0 (no skips).
//   2. Use DebugInt, DebugFloat, or DebugBool to collect the debug information.
//   3. Place the DebugPrint(); after all the debugInt calls. (Takes 10ms or less to run).
//
//   The data is output to the debug stream and to a file called "debug.txt"
//   The data going to the file is limited to 10000 characters. This is about 151 rows with 10 columns.
//
//  Written by David Lempia
//
#define DBG_MAX_COL 10
string debugColName[10];
string debugValue[10];
int debugI=0;
int debugSkip=0;
int debugSkipI=0;
int iFrameCntDebug=0;

short DebugFile;
TFileIOResult isOk;
int nFileSize=10000;
string debugFileName="debug.txt";

// ---------------- Capture integer debug information ---------------------//
void DebugInt(const string name, int value){
  string tmp;
  if (debugI < DBG_MAX_COL){
    if (iFrameCntDebug==0){
      strncat(tmp,name,5);
      sprintf(debugColName[debugI],"%5s",tmp);
    }
    sprintf(debugValue[debugI++],"%5d",value);
  }
  return;
}
// ---------------- Capture float debug information ---------------------//
void DebugFloat(const string name, float value){
  string tmp;
  if (debugI < DBG_MAX_COL){
    if (iFrameCntDebug==0){
      strncat(tmp,name,5);
      sprintf(debugColName[debugI],"%5s",tmp);
    }
    sprintf(debugValue[debugI++],"% 2.2f",value);
  }
  return;
}
// ---------------- Capture boolean debug information ---------------------//
void DebugBool(const string name, bool value){
  string tmp;
  if (debugI < DBG_MAX_COL){
    if (iFrameCntDebug==0){
      strncat(tmp,name,5);
      sprintf(debugColName[debugI],"%5s",tmp);
    }
    if (value==true)
      debugValue[debugI++]="    T";
    else
      debugValue[debugI++]="    F";
  }
  return;
}

// Set the number of times the printout is skipped (Default is to print every time)
void DebugSkip(int skip){
  debugSkip=skip;
}

// Send the print data to the debug window and the debug file "debug.txt"
void DebugPrint(){
  char str[70];
  string tmp1,tmp2;
  int i=0;

  if (debugI>0){// Do not send out Debug info if not needed
    if (iFrameCntDebug==0){
      //writeDebugStream("col size %d", debugI);
      Delete(debugFileName, isOk);
      OpenWrite(DebugFile, isOk, debugFileName, nFileSize);

      strcpy(str,"Frame,");
      for (i=0; i < (debugI-1); ++i){
        strcat(str,debugColName[i]);
        strcat(str,",");
      }
      strcat(str,debugColName[i]);
      strcat(str, "\n");

      writeDebugStream(str);
      WriteString(DebugFile, isOk, str);
    }

    if (debugSkip < debugSkipI++){
      strcpy(str,"");
      sprintf(str,"%5d,",iFrameCntDebug);
      for (i=0; i < (debugI-1); ++i){
        strcat(str,debugValue[i]);
        strcat(str,",");
      }
      strcat(str,debugValue[i]);
      strcat(str,"\n");

      writeDebugStream(str);
      WriteString(DebugFile, isOk, str);

      debugSkipI=0;
    }
    iFrameCntDebug++;
    debugI=0; //Clear the debug buffer
  }
}
//#define TEST_DEBUG //-------------------------------------------------
#ifdef TEST_DEBUG
// Unit Test
// [x] Normal test
// [x] To many debugInt(3) calls
// [x] No calls to debugInt() - No output should happen.
// [x] Skip number is right
// [x] The name is longer than 5 characters. Should be limited to 5.
// [x] Output to file and debug stream match
// [x] Test max file size (File output ends without issue when the max file is hit)
// [x] Test worst case time. It takes between 3 and 10 ms to output all 10 columns of data.
task main(){
  int tmp=0;
  //DebugSkip(2); //

  ClearTimer(T2);

  for (int i=0; i < 10; i++){
    DebugInt("r1",time1[T2]);
    DebugInt("r2",time1[T2]);
    DebugInt("r3",time1[T2]);
    DebugInt("r4",time1[T2]);
    DebugInt("r5",time1[T2]);
    DebugInt("r6",time1[T2]);
    DebugInt("r7",time1[T2]);
    DebugInt("r8",time1[T2]);
    DebugInt("r9",time1[T2]);
    DebugInt("r10",time1[T2]);
    DebugInt("r11",time1[T2]);

    /*DebugInt("1234567",tmp++);
    DebugFloat("r2",2.5);
    DebugBool("r3",false);*/

    //-----------------Print the debug statements out------------------//
    DebugPrint();
  }
}
#endif

Correct usage of the debug software requires a minimum of two function calls, a DebugInt(), DebugFloat(), or DebugBool() (i.e. watch functions) to capture the watch information, and a DebugPrint() to send the print information out to the debug window and the debug file. Up to 10 watch variables can be watched at a time.

Use DebugInt, DebugFloat, or DebugBool function calls to watch variables in the software. Be careful to place the function calls in a place where they will be called on every itteration. If the calls are inside an if-then-else and are not called on an itteration, the past value will be printed out.

Place DebugPrint() in a location that is called after all of the watch functions are called. That is all there is to it. Here is the watch window (tabular format) for the tests I ran on a RiseEdge function:



Here is the plot version for the tests I ran on a RiseEdge function:


To plot this file, I copied the file off of the NXT using Robot C the main menu-> Robot -> NXT Brick -> File Management Utility. I then opened the file in MS Excel and used a line plot.

Let me know if you find this function useful.