This is an example program that actually allows the car to race using the FRDM-TFC library!

Dependencies:   FRDM-TFC

Fork of TFC-RACING-DEMO by Daniel Hadad

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Spices.cpp Source File

Spices.cpp

00001 #include "mbed.h"
00002 
00003 #include "Spices.h"
00004 #include "common.h"
00005 #include "TFC.h"
00006 
00007 // camera params
00008 #define NUM_LINE_SCAN 128
00009 #define MAX_LINE_SCAN NUM_LINE_SCAN-1
00010 #define MIN_LINE_WIDTH  0
00011 #define MAX_LINE_WIDTH  15
00012 #define FILTER_ENDS 0                              // # of pixels at end of camera data to ignore; set to 0 for now, later make 15
00013 #define RANGE (NUM_LINE_SCAN - (2 * FILTER_ENDS))  // range of camera pixels to consider
00014 #define ERR_RATIO 0.85                             // ratio of max possible error to pixels (have to measure!)
00015 #define DER_RATIO 0.5                              // ratio for der threshold level (was 0.5 initially, may put back)
00016 
00017 // steer/servo params
00018 #define MAX_STEER_LEFT -0.51                       // value determined by demo mode 1 measure (have to be adjusted with every servo horn attach)
00019 #define MAX_STEER_RIGHT 0.39                       // value determined by demo mode 1 measure
00020 #define DT 0.02                                    // # MS of time between intervals (doesn't really matter)
00021 
00022 // logging parameters
00023 #define NUM_LOG_FRAMES 700                         // # of frames to log (when logging active) ~14 sec worth!
00024 
00025 // ******  for debug tuning   ******
00026 #define TUNE_SPEED 0.7
00027 #define TUNE_KP 0.008
00028 #define TUNE_KI 0
00029 #define TUNE_KD 0
00030 #define MIN_POWER 60                               // percent min power (estimating for a 2-ft turn => 24" / (24" + 6" car) = 4/5; speed of inner wheel is 20% lower worst case
00031 #define SPEED_ADJUST 4
00032 #define ABS_ERROR_THRESH 10                        // number of pixels line position offset before changing KP value
00033 #define CONTROL_METHOD 2                           // which control method to use
00034 
00035 
00036 // Drive/motor params
00037 // 0.4 way too slow!!  need to charge battery
00038 #define SUB_LIGHT_SPEED 0.5                        // moderate speed (value 0 to 1 sent to motors)
00039 #define LIGHT_SPEED 0.6                            // fast...
00040 #define RIDICULOUS_SPEED 0.7                       // faster...
00041 #define LUDICROUS_SPEED 0.9                        // faster still!
00042 #define MAX_POWER 100                              // percent max power (for speed adjustments)
00043 
00044 // algo params
00045 #define UNKNOWN_COUNT_MAX  50                      // max value to allow for unknown track conditions before killing engine
00046 #define STARTGATEFOUNDMAX  0                       // max value to allow for finding starting gate before killing engine
00047 #define STARTGATEDELAY     50                      // Delay before searching for starting gate to kill engine
00048 
00049 #define MMA8451_I2C_ADDRESS (0x1d<<1)              // address for accelerometer?
00050 
00051 
00052 /* CAR INTERFACE
00053 
00054   DIP SWITCH:
00055   -----------------------------------------------------------------
00056   1 - ON: Run MCP below; OFF: Run Demo program (see main.cpp)
00057   2 - ON: Log frame data to array
00058   3 - ON: Risky race option; OFF: Conservative race option
00059   4 - ON: Start Gate Kill Switch Active
00060   
00061   POTS
00062   -----------------------------------------------------------------
00063   0 - controls nothing at the moment
00064   1 - controls nothing at the moment
00065 
00066   PUSHBUTTONS  
00067   -----------------------------------------------------------------
00068   A -  START car race!
00069   B -  END CAR RACE / (while holding down when 'log frame data' active will also output terminal data)
00070   
00071 */
00072 
00073 // LEARNING CAR CLUB 9/10/13: 
00074 // IP need to test-- get around U turns -- more aggressive proportional control?  Or derivative?  Have camera look farther ahead but not too far ahead
00075 // IP need to test -- fix lighting for tunnels (if good algo doesn't need lighting!!)
00076 // -- increase power up to get up hills, brake down hills (accel: http://mbed.org/users/SomeRandomBloke/code/MMA8451Q/#)
00077 // -- add speed control? (hall effect sensor)
00078 // DONE -- make sure steering won't go past limits!!
00079 // 9/12/13 - adjust camera exposure time based on maximum light intensity!!
00080 // 9/14/13 - DONE -- make derivative threshold related to maximum light intensity!
00081 // 9/16/13 - crash at car club blew out resistor R8, replaced it and getty twitching when powering servo from USB only.  Ok when powering from battery.
00082 // 9/17/13 - experiments show that derivative control just doesn't work very well-- the tinest error delta causes huge drastic changes, need to use non-linear proportional
00083 //           control instead... parabolic??
00084 //           DONE -- Also need to slow down on curves
00085 //           speed up hills and slow down on downhill...
00086 //           measure speed?
00087 // 9/18/13 - Test track work: doesn't appear to be sampling camera fast enough--  not able to handle the wiggly track and sometimes not able to handle the curves!
00088 //           TODO: Need to increase rate at which camera sampled and decisions are made!!  Look to codewarrior code
00089 //           Need to cut off left/right ends of camera data-- seems to not read line properly in well lit rooms
00090 //           DONE Speed adjust as you go round; Need to have it slow down "into" curve, speed up again "out of curve"
00091 // 9/23/13 - Definitely not processing camera fast enough-- seems to not react well when speed up the car.  Goes slow just fine all the way 'round.
00092 //           TODO: Need to see how often TFC_LineScanImageReady gets updated. If update camera sample freq how will that impact exposure? Light adjustment algos should
00093 //           be able to handle it. Need to be able to measure the 'processing time' required.  Also wondering if 20mS is fast enough for updating the servo?? Need
00094 //           to calculate how fast servo needs to really go based on track curves and speed.
00095 //           TODO: Need a way to measure speed I think as well.
00096 //           TODO: Need to control speed differentially across the different motors!! See Eli video!!
00097 //           TODO: Use this to get excel feedback quicker: http://strokescribe.com/en/serial-port-download.html  (Doesn't work too well-- prefer my own excel method)
00098 //           TODO: Use PID control for steering!
00099 //           TODO: Use Speed control (PID?) --- add speed sensor!
00100 // 10/8/13   Reduced speed control to 90% (was 85%)-- seems to go off track and lose line at high speed-- mainly on the U turns
00101 //           Need to figure out why derivative control in steering doesn't work well.  Add integral control.
00102 //           Latest track times (no hill, no bumps, no tunnel, only squiggles) = 11.8sec at high speed with speed control
00103 //           Losing time on the curves-- need to optimize!!
00104 //           Worry about hill later-- need to get track times down around 8sec first.
00105 //        *****************************
00106 //        ** Implement some method to acount for U-TURNS on track-- and to help even go beyond what camera can see as far as estimating line position
00107 //        ** (U-TURNS take the longest time out of track)
00108 //        ** Method to help find the line even when not really visible
00109 //        *****************************
00110 // 10/17/13
00111 //        NEED CURVE AND WIGGLE DETECT!!
00112 // 10/20/13  Added logging capability and 'algo time' detect
00113 //           It is not finding the line on the edges when on a curve-- likely because my line detect algo requires both edges
00114 //           TODO-- need method that can use only one edge!
00115 
00116 // TODO LIST
00117 // - Speed Control via Sensor WAITING ON SENSOR
00118 // - Differential drive around curves DONE -- still trying to figure out what %age to drop on turns
00119 // - Full PID steering control IN PROGRESS
00120 // - Starting gate kill engine debug IN PROGRESS -- need to add delay
00121 // - Off track kill engine debug -- IN PROGRESS-- seems to kill car prematurely
00122 
00123 // image processing vars
00124 uint16_t   GrabLineScanImage0[NUM_LINE_SCAN];      // snapshot of camera data for this 'frame'
00125 float      DerivLineScanImage0[NUM_LINE_SCAN];     // derivative of line scan data
00126 float      NegEdges[NUM_LINE_SCAN];                // array-- set of where in line scan data negative edges found
00127 float      PosEdges[NUM_LINE_SCAN];                // array-- set of where in line scan data positive edges found
00128 uint16_t   numNegEdges = 0, numPosEdges = 0;       // max value of valid neg and positive indices (also serves as a count of # edges found)
00129 uint16_t   MaxLightIntensity = 0;                  // max measured light intensity -- to account for lighting differences
00130 uint16_t   MinLightIntensity = (1 << 12);          // min measured light intensity -- to account for lighting differences
00131 float      maxDerVal = 0;                          // max deriv value
00132 float      minDerVal = (float) (1 << 12);          // min deriv value
00133 float      aveDerVal = 0;                          // average deriv value
00134 float      DerivThreshold = (1 << 9);              // Derivative Threshold (default)
00135 float      PosDerivThreshold = (1 << 9);           // Pos Edge Derivative Threshold (default)
00136 float      NegDerivThreshold = (1 << 9);           // Neg Edge Derivative Threshold (default)
00137 
00138 
00139 // Steering control variables
00140 float      CurrentLinePosition;                    // Current position of track line (in pixels -- 0 to 127)
00141 float      LastLinePosition;                       // Last position of track line (in pixels -- 0 to 127)
00142 float      CurrentLinePosError = 0;                // Current line position error (used for derivative calc)
00143 float      AbsError;
00144 float      LastLinePosError = 0;                   // Last line position error (used for derivative calc)
00145 float      SumLinePosError = 0;                    // Sum of line position error (used for integral calc)
00146 float      DerivError = 0;                         // Derivative of error
00147 float      CurrentSteerSetting = (MAX_STEER_RIGHT + MAX_STEER_LEFT) / 2;  // drive straight at first
00148 float      CurrentLeftDriveSetting = 0;            // Drive setting (left wheel)
00149 float      CurrentRightDriveSetting = 0;           // Drive setting (right wheel)
00150 
00151 // Speed control vars
00152 float      MaxSpeed;                                    // maximum speed allowed
00153 
00154 uint16_t   startRaceTicker;                        // ticker at start of race1
00155 
00156 // Custom Data Types
00157 typedef enum TrackStatusType {Unknown,
00158                               LineFound,
00159                               StartGateFound,
00160                               LineJustLeft} TrackStatusType;
00161                               
00162 TrackStatusType CurrentTrackStatus;                // current track status
00163 TrackStatusType LastTrackStatus;                   // last track status                       
00164 
00165 /* typedef enum TrackType {NotSure,
00166                         Straight,
00167                         Curve,
00168                         Wiggle,
00169                         Bumps,
00170                         StartGate,
00171                         UpHill,
00172                         DownHill} TrackType;
00173 
00174 TrackType CurrentTrack; */
00175 
00176 
00177 struct LogData {
00178   float linepos;
00179   float steersetting;
00180   float leftdrivesetting;
00181   float rightdrivesetting;
00182 };
00183 
00184 LogData    frameLogs[NUM_LOG_FRAMES];              // array of log data to store  
00185 int        logDataIndex;                           // index for log data
00186 
00187 
00188 int        StartGateFoundCount = 0;                // how many times start gate has been found
00189 int        UnknownCount = 0;                       // how many times nothing has been found (to help with kill switch implementation)
00190 bool       go = false;                             // Car can go!  Should be set to false to start.
00191 
00192 // EXTRA CONTROL PARAMETERS
00193 bool debugFakeMode = false;         // if true, ignores real camera and uses fake camera input instead; used for data processing debug
00194 int terminalOutput = 0;             // set debug level for terminal output
00195                                     //    0 : no terminal output, race!
00196                                     //    1 : output just to measure frame rate
00197                                     //    2 : output for measuring time of operations
00198                                     //    3 : output with delay
00199 bool doLogData = false;             // whether to capture log data to output later on
00200 bool killSwitch = false;             // whether to enable Kill Switch (allow engine to stop after not finding track)
00201 bool startGateStop = false;         // whether to stop or not depending on starting gate reading
00202 bool doRisky = false;               // race style-- whether conservative or risky
00203 
00204 // timer stuff
00205 Timer timer;
00206 int after_time, before_time, start_time, last_start_time;
00207 bool run_once = false;
00208 
00209 void MasterControlProgram()
00210 {
00211 
00212   // put here all things want to run only once after reset
00213   if (!run_once){
00214     if ((terminalOutput == 1) || (terminalOutput == 2)){
00215       timer.start();
00216     }
00217     run_once = true;
00218   }
00219   
00220   // read DIP switches and Pots for data
00221   readSwitches();
00222                                                           
00223   // Every 4s (or Terminal Output is off, i.e. race mode!)
00224   //    AND line scan image ready (or fake mode where image is always ready)
00225   //   (ticker updates every 2ms) (Line scan image ready very 20mS?)
00226   if((TFC_Ticker[0]>2000 || (terminalOutput != 3)) && (TFC_LineScanImageReady>0 || debugFakeMode))
00227    {
00228 
00229      // stuff that needs to be reset with each image frame
00230      if (terminalOutput == 1) {
00231        last_start_time = start_time;
00232        start_time = timer.read_us();
00233        TERMINAL_PRINTF("TIME:Between frames:%d:uSec\r\n", start_time - last_start_time);
00234        before_time = timer.read_us();
00235      }
00236      TFC_Ticker[0] = 0;
00237      TFC_LineScanImageReady=0; // must reset to 0 after detecting non-zero
00238      MaxLightIntensity = 0;                  // reset
00239      MinLightIntensity = (1 << 12);          // reset
00240 
00241      // grab camera frame
00242      grabCameraFrame();
00243      
00244      if (terminalOutput == 2) {
00245        after_time = timer.read_us();
00246        TERMINAL_PRINTF("TIME:TO AFTER grabCameraFrame:%d:uSec\r\n", after_time - before_time);
00247        before_time = timer.read_us();
00248      }
00249   
00250      // calcalate derivative of linescandata, filter starttime data
00251      derivativeLineScan(&GrabLineScanImage0[0], &DerivLineScanImage0[0]);
00252 
00253      if (terminalOutput == 2) {
00254        after_time = timer.read_us();
00255        TERMINAL_PRINTF("TIME:TO AFTER derivativeLineScan:%d:uSec\r\n", after_time - before_time);
00256        before_time = timer.read_us();
00257      }
00258             
00259      // adjust deriv threshold based on max lighting value
00260      // has to be called before find edges
00261      adjustLights();
00262      
00263      if (terminalOutput == 2) {
00264        after_time = timer.read_us();
00265        TERMINAL_PRINTF("TIME:TO AFTER adjustLights:%d:uSec\r\n", after_time - before_time);
00266        before_time = timer.read_us();
00267      } 
00268          
00269      //find edges from derivative data
00270      findEdges_v2(&DerivLineScanImage0[0]);
00271 
00272      if (terminalOutput == 2) {
00273        after_time = timer.read_us();
00274        TERMINAL_PRINTF("TIME:TO AFTER findEdges_v2:%d:uSec\r\n", after_time - before_time);
00275        before_time = timer.read_us();
00276      } 
00277      
00278      // turn on terminal output if line not found -- FOR DEBUG
00279      //if (CurrentTrackStatus == Unknown)
00280      //  terminalOutput = 1;
00281 
00282      //review edge data and set position or starting flag appropriately
00283      reviewEdges();
00284      
00285      if (terminalOutput == 2) {
00286        after_time = timer.read_us();
00287        TERMINAL_PRINTF("TIME:TO AFTER reviewEdges:%d:uSec\r\n", after_time - before_time);
00288        before_time = timer.read_us();
00289      }      
00290     
00291      if (terminalOutput == 3) {
00292        // print data to Terminal for camera 0
00293        printLineScanData(&GrabLineScanImage0[0]);
00294 
00295      // print deriviative of linescandata, filter starttime data
00296        printDerivLineScanData(&DerivLineScanImage0[0]);
00297        
00298        printAdjustLightsData();
00299        
00300        printEdgesFound();
00301 
00302      }
00303      
00304      // ** Track Status available at this point **  
00305       
00306      
00307      // test out accelerometer
00308     // accelTest();
00309       
00310      // Update things based on latest track status
00311      // e.g. change steering setting, stop car, ...
00312      ActOnTrackStatus();
00313      
00314      if (terminalOutput == 2) {
00315        after_time = timer.read_us();
00316        TERMINAL_PRINTF("TIME:TO AFTER ActOnTrackStatus:%d:uSec\r\n", after_time - before_time);
00317        before_time = timer.read_us();
00318      }      
00319 
00320 
00321      //give LED feedback as to track status
00322      feedbackLights();
00323      
00324      if (terminalOutput == 2) {
00325        after_time = timer.read_us();
00326        TERMINAL_PRINTF("TIME:TO AFTER feedbackLights:%d:uSec\r\n", after_time - before_time);
00327        before_time = timer.read_us();       
00328      }  
00329      
00330      // control max power (speed)
00331      SpeedControl();
00332      
00333      if (terminalOutput == 2) {
00334        after_time = timer.read_us();
00335        TERMINAL_PRINTF("TIME:TO AFTER SpeedControl:%d:uSec\r\n", after_time - before_time);
00336        before_time = timer.read_us();
00337      }       
00338 
00339      // Drive!!     
00340      Drive();
00341      
00342      if (terminalOutput == 2) {
00343        after_time = timer.read_us();
00344        TERMINAL_PRINTF("TIME:TO AFTER Drive:%d:uSec\r\n", after_time - before_time);
00345        before_time = timer.read_us();
00346      }         
00347      
00348      // wait_ms(1);
00349 
00350      // Capture Log data while driving
00351      if (go && doLogData) {
00352        captureData();
00353      }
00354      
00355      // Dump Log data to Terminal while stopped and holding B button
00356      if (!go && doLogData && TFC_PUSH_BUTTON_1_PRESSED) {
00357        dumpData();
00358      }
00359      
00360      if (terminalOutput == 2) {
00361        after_time = timer.read_us();
00362        TERMINAL_PRINTF("TIME: ENTIRE FRAME (include prints):%d:uSec\r\n", after_time - start_time);
00363        before_time = timer.read_us();
00364      }
00365 
00366      if (terminalOutput == 3) {
00367        TERMINAL_PRINTF("\r\n**************************END********************************\r\n");
00368      }
00369 
00370    } 
00371 }
00372 
00373 void dumpData()
00374 {
00375    TERMINAL_PRINTF("INDEX,LINEPOS,STEERSETTING,LEFTDRIVESETTING,RIGHTDRIVESETTING\r\n");
00376        for(logDataIndex=0;logDataIndex<NUM_LOG_FRAMES;logDataIndex++) {
00377          TERMINAL_PRINTF("%d,%6.2f,%6.2f,%6.2f,%6.2f\r\n",logDataIndex,frameLogs[logDataIndex].linepos,frameLogs[logDataIndex].steersetting,frameLogs[logDataIndex].leftdrivesetting,frameLogs[logDataIndex].rightdrivesetting);
00378        }
00379 }
00380 
00381 void captureData()
00382 {
00383   frameLogs[logDataIndex].linepos = CurrentLinePosition;
00384   frameLogs[logDataIndex].steersetting = CurrentSteerSetting;
00385   frameLogs[logDataIndex].leftdrivesetting = CurrentLeftDriveSetting;
00386   frameLogs[logDataIndex].rightdrivesetting = CurrentRightDriveSetting;
00387 
00388   // increment index                
00389   logDataIndex++;
00390   if (logDataIndex > NUM_LOG_FRAMES) 
00391     logDataIndex = 0;
00392 }
00393 
00394 void readSwitches()
00395 {
00396 
00397   // ********* GATHER DIP SWITCH INPUTS *********
00398   if(TFC_GetDIP_Switch()&0x02)     // SWITCH 2 
00399     doLogData = true;              // Log data to array
00400   else
00401     doLogData = false;             // normal operation
00402   
00403   if(TFC_GetDIP_Switch()&0x04)     // SWITCH 3
00404     doRisky = true;     
00405   else
00406     doRisky = false;          
00407     
00408   if(TFC_GetDIP_Switch()&0x08)     // SWITCH 4 control start stop gate
00409     startGateStop = true;
00410   else
00411     startGateStop = false;
00412 
00413 
00414 }
00415 
00416 void grabCameraFrame()
00417 {
00418   uint32_t i = 0;
00419   uint8_t fake_type = 4;                   // type of fake data if used
00420 
00421   for(i=0;i<NUM_LINE_SCAN;i++) // print one line worth of data (128) from Camera 0
00422   { 
00423   
00424     if (debugFakeMode) {                   // use fake camera data
00425       switch (fake_type) {
00426       case 0:                              // ideal track -- line in center
00427          if (i<57 || i > 70)
00428            GrabLineScanImage0[i] = 0xFFF;  // no line
00429          else
00430            GrabLineScanImage0[i] = 0x4B0;  // line
00431          break;
00432       case 1:                              // ideal track -- line to the left
00433          if (i<27 || i > 40)
00434            GrabLineScanImage0[i] = 0xFFF;  // no line
00435          else
00436            GrabLineScanImage0[i] = 0x4B0;  // line
00437          break;
00438       case 2:                              // ideal track -- line to the right
00439          if (i<87 || i > 100)
00440            GrabLineScanImage0[i] = 0xFFF;  // no line
00441          else
00442            GrabLineScanImage0[i] = 0x4B0;  // line
00443          break; 
00444       case 3:                              // ideal track -- starting gate!
00445          // TBD
00446          break;              
00447       case 4:                              // less than ideal track -- debug multi-edge issue!
00448          if (i<54)
00449            GrabLineScanImage0[i] = 4000;   // no line
00450          if (i == 54)
00451            GrabLineScanImage0[i] = 3370;   // neg edge
00452          if (i == 55)
00453            GrabLineScanImage0[i] = 3309;   // neg edge
00454          if (i == 56)
00455            GrabLineScanImage0[i] = 2016;   // neg edge
00456          if (i == 57)
00457            GrabLineScanImage0[i] = 711;    // neg edge
00458          if (i == 58)
00459            GrabLineScanImage0[i] = 696;    // neg edge
00460          if ((i>58) && (i<69))
00461            GrabLineScanImage0[i] = 500;    // line
00462          if (i == 69)
00463            GrabLineScanImage0[i] = 1800;   // pos edge
00464          if (i > 69)
00465            GrabLineScanImage0[i] = 4000;   // no line
00466       default:
00467          break;
00468       }
00469     
00470     } else {                               // use real camera data
00471       GrabLineScanImage0[i] = TFC_LineScanImage0[i];
00472     }
00473   }
00474 
00475 
00476 }
00477 
00478 void printLineScanData(uint16_t* LineScanData)
00479 {
00480   uint32_t i = 0;
00481   float Val;
00482 
00483   TERMINAL_PRINTF("LINE SCAN DATA:,");
00484 
00485   for(i=0;i<NUM_LINE_SCAN;i++) // print one line worth of data (128) from Camera 0
00486   { 
00487     if (1 == 1) { // use float to print
00488       Val = (float) LineScanData[i];
00489       TERMINAL_PRINTF("%9.3f",Val);
00490       if(i==MAX_LINE_SCAN)  // when last data reached put in line return
00491         TERMINAL_PRINTF("\r\n");
00492       else
00493         TERMINAL_PRINTF(",");
00494     } else {         
00495       TERMINAL_PRINTF("0x%X",LineScanData[i]);
00496       if(i==MAX_LINE_SCAN)  // when last data reached put in line return
00497         TERMINAL_PRINTF("\r\n",LineScanData[i]);
00498       else
00499         TERMINAL_PRINTF(",",LineScanData[i]);
00500       }
00501   }
00502 
00503 }
00504 
00505 void printDerivLineScanData(float* derivLineScanData)
00506 {
00507   uint32_t i, minCnt = 0, maxCnt = 0;
00508   
00509   minCnt = FILTER_ENDS;
00510   maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
00511 
00512   TERMINAL_PRINTF("DERIVATIVE DATA:,");
00513 
00514   for(i=minCnt;i<maxCnt;i++) // print one line worth of data (128) from Camera 0
00515   {          
00516     TERMINAL_PRINTF("%9.3f",derivLineScanData[i]);
00517     if(i==maxCnt-1)          // when last data reached put in line return
00518       TERMINAL_PRINTF("\r\n",derivLineScanData[i]);
00519     else
00520       TERMINAL_PRINTF(", ",derivLineScanData[i]);
00521   }
00522 
00523 }
00524 
00525 void derivativeLineScan(uint16_t* LineScanDataIn, float* DerivLineScanDataOut)
00526 {
00527 
00528   uint32_t i, minCnt = 0, maxCnt = 0;
00529   float DerVal, upperDerVal, lowerDerVal = 0;
00530   
00531   maxDerVal = 0;
00532   minDerVal = (float) (1 << 12);
00533   aveDerVal = 0;
00534   
00535   minCnt = FILTER_ENDS;
00536   maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
00537 
00538   // TERMINAL_PRINTF("i, upperDerVal, lowerDerVal, DerVal\r\n");
00539   
00540   for(i=minCnt;i<maxCnt;i++) // print one line worth of data from Camera 0
00541   {  
00542 
00543      // store max light intensity value
00544      if (LineScanDataIn[i] > MaxLightIntensity)
00545        MaxLightIntensity = LineScanDataIn[i];
00546 
00547      // store min light intensity value
00548      if (LineScanDataIn[i] < MinLightIntensity)
00549        MinLightIntensity = LineScanDataIn[i];
00550        
00551 
00552      // Central Derivative
00553      if (i==minCnt) {                       // start point
00554        upperDerVal = (float)(LineScanDataIn[i+1]);
00555        lowerDerVal = (float)(LineScanDataIn[i]);  // make same as start point
00556      } else if (i==maxCnt - 1){             // end point
00557        upperDerVal = (float)(LineScanDataIn[i]);   // make same as end point
00558        lowerDerVal = (float)(LineScanDataIn[i-1]);
00559      } else {                               // any other point
00560        upperDerVal = (float)(LineScanDataIn[i+1]);
00561        lowerDerVal = (float)(LineScanDataIn[i-1]);
00562      }
00563      DerVal = (upperDerVal - lowerDerVal) / 2;
00564    //  TERMINAL_PRINTF("%d,%9.3f,%9.3f,%9.3f\r\n", i, upperDerVal, lowerDerVal, DerVal);
00565             
00566      if (DerVal > maxDerVal) {
00567        maxDerVal = DerVal;
00568      } 
00569      if (DerVal < minDerVal) {
00570        minDerVal = DerVal;
00571      }
00572      aveDerVal = aveDerVal + DerVal;           //get sum
00573      DerivLineScanDataOut[i] = DerVal;    
00574   }
00575   aveDerVal = (float) aveDerVal / (maxCnt - minCnt);
00576 }
00577 
00578 //Not reliable for finding edges!
00579 void findEdges(float* derivLineScanData)
00580 {
00581   // search for edges in deriviative data using a threshold
00582   // need to store in a hash if that's possible...
00583   // combine edges that are a pixel apart
00584  
00585   int i, minCnt = 0, maxCnt = 0;
00586   int multiNegEdgeCnt = 1, multiNegEdgeSum = 0;
00587   int multiPosEdgeCnt = 1, multiPosEdgeSum = 0;
00588       
00589   minCnt = FILTER_ENDS;
00590   maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
00591   
00592   numNegEdges = 0;
00593   numPosEdges = 0;
00594   for(i=minCnt;i<maxCnt;i++) // print one line worth of data from Camera 0
00595   {  
00596      if (derivLineScanData[i] <= NegDerivThreshold)     // NEGATIVE EDGE FOUND!
00597      {
00598        if (terminalOutput == 3) {
00599          TERMINAL_PRINTF("NEG EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
00600        }
00601        
00602        if ((numNegEdges > 0) && (NegEdges[numNegEdges - 1] + 1 == i )) // if no multi edges
00603        {  // edge actually across multiple pixels
00604          multiNegEdgeCnt++;
00605          multiNegEdgeSum = multiNegEdgeSum + i;
00606          if (terminalOutput == 3) {
00607            TERMINAL_PRINTF("MULTIEDGE FOUND! MultiNegEdgeCnt: %d; MultiNegEdgeSum: %d\r\n", multiNegEdgeCnt, multiNegEdgeSum);
00608          }
00609        } else {  // not a multi-pixel edge known at this time, store negative edge index value
00610          numNegEdges++;
00611          if (terminalOutput == 3) {
00612            TERMINAL_PRINTF("NEG EDGE STORED WITH INDEX %d.  NUM NEG EDGES = %d\r\n", i, numNegEdges);
00613          }
00614          NegEdges[numNegEdges - 1] = (float) i;
00615          multiNegEdgeSum = i;
00616        }
00617  
00618  
00619      } else if (derivLineScanData[i] > PosDerivThreshold) {    // POSITIVE EDGE FOUND!
00620      
00621        if (terminalOutput == 3) {
00622          TERMINAL_PRINTF("POS EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
00623        }
00624        
00625        if ((numPosEdges > 0) && (PosEdges[numPosEdges - 1] + 1 == i ))
00626        {  // edge actually across multiple pixels
00627          multiPosEdgeCnt++;
00628          multiPosEdgeSum = multiPosEdgeSum + i;
00629          if (terminalOutput == 3) {
00630            TERMINAL_PRINTF("MULTIEDGE FOUND! MultiPosEdgeCnt: %d; MultiPosEdgeSum: %d\r\n", multiPosEdgeCnt, multiPosEdgeSum);
00631          }
00632        } else {  // not a multi-pixel edge known at this time, store Posative edge index value
00633          if (terminalOutput == 3) {
00634            TERMINAL_PRINTF("POS EDGE STORED WITH INDEX %d.  NUM POS EDGES = %d\r\n", i, numPosEdges);
00635          }
00636          numPosEdges++;
00637          PosEdges[numPosEdges - 1] = (float) i;
00638          multiPosEdgeSum = i;
00639        }      
00640        
00641      }  else {  // NO EDGE FOUND
00642        // combine multi-edges if there are any
00643        if (multiNegEdgeCnt > 1)
00644        { 
00645           NegEdges[numNegEdges - 1] = (float) multiNegEdgeSum / multiNegEdgeCnt;
00646           multiNegEdgeCnt = 1; multiNegEdgeSum = 0;
00647        }
00648        if (multiPosEdgeCnt > 1)
00649        { 
00650           PosEdges[numPosEdges - 1] = (float) multiPosEdgeSum / multiPosEdgeCnt;
00651           multiPosEdgeCnt = 1; multiPosEdgeSum = 0;
00652        }       
00653      
00654      }
00655   }
00656 
00657 }
00658 
00659 
00660 void findEdges_v2(float* derivLineScanData)
00661 {
00662   // search for edges in deriviative data using a threshold
00663   // need to store in a hash if that's possible...
00664   // combine edges that are a pixel apart
00665  
00666   int i;
00667   
00668   int NegEdgeBufCnt = 0, NegEdgeBufSum = 0;     // serves as buffer to store neg edges found next to each other
00669   int PosEdgeBufCnt = 0, PosEdgeBufSum = 0;     // serves as buffer to store pos edges found next to each other
00670       
00671   int minCnt = FILTER_ENDS;
00672   int maxCnt = NUM_LINE_SCAN - FILTER_ENDS;
00673   
00674   
00675   
00676   numNegEdges = 0;                              // count of neg edges found thus far
00677   numPosEdges = 0;                              // count of pos edges found thus far
00678   for(i=minCnt;i<maxCnt;i++) // print one line worth of data from Camera 0
00679   {  
00680      
00681      if (derivLineScanData[i] <= NegDerivThreshold)          // NEGATIVE EDGE FOUND!
00682      {
00683        
00684        if (terminalOutput == 3) {
00685          TERMINAL_PRINTF("NEG EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
00686        }
00687 
00688        NegEdgeBufCnt++;                                      // add value to neg edge buffer
00689        NegEdgeBufSum = NegEdgeBufSum + i;
00690        
00691      } else if (derivLineScanData[i] > PosDerivThreshold) {  // POSITIVE EDGE FOUND!
00692        
00693        if (terminalOutput == 3) {
00694          TERMINAL_PRINTF("POS EDGE FOUND AT INDEX %d WITH VALUE %9.3f\r\n", i, derivLineScanData[i]);
00695        }
00696 
00697        PosEdgeBufCnt++;                                      // add value to pos edge buffer
00698        PosEdgeBufSum = PosEdgeBufSum + i;
00699        
00700      }  else {                                               // NO EDGE FOUND
00701          
00702        // POP EDGE BUFFERS IF NON-EMPTY AND STORE TO EDGE "STACK" (i.e. edges found)
00703        
00704        if (NegEdgeBufCnt > 0) {
00705          // store edge value
00706          numNegEdges++;
00707          NegEdges[numNegEdges - 1] = (float) NegEdgeBufSum / NegEdgeBufCnt;
00708          
00709          // clear edge buffer      
00710          NegEdgeBufSum = 0; NegEdgeBufCnt = 0;
00711        }
00712 
00713        if (PosEdgeBufCnt > 0) {
00714          // store edge value
00715          numPosEdges++;
00716          PosEdges[numPosEdges - 1] = (float) PosEdgeBufSum / PosEdgeBufCnt;
00717          
00718          // clear edge buffer
00719          PosEdgeBufSum = 0; PosEdgeBufCnt = 0;
00720        }        
00721      
00722      }
00723      
00724   }
00725 
00726 }
00727 
00728 void printEdgesFound()
00729 {
00730   int i;
00731   
00732     // Check that neg edges captured ok
00733     TERMINAL_PRINTF("NEGATIVE EDGES FOUND:,");
00734     for(i=0;i<=numNegEdges-1;i++)
00735     {
00736       TERMINAL_PRINTF("%9.3f",NegEdges[i]);
00737       if(i==numNegEdges-1)              // when last data reached put in line return
00738         TERMINAL_PRINTF("\r\n");
00739       else
00740         TERMINAL_PRINTF(", ");
00741     }
00742   
00743   
00744     // Check that pos edges captured ok
00745     TERMINAL_PRINTF("POSITIVE EDGES FOUND:,");
00746     for(i=0;i<=numPosEdges-1;i++)
00747     {
00748       TERMINAL_PRINTF("%9.3f",PosEdges[i]);
00749       if(i==numPosEdges-1)              // when last data reached put in line return
00750         TERMINAL_PRINTF("\r\n");
00751       else
00752         TERMINAL_PRINTF(", ");
00753     }
00754 
00755 }
00756 
00757 void reviewEdges()
00758 {
00759   LastTrackStatus = CurrentTrackStatus;
00760   
00761   if ((numPosEdges == 1) && (numNegEdges == 1))  // only one negative and positive edge found (LINE)
00762   {
00763     if (((PosEdges[0] - NegEdges[0]) >= MIN_LINE_WIDTH) && ((PosEdges[0] - NegEdges[0]) <= MAX_LINE_WIDTH)) // has proper expected width
00764     {
00765        CurrentTrackStatus = LineFound;                                   // report line found!
00766        UnknownCount = 0;                                          // reset unknown status count
00767        LastLinePosition = CurrentLinePosition;
00768        CurrentLinePosition = (PosEdges[0]+NegEdges[0]) / 2;       // update line position
00769     }
00770   } else if ((numPosEdges == 1) && (numNegEdges == 0))  {     // 1 pos edge found (POSSIBLE LINE)
00771     if ((PosEdges[0] <= MAX_LINE_WIDTH) && (LastLinePosError < 0))       // pos edge is within line width of edge of camera (LEFT)
00772     {
00773        CurrentTrackStatus = LineFound;                                   // report line found!
00774        UnknownCount = 0;                                                 // reset unknown status count
00775        LastLinePosition = CurrentLinePosition;
00776        CurrentLinePosition = PosEdges[0] - ( MAX_LINE_WIDTH / 2);        // update line position
00777      //  TERMINAL_PRINTF("*** SINGLE POSEDGE LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
00778     }
00779   } else if ((numNegEdges == 1) && (numPosEdges == 0))  {     // 1 neg edge found (POSSIBLE LINE)
00780     if ((NegEdges[0] >= (MAX_LINE_SCAN - MAX_LINE_WIDTH)) && (LastLinePosError > 0))    // neg edge is within line width of edge of camera (RIGHT)
00781     {
00782        CurrentTrackStatus = LineFound;                                   // report line found!
00783        UnknownCount = 0;                                                 // reset unknown status count
00784        LastLinePosition = CurrentLinePosition;
00785        CurrentLinePosition = NegEdges[0] + ( MAX_LINE_WIDTH / 2);        // update line position
00786     //   TERMINAL_PRINTF("*** SINGLE NEGEDGE LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
00787     } 
00788   } else if ((numPosEdges == 2) && (numNegEdges == 2))  {     // 2 negative and 2 positive edges found (STARTING/FINISH GATE)
00789   
00790     if ((((NegEdges[0] - PosEdges[0]) >= MIN_LINE_WIDTH) && ((NegEdges[0] - PosEdges[0]) <= MAX_LINE_WIDTH)) &&    // white left 'line'
00791         (((NegEdges[1] - PosEdges[1]) >= MIN_LINE_WIDTH) && ((NegEdges[1] - PosEdges[1]) <= MAX_LINE_WIDTH)) &&    // white right 'line'
00792         (((PosEdges[1] - NegEdges[0]) >= MIN_LINE_WIDTH) && ((PosEdges[1] - NegEdges[0]) <= MAX_LINE_WIDTH))       // actual track line
00793         )
00794            
00795        
00796     if (startRaceTicker > STARTGATEDELAY) {                      // only start counting for starting gate until after delay
00797       StartGateFoundCount++;
00798     }
00799     
00800     CurrentTrackStatus = StartGateFound;
00801     UnknownCount = 0;                                            // reset unknown status count
00802            
00803   } else if ((numPosEdges > 1) && (numNegEdges > 1)) {   // more than 1 negative edge and positive edge found (but not 2 for both) (STARTING / FINISH GATE)
00804   
00805    // remove edges that aren't close to center TBD DDHH
00806    
00807       if (terminalOutput == 3) {
00808          TERMINAL_PRINTF("***************************************** \r\n");
00809          TERMINAL_PRINTF("********** NOT SURE FOUND ********** \r\n");
00810          TERMINAL_PRINTF("***************************************** \r\n");
00811        } 
00812     CurrentTrackStatus = Unknown; 
00813   
00814   } else {  // no track or starting gate found
00815   
00816     if (terminalOutput == 3) {
00817       TERMINAL_PRINTF("***************************************** \r\n");
00818       TERMINAL_PRINTF("*** !!!!!!!!!! LINE NOT FOUND !!!!!!! *** \r\n", CurrentLinePosition);
00819       TERMINAL_PRINTF("***************************************** \r\n");
00820     }
00821   
00822     CurrentTrackStatus = Unknown;
00823     UnknownCount++;
00824   }
00825   
00826   
00827 
00828 
00829 }
00830 
00831 void ActOnTrackStatus()
00832 {
00833   // Decide what to do next based on current track status
00834 
00835   if (CurrentTrackStatus == LineFound)   {             // LINE FOUND!
00836   
00837     if (terminalOutput == 3) {
00838       TERMINAL_PRINTF("***************************************** \r\n");
00839       TERMINAL_PRINTF("*** LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
00840       TERMINAL_PRINTF("***************************************** \r\n");
00841     }
00842   
00843     // Update steering position 
00844     SteeringControl();
00845 
00846     // Apply to servo    
00847     Steer();
00848     
00849   } else if (CurrentTrackStatus == StartGateFound) {   // STARTING GATE FOUND
00850   
00851     if (terminalOutput == 3) {
00852       TERMINAL_PRINTF("***************************************** \r\n");
00853       TERMINAL_PRINTF("********** STARTING GATE FOUND ********** \r\n");
00854       TERMINAL_PRINTF("**********     count = %d      ********** \r\n", StartGateFoundCount);
00855       TERMINAL_PRINTF("***************************************** \r\n");
00856     }
00857     
00858     // END RACE!
00859     if (startGateStop) {
00860       if (StartGateFoundCount > STARTGATEFOUNDMAX)
00861       {
00862        go = false;   // STOP!!
00863       } 
00864     }
00865   
00866   }
00867   
00868 
00869 
00870 }
00871 
00872 void SteeringControl()
00873 {
00874 
00875   float targetPosition = (float)( (NUM_LINE_SCAN / 2) - 0.5);  // target to achieve for line position
00876 
00877   float KP;                                                    // proportional control factor
00878   float KI;                                                    // integral control factor
00879   float KD;                                                    // derivative control factor
00880   
00881   float Pout, Iout, Dout;                                      // PID terms
00882   
00883   // Calculate error
00884   // make error to the right positive
00885   // i.e. if LINE to the right-- then CurrentLinePosError > 0
00886   //      if LINE to the left -- then CurrentLinePosError < 0
00887   CurrentLinePosError = CurrentLinePosition - targetPosition;
00888 
00889   // Get absolute error
00890   if (CurrentLinePosError >= 0) 
00891     AbsError = CurrentLinePosError;
00892   else
00893     AbsError = -1 * CurrentLinePosError;
00894 
00895   // CHOOSE SET OF PID CONTROL PARAMETERS
00896   switch (CONTROL_METHOD) {
00897     case 0:
00898       // Pure proportional control based on range of steering values vs. range of error values
00899       KP = (float) ( MAX_STEER_RIGHT - MAX_STEER_LEFT ) / ( NUM_LINE_SCAN - (2*FILTER_ENDS) - MIN_LINE_WIDTH );
00900       KD = 0;
00901       KI = 0;
00902       break;
00903    case 1:
00904       // Proportional control with 50% bit more oomph --- a bit more aggressive around the bends
00905       KP = (float) ( MAX_STEER_RIGHT - MAX_STEER_LEFT ) / ( NUM_LINE_SCAN - (2*FILTER_ENDS) - MIN_LINE_WIDTH );
00906       KP = KP * 1.5;
00907       KD = 0;
00908       KI = 0;
00909       break;
00910     case 2:  // MANUAL TUNING CASE 1 (use pot to help determine tuning parameters)
00911       KP = TUNE_KP;
00912       KI = TUNE_KI;
00913       KD = TUNE_KD;
00914     case 3:
00915       if (AbsError < ABS_ERROR_THRESH) {
00916         KP = 0.003;  // when relatively straight, keep KP gain low
00917       } else {
00918         KP = 0.010;  // when curve begins or off track, increase KP gain
00919       }
00920       KI = 0;
00921       KD = 0;
00922       
00923     default:
00924       break;
00925   }
00926   
00927   /* Pseudocode
00928    previous_error = 0
00929    integral = 0 
00930    start:
00931      error = setpoint - measured_value
00932      integral = integral + error*dt
00933      derivative = (error - previous_error)/dt
00934      output = Kp*error + Ki*integral + Kd*derivative
00935      previous_error = error
00936      wait(dt)
00937      goto start 
00938   */
00939   
00940   
00941   if (terminalOutput == 3) {
00942     TERMINAL_PRINTF("KP = %6.4f\r\n", KP);
00943     TERMINAL_PRINTF("TARGET %6.3f\r\n", targetPosition);
00944   }
00945   
00946 
00947 
00948     // Update integral of error
00949     // i.e. if LINE stays to the right, then SumLinePosError increases
00950     // i.e. if LINE stays to the left, then SumLinePosError decreases
00951     SumLinePosError = SumLinePosError + ( CurrentLinePosError * DT );
00952 
00953     DerivError = (CurrentLinePosError - LastLinePosError) / DT;
00954     
00955     if (terminalOutput == 3) {
00956       TERMINAL_PRINTF("CURRENT LINE POSITION %9.3f\r\n", CurrentLinePosition);
00957       TERMINAL_PRINTF("CURRENT LINE POSITION ERROR %9.3f\r\n", CurrentLinePosError);
00958     }
00959     
00960     // SECOND- calculate new servo position
00961     
00962     // proportional control term
00963     Pout = KP * CurrentLinePosError;
00964 
00965     // integral control term
00966     Iout = KI * SumLinePosError;
00967 
00968     // Derivative control term
00969     Dout = KD * DerivError;
00970 
00971     if (terminalOutput == 3) {
00972       TERMINAL_PRINTF("KP = %6.4f\r\n", KP);
00973       TERMINAL_PRINTF("KI = %6.4f\r\n", KI);
00974       TERMINAL_PRINTF("KD = %6.4f\r\n", KD);
00975       TERMINAL_PRINTF("Pout = %6.4f\r\n", Pout);
00976       TERMINAL_PRINTF("Iout = %6.4f\r\n", Iout);
00977       TERMINAL_PRINTF("Dout = %6.4f\r\n", Dout);
00978     }
00979 
00980     // Finally add offset to steering to account for non-centered servo mounting
00981     // CurrentSteerSetting = Pout + Iout + Dout + ( (float) (MAX_STEER_LEFT + MAX_STEER_RIGHT) / 2 );
00982     CurrentSteerSetting = Pout + ( (float) (MAX_STEER_LEFT + MAX_STEER_RIGHT) / 2 );
00983     
00984     // store for next cycle deriv calculation
00985     LastLinePosError = CurrentLinePosError;
00986 
00987     // for tuning control algo only
00988     if (1 == 0) {
00989       TERMINAL_PRINTF("*** ******************************** \r\n");
00990       TERMINAL_PRINTF("*** LINE FOUND AT POSITION %9.3f *** \r\n", CurrentLinePosition);
00991       TERMINAL_PRINTF("*** ERROR %9.3f *** \r\n", CurrentLinePosError);
00992       TERMINAL_PRINTF("*** INTEGRAL ERROR %9.3f *** \r\n", SumLinePosError);
00993       TERMINAL_PRINTF("*** DERIVATIVE ERROR %9.3f *** \r\n", DerivError);
00994       TERMINAL_PRINTF("*** P STEER SETTING %9.3f *** \r\n", CurrentSteerSetting);
00995       TERMINAL_PRINTF("*** PI STEER SETTING  %9.3f *** \r\n", (CurrentSteerSetting + Iout));
00996       TERMINAL_PRINTF("*** ******************************** \r\n");
00997       wait_ms(1000);
00998     }
00999 
01000 }
01001 
01002 void Steer()
01003 {
01004 
01005     // make sure doesn't go beyond steering limits
01006     if (CurrentSteerSetting > MAX_STEER_RIGHT)
01007     { 
01008        CurrentSteerSetting = MAX_STEER_RIGHT;
01009     } else if (CurrentSteerSetting < MAX_STEER_LEFT)
01010     {
01011        CurrentSteerSetting = MAX_STEER_LEFT;
01012     }
01013 
01014     if (terminalOutput == 3) {
01015       TERMINAL_PRINTF("APPLYING SERVO SETTING %5.3f\r\n", CurrentSteerSetting);
01016     }
01017     TFC_SetServo(0,CurrentSteerSetting);  
01018 
01019 }
01020 
01021 void SpeedControl()
01022 {
01023 
01024   // Get max speed setting from reading pot0
01025   // then adjust
01026   
01027   float ErrLimit;
01028   float LeftDriveRatio, RightDriveRatio;
01029   
01030   // set maximum speed allowed
01031   switch (1)
01032     {
01033       case 0:
01034         // read value off pot0
01035         MaxSpeed = TFC_ReadPot(0);
01036         break;
01037       case 1:
01038         if (doRisky)
01039           MaxSpeed = TUNE_SPEED + 0.1;
01040         else 
01041           MaxSpeed = TUNE_SPEED;
01042         break;
01043       case 2:
01044         MaxSpeed = SUB_LIGHT_SPEED;
01045         break;
01046       case 3:
01047         MaxSpeed = LIGHT_SPEED;
01048         break;
01049       case 4:
01050         MaxSpeed = RIDICULOUS_SPEED;      
01051         break;
01052       case 5:
01053         MaxSpeed = LUDICROUS_SPEED;      
01054         break;        
01055       default:
01056         break;
01057     }
01058     
01059   switch (SPEED_ADJUST)
01060    {
01061      case 0:
01062        // SPEED ADJUST METHOD 0
01063        // no speed adjust
01064        LeftDriveRatio = MAX_POWER;
01065        RightDriveRatio = LeftDriveRatio;
01066      case 1:
01067        // SPEED ADJUST METHOD 1
01068        // High speed when error is low, low speed when error is high
01069        // lower speed when more than third outside of center
01070        ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO * 0.33;
01071        if (AbsError > ErrLimit) {
01072          LeftDriveRatio = MIN_POWER;
01073        } else {
01074          LeftDriveRatio = MAX_POWER;
01075        }
01076        RightDriveRatio = LeftDriveRatio;
01077        break;
01078      case 2:
01079        // SPEED ADJUST METHOD 2
01080        // Have max/min speed adjust proportional to absolute value of line error
01081        ErrLimit = ((float) RANGE )  * 0.5 * ERR_RATIO; 
01082        LeftDriveRatio = MAX_POWER - ((MAX_POWER - MIN_POWER) * (AbsError / ErrLimit));
01083        RightDriveRatio = LeftDriveRatio;
01084        break;
01085      case 3:
01086        // SPEED ADJUST METHOD 3
01087        // have wheel relative speed proportional to absolute value of line error
01088        ErrLimit = ((float) RANGE )  * 0.5 * ERR_RATIO;
01089        if (CurrentLinePosError > 0) {           // heading right
01090          LeftDriveRatio = MAX_POWER;
01091          RightDriveRatio = (MIN_POWER - MAX_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) ) + MAX_POWER;
01092        } else if (CurrentLinePosError < 0) {    // heading left
01093          RightDriveRatio = MAX_POWER;
01094          LeftDriveRatio = (MAX_POWER - MIN_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) ) + MAX_POWER;
01095        } else {
01096          LeftDriveRatio = MAX_POWER;
01097          RightDriveRatio = MAX_POWER;
01098        }
01099        break;
01100      case 4:
01101        // SPEED ADJUST METHOD 4
01102        // have wheel relative speed proportional to absolute value of line error
01103        // only when above a certain error
01104        ErrLimit = ((float) RANGE )  * 0.5 * ERR_RATIO * 0.1;
01105        if (CurrentLinePosError > ErrLimit) {           // heading right
01106          LeftDriveRatio = MAX_POWER - (MAX_POWER - MIN_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) );
01107          RightDriveRatio = MIN_POWER;
01108        } else if (CurrentLinePosError < (-1 * ErrLimit)) {    // heading left
01109          RightDriveRatio = MAX_POWER - (MAX_POWER - MIN_POWER) * (CurrentLinePosError * 2 / ( (float) RANGE ) );
01110          LeftDriveRatio = MIN_POWER;
01111        } else {
01112          LeftDriveRatio = MAX_POWER;
01113          RightDriveRatio = MAX_POWER;
01114        }
01115        break; 
01116      case 5:
01117        // SPEED ADJUST METHOD 5
01118        // High speed when error is low, low speed when error is high
01119        // lower speed when more than third outside of center
01120        ErrLimit = ((float) RANGE ) * 0.5 * ERR_RATIO * 0.2;
01121        if (AbsError > ErrLimit) {
01122          LeftDriveRatio = MIN_POWER;
01123        } else {
01124          LeftDriveRatio = MAX_POWER;
01125        }
01126        RightDriveRatio = LeftDriveRatio;
01127        break;   
01128      case 6:
01129        // SPEED ADJUST METHOD 6
01130        // High speed when error is low, low speed when error is high
01131        // lower speed when more than third outside of center
01132        if (AbsError > ABS_ERROR_THRESH) {
01133          LeftDriveRatio = MIN_POWER;
01134        } else {
01135          LeftDriveRatio = MAX_POWER;
01136        }
01137        RightDriveRatio = LeftDriveRatio;
01138        break;                 
01139      default:
01140        break;
01141        
01142   }
01143   // TBD-- add speed adjust based on Xaccel sensor!
01144 
01145 
01146   // currently no control mechanism as don't have speed sensor  
01147   CurrentLeftDriveSetting = (float) (LeftDriveRatio / 100) * MaxSpeed;
01148   CurrentRightDriveSetting = (float) (RightDriveRatio / 100) * MaxSpeed;
01149 
01150   
01151   if (terminalOutput == 3) {
01152     TERMINAL_PRINTF("Abs Error: %4.2f\r\n", AbsError);
01153     TERMINAL_PRINTF("Error Limit: %4.2f\r\n", ErrLimit);
01154     TERMINAL_PRINTF("MAX SPEED = %5.2f\n", MaxSpeed);
01155     TERMINAL_PRINTF("Current Left Drive Setting: %5.2f\r\n", CurrentLeftDriveSetting);
01156     TERMINAL_PRINTF("Current Right Drive Setting: %5.2f\r\n", CurrentRightDriveSetting);
01157   }
01158   if (1 == 0) {
01159    TERMINAL_PRINTF("Current Left Drive Setting: %5.2f\r\n", CurrentLeftDriveSetting);
01160    TERMINAL_PRINTF("Current Right Drive Setting: %5.2f\r\n", CurrentRightDriveSetting);
01161   }
01162 
01163 }
01164 
01165 void Drive()
01166 {
01167 
01168   // START!
01169   // if not going, go when button A is pressed
01170   if (!go) {
01171     if(TFC_PUSH_BUTTON_0_PRESSED) {
01172       go = true;
01173       UnknownCount = 0;
01174       StartGateFoundCount = 0;
01175       startRaceTicker = TFC_Ticker[0];  // keep track of start of race
01176       logDataIndex = 0;                 // reset log data index
01177     }
01178   }
01179   
01180   // STOP!
01181   // if going, stop when button A is pressed
01182   if (go) {              
01183    if(TFC_PUSH_BUTTON_1_PRESSED) {
01184       go = false;
01185       StartGateFoundCount = 0;
01186     }
01187   }
01188 
01189   // EMERGENCY STOP!
01190   // 'kill switch' to prevent crashes off-track
01191   if (killSwitch) {
01192     if (UnknownCount > UNKNOWN_COUNT_MAX) {  // if track not found after certain time
01193       go = false;                            // kill engine
01194       StartGateFoundCount = 0;
01195     }
01196   }
01197 
01198 // ****************
01199   
01200   if (!go) { // stop!
01201     TFC_SetMotorPWM(0,0); //Make sure motors are off 
01202     TFC_HBRIDGE_DISABLE;
01203   }
01204 
01205   if (go) {  // go!
01206     TFC_HBRIDGE_ENABLE;
01207     // motor A = right, motor B = left based on way it is mounted
01208     TFC_SetMotorPWM(CurrentRightDriveSetting,CurrentLeftDriveSetting);
01209   }
01210 }
01211 
01212 
01213 void adjustLights()
01214 {
01215 
01216   // LIGHT ADJUST METHOD 1
01217   // threshold is 1/5 of light intensity 'range'
01218   if (1 == 0) {
01219     DerivThreshold = (float) (MaxLightIntensity - MinLightIntensity) / 5;
01220     NegDerivThreshold = (float) -1 * (DerivThreshold);
01221     PosDerivThreshold = (float) (DerivThreshold);
01222   } else {
01223   // LIGHT ADJUST METHOD 2 -- SEEMS TO WORK MUCH BETTER
01224   // pos edge threshold is half range of max deriv above aver derive
01225   // neg edge threshold is half range of min deriv above aver derive
01226    
01227     NegDerivThreshold = (float) (minDerVal - aveDerVal) * DER_RATIO;
01228     PosDerivThreshold = (float) (maxDerVal - aveDerVal) * DER_RATIO;
01229   
01230   }
01231 
01232   printAdjustLightsData();
01233 
01234 }
01235 
01236 void printAdjustLightsData()
01237 {
01238   if (terminalOutput == 3) {
01239     TERMINAL_PRINTF("Max Light Intensity: %4d\r\n", MaxLightIntensity);
01240     TERMINAL_PRINTF("Min Light Intensity: %4d\r\n", MinLightIntensity);
01241     TERMINAL_PRINTF("Deriv Threshold: %9.3f\r\n", DerivThreshold);
01242   }
01243 
01244 }
01245 
01246 void feedbackLights()
01247 {
01248    switch (CurrentTrackStatus)
01249    {
01250      case LineFound:
01251        TFC_BAT_LED0_OFF;
01252        TFC_BAT_LED1_ON;     
01253        TFC_BAT_LED2_ON;
01254        TFC_BAT_LED3_OFF;
01255        break;
01256      case StartGateFound:
01257        TFC_BAT_LED0_ON;
01258        TFC_BAT_LED1_OFF;     
01259        TFC_BAT_LED2_OFF;
01260        TFC_BAT_LED3_ON;   
01261        break;
01262      default:
01263        TFC_BAT_LED0_OFF;
01264        TFC_BAT_LED1_OFF;     
01265        TFC_BAT_LED2_OFF;
01266        TFC_BAT_LED3_OFF;
01267    }
01268     
01269 }
01270