Mercurial > public > ostc4
view Discovery/Src/motion.c @ 373:7b981f8bdd41 MotionDetection
Add scroll event by pitch angle detection:
Add a function to scroll through the custom view in case the computer is not in the typical center position
Refine Pitch Detection:
Changed implementation to use sectors for detection. Increased detection window
author | ideenmodellierer |
---|---|
date | Sun, 08 Sep 2019 13:45:17 +0200 |
parents | fca370f847f8 |
children | 834e087505ec |
line wrap: on
line source
/* * motion.c * * Created on: 20.05.2019 * Author: Thorsten Sonntag */ #include <stdint.h> #include <string.h> #include <stdlib.h> #include <math.h> #include "motion.h" #include "data_central.h" #include "t7.h" #include "settings.h" #define STABLE_STATE_COUNT 2 /* number of count to declare a state as stable (at the moment based on 100ms) */ #define STABLE_STATE_TIMEOUT 5 /* Detection shall be aborted if a movement state is stable for more than 500ms */ #define MOVE_DELTA_SPEED 4 /* Delta speed needed to identify a valid movement */ #define PITCH_DELTA_COUNT 10 /* Delta count needed to identify a valid minima / maxima */ #define PITCH_DELTA_END 10 /* Delta allowed between start and end position */ #define SECTOR_WINDOW 80.0 /* Pitch window which is used for custom view projection */ #define SECTOR_HYSTERY 3 /* Additional offset to avoid fast changing displays */ #define SECTOR_BORDER 400.0 /* Define a value which is out of limit to avoid not wanted key events */ #define SECTOR_FILTER 10 /* Define speed for calculated angle to follow real value */ #define SECTOR_MAX 20 /* maximum number of sectors */ #define SECTOR_SCROLL 6 /* number of sectors used for scroll detection */ detectionState_t detectionState = DETECT_NOTHING; uint8_t curSector; static uint8_t targetSector; static uint8_t sectorSize; static uint8_t sectorCount; SSector PitchSector[SECTOR_MAX]; /* max number of enabled custom views */ uint8_t GetSectorForPitch(float pitch) { static float lastPitch = 1000; float newPitch; uint8_t index; uint8_t sector = 0; if(lastPitch == 1000) /* init at first call */ { lastPitch = pitch; } newPitch = lastPitch + (pitch / SECTOR_FILTER); for(index = 1; index < sectorCount; index++) { if((pitch < PitchSector[index].upperlimit) && (pitch > PitchSector[index].lowerlimit )) { sector = index; break; } } lastPitch = newPitch; return sector; } void DefinePitchSectors(float centerPitch,uint8_t numOfSectors) { uint8_t index; if(numOfSectors == CUSTOMER_DEFINED_VIEWS) { sectorCount = t7_GetEnabled_customviews(); if(sectorCount > 7) { sectorCount = 7; /* more views are hard to manually control */ } } else if(numOfSectors != CUSTOMER_KEEP_LAST_SECTORS) { sectorCount = numOfSectors; } sectorSize = SECTOR_WINDOW / sectorCount; PitchSector[0].upperlimit = centerPitch + (SECTOR_WINDOW / 2); PitchSector[0].lowerlimit = PitchSector[0].upperlimit - sectorSize - SECTOR_HYSTERY; for(index = 1; index < sectorCount; index++) { PitchSector[index].upperlimit = PitchSector[0].upperlimit - index * sectorSize + SECTOR_HYSTERY; PitchSector[index].lowerlimit = PitchSector[0].upperlimit - (index + 1) * sectorSize - SECTOR_HYSTERY; } PitchSector[0].upperlimit = SECTOR_BORDER; PitchSector[index - 1].lowerlimit = SECTOR_BORDER * -1.0; /* get the current sector */ curSector = GetSectorForPitch(stateRealGetPointer()->lifeData.compass_pitch); targetSector = curSector; } void InitMotionDetection(void) { targetSector = 0; curSector = 0; sectorSize = 0; sectorCount = 0; switch(settingsGetPointer()->MotionDetection) { case MOTION_DETECT_SECTOR: DefinePitchSectors(0,CUSTOMER_DEFINED_VIEWS); break; case MOTION_DETECT_MOVE: DefinePitchSectors(0,SECTOR_MAX); break; case MOTION_DETECT_SCROLL: DefinePitchSectors(0,SECTOR_SCROLL); break; default: break; } } /* Map the current pitch value to a sector and create button event in case the sector is left */ detectionState_t detectSectorButtonEvent(float curPitch) { static uint8_t lastTargetSector = 0; uint8_t newTargetSector; uint8_t PitchEvent = DETECT_NOTHING; /* only change sector if reading is stable */ newTargetSector = GetSectorForPitch(stateRealGetPointer()->lifeData.compass_pitch); if(lastTargetSector == newTargetSector) { targetSector = newTargetSector; } lastTargetSector = newTargetSector; if(targetSector != curSector) { if(targetSector > curSector) { curSector++; PitchEvent = DETECT_POS_PITCH; } else { curSector--; PitchEvent = DETECT_NEG_PITCH; } } return PitchEvent; } /* Check if pitch is not in center position and trigger a button action if needed */ detectionState_t detectScrollButtonEvent(float curPitch) { static uint8_t delayscroll = 0; /* slow dow the number of scroll events */ uint8_t PitchEvent = DETECT_NOTHING; uint8_t newSector; if(delayscroll == 0) { newSector = GetSectorForPitch(stateRealGetPointer()->lifeData.compass_pitch); /* for scroll detection the motion windoe is split into 6 sectors => set event accoring to the sector number*/ switch(newSector) { case 0: case 1: PitchEvent = DETECT_POS_PITCH; break; case 4: case 5: PitchEvent = DETECT_NEG_PITCH; break; default: break; } if(PitchEvent != DETECT_NOTHING) { delayscroll = 5; } } else { delayscroll--; } return PitchEvent; } uint8_t sectorhist[20]; uint8_t sectorindex = 0; /* Detect if user is generating an pitch including return to starting position */ /* This is done by feeding the past movements value per value into a state machine */ detectionState_t detectPitch(float currentPitch) { static uint8_t lastSector = 0; static uint8_t startSector = 0; static uint8_t stableCnt = 0; if((detectionState == DETECT_NEG_PITCH) || (detectionState == DETECT_POS_PITCH)) /* discard last detection */ { detectionState = DETECT_NOTHING; } curSector = GetSectorForPitch(stateRealGetPointer()->lifeData.compass_pitch); sectorhist[sectorindex++] = curSector; if(sectorindex == 20) sectorindex=0; /* feed value into state machine */ switch (detectionState) { case DETECT_NOTHING: if(curSector == lastSector) /* detect a stable condition before evaluating for the next move */ { stableCnt++; } else { stableCnt=0; } if(stableCnt > STABLE_STATE_COUNT) { detectionState = DETECT_START; stableCnt = 0; startSector = lastSector; } break; case DETECT_START: if(curSector != lastSector) { if(abs(curSector - startSector) > 1) { if(curSector > lastSector) { detectionState = DETECT_POS_MOVE; } else { detectionState = DETECT_NEG_MOVE; } stableCnt = 0; startSector = lastSector; } else { stableCnt++; /* reset start sector in case of slow movement */ } // startPitch = lastPitch; } break; case DETECT_NEG_MOVE: case DETECT_POS_MOVE: if(curSector != lastSector) /* still moving? */ { stableCnt++; } else { // if(stableCnt >= 1) /* debounce movement */ { if(abs(startSector - curSector) > 2) { detectionState++; stableCnt = 0; } else { // detectionState = DETECT_NOTHING; stableCnt++; /* maybe on the boundary of a sector => handle as stable */ } } #if 0 else { // detectionState = DETECT_NOTHING; stableCnt++; /* maybe on the boundary of a sector => handle as stable */ } #endif } break; case DETECT_MINIMA: case DETECT_MAXIMA: /* stay at maximum for short time to add a pattern for user interaction */ if(curSector == lastSector) { stableCnt++; } else { if(stableCnt > 0) /* restart movement after a short break? */ { detectionState++; stableCnt = 0; } else { // detectionState = DETECT_NOTHING; stableCnt++; /* maybe on the boundary of a sector => handle as stable */ } } break; case DETECT_RISEBACK: case DETECT_FALLBACK: if(curSector == lastSector) /* check if we are back at start position at end of movement */ { if(abs(startSector - curSector) <= 1) //(curSector == startSector) { detectionState++; stableCnt = 0; } else { // detectionState = DETECT_NOTHING; stableCnt++; /* maybe on the boundary of a sector => handle as stable */ } } else { stableCnt++; } break; default: detectionState = DETECT_NOTHING; break; } lastSector = curSector; if(stableCnt > STABLE_STATE_TIMEOUT) { detectionState = DETECT_NOTHING; stableCnt = 0; } #if 0 curSpeed = currentPitch - lastPitch; /* feed value into state machine */ switch (detectionState) { case DETECT_NOTHING: if(fabsf(curSpeed) < MOVE_DELTA_SPEED) /* detect a stable condition before evaluating for the next move */ { stableCnt++; } else { stableCnt=0; } if(stableCnt > STABLE_STATE_COUNT) { detectionState = DETECT_START; stableCnt = 0; } break; case DETECT_START: if(fabsf(curSpeed) > MOVE_DELTA_SPEED) { if(curSpeed > 0) { detectionState = DETECT_POS_MOVE; } else { detectionState = DETECT_NEG_MOVE; } stableCnt = 0; startPitch = lastPitch; } break; case DETECT_NEG_MOVE: case DETECT_POS_MOVE: if(fabsf(curSpeed) > MOVE_DELTA_SPEED ) { stableCnt++; } else { if(stableCnt >= STABLE_STATE_COUNT) /* debounce movement */ { if(fabsf(startPitch - currentPitch) > PITCH_DELTA_COUNT) { detectionState++; } else { detectionState = DETECT_NOTHING; } } else { detectionState = DETECT_NOTHING; } stableCnt = 0; } break; case DETECT_MINIMA: case DETECT_MAXIMA: /* stay at maximum for short time to add a pattern for user interaction */ if(fabsf(curSpeed) < MOVE_DELTA_SPEED ) { stableCnt++; } else { if(stableCnt > 0) { detectionState++; } else { detectionState = DETECT_NOTHING; } stableCnt = 0; } break; case DETECT_RISEBACK: case DETECT_FALLBACK: if(fabsf(curSpeed) < MOVE_DELTA_SPEED) { if(fabsf(startPitch - currentPitch) < PITCH_DELTA_END) { detectionState++; } } stableCnt++; break; default: detectionState = DETECT_NOTHING; break; } if(stableCnt > STABLE_STATE_TIMEOUT) { detectionState = DETECT_NOTHING; stableCnt = 0; } lastPitch = currentPitch; #endif return detectionState; }