diff options
author | Nick Van Doorn <vandoorn.nick@gmail.com> | 2019-03-05 15:46:49 -0800 |
---|---|---|
committer | Nick Van Doorn <vandoorn.nick@gmail.com> | 2019-03-05 15:46:49 -0800 |
commit | be9c7cd92384c7044c65a83ebc846c15963ff9ed (patch) | |
tree | c402d13ad92d6d10d1f467e7a8c63c42693c28f6 | |
parent | 51f80a20fd057705ad9fb7a3ff781dbcaf72dd83 (diff) |
Add tests
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | core.c | 280 | ||||
-rw-r--r-- | core.h | 29 | ||||
-rw-r--r-- | main.c | 296 | ||||
-rw-r--r-- | test.c | 14 |
5 files changed, 331 insertions, 297 deletions
@@ -2,9 +2,9 @@ # http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/makefile.4 CC=gcc CFLAGS=-I. -Wall -DEPS= -OBJ= -MAIN_OBJ = main.o +DEPS=core.h +OBJ=core.o +MAIN_OBJ=main.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) @@ -12,6 +12,9 @@ MAIN_OBJ = main.o main: $(OBJ) $(MAIN_OBJ) $(CC) -o $@ $^ $(CFLAGS) +test: $(OBJ) test.o + $(CC) -o $@ $^ $(CFLAGS) + .PHONY: clean clean: rm -rf *.o @@ -0,0 +1,280 @@ +#include "core.h" +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define FAKE_SENSOR_VAL 50 +#define MOTOR_OFFSET_RIGHT 0 +#define MOTOR_OFFSET_LEFT 0 + +#define POWER_LEVEL_LOW 30 +#define POWER_LEVEL_MED 50 +#define POWER_LEVEL_HIGH 70 +#define SEND_IT 100 + +#define IR_THRESHOLD_STRAIGHT 50 +#define IR_THRESHOLD_HAS_SIGNAL 200 +#define MIN_FREE_FORWARD_SPACE 50 // cm + +#define MOVE_TIME_SEARCH 1000 + +#define POWER_LEVEL_APPROACH 20 +#define MOVE_TIME_APPROACH 500 + +enum RobotCorrection_t { MOVE_STRAIGHT = 0, MOVE_RIGHT = 1, MOVE_LEFT = 2 }; + +// use a global +struct RobotContext_t { + enum RobotState_t state; + enum RobotState_t prevState; +}; + +int randomIntInRange(int upper, int lower) { + return rand() % (upper + 1 - lower) + lower; +} + +void waitMs(int waitTimeMs) { usleep(waitTimeMs * 1000); } + +char *stateToString(enum RobotState_t state) { + switch (state) { + case IDLE: + return "IDLE"; + case SEARCHING: + return "SEARCHING"; + case ALIGNMENT: + return "ALIGNMENT"; + case DROP_OBJECT: + return "DROP_OBJECT"; + case COMPLETE: + return "COMPLETE"; + case COLLISION: + return "COLLISION"; + default: + return "STATE OUT OF BOUNDS"; + } +} + +void setMotor(int motorNumber, int powerLevel) { + printf("Setting motor #%d to %d power level\n", motorNumber, powerLevel); +} + +int readSensor(char *sensorName) { + int val = randomIntInRange(500, -500); + printf("Reading %d from sensor %s\n", val, sensorName); + // all sensors can return an int in [-500,500] + return val; +} + +int readIrRight() { return readSensor("Photo Transistor Right"); } + +int readIrLeft() { return readSensor("Photo Transistor Left"); } + +enum RobotCorrection_t readIrDirection() { + int irRight, irLeft, irDiff; + bool diffInRange, hasRightSignal, hasLeftSignal; + irRight = readIrRight(); + irLeft = readIrLeft(); + irDiff = irLeft - irRight; + diffInRange = irDiff < IR_THRESHOLD_STRAIGHT; + hasRightSignal = irRight > IR_THRESHOLD_HAS_SIGNAL; + hasLeftSignal = irLeft > IR_THRESHOLD_HAS_SIGNAL; + if (diffInRange && hasRightSignal && hasLeftSignal) + return MOVE_STRAIGHT; + else if (irDiff < 0) { + return MOVE_RIGHT; + } else { + return MOVE_LEFT; + } +} + +int readForwardDistance() { return readSensor("Sonic Distance Sensor"); } + +// TODO +void sonicSensorCorrection() {} + +void dropObject() { printf("Dropping object...\n"); } + +void controlDriveMotors(int powerLevelLeft, int powerLevelRight) { + // motor "1" is the left wheel + setMotor(1, powerLevelLeft + MOTOR_OFFSET_LEFT); + // motor "2" is the right wheel; + setMotor(2, powerLevelRight + MOTOR_OFFSET_RIGHT); +} + +void driveStraight(int powerLevel, int time) { + controlDriveMotors(powerLevel, powerLevel); + waitMs(time); + controlDriveMotors(0, 0); +} + +void turnRight(int powerLevel, int time) { + controlDriveMotors(powerLevel, -powerLevel); + waitMs(time); + controlDriveMotors(0, 0); +} + +void turnLeft(int powerLevel, int time) { + controlDriveMotors(-powerLevel, powerLevel); + waitMs(time); + controlDriveMotors(0, 0); +} + +// TODO +bool checkForCollisions() { return false; } + +void waitForStartButton() { + char c; + while (true) { + c = getchar(); + if (c == 'n') { + return; + } + } +} + +void handleIdle(struct RobotContext_t *ctx) { + // wait for start button + waitForStartButton(); + // and then go into our "main" + // searching state + ctx->state = SEARCHING; +} + +bool shouldLeaveSearching() { return true; } + +/** + * How do we want our search to work? + * + * Each time we run this, + * we'll follow roughly the steps below + * + * 1. Check how much space is directly ahead of us. + * If there is only a small amount (a few cm or less), + * we should turn until we have more "search space". + * This makes sure we're never pointed at a wall + * + * 2. Look at the difference between the readings + * on the two phototransistors. + * + * If the diff ~ 0 and signal levels meet a + * certain minimum threshold, then we should proceed + * straight for a short distance + * + * If the diff > 0, we should turn until + * this difference gets closer. If it strats increasing, + * back track and turn the other way. + */ +void handleSearching(struct RobotContext_t *ctx) { + enum RobotCorrection_t move; + if (readForwardDistance() < MIN_FREE_FORWARD_SPACE) { + sonicSensorCorrection(); + } + move = readIrDirection(); + switch (move) { + case MOVE_STRAIGHT: + driveStraight(POWER_LEVEL_MED, MOVE_TIME_SEARCH); + break; + case MOVE_RIGHT: + turnRight(POWER_LEVEL_LOW, MOVE_TIME_SEARCH); + break; + case MOVE_LEFT: + turnLeft(POWER_LEVEL_LOW, MOVE_TIME_SEARCH); + break; + } + if (shouldLeaveSearching()) { + ctx->state = ALIGNMENT; + } +}; + +/** + * This state is reponsible for the approach, + * so if things get too inaccurate, you kick back + * to the SEARCHING state + * + * TODO + */ +void handleAlignment(struct RobotContext_t *ctx) { + enum RobotCorrection_t move = readIrDirection(); + // if we aren't still aligned + // to move straight, kick back + // to the searching state, + // and bail hard on this + if (move != MOVE_STRAIGHT) { + ctx->state = SEARCHING; + return; + } +} + +void handleDrop(struct RobotContext_t *ctx) { + dropObject(); + waitMs(1000); + driveStraight(-POWER_LEVEL_MED, 2000); +} + +void handleComplete(struct RobotContext_t *ctx) { printf("Done!\n"); } + +void handleCollision(struct RobotContext_t *ctx) { + // we must search again before returning + // to the alignment state after recovering from + // a collision + if (ctx->prevState == ALIGNMENT) { + ctx->state = SEARCHING; + } else { + ctx->state = ctx->prevState; + } +} + +void startStateMachine(struct RobotContext_t *ctx) { + bool inCollisionState; + while (true) { + printf("Current state %s\n", stateToString(ctx->state)); + // we assume one of routines called in the switch + // statement changes ctx->state, so we make a backup + // of it before any of those routines run + ctx->prevState = ctx->state; + inCollisionState = ctx->state == COLLISION; + if (checkForCollisions() && !inCollisionState) { + ctx->state = COLLISION; + } + switch (ctx->state) { + case IDLE: + handleIdle(ctx); + break; + case SEARCHING: + handleSearching(ctx); + break; + case ALIGNMENT: + handleAlignment(ctx); + break; + case DROP_OBJECT: + handleDrop(ctx); + break; + case COMPLETE: + handleComplete(ctx); + break; + case COLLISION: + handleCollision(ctx); + break; + } + } +} +/** + * Problem description: + * + * 1. Robot must locate infrared beacon + * 2. Get within a certain distance to the infrared beacon + * 3. Drop object on beacon + * 4. Signal completion somehow + * + * * must avoid walls as best possible + */ +void core() { + // RobotContext_t refers to the "state of the world" + // and "context of our robot" in our current enviornment + + // initial state is idle + struct RobotContext_t ctx = {.state = IDLE}; + struct RobotContext_t *ctxPtr = &ctx; + startStateMachine(ctxPtr); +} @@ -0,0 +1,29 @@ +#ifndef AMERICANO_MAIN_H +#define AMERICANO_MAIN_H + +enum RobotState_t { + // right at the start before + // we do anything + IDLE = 0, + // This is the state we'll likely spend + // the most time in (looking for the beacon) + SEARCHING = 1, + // When we're near the beacon + // but not quite aligned for a drop yet + ALIGNMENT = 2, + // In position to drop the object + DROP_OBJECT = 3, + // signal that we're done somehow + // and stop moving + COMPLETE = 4, + // special state that we use to recover + // from collisions (can be thought of + // like an interrupt state) + COLLISION = 5 +}; + +char *stateToString(enum RobotState_t state); + +void core(); + +#endif @@ -1,295 +1,3 @@ -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> +#include <core.h> -#define FAKE_SENSOR_VAL 50 -#define MOTOR_OFFSET_RIGHT 0 -#define MOTOR_OFFSET_LEFT 0 - -#define POWER_LEVEL_LOW 30 -#define POWER_LEVEL_MED 50 -#define POWER_LEVEL_HIGH 70 -#define SEND_IT 100 - -#define IR_THRESHOLD_STRAIGHT 50 -#define IR_THRESHOLD_HAS_SIGNAL 200 -#define MIN_FREE_FORWARD_SPACE 50 // cm - -#define MOVE_TIME_SEARCH 1000 - -#define POWER_LEVEL_APPROACH 20 -#define MOVE_TIME_APPROACH 500 - -enum RobotState_t { - // right at the start before - // we do anything - IDLE = 0, - // This is the state we'll likely spend - // the most time in (looking for the beacon) - SEARCHING = 1, - // When we're near the beacon - // but not quite aligned for a drop yet - ALIGNMENT = 2, - // In position to drop the object - DROP_OBJECT = 3, - // signal that we're done somehow - // and stop moving - COMPLETE = 4, - // special state that we use to recover - // from collisions (can be thought of - // like an interrupt state) - COLLISION = 5 -}; - -enum RobotCorrection_t { MOVE_STRAIGHT = 0, MOVE_RIGHT = 1, MOVE_LEFT = 2 }; - -// use a global -struct RobotContext_t { - enum RobotState_t state; - enum RobotState_t prevState; -}; - -void waitMs(int waitTimeMs) { usleep(waitTimeMs * 1000); } - -char *stateToString(enum RobotState_t state) { - switch (state) { - case IDLE: - return "IDLE"; - case SEARCHING: - return "SEARCHING"; - case ALIGNMENT: - return "ALIGNMENT"; - case DROP_OBJECT: - return "DROP_OBJECT"; - case COMPLETE: - return "COMPLETE"; - case COLLISION: - return "COLLISION"; - default: - return "STATE OUT OF BOUNDS"; - } -} - -void setMotor(int motorNumber, int powerLevel) { - printf("Setting motor #%d to %d power level", motorNumber, powerLevel); -} - -int readSensor(char *sensorName) { - printf("Reading from sensor %s\n", sensorName); - return FAKE_SENSOR_VAL; -} - -int readIrRight() { return readSensor("Photo Transistor Right"); } - -int readIrLeft() { return readSensor("Photo Transistor Left"); } - -enum RobotCorrection_t readIrDirection() { - int irRight, irLeft, irDiff; - bool diffInRange, hasRightSignal, hasLeftSignal; - irRight = readIrRight(); - irLeft = readIrLeft(); - irDiff = irLeft - irRight; - diffInRange = irDiff < IR_THRESHOLD_STRAIGHT; - hasRightSignal = irRight > IR_THRESHOLD_HAS_SIGNAL; - hasLeftSignal = irLeft > IR_THRESHOLD_HAS_SIGNAL; - if (diffInRange && hasRightSignal && hasLeftSignal) - return MOVE_STRAIGHT; - else if (irDiff < 0) { - return MOVE_RIGHT; - } else { - return MOVE_LEFT; - } -} - -int readForwardDistance() { return readSensor("Sonic Distance Sensor"); } - -// TODO -// normal comment -void sonicSensorCorrection() {} - -void dropObject() { printf("Dropping object...\n"); } - -void controlDriveMotors(int powerLevelLeft, int powerLevelRight) { - // motor "1" is the left wheel - setMotor(1, powerLevelLeft + MOTOR_OFFSET_LEFT); - // motor "2" is the right wheel; - setMotor(2, powerLevelRight + MOTOR_OFFSET_RIGHT); -} - -void driveStraight(int powerLevel, int time) { - controlDriveMotors(powerLevel, powerLevel); - waitMs(time); - controlDriveMotors(0, 0); -} - -void turnRight(int powerLevel, int time) { - controlDriveMotors(powerLevel, -powerLevel); - waitMs(time); - controlDriveMotors(0, 0); -} - -void turnLeft(int powerLevel, int time) { - controlDriveMotors(-powerLevel, powerLevel); - waitMs(time); - controlDriveMotors(0, 0); -} - -// TODO -bool checkForCollisions() { return false; } - -void waitForStartButton() { - char c; - while (true) { - c = getchar(); - if (c == 'n') { - return; - } - } -} - -void handleIdle(struct RobotContext_t *ctx) { - // wait for start button - waitForStartButton(); - // and then go into our "main" - // searching state - ctx->state = SEARCHING; -} - -bool shouldLeaveSearching() { return true; } - -/** - * How do we want our search to work? - * - * Each time we run this, - * we'll follow roughly the steps below - * - * 1. Check how much space is directly ahead of us. - * If there is only a small amount (a few cm or less), - * we should turn until we have more "search space". - * This makes sure we're never pointed at a wall - * - * 2. Look at the difference between the readings - * on the two phototransistors. - * - * If the diff ~ 0 and signal levels meet a - * certain minimum threshold, then we should proceed - * straight for a short distance - * - * If the diff > 0, we should turn until - * this difference gets closer. If it strats increasing, - * back track and turn the other way. - */ -void handleSearching(struct RobotContext_t *ctx) { - enum RobotCorrection_t move; - if (readForwardDistance() < MIN_FREE_FORWARD_SPACE) { - sonicSensorCorrection(); - } - move = readIrDirection(); - switch (move) { - case MOVE_STRAIGHT: - driveStraight(POWER_LEVEL_MED, MOVE_TIME_SEARCH); - break; - case MOVE_RIGHT: - turnRight(POWER_LEVEL_LOW, MOVE_TIME_SEARCH); - break; - case MOVE_LEFT: - turnLeft(POWER_LEVEL_LOW, MOVE_TIME_SEARCH); - break; - } - if (shouldLeaveSearching()) { - ctx->state = ALIGNMENT; - } -}; - -/** - * This state is reponsible for the approach, - * so if things get too inaccurate, you kick back - * to the SEARCHING state - * - * TODO - */ -void handleAlignment(struct RobotContext_t *ctx) { - enum RobotCorrection_t move = readIrDirection(); - // if we aren't still aligned - // to move straight, kick back - // to the searching state, - // and bail hard on this - if (move != MOVE_STRAIGHT) { - ctx->state = SEARCHING; - return; - } -} - -void handleDrop(struct RobotContext_t *ctx) { - dropObject(); - waitMs(1000); - driveStraight(-POWER_LEVEL_MED, 2000); -} - -void handleComplete(struct RobotContext_t *ctx) { printf("Done!\n"); } - -void handleCollision(struct RobotContext_t *ctx) { - // we must search again before returning - // to the alignment state after recovering from - // a collision - if (ctx->prevState == ALIGNMENT) { - ctx->state = SEARCHING; - } else { - ctx->state = ctx->prevState; - } -} - -void startStateMachine(struct RobotContext_t *ctx) { - bool inCollisionState; - while (true) { - printf("Current state %s\n", stateToString(ctx->state)); - // we assume one of routines called in the switch - // statement changes ctx->state, so we make a backup - // of it before any of those routines run - ctx->prevState = ctx->state; - inCollisionState = ctx->state == COLLISION; - if (checkForCollisions() && !inCollisionState) { - ctx->state = COLLISION; - } - switch (ctx->state) { - case IDLE: - handleIdle(ctx); - break; - case SEARCHING: - handleSearching(ctx); - break; - case ALIGNMENT: - handleAlignment(ctx); - break; - case DROP_OBJECT: - handleDrop(ctx); - break; - case COMPLETE: - handleComplete(ctx); - break; - case COLLISION: - handleCollision(ctx); - break; - } - } -} -/** - * Problem description: - * - * 1. Robot must locate infrared beacon - * 2. Get within a certain distance to the infrared beacon - * 3. Drop object on beacon - * 4. Signal completion somehow - * - * * must avoid walls as best possible - */ -int main() { - // RobotContext_t refers to the "state of the world" - // and "context of our robot" in our current enviornment - - // initial state is idle - struct RobotContext_t ctx = {.state = IDLE}; - struct RobotContext_t *ctxPtr = &ctx; - startStateMachine(ctxPtr); -} +int main() { core(); } @@ -0,0 +1,14 @@ +#include "core.h" +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main() { + char *result, *expected; + int isSame; + result = stateToString(SEARCHING); + expected = "SEARCHING"; + isSame = strcmp(expected, result) == 0; + assert(isSame); +} |