| /******************************************************************************** |
| * Project : FIRST Motor Controller |
| * File Name : BaeUtilities.cpp |
| * Contributors : JDG, ELF, EMF |
| * Creation Date : July 20, 2008 |
| * Revision History : Source code & revision history maintained at sourceforge.WPI.edu |
| * File Description : Open source utility extensions for FIRST Vision API. |
| */ |
| /*----------------------------------------------------------------------------*/ |
| /* Copyright (c) FIRST 2008. All Rights Reserved. */ |
| /* Open Source Software - may be modified and shared by FRC teams. The code */ |
| /* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */ |
| /*----------------------------------------------------------------------------*/ |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <math.h> |
| #include <stdioLib.h> |
| |
| #include "BaeUtilities.h" |
| #include "Servo.h" |
| #include "Timer.h" |
| |
| /** |
| * Utility functions |
| */ |
| |
| /** |
| * debug output flag options: |
| * DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY, DEBUG_SCREEN_AND_FILE |
| */ |
| static DebugOutputType dprintfFlag = DEBUG_OFF; |
| |
| /** |
| * Set the debug flag to print to screen, file on cRIO, both or neither |
| * @param tempString The format string. |
| */ |
| void SetDebugFlag ( DebugOutputType flag ) |
| { dprintfFlag = flag; } |
| |
| /** |
| * Debug print to a file and/or a terminal window. |
| * Call like you would call printf. |
| * Set functionName in the function if you want the correct function name to print out. |
| * The file line number will also be printed. |
| * @param tempString The format string. |
| */ |
| void dprintf ( char * tempString, ... ) /* Variable argument list */ |
| { |
| va_list args; /* Input argument list */ |
| int line_number; /* Line number passed in argument */ |
| int type; |
| char *functionName; /* Format passed in argument */ |
| char *fmt; /* Format passed in argument */ |
| char text[512]; /* Text string */ |
| char outtext[512]; /* Text string */ |
| FILE *outfile_fd; /* Output file pointer */ |
| char filepath[128]; /* Text string */ |
| int fatalFlag=0; |
| char *filename; |
| int index; |
| int tempStringLen; |
| |
| if (dprintfFlag == DEBUG_OFF) { return; } |
| |
| va_start (args, tempString); |
| |
| tempStringLen = strlen(tempString); |
| filename = tempString; |
| for (index=0;index<tempStringLen;index++){ |
| if (tempString[index] == ' ') { |
| printf( "ERROR in dprintf: malformed calling sequence (%s)\n",tempString);return; |
| } |
| if (tempString[index] == '\\' || tempString[index] == '/') |
| filename = tempString + index + 1; |
| } |
| |
| /* Extract function name */ |
| functionName = va_arg (args, char *); |
| |
| /* Extract line number from argument list */ |
| line_number = va_arg (args, int); |
| |
| /* Extract information type from argument list */ |
| type = va_arg (args, int); |
| |
| /* Extract format from argument list */ |
| fmt = va_arg (args, char *); |
| |
| vsprintf (text, fmt, args); |
| |
| va_end (args); |
| |
| /* Format output statement */ |
| switch (type) |
| { |
| case DEBUG_TYPE: |
| sprintf (outtext, "[%s:%s@%04d] DEBUG %s\n", |
| filename, functionName, line_number, text); |
| break; |
| case INFO_TYPE: |
| sprintf (outtext, "[%s:%s@%04d] INFO %s\n", |
| filename, functionName, line_number, text); |
| break; |
| case ERROR_TYPE: |
| sprintf (outtext, "[%s:%s@%04d] ERROR %s\n", |
| filename, functionName, line_number, text); |
| break; |
| case CRITICAL_TYPE: |
| sprintf (outtext, "[%s:%s@%04d] CRITICAL %s\n", |
| filename, functionName, line_number, text); |
| break; |
| case FATAL_TYPE: |
| fatalFlag = 1; |
| sprintf (outtext, "[%s:%s@%04d] FATAL %s\n", |
| filename, functionName, line_number, text); |
| break; |
| default: |
| printf( "ERROR in dprintf: malformed calling sequence\n"); |
| return; |
| break; |
| } |
| |
| sprintf (filepath, "%s.debug", filename); |
| |
| /* Write output statement */ |
| switch (dprintfFlag) |
| { |
| default: |
| case DEBUG_OFF: |
| break; |
| case DEBUG_MOSTLY_OFF: |
| if (fatalFlag) { |
| if ((outfile_fd = fopen (filepath, "a+")) != NULL) { |
| fwrite (outtext, sizeof (char), strlen (outtext), outfile_fd); |
| fclose (outfile_fd); |
| } |
| } |
| break; |
| case DEBUG_SCREEN_ONLY: |
| printf ("%s", outtext); |
| break; |
| case DEBUG_FILE_ONLY: |
| if ((outfile_fd = fopen (filepath, "a+")) != NULL) { |
| fwrite (outtext, sizeof (char), strlen (outtext), outfile_fd); |
| fclose (outfile_fd); |
| } |
| break; |
| case DEBUG_SCREEN_AND_FILE: // BOTH |
| printf ("%s", outtext); |
| if ((outfile_fd = fopen (filepath, "a+")) != NULL) { |
| fwrite (outtext, sizeof (char), strlen (outtext), outfile_fd); |
| fclose (outfile_fd); |
| } |
| break; |
| } |
| } |
| |
| /** |
| * @brief Normalizes a value in a range, used for drive input |
| * @param position The position in the range, starting at 0 |
| * @param range The size of the range that position is in |
| * @return The normalized position from -1 to +1 |
| */ |
| double RangeToNormalized(double position, int range){ |
| return(((position*2.0)/(double)range)-1.0); |
| } |
| |
| /** |
| * @brief Convert a normalized value to the corresponding value in a range. |
| * This is used to convert normalized values to the servo command range. |
| * @param normalizedValue The normalized value (in the -1 to +1 range) |
| * @param minRange The minimum of the range (0 is default) |
| * @param maxRange The maximum of the range (1 is default) |
| * @return The value in the range corresponding to the input normalized value |
| */ |
| float NormalizeToRange(float normalizedValue, float minRange, float maxRange) { |
| float range = maxRange-minRange; |
| float temp = (float)((normalizedValue / 2.0)+ 0.5)*range; |
| return (temp + minRange); |
| } |
| float NormalizeToRange(float normalizedValue) { |
| return (float)((normalizedValue / 2.0) + 0.5); |
| } |
| |
| /** |
| * @brief Displays an activity indicator to console. |
| * Call this function like you would call printf. |
| * @param fmt The format string |
| */ |
| void ShowActivity (char *fmt, ...) |
| { |
| static char activity_indication_string[] = "|/-\\"; |
| static int ai = 3; |
| va_list args; |
| char text[1024]; |
| |
| va_start (args, fmt); |
| |
| vsprintf (text, fmt, args); |
| |
| ai = ai == 3 ? 0 : ai + 1; |
| |
| printf ("%c %s \r", activity_indication_string[ai], text); |
| fflush (stdout); |
| |
| va_end (args); |
| } |
| |
| #define PI 3.14159265358979 |
| /** |
| * @brief Calculate sine wave increments (-1.0 to 1.0). |
| * The first time this is called, it sets up the time increment. Subsequent calls |
| * will give values along the sine wave depending on current time. If the wave is |
| * stopped and restarted, it must be reinitialized with a new "first call". |
| * |
| * @param period length of time to complete a complete wave |
| * @param sinStart Where to start the sine wave (0.0 = 2 pi, pi/2 = 1.0, etc.) |
| */ |
| double SinPosition (double *period, double sinStart) |
| { |
| double rtnVal; |
| static double sinePeriod=0.0; |
| static double timestamp; |
| double sinArg; |
| |
| //1st call |
| if (period != NULL) { |
| sinePeriod = *period; |
| timestamp = GetTime(); |
| return 0.0; |
| } |
| |
| //Multiplying by 2*pi to the time difference makes sinePeriod work if it's measured in seconds. |
| //Adding sinStart to the part multiplied by PI, but not by 2, allows it to work as described in the comments. |
| sinArg = PI *((2.0 * (GetTime() - timestamp)) + sinStart) / sinePeriod; |
| rtnVal = sin (sinArg); |
| return (rtnVal); |
| } |
| |
| |
| /** |
| * @brief Find the elapsed time since a specified time. |
| * @param startTime The starting time |
| * @return How long it has been since the starting time |
| */ |
| double ElapsedTime ( double startTime ) |
| { |
| double realTime = GetTime(); |
| return (realTime-startTime); |
| } |
| |
| /** |
| * @brief Initialize pan parameters |
| * @param period The number of seconds to complete one pan |
| */ |
| void panInit() { |
| double period = 3.0; // number of seconds for one complete pan |
| SinPosition(&period, 0.0); // initial call to set up time |
| } |
| |
| void panInit(double period) { |
| if (period < 0.0) period=3.0; |
| SinPosition(&period, 0.0); // initial call to set up time |
| } |
| |
| /** |
| * @brief Move the horizontal servo back and forth. |
| * @param panServo The servo object to move |
| * @param sinStart The position on the sine wave to begin the pan |
| */ |
| void panForTarget(Servo *panServo) { panForTarget(panServo, 0.0); } |
| |
| void panForTarget(Servo *panServo, double sinStart) { |
| float normalizedSinPosition = (float)SinPosition(NULL, sinStart); |
| float newServoPosition = NormalizeToRange(normalizedSinPosition); |
| panServo->Set( newServoPosition ); |
| //ShowActivity ("pan x: normalized %f newServoPosition = %f" , |
| // normalizedSinPosition, newServoPosition ); |
| } |
| |
| |
| /** @brief Read a file and return non-comment output string |
| |
| Call the first time with 0 lineNumber to get the number of lines to read |
| Then call with each lineNumber to get one camera parameter. There should |
| be one property=value entry on each line, i.e. "exposure=auto" |
| |
| * @param inputFile filename to read |
| * @param outputString one string |
| * @param lineNumber if 0, return number of lines; else return that line number |
| * @return int number of lines or -1 if error |
| **/ |
| int processFile(char *inputFile, char *outputString, int lineNumber) |
| { |
| FILE *infile; |
| int stringSize = 80; // max size of one line in file |
| char inputStr[stringSize]; |
| struct stat fileStatus; |
| int fileSize=0; |
| int lineCount=0; |
| |
| if (lineNumber < 0) |
| return (-1); |
| |
| if ((infile = fopen (inputFile, "r")) == NULL) { |
| printf ("Fatal error opening file %s\n",inputFile); |
| return (0); |
| } |
| memset (&fileStatus, 0, sizeof(fileStatus)); |
| if (!stat(inputFile, &fileStatus)) { |
| if (S_ISREG(fileStatus.st_mode)) { |
| fileSize = fileStatus.st_size; |
| } |
| } |
| |
| while (!feof(infile)) { |
| if (fgets (inputStr, stringSize, infile) != NULL) { |
| // Skip empty lines |
| if (emptyString(inputStr)) |
| continue; |
| // Skip comment lines |
| if (inputStr[0] == '#' || inputStr[0] == '!') |
| continue; |
| |
| lineCount++; |
| if (lineNumber == 0) |
| continue; |
| else |
| { |
| if (lineCount == lineNumber) |
| break; |
| } |
| } |
| } |
| |
| // close file |
| fclose (infile); |
| // if number lines requested return the count |
| if (lineNumber == 0) |
| return (lineCount); |
| // check for input out of range |
| if (lineNumber > lineCount) |
| return (-1); |
| // return the line selected |
| if (lineCount) { |
| stripString(inputStr); |
| strcpy(outputString, inputStr); |
| return(lineCount); |
| } |
| else { |
| return(-1); |
| } |
| } |
| |
| /** Ignore empty string |
| * @param string to check if empty |
| **/ |
| int emptyString(char *string) |
| { |
| int i,len; |
| |
| if(string == NULL) |
| return(1); |
| |
| len = strlen(string); |
| for(i=0; i<len; i++) { |
| // Ignore the following characters |
| if (string[i] == '\n' || string[i] == '\r' || |
| string[i] == '\t' || string[i] == ' ') |
| continue; |
| return(0); |
| } |
| return(1); |
| } |
| |
| /** Remove special characters from string |
| * @param string to process |
| **/ |
| void stripString(char *string) |
| { |
| int i,j,len; |
| |
| if(string == NULL) |
| return; |
| |
| len = strlen(string); |
| for(i=0,j=0; i<len; i++) { |
| // Remove the following characters from the string |
| if (string[i] == '\n' || string[i] == '\r' || string[i] == '\"') |
| continue; |
| // Copy anything else |
| string[j++] = string[i]; |
| } |
| string[j] = '\0'; |
| } |
| |
| |