blob: f09ec646bc0ac5eda2e21dda6ed4dd0afb8e174f [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2014. All Rights Reserved.
3 */
4/* Open Source Software - may be modified and shared by FRC teams. The code */
5/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
6/*----------------------------------------------------------------------------*/
7#include <stdio.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <unistd.h>
11#include <string.h>
12#include <math.h>
13#include <stdlib.h>
14#include <stdarg.h>
15
16#include "Vision/BaeUtilities.h"
17#include "Servo.h"
18#include "Timer.h"
19
20/** @file
21 * Utility functions
22 */
23
24/**
25 * debug output flag options:
26 * DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY,
27 * DEBUG_SCREEN_AND_FILE
28 */
29static DebugOutputType dprintfFlag = DEBUG_OFF;
30
31/**
32 * Set the debug flag to print to screen, file on cRIO, both or neither
33 * @param tempString The format string.
34 */
35void SetDebugFlag(DebugOutputType flag) { dprintfFlag = flag; }
36
37/**
38 * Debug print to a file and/or a terminal window.
39 * Call like you would call printf.
40 * Set functionName in the function if you want the correct function name to
41 * print out.
42 * The file line number will also be printed.
43 * @param tempString The format string.
44 */
45void dprintf(const char *tempString, ...) /* Variable argument list */
46{
47 va_list args; /* Input argument list */
48 int line_number; /* Line number passed in argument */
49 int type;
50 const char *functionName; /* Format passed in argument */
51 const char *fmt; /* Format passed in argument */
52 char text[512]; /* Text string */
53 char outtext[512]; /* Text string */
54 FILE *outfile_fd; /* Output file pointer */
55 char filepath[128]; /* Text string */
56 int fatalFlag = 0;
57 const char *filename;
58 int index;
59 int tempStringLen;
60
61 if (dprintfFlag == DEBUG_OFF) {
62 return;
63 }
64
65 va_start(args, tempString);
66
67 tempStringLen = strlen(tempString);
68 filename = tempString;
69 for (index = 0; index < tempStringLen; index++) {
70 if (tempString[index] == ' ') {
71 printf("ERROR in dprintf: malformed calling sequence (%s)\n", tempString);
72 va_end(args);
73 return;
74 }
75 if (tempString[index] == '\\' || tempString[index] == '/')
76 filename = tempString + index + 1;
77 }
78
79 /* Extract function name */
80 functionName = va_arg(args, const char *);
81
82 /* Extract line number from argument list */
83 line_number = va_arg(args, int);
84
85 /* Extract information type from argument list */
86 type = va_arg(args, int);
87
88 /* Extract format from argument list */
89 fmt = va_arg(args, const char *);
90
91 vsprintf(text, fmt, args);
92
93 va_end(args);
94
95 /* Format output statement */
96 switch (type) {
97 case DEBUG_TYPE:
98 sprintf(outtext, "[%s:%s@%04d] DEBUG %s\n", filename, functionName,
99 line_number, text);
100 break;
101 case INFO_TYPE:
102 sprintf(outtext, "[%s:%s@%04d] INFO %s\n", filename, functionName,
103 line_number, text);
104 break;
105 case ERROR_TYPE:
106 sprintf(outtext, "[%s:%s@%04d] ERROR %s\n", filename, functionName,
107 line_number, text);
108 break;
109 case CRITICAL_TYPE:
110 sprintf(outtext, "[%s:%s@%04d] CRITICAL %s\n", filename, functionName,
111 line_number, text);
112 break;
113 case FATAL_TYPE:
114 fatalFlag = 1;
115 sprintf(outtext, "[%s:%s@%04d] FATAL %s\n", filename, functionName,
116 line_number, text);
117 break;
118 default:
119 printf("ERROR in dprintf: malformed calling sequence\n");
120 return;
121 break;
122 }
123
124 sprintf(filepath, "%s.debug", filename);
125
126 /* Write output statement */
127 switch (dprintfFlag) {
128 default:
129 case DEBUG_OFF:
130 break;
131 case DEBUG_MOSTLY_OFF:
132 if (fatalFlag) {
133 if ((outfile_fd = fopen(filepath, "a+")) != nullptr) {
134 fwrite(outtext, sizeof(char), strlen(outtext), outfile_fd);
135 fclose(outfile_fd);
136 }
137 }
138 break;
139 case DEBUG_SCREEN_ONLY:
140 printf("%s", outtext);
141 break;
142 case DEBUG_FILE_ONLY:
143 if ((outfile_fd = fopen(filepath, "a+")) != nullptr) {
144 fwrite(outtext, sizeof(char), strlen(outtext), outfile_fd);
145 fclose(outfile_fd);
146 }
147 break;
148 case DEBUG_SCREEN_AND_FILE: // BOTH
149 printf("%s", outtext);
150 if ((outfile_fd = fopen(filepath, "a+")) != nullptr) {
151 fwrite(outtext, sizeof(char), strlen(outtext), outfile_fd);
152 fclose(outfile_fd);
153 }
154 break;
155 }
156}
157
158/**
159 * @brief Normalizes a value in a range, used for drive input
160 * @param position The position in the range, starting at 0
161 * @param range The size of the range that position is in
162 * @return The normalized position from -1 to +1
163 */
164double RangeToNormalized(double position, int range) {
165 return (((position * 2.0) / (double)range) - 1.0);
166}
167
168/**
169 * @brief Convert a normalized value to the corresponding value in a range.
170 * This is used to convert normalized values to the servo command range.
171 * @param normalizedValue The normalized value (in the -1 to +1 range)
172 * @param minRange The minimum of the range (0 is default)
173 * @param maxRange The maximum of the range (1 is default)
174 * @return The value in the range corresponding to the input normalized value
175 */
176float NormalizeToRange(float normalizedValue, float minRange, float maxRange) {
177 float range = maxRange - minRange;
178 float temp = (float)((normalizedValue / 2.0) + 0.5) * range;
179 return (temp + minRange);
180}
181float NormalizeToRange(float normalizedValue) {
182 return (float)((normalizedValue / 2.0) + 0.5);
183}
184
185/**
186 * @brief Displays an activity indicator to console.
187 * Call this function like you would call printf.
188 * @param fmt The format string
189*/
190void ShowActivity(char *fmt, ...) {
191 static char activity_indication_string[] = "|/-\\";
192 static int ai = 3;
193 va_list args;
194 char text[1024];
195
196 va_start(args, fmt);
197
198 vsprintf(text, fmt, args);
199
200 ai = ai == 3 ? 0 : ai + 1;
201
202 printf("%c %s \r", activity_indication_string[ai], text);
203 fflush(stdout);
204
205 va_end(args);
206}
207
208#define PI 3.14159265358979
209/**
210 * @brief Calculate sine wave increments (-1.0 to 1.0).
211 * The first time this is called, it sets up the time increment. Subsequent
212 * calls
213 * will give values along the sine wave depending on current time. If the wave
214 * is
215 * stopped and restarted, it must be reinitialized with a new "first call".
216 *
217 * @param period length of time to complete a complete wave
218 * @param sinStart Where to start the sine wave (0.0 = 2 pi, pi/2 = 1.0, etc.)
219 */
220double SinPosition(double *period, double sinStart) {
221 double rtnVal;
222 static double sinePeriod = 0.0;
223 static double timestamp;
224 double sinArg;
225
226 // 1st call
227 if (period != nullptr) {
228 sinePeriod = *period;
229 timestamp = GetTime();
230 return 0.0;
231 }
232
233 // Multiplying by 2*pi to the time difference makes sinePeriod work if it's
234 // measured in seconds.
235 // Adding sinStart to the part multiplied by PI, but not by 2, allows it to
236 // work as described in the comments.
237 sinArg = PI * ((2.0 * (GetTime() - timestamp)) + sinStart) / sinePeriod;
238 rtnVal = sin(sinArg);
239 return (rtnVal);
240}
241
242/**
243 * @brief Find the elapsed time since a specified time.
244 * @param startTime The starting time
245 * @return How long it has been since the starting time
246 */
247double ElapsedTime(double startTime) {
248 double realTime = GetTime();
249 return (realTime - startTime);
250}
251
252/**
253 * @brief Initialize pan parameters
254 * @param period The number of seconds to complete one pan
255 */
256void panInit() {
257 double period = 3.0; // number of seconds for one complete pan
258 SinPosition(&period, 0.0); // initial call to set up time
259}
260
261void panInit(double period) {
262 if (period < 0.0) period = 3.0;
263 SinPosition(&period, 0.0); // initial call to set up time
264}
265
266/**
267 * @brief Move the horizontal servo back and forth.
268 * @param panServo The servo object to move
269 * @param sinStart The position on the sine wave to begin the pan
270 */
271void panForTarget(Servo *panServo) { panForTarget(panServo, 0.0); }
272
273void panForTarget(Servo *panServo, double sinStart) {
274 float normalizedSinPosition = (float)SinPosition(nullptr, sinStart);
275 float newServoPosition = NormalizeToRange(normalizedSinPosition);
276 panServo->Set(newServoPosition);
277 // ShowActivity ("pan x: normalized %f newServoPosition = %f" ,
278 // normalizedSinPosition, newServoPosition );
279}
280
281/** @brief Read a file and return non-comment output string
282
283Call the first time with 0 lineNumber to get the number of lines to read
284Then call with each lineNumber to get one camera parameter. There should
285be one property=value entry on each line, i.e. "exposure=auto"
286
287 * @param inputFile filename to read
288 * @param outputString one string
289 * @param lineNumber if 0, return number of lines; else return that line number
290 * @return int number of lines or -1 if error
291 **/
292int processFile(char *inputFile, char *outputString, int lineNumber) {
293 FILE *infile;
294 const int stringSize = 80; // max size of one line in file
295 char inputStr[stringSize];
296 inputStr[0] = '\0';
297 int lineCount = 0;
298
299 if (lineNumber < 0) return (-1);
300
301 if ((infile = fopen(inputFile, "r")) == nullptr) {
302 printf("Fatal error opening file %s\n", inputFile);
303 return (0);
304 }
305
306 while (!feof(infile)) {
307 if (fgets(inputStr, stringSize, infile) != nullptr) {
308 // Skip empty lines
309 if (emptyString(inputStr)) continue;
310 // Skip comment lines
311 if (inputStr[0] == '#' || inputStr[0] == '!') continue;
312
313 lineCount++;
314 if (lineNumber == 0)
315 continue;
316 else {
317 if (lineCount == lineNumber) break;
318 }
319 }
320 }
321
322 // close file
323 fclose(infile);
324 // if number lines requested return the count
325 if (lineNumber == 0) return (lineCount);
326 // check for input out of range
327 if (lineNumber > lineCount) return (-1);
328 // return the line selected; lineCount guaranteed to be greater than zero
329 stripString(inputStr);
330 strcpy(outputString, inputStr);
331 return (lineCount);
332}
333
334/** Ignore empty string
335 * @param string to check if empty
336 **/
337int emptyString(char *string) {
338 int i, len;
339
340 if (string == nullptr) return (1);
341
342 len = strlen(string);
343 for (i = 0; i < len; i++) {
344 // Ignore the following characters
345 if (string[i] == '\n' || string[i] == '\r' || string[i] == '\t' ||
346 string[i] == ' ')
347 continue;
348 return (0);
349 }
350 return (1);
351}
352
353/** Remove special characters from string
354 * @param string to process
355 **/
356void stripString(char *string) {
357 int i, j, len;
358
359 if (string == nullptr) return;
360
361 len = strlen(string);
362 for (i = 0, j = 0; i < len; i++) {
363 // Remove the following characters from the string
364 if (string[i] == '\n' || string[i] == '\r' || string[i] == '\"') continue;
365 // Copy anything else
366 string[j++] = string[i];
367 }
368 string[j] = '\0';
369}