def test_noCommonAncestor(self): a = self.newSysClock() b = self.newSysClock() a1 = CorrelatedClock(a, tickRate=1000, correlation=Correlation(0, 0)) b1 = CorrelatedClock(b, tickRate=1000, correlation=Correlation(0, 0)) a2 = CorrelatedClock(a1, tickRate=1000, correlation=Correlation(0, 0)) b2 = CorrelatedClock(b1, tickRate=1000, correlation=Correlation(0, 0)) self.assertRaises(NoCommonClock, lambda: a.toOtherClockTicks(b, 5)) self.assertRaises(NoCommonClock, lambda: a.toOtherClockTicks(b1, 5)) self.assertRaises(NoCommonClock, lambda: a.toOtherClockTicks(b2, 5)) self.assertRaises(NoCommonClock, lambda: a1.toOtherClockTicks(b, 5)) self.assertRaises(NoCommonClock, lambda: a1.toOtherClockTicks(b1, 5)) self.assertRaises(NoCommonClock, lambda: a1.toOtherClockTicks(b2, 5)) self.assertRaises(NoCommonClock, lambda: a2.toOtherClockTicks(b, 5)) self.assertRaises(NoCommonClock, lambda: a2.toOtherClockTicks(b1, 5)) self.assertRaises(NoCommonClock, lambda: a2.toOtherClockTicks(b2, 5)) self.assertRaises(NoCommonClock, lambda: b.toOtherClockTicks(a, 5)) self.assertRaises(NoCommonClock, lambda: b.toOtherClockTicks(a1, 5)) self.assertRaises(NoCommonClock, lambda: b.toOtherClockTicks(a2, 5)) self.assertRaises(NoCommonClock, lambda: b1.toOtherClockTicks(a, 5)) self.assertRaises(NoCommonClock, lambda: b1.toOtherClockTicks(a1, 5)) self.assertRaises(NoCommonClock, lambda: b1.toOtherClockTicks(a2, 5)) self.assertRaises(NoCommonClock, lambda: b2.toOtherClockTicks(a, 5)) self.assertRaises(NoCommonClock, lambda: b2.toOtherClockTicks(a1, 5)) self.assertRaises(NoCommonClock, lambda: b2.toOtherClockTicks(a2, 5))
def test_quantifyChange(self): a = self.newSysClock() b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0)) self.assertEquals(float('inf'), b.quantifyChange(Correlation(0, 0), 1.01)) self.assertEquals(1.0, b.quantifyChange(Correlation(0, 1000), 1.0)) b.speed = 0.0 self.assertEquals(0.005, b.quantifyChange(Correlation(0, 5), 0.0))
def test_distantParentMidHierarchy(self): a = self.newSysClock(tickRate=1000000) a1 = CorrelatedClock(a, tickRate=100, correlation=Correlation(50, 0)) a2 = CorrelatedClock(a1, tickRate=78, correlation=Correlation(28, 999)) a3 = CorrelatedClock(a2, tickRate=178, correlation=Correlation(5, 1003)) a4 = CorrelatedClock(a3, tickRate=28, correlation=Correlation(17, 9)) self.assertEquals(a3.toOtherClockTicks(a1, 500), a2.toParentTicks(a3.toParentTicks(500)))
def test_setCorrelationAndSpeed(self): a = self.newSysClock(tickRate=1000000) b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0)) b.speed = 1.0 db = MockDependent() b.bind(db) b.setCorrelationAndSpeed(Correlation(5, 0), 2) db.assertNotificationsEqual([b]) self.assertEqual(b.toParentTicks(10), 5005)
def test_setParent(self): a = self.newSysClock(tickRate=1000) b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0)) c = CorrelatedClock(a, 1000, correlation=Correlation(10, 0)) d = MockDependent() b.bind(d) d.assertNotNotified() b.setParent(c) d.assertNotificationsEqual([b]) self.assertEquals(b.getParent(), c)
def test_setParentNoChangeNoNotify(self): a = self.newSysClock(tickRate=1000) b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0)) c = CorrelatedClock(a, 1000, correlation=Correlation(10, 0)) d = MockDependent() b.bind(d) d.assertNotNotified() b.setParent(a) d.assertNotNotified() self.assertEquals(b.getParent(), a)
def test_changeCorrelation(self): mockTime = self.mockTime b = self.newSysClock() mockTime.timeNow = 5020.8 c = CorrelatedClock(b, 1000, correlation=Correlation(0, 300)) self.assertAlmostEqual(c.ticks, 5020.8 * 1000 + 300, places=5) c.correlation = Correlation(50000, 320) self.assertEqual(c.correlation, Correlation(50000, 320)) self.assertAlmostEqual(c.ticks, (int(5020.8 * 1000000) - 50000) / 1000 + 320, places=5)
def test_fromParentTicks(self): mockTime = self.mockTime b = self.newSysClock(tickRate=2000000) mockTime.timeNow = 1000.0 c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300)) self.assertAlmostEqual(c.fromParentTicks(50 + (400 - 300) * 2000), 400, places=5) c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300), speed=0) self.assertEquals(c.fromParentTicks(50), 300) self.assertEquals(c.fromParentTicks(100), 300)
def test_toParentTicks(self): mockTime = self.mockTime b = self.newSysClock(tickRate=2000000) mockTime.timeNow = 1000.0 c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300)) self.assertAlmostEqual(c.toParentTicks(400), 50 + (400 - 300) * 2000, places=5) c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300), speed=0) self.assertEquals(c.toParentTicks(300), 50) self.assertTrue(math.isnan(c.toParentTicks(400)))
def __init__(self, clock): """\ :param clock: The :class:`~dvbcss.clock.CorrelatedClock` that is to be set. Note that this predictor does not actually set the clock. It just needs it in order to calculate the correct correlation for it. """ self.clock = clock self.correlation = Correlation(0, 0, 0, float("+inf"))
def threadrun(): # separate thread in which we'll use sleepUntil on a clock that uses tClock as its parent cClock = CorrelatedClock(tClock, tickRate=10, correlation=Correlation(tClock.ticks, 0)) for i in range(0, 10): sleepUntil(cClock, i * 10) print "Tick", cClock.ticks
def test_changeCorrelationNotifies(self): """Check a change to the correlation propagates notifications to dependents of this clock""" b = self.newSysClock() c = CorrelatedClock(b, 1000, correlation=Correlation(0, 300)) cc = CorrelatedClock(c, 50) d1 = MockDependent() d2 = MockDependent() d3 = MockDependent() b.bind(d1) c.bind(d2) cc.bind(d3) c.correlation = Correlation(50, 20) d1.assertNotNotified() d2.assertNotificationsEqual([c]) d3.assertNotificationsEqual([cc])
def test_rangeConversion(self): mockTime = self.mockTime b = self.newSysClock(tickRate=1000) mockTime.timeNow = 0.0 c = RangeCorrelatedClock(b, 1000, correlation1=Correlation(100, 1000), correlation2=Correlation(200, 1110)) mockTime.timeNow = b.calcWhen(100) self.assertEqual(c.ticks, 1000) mockTime.timeNow = b.calcWhen(200) self.assertEqual(c.ticks, 1110) mockTime.timeNow = b.calcWhen(150) self.assertEqual(c.ticks, 1055)
def test_ticking(self): mockTime = self.mockTime b = self.newSysClock() mockTime.timeNow = 5020.8 c = CorrelatedClock(b, 1000, correlation=Correlation(0, 300)) self.assertAlmostEqual(c.ticks, 5020.8 * 1000 + 300, places=5) mockTime.timeNow += 22.7 self.assertAlmostEqual(c.ticks, (5020.8 + 22.7) * 1000 + 300, places=5)
def calcCorrelationFor(self, clock, localMaxFreqErrorPpm=None): r"""\ Calculates and returns the :class:`~dvbcss.clock.Correlation` for a :class:`~dvbcss.clock.CorrelatedClock` that is equivalent to this candidate. The returned correlation can then be applied to the clock to model the time at the server. This includes the error bounds information needed to enable the clock to correctly calculate dispersion. :param clock: :class:`~dvbcss.clock.CorrelatedClock` that will model the server clock. Its parent must be the one that was measured for `t1` and `t4` this candidate. :param localMaxFreqErrorPpm: Optional. By defeault the :func:`~dvbcss.clock.ClockBase.getRootMaxFreqError` of the `clock` is used. Provide this value to override that. :returns: :class:`~dvbcss.clock.Correlation` representing this `candidate`, and that can be used with the :class:`~dvbcss.clock.CorrelatedClock`. .. note:: The parameters of the correlation are calculated by this function as follows: * **parentTicks** = (t1' + t4') / 2 * **childTicks** = (t2' + t3') / 2 * **initialError** = precision + ( rtt/2 + mfeC * (t4 - t1) + mfeS * (t3 - t2) ) / 10\ :sup:`9` * **errorGrowthRate** = mfeC + mfeS Where: * **t1**, **t2**, **t3** and **t4** are in units of nanoseconds * **t1'** and **t4'** are the same as t1 and t4 but converted to ticks of the parent of the specified clock * **t2'** and **t3'** are the same as t2 and t3 but converted to ticks of the specified clock * **mfeC** is the clock's :func:`~dvbcss.clock.ClockBase.getRootMaxFreqError`, converted from ppm to a fraction by dividing by 10\ :sup:`6` * **mfeS** is the max freq error reported by the server, converted from ppm to a fraction by dividing by 10\ :sup:`6` .. versionadded:: 0.4 """ # convert to units of the clock t1 = clock.getParent().nanosToTicks(self.t1) t4 = clock.getParent().nanosToTicks(self.t4) t2 = clock.nanosToTicks(self.t2) t3 = clock.nanosToTicks(self.t3) if localMaxFreqErrorPpm is None: localMaxFreqErrorPpm = clock.getRootMaxFreqError() mfeC = localMaxFreqErrorPpm / 1000000.0 # ppm to fraction mfeS = self.maxFreqError / 1000000.0 # ppm to fraction return Correlation( parentTicks=(t1 + t4) / 2.0, childTicks=(t2 + t3) / 2.0, initialError=self.precision + # server precision. does not include local clock precision since this is already accounted for (self.rtt / 2.0 + mfeC * (self.t4 - self.t1) + mfeS * (self.t3 - self.t2)) / 1000000000.0, # nanos to seconds errorGrowthRate=(mfeC + mfeS))
def test_dispersionCalcForError(self): """With non-zero error contribution, the dispersion at different times is additional to that of the parent clock, both before and after the correlation time""" a = self.newSysClock(tickRate=1000) b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0, 0.5, 0.1)) self.assertEquals( a.dispersionAtTime(10) + 0.5 + 0.1 * 10 / 1000, b.dispersionAtTime(10)) self.assertEquals( a.dispersionAtTime(20) + 0.5 + 0.1 * 20 / 1000, b.dispersionAtTime(20)) self.assertEquals( a.dispersionAtTime(-10) + 0.5 + 0.1 * 10 / 1000, b.dispersionAtTime(-10))
def test_tupleEquivEquality(self): """Correlations can be compared with 2-tuples for equality of the parentTicks and childTicks""" self.assertEqual(Correlation(1, 2, 3, 4), (1, 2)) self.assertEqual((1, 2), Correlation(1, 2, 3, 4)) self.assertNotEqual(Correlation(9, 2, 3, 4), (1, 2)) self.assertNotEqual(Correlation(1, 9, 3, 4), (1, 2)) self.assertNotEqual((1, 2), Correlation(9, 2, 3, 4)) self.assertNotEqual((1, 2), Correlation(1, 9, 3, 4))
def test_withTuples(self): mockTime = self.mockTime b = self.newSysClock(tickRate=1000) mockTime.timeNow = 0.0 c = RangeCorrelatedClock(b, 1000, correlation1=(100, 1000), correlation2=(200, 1110)) self.assertEqual(Correlation(100, 1000), c.correlation1) self.assertEqual(Correlation(200, 1110), c.correlation2) mockTime.timeNow = b.calcWhen(100) self.assertEqual(c.ticks, 1000) mockTime.timeNow = b.calcWhen(200) self.assertEqual(c.ticks, 1110) c.correlation1 = (100, 2000) c.correlation2 = (200, 2110) self.assertEqual(Correlation(100, 2000), c.correlation1) self.assertEqual(Correlation(200, 2110), c.correlation2) self.assertEqual(c.ticks, 2110)
def test_clockDiff(self): a = self.newSysClock() self.mockTime.timeNow = 1 b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0)) c = CorrelatedClock(b, 2000, correlation=Correlation(0, 0)) d = CorrelatedClock(c, 3000, correlation=Correlation(0, 0)) e = RangeCorrelatedClock(d, 1000, Correlation(5, 0), Correlation(15005, 5000)) self.assertEquals(float('inf'), b.clockDiff(c)) self.assertEquals(float('inf'), b.clockDiff(d)) self.assertAlmostEquals(0.001666667, b.clockDiff(e), delta=0.000001) self.mockTime.timeNow += 10000000 self.assertEquals(float('inf'), b.clockDiff(c)) self.assertEquals(float('inf'), b.clockDiff(d)) self.assertAlmostEquals(0.001666667, b.clockDiff(e), delta=0.000001) c.tickRate = 1000 self.assertEquals(0, b.clockDiff(c)) c.speed = 1.01 self.assertEquals(float('inf'), b.clockDiff(c))
def test_speedChangePropagates(self): mockTime = self.mockTime a = self.newSysClock(tickRate=1000) mockTime.timeNow = 5 a1 = CorrelatedClock(a, tickRate=1000, correlation=Correlation(50, 0)) a2 = CorrelatedClock(a1, tickRate=100, correlation=Correlation(28, 999)) a3 = CorrelatedClock(a2, tickRate=50, correlation=Correlation(5, 1003)) a4 = CorrelatedClock(a3, tickRate=25, correlation=Correlation(1000, 9)) b3 = CorrelatedClock(a2, tickRate=1000, correlation=Correlation(500, 20)) b4 = CorrelatedClock(b3, tickRate=2000, correlation=Correlation(15, 90)) at1, a1t1, a2t1, a3t1, a4t1, b3t1, b4t1 = [ x.ticks for x in [a, a1, a2, a3, a4, b3, b4] ] a3.speed = 0.5 a4.speed = 0.2 self.assertEquals(1.0, a.getEffectiveSpeed()) self.assertEquals(1.0, a1.getEffectiveSpeed()) self.assertEquals(1.0, a2.getEffectiveSpeed()) self.assertEquals(0.5, a3.getEffectiveSpeed()) self.assertEquals(0.1, a4.getEffectiveSpeed()) a3.speed = 0 a4.speed = 1.0 self.assertEquals(1.0, a.getEffectiveSpeed()) self.assertEquals(1.0, a1.getEffectiveSpeed()) self.assertEquals(1.0, a2.getEffectiveSpeed()) self.assertEquals(0.0, a3.getEffectiveSpeed()) self.assertEquals(0.0, a4.getEffectiveSpeed()) mockTime.timeNow = 6 # advance time 1 second at2, a1t2, a2t2, a3t2, a4t2, b3t2, b4t2 = [ x.ticks for x in [a, a1, a2, a3, a4, b3, b4] ] self.assertEquals(at2, at1 + 1000) # a still advances self.assertEquals(a1t2, a1t1 + 1000) # a1 still advances self.assertEquals(a2t2, a2t1 + 100) # a2 still advances self.assertEquals( a3t2, 1003) # a3 is speed zero, is now at the correlation point self.assertEquals( a4t2, 10.5 ) # a4 is speed zero, a3.ticks is 3 ticks from correlation point for a4, translating to 1.5 ticks from a4 correlation point at its tickRate self.assertEquals(b3t2, b3t1 + 1000) # b3 still advances self.assertEquals(b4t2, b4t1 + 2000) # b4 is paused
def test_changeFreq(self): mockTime = self.mockTime b = self.newSysClock() mockTime.timeNow = 5020.8 c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300)) self.assertAlmostEqual(c.ticks, (5020.8 * 1000000 - 50) / (1000000 / 1000) + 300, places=5) self.assertEqual(c.tickRate, 1000) c.tickRate = 500 self.assertEqual(c.tickRate, 500) self.assertAlmostEqual(c.ticks, (5020.8 * 1000000 - 50) / (1000000 / 500) + 300, places=5)
def _onControlTimestamp(self, ct): self.latestCt = ct self.log.debug("New Control Timestamp: "+str(ct)) available = ct.timestamp.contentTime is not None availChanged = bool(available) != bool(self.timelineClock.isAvailable()) # only extract new corelation and and compare to existing clock # if the control timestamp indicates that the timeline is actually available if available: speed = float(ct.timelineSpeedMultiplier) corr = Correlation(ct.timestamp.wallClockTime, ct.timestamp.contentTime) corrSpeedChanged = self.timelineClock.isChangeSignificant(corr, speed, self._changeThreshold) speedChanged = self.timelineClock.speed != speed else: corrSpeedChanged = False # update correlation and speed, then update availability, to # ensure a correlation is not changed immediately *after* the clock # becomes available. Better it happens before, so downstream processing # can ignore while unavailable. if corrSpeedChanged: self.timelineClock.setCorrelationAndSpeed(corr, speed) if availChanged: self.timelineClock.setAvailability(available) # notification calls if available and corrSpeedChanged: self.log.debug("Speed has changed and/or correlation has changed by more than threshold amount") self.onTimingChange(speedChanged=speedChanged) if availChanged: if available: self.log.debug("Timeline has become available.") self.onTimelineAvailable() else: self.log.debug("Timeline has become unavailable.") self.onTimelineUnavailable()
def test_changeFreqNotifies(self): mockTime = self.mockTime b = self.newSysClock() mockTime.timeNow = 5020.8 c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300)) cc = CorrelatedClock(c, 50) d1 = MockDependent() d2 = MockDependent() d3 = MockDependent() b.bind(d1) c.bind(d2) cc.bind(d3) c.tickRate = 500 d1.assertNotNotified() d2.assertNotificationsEqual([c]) d3.assertNotificationsEqual([cc])
def test_differentBranches(self): a = self.newSysClock(tickRate=1000000) a1 = CorrelatedClock(a, tickRate=100, correlation=Correlation(50, 0)) a2 = CorrelatedClock(a1, tickRate=78, correlation=Correlation(28, 999)) a3 = CorrelatedClock(a2, tickRate=178, correlation=Correlation(5, 1003)) a4 = CorrelatedClock(a3, tickRate=28, correlation=Correlation(17, 9)) b3 = CorrelatedClock(a2, tickRate=1000, correlation=Correlation(10, 20)) b4 = CorrelatedClock(b3, tickRate=2000, correlation=Correlation(15, 90)) v = a4.toParentTicks(500) v = a3.toParentTicks(v) v = b3.fromParentTicks(v) v = b4.fromParentTicks(v) self.assertEquals(a4.toOtherClockTicks(b4, 500), v)
def test_immutable(self): c = Correlation(1, 2, 3, 4) try: c.parentTicks = 5 self.fail(msg="Assignment of parentTicks property") except: pass try: c.childTicks = 5 self.fail(msg="Assignment of childTicks property") except: pass try: c.initialError = 5 self.fail(msg="Assignment of initialError property") except: pass try: c.errorGrowthRate = 5 self.fail(msg="Assignment of errorGrowthRate property") except: pass
def test_distantParent(self): a = self.newSysClock(tickRate=1000000) a1 = CorrelatedClock(a, tickRate=100, correlation=Correlation(50, 0)) a2 = CorrelatedClock(a1, tickRate=78, correlation=Correlation(28, 999)) self.assertEquals(a2.toOtherClockTicks(a, 500), a1.toParentTicks(a2.toParentTicks(500)))
def test_dispersionCalcForZeroError(self): """With zero error contribution, the dispersion at different times is the same as for the parent clock""" a = self.newSysClock(tickRate=1000) b = CorrelatedClock(a, 1000, correlation=Correlation(0, 0)) self.assertEquals(a.dispersionAtTime(10), b.dispersionAtTime(10)) self.assertEquals(a.dispersionAtTime(20), b.dispersionAtTime(20))
def test_correlationAndFreqPropertiesInitialised(self): b = self.newSysClock() c = CorrelatedClock(b, 1000, correlation=Correlation(0, 300)) self.assertEqual(c.correlation, Correlation(0, 300)) self.assertEqual(c.tickRate, 1000)
def test_getParent(self): b = self.newSysClock() c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300)) self.assertEqual(c.getParent(), b)
def test_rebase(self): b = self.newSysClock(tickRate=1000) c = CorrelatedClock(b, 1000, correlation=Correlation(50, 300)) c.rebaseCorrelationAtTicks(400) self.assertEquals(c.correlation, Correlation(150, 400))