Racelogic / Mbed 2 deprecated VIPS_LTC_RAW_IMU

Dependencies:   BufferedSerial FatFileSystemCpp mbed

Revision:
0:97661408d0f9
Child:
1:dd1f7e162f91
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Jan 15 11:49:01 2021 +0000
@@ -0,0 +1,415 @@
+#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
+}