Moved to Team 9.

Fork of LineScan by Nicholas Gan

LineScan.cpp

Committer:
ng3600
Date:
2015-11-13
Revision:
33:cc3810ac5365
Parent:
28:3006d46dcec5
Child:
34:5c35287ff49e
Child:
35:ac9f45bd5352

File content as of revision 33:cc3810ac5365:

#include "LineScan.h"
#include <string.h>

#define THRESH 1000

#define MEAN_REF 10000      //ideal mean we should see
#define CAM_CTRL_GAIN 0.0003 //should be small

#define NEUTRAL 0
#define FIND_PEAK 1
#define FIND_TROUGH 2

#define IDX_ARY_LEN 10

uint16_t read1Bit(AnalogIn cam, DigitalOut *camClk){
    uint16_t pixel;
    
    //read pixel n
    pixel = cam.read_u16();
    
    //clock pulse for next pixel n + 1
    *camClk = 1;
    *camClk = 0;
    
    return pixel;  //return result as an uint16_t (16 bit integer)
}

void startRead(DigitalOut *camSi, DigitalOut *camClk){
    //pulse first clock cycle and start integral pin
    *camSi = 1;
    *camClk = 1;
    *camSi = 0;
    *camClk = 0;
}

float processFn(uint16_t *array, int arraySz, int* exposure, bool preferLeftLine, int *numLines){
    const static int frameLen = NUM_PIX -  2 * SKIP;
    int avg[frameLen];
    int diff[frameLen];
    int highest = 0;
    int lowest = 0;
    int total = 0;
    int h_idx_ary[IDX_ARY_LEN];
    int h_idx_pos = 1;
    int l_idx_ary[IDX_ARY_LEN];
    int l_idx_pos = 1;
    int state = NEUTRAL;
    int *l_walker, *h_walker;
    int i;
    float out = -1.0;
    
    //for AGC
    float exposureChange;
    
    //line counter initialization
    *numLines = 0;
    
    h_idx_ary[0] = -1;
    l_idx_ary[0] = -1;
    h_idx_ary[1] = -1;
    l_idx_ary[1] = -1;
    
    if(array){    
        avg[0] = array[SKIP - 1]/2 + array[SKIP]/2;
        diff[0] = 0;
        
        total += avg[0];    //AGC
        
        for(i = 1; i < frameLen; i++){
            avg[i] = array[i - 1 + SKIP]/2 + array[i + SKIP]/2;   //smoothing
            diff[i] = avg[i - 1] - avg[i];          //differential
            
            total += avg[i];    //AGC
            
            
            //Finite State Machine
            //problem in this section, rewrite? find peaks and troughs
            switch(state){
                case NEUTRAL:
                    //serial.printf("Neutral case in processFn\r\n");
                    if( diff[i] > THRESH ){
                        state = FIND_PEAK;
                        highest = diff[i];
                        h_idx_ary[h_idx_pos] = i;
                    }else if( diff[i] < -THRESH ){
                        state = FIND_TROUGH;
                        lowest = diff[i];
                        l_idx_ary[l_idx_pos] = i;
                    }
                    break;
                    
                case FIND_PEAK:
                    //serial.printf("Peak case in processFn\r\n");
                    if( diff[i] <= THRESH ){
                        state = NEUTRAL;
                        h_idx_pos++;
                        h_idx_ary[h_idx_pos] = -1;  //last element is always -1
                    }
                    else if(diff[i] > highest){
                        highest = diff[i];
                        h_idx_ary[h_idx_pos] = i;  //insert to array, clobbers
                    }
                    break;
                    
                case FIND_TROUGH:
                    //serial.printf("Trough case in processFn\r\n");
                    if( diff[i] >= -THRESH ){
                        state = NEUTRAL;
                        l_idx_pos++;
                        l_idx_ary[l_idx_pos] = -1;  //last element is always -1
                    }
                    else if(diff[i] < lowest){
                        lowest = diff[i];
                        l_idx_ary[l_idx_pos] = i;  //insert to array, clobbers
                    }
                    break;
                    
                default:
                    //exit case if exception happened
                    serial.printf("Default case in processFn\r\n");
                    return out;
            }
        }
        //AGC, simple proportional controller
        total = total / frameLen;
        exposureChange = ((float)(MEAN_REF - total)) * CAM_CTRL_GAIN;    
        *exposure += (int)exposureChange;
        if(*exposure < 0)
            *exposure = 0;
        else if(*exposure > UPDATE_RATE)
            *exposure = UPDATE_RATE;
    }
    if(!preferLeftLine){ //TODO verify
        //search left to right and return first result
        l_walker = l_idx_ary + 1;
        h_walker = h_idx_ary + 1;
        
        while(*l_walker != -1 && *h_walker != -1 && out < 0){
            //evaluate out and advance if black line on white background and returns center of smallest black band
            //if interval is black on white, advance the pointer for the peak array
            //width needs to be larger than 2 pixels wide to be a good read.
            
            if(*h_walker > *l_walker && (*h_walker - *l_walker) < 10 && (*h_walker - *l_walker) > 2){
                out = ((float)(*h_walker + *l_walker)) / (2.0 * (float)frameLen);    //0.5 is center
                *numLines++;
                h_walker++;
            }
            l_walker++;
        }
    } else {
         //search right to left and return first result
        l_walker = l_idx_ary + IDX_ARY_LEN - 1;
        h_walker = h_idx_ary + IDX_ARY_LEN - 1;
        
        while(*l_walker != -1 && *h_walker != -1 && out < 0){
            //evaluate out and advance if black line on white background and returns center of smallest black band
            //if interval is black on white, advance the pointer for the peak array
            //width needs to be larger than 2 pixels wide to be a good read.
            
            if(*h_walker > *l_walker && (*h_walker - *l_walker) < 10 && (*h_walker - *l_walker) > 2){
                out = ((float)(*h_walker + *l_walker)) / (2.0 * (float)frameLen);    //0.5 is center
                *numLines++;
                l_walker--;
            }
            h_walker--;
        }
    }
    
    return out;
}

//call after integration time is done, returns index of array line is expected to be at and new exposure time
float getLinePos(AnalogIn cam, 
                 DigitalOut *camSi, 
                 DigitalOut *camClk, 
                 int *exposure, 
                 bool preferLeft,
                 int *lineCt,
                 telemetry::NumericArray<uint16_t, NUM_PIX> &tele_linescan){
    uint16_t lineAry[NUM_PIX];
    float position;
    
    //read 
    startRead(camSi, camClk);
    for(int i = 0; i < NUM_PIX; i++){
        lineAry[i] = read1Bit(cam, camClk);
        tele_linescan[i] = lineAry[i];
    }
    
    //process line scan data
    position  = processFn(lineAry, NUM_PIX, exposure, preferLeft, lineCt);        //TODO integrate line side preference
    
    return position;
}

/*
sample call (handler thread):

while(1){
   linePos  = getLinePos(cameraIn1, si, clk, &exposureTime);   //volatile linePos, replace with steering angle and read 2 cameras?
   Thread::wait(exposureTime);    //sleep for 14 us
}
*/