Racelogic / Mbed 2 deprecated VIPS_LTC_RAW_IMU

Dependencies:   BufferedSerial FatFileSystemCpp mbed

main.cpp

Committer:
AndyA
Date:
2021-01-15
Revision:
0:97661408d0f9
Child:
1:dd1f7e162f91

File content as of revision 0:97661408d0f9:

#include "mbed.h"
#include "LTCApp.h"

const int framesToCount = 300;
const int MaxTimeErrorUS = 150;
const int timerOverheadTime = 19;

BufferedSerial pc(USBTX, USBRX);
VIPSSerial VIPS(p28, p27);
BufferedSerial COM1(p13, p14);
BufferedSerial COM3(p9, p10);

DigitalOut led1(LED1);
DigitalOut PPS(p12);

DigitalOut led2(LED2);
DigitalOut led3(LED3);

DigitalOut frameToggle(LED4);
DigitalOut second(p23);

LTCDecode LTCInput(p7);
InterruptIn PPFin(p29);
InterruptIn Syncin(p8);

// clock to time everything with
Timer inputTimer;

// Time since last frame event, used for position output interpolation
Timer TimeSinceLastFrame;
uint32_t TimeSinceLastFrameWrap;

// used to start PPS at the correct point
Timeout PPSsyncTimer;

// used to generate PPS edges
Ticker PPSOutputTimer;

frameRates detectedRate;
bool ppsRunning = false; // set when PPS start has been scheduled
volatile bool ppsActive = false;  // set when PPS is actuallt going (up to 1 second later)

volatile bool PPSHigh;
bool resync = false;
bool resyncDone = false;
uint32_t resyncPeriod;
bool OKToCheckSync = false;
volatile uint32_t VBOXTicks = 0; // time at the NEXT PPS edge
uint32_t lastPPSSecondStart;

#define _longPPMTrackLen_ 20
float PPMErrors[_longPPMTrackLen_];
float PPMTrackTotal;
int PPMTrackIndex;
float PPMHighAcc;
float remainingClockError;
bool ppmCorrection;


void sendPosition(position *posPtr) {
    if (posPtr) {
      COM1.write(posPtr, sizeof(position));
      COM3.write(posPtr, sizeof(position));
      printf("send\r\n");
    }
}


void OnPPSEdge() {
  if (PPSHigh) {
    PPS = 0;
    PPSHigh = false;
  } else {
    PPS = 1;
    PPSHigh = true;

    if (resync && !ppmCorrection) {
      PPSOutputTimer.detach();
      PPSOutputTimer.attach(callback(OnPPSEdge), resyncPeriod);
      resync = false;
      resyncDone = true;
    } else if (resyncDone) {
      resyncDone = false;
      PPSOutputTimer.detach();
      PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000);
      remainingClockError = 0;
    }

    if ((VBOXTicks % 100) == 0) {
      lastPPSSecondStart = inputTimer.read_us();
      second = 1;
      remainingClockError+=PPMHighAcc;
      int adjustment = (int)(remainingClockError/2);
      if (!resyncDone && ((adjustment > 2)|| (adjustment <-2))) {
            PPSOutputTimer.detach();
            PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000+adjustment-timerOverheadTime);
            ppmCorrection=true;
            remainingClockError-=2*adjustment;
        }
    } else {
      if ((VBOXTicks % 100) == 1) {
        second = 0;
        if (ppmCorrection) {
            PPSOutputTimer.detach();
            PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000);
            ppmCorrection=false;
        }
      } 
    }

    VBOXTicks++;
    if (VBOXTicks >= (24 * 60 * 60 * 100))
      VBOXTicks -= (24 * 60 * 60 * 100);
  }
}

void OnPPSCync() {
  PPS = 1;
  PPSOutputTimer.detach();
  PPSOutputTimer.attach_us(callback(OnPPSEdge), 5000);
  PPSHigh = true;
  VBOXTicks++;
  ppsActive = true;
  led1 = 1;
}

const char *ticksToTime(unsigned long vboxTickCount) {
  static char timeString[32];
  int hours = vboxTickCount / 360000;
  int minutes = (vboxTickCount / 6000) % 60;
  int seconds = (vboxTickCount / 100) % 60;
  int ms = 10 * (vboxTickCount % 100);
  sprintf(timeString, "%02d:%02d:%02d.%03d", hours, minutes, seconds, ms);
  return timeString;
}

bool isStartOfSecond(int minutes, int seconds, int frame, bool dropFrame) {
  if (frame == 0)
    return true;
  if (dropFrame && (2 == frame) && (0 == seconds) && (minutes % 10 != 0))
    return true;
  return false;
}


int getClosestRateIndex(long int framePeriodUS) {
  int indexOver = 1;
  while (framePeriodUS <= frameRateInfo::FramePeriods[indexOver])
    indexOver++;
  float amountOver = framePeriodUS - frameRateInfo::FramePeriods[indexOver];
  float amountUnder = frameRateInfo::FramePeriods[indexOver - 1] - framePeriodUS;
  if (amountOver > amountUnder)
    return indexOver - 1;
  return indexOver;
}

// frame rate and clock error calculation
void frameRateMeasure(uint32_t frameStartTime, bool dropFrame=false) {
  frameToggle =!frameToggle;
  static int frameRateCount = 0;
  static uint32_t rateCalcStartTime;
  if (frameRateCount == 0) {
    rateCalcStartTime = frameStartTime;
    frameRateCount = 1;
  } else {
    if (frameRateCount == framesToCount) {
      uint32_t timeTaken = frameStartTime - rateCalcStartTime;
      long int timePerFrame = timeTaken / framesToCount;
      detectedRate.setRate(getClosestRateIndex(timePerFrame));
      float TrueSeconds = framesToCount / detectedRate.currentRate();
      // +ve PPM means I counted more than real time = my clock is fast.
      float ppmError = (timeTaken - (1000000 * TrueSeconds)) / TrueSeconds;
      if ((ppmError<250) && (ppmError>-250)) { // something went very wrong with ppm calculation.
          if (PPMHighAcc!=0) {
            PPMTrackTotal -= PPMErrors[PPMTrackIndex];
            PPMTrackTotal += ppmError;
            PPMErrors[PPMTrackIndex++] = ppmError;
            PPMHighAcc = PPMTrackTotal / _longPPMTrackLen_;
        } else { // first time
              for (int i=0;i<_longPPMTrackLen_;i++)
                PPMErrors[i] = ppmError;
            PPMTrackTotal = ppmError*_longPPMTrackLen_;
            PPMHighAcc = ppmError;
        }
          if (PPMTrackIndex == _longPPMTrackLen_)
            PPMTrackIndex = 0;
        printf("Frame rate detected as %s. My clock is %.4f ppm fast\r\n", detectedRate.frameRateString(), PPMHighAcc);
      } else {
          printf("Frame rate unclear\r\n");
          PPMHighAcc=0;
          detectedRate.setRate(0);
      }
      frameRateCount = 0;
    } else {
      frameRateCount++;
    }
  }
}


//called once per frame to output the current postition
void framePositionOutput() {
    sendPosition(VIPS.sendPositionForTime(TimeSinceLastFrame.read_us()));
    TimeSinceLastFrame.reset();
}

// called by background loop at the end of each frame
// frameStartTime = time of inputTimer at the start of the first bit of the
// frame. dropFrame = true if the protocol includes frameDrops
void frameComplete(int hours, int minutes, int seconds, int frame,
                   bool dropFrame, uint32_t frameStartTime) {
  static int lastFrame = 0;
  static float ppmError;
  static int outCount = 0;
  if ((frame != (lastFrame + 1)) && detectedRate.currentRate()) {
    long timeSinceSecondStart = detectedRate.getOffsetFromSecondStart(minutes, seconds, frame);
//    printf("Time Code %02d:%02d:%02d:%02d - VBOX time %s\r", hours, minutes, seconds, frame, ticksToTime(VBOXTicks));
    uint32_t ThisSecondStart = frameStartTime - timeSinceSecondStart;
    if (!ppsRunning) {
      uint32_t nextSecondStart = ThisSecondStart + 1000000 + (int)(ppmError + 0.5);
      ppsRunning = true;
      __disable_irq();
      uint32_t nextSec =  nextSecondStart - inputTimer.read_us();
      PPSsyncTimer.attach(callback(OnPPSCync), nextSec);
      __enable_irq();
      printf("PPS start scheduled for %0.3f seconds.\r\n", nextSec / 1000000.0f);
      VBOXTicks = (hours * 3600 + minutes * 60 + (seconds + 1)) * 100;
    } else if (detectedRate.isSyncable()) { // PPS running and from a syncable source
      long timeError = (lastPPSSecondStart - ThisSecondStart);
      if (timeError < -500000)
        timeError += 1000000;
      timeError = timeError % 1000000;
//      printf("PPS error measured as %ld us.\r", timeError);

      if ((timeError > MaxTimeErrorUS) || (timeError < -MaxTimeErrorUS)) {
        outCount++;
        if (outCount > 3) {
          int newPeriod = 5000 - timeError / 2;
          if (newPeriod < 4500)
            newPeriod = 4500;
          if (newPeriod > 5500)
            newPeriod = 5500;

    //      printf("Resync attempt. Error was %ld. 1 cycle at %d us\r\n", timeError, newPeriod);
          __disable_irq();
          resyncPeriod = newPeriod - timerOverheadTime;
          resync = true;
          __enable_irq();
        }
      } else
        outCount = 0;
    } // either no PPS and can't start it yet or running from unsyncable source
      // so just free run.
  }
  lastFrame = frame;

    // frame rate and clock error calculation
  frameRateMeasure(frameStartTime, dropFrame);
  framePositionOutput();  
}

// called by background loop at the end of each frame when running from a sync signal not timecode
void framePulse(uint32_t frameStartTime) {
  static int outCount = 0;
  uint32_t patternStartTime;
  if (detectedRate.isValid()) {
    uint32_t ThisSecondStart = frameStartTime ;
    if (!ppsRunning) {
      uint32_t pulseStartTime = frameStartTime + (int)(detectedRate.currentPeriodUS()*5+0.5f);
      uint32_t timeTillStart =  pulseStartTime - inputTimer.read_us();
      ppsRunning = true;
      __disable_irq();
      PPSsyncTimer.attach_us(callback(OnPPSCync), timeTillStart);
      __enable_irq();
      printf("PPS start scheduled for %0.3f seconds.\r\n",
             timeTillStart / 1000000.0f);
      VBOXTicks = 0;
      outCount = 0;
    } else {
//        if (outCount == 0)
//            patternStartTime = inputTimer.elapsed_time();
//        else {
//            expectedTotalUS = outCount
//        }
    }
  }
  // frame rate and clock error calculation
  frameRateMeasure(frameStartTime, false);
  framePositionOutput();  
  }

volatile bool NewFramePulse= false;
uint32_t FramePulseTime;
volatile int framesIn = 0;
void OnPPFInputStartup(void) {
    framesIn++;
}

volatile int SyncInCount = 0;
void OnSyncInputStartup(void) {
    SyncInCount++;
}


void OnPPFInput(void) {
    FramePulseTime = inputTimer.read_us();
    NewFramePulse = true;
}


int main() {
  pc.baud(115200);
//  COM1.baud(115200);
//  COM3.baud(115200);
  inputTimer.reset();
  inputTimer.start();

  pc.printf("Startup\r\n");
  led1 = 0;
//  LTCInput.setInputTimer(&inputTimer);
//  PPFin.rise(callback(&OnPPFInputStartup));

  while (true) {
      pc.putc('.');
      led1=!led1;
      wait_us(1000*100);
      }

  for (int i = 0; i < _longPPMTrackLen_; i++)
    PPMErrors[i] = 0;
  pc.printf("Startup\r\n");
  PPMTrackIndex = 0;
  PPMTrackTotal = 0;
  remainingClockError = 0;
  bool GenLockOnlyMode = false;
  bool GenLockToSync = false;

  TimeSinceLastFrame.reset();
  TimeSinceLastFrame.start();
  while (true) {
    pc.printf("Waiting for sync\r\n");
    PPFin.rise(callback(&OnPPFInputStartup));
    Syncin.rise(callback(&OnSyncInputStartup));
    framesIn = 0;
    SyncInCount = 0;
    GenLockOnlyMode = false;
    GenLockToSync = false;
    while (!LTCInput.searchForSync()) {
      if (framesIn == 100) {
        GenLockOnlyMode = true;
        break;
      }
      if ((SyncInCount == 100) && (framesIn<45)) { // prefer frame input pin, sync may be twice as high for interlaced systems.
        GenLockOnlyMode = true;
        GenLockToSync= true;
        break;
      }

      int message = VIPS.getStatusMessage();
      if (message)
          pc.printf("%s\r\n",VIPSStatusMessages[message-1]);
        
    }
    if (GenLockOnlyMode) {
        if (GenLockToSync) {
            pc.printf("No LTC detected for 100 frames. Falling back to genlock on sync input\r\n");
            Syncin.rise(callback(&OnPPFInput));
            PPFin.rise(NULL);
        } else {
            pc.printf("No LTC detected for 100 frames. Falling back to genlock on frame input\r\n");
            PPFin.rise(callback(&OnPPFInput));
            Syncin.rise(NULL);
        }
      uint32_t lastframeTime = inputTimer.read_us();

      while (true) {
        if (NewFramePulse) { // running on a frame
//            printf("frame\r");
          lastframeTime = FramePulseTime;
          framePulse(lastframeTime);
          NewFramePulse = false;
        }
        if ((inputTimer.read_us()-lastframeTime) > 1000000) {
          pc.printf("No frames for > 1 second\r\n");
            break;
        }
        int message = VIPS.getStatusMessage();
        if (message)
            pc.printf("%s\r\n",VIPSStatusMessages[message-1]);
      }
    } else { // not genlock
      pc.printf("Got LTC sync\r\n");
      PPFin.rise(NULL);        
      while (LTCInput.synced()) {
        position *posPtr = VIPS.getWaitingPostion();
        if (posPtr) {
            sendPosition(posPtr);
        }
        if (LTCInput.readWaitingData()) {
          if (LTCInput.synced()) {
            const LTCDecode::LTCData_t *frameData = LTCInput.getLastFrame();
            detectedRate.setDrop(frameData->frameDrop);
            frameComplete(frameData->hours, frameData->minutes,
                          frameData->seconds, frameData->frame,
                          frameData->frameDrop, frameData->frameStartTime);
          } // end if good frame
        }   // end if new data
        int message = VIPS.getStatusMessage();
        if (message)
            pc.printf("%s\r\n",VIPSStatusMessages[message-1]);
      }     // end while synced
      pc.printf("Sync lost\r");
    }
  } // end while true
}