def test_simplePayload(self): m = WCMessage(WCMessage.TYPE_RESPONSE, 5, 7680, 1000, 2000, 3000) payload = m.pack() self.assertEqual( payload, "\x00\x01\x05\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8" )
def test_simplePayloadOverridingOriginate(self): m = WCMessage(WCMessage.TYPE_RESPONSE, 5, 12160, 1000, 2000, 3000, (0xaabbccdd, 0xeeff1122)) payload = m.pack() self.assertEqual( payload, "\x00\x01\x05\x00\x00\x00\x2f\x80\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8" )
def handle(self, socket, data, srcaddr): recv_ticks, tickrate=self.clock.ticks, self.clock.tickRate msg=WCMessage.unpack(data) reply=msg.copy() if msg.msgtype==WCMessage.TYPE_REQUEST: reply.receiveNanos = recv_ticks * 1000000000 / tickrate if self.followup: reply.msgtype = WCMessage.TYPE_RESPONSE_WITH_FOLLOWUP else: reply.msgtype = WCMessage.TYPE_RESPONSE reply.setPrecision(self.precision) reply.setMaxFreqError(self.maxFreqErrorPpm) reply.transmitNanos = self.clock.ticks * 1000000000 / tickrate socket.sendto(reply.pack(), srcaddr) if self.followup: followupReply = reply.copy() followupReply.transmitNanos = self.clock.ticks * 1000000000 / tickrate followupReply.msgtype = WCMessage.TYPE_FOLLOWUP socket.sendto(followupReply.pack(), srcaddr) self.log.debug("Received :"+str(msg)+"\n") self.log.info("Responding to request from %s port %d with originate time=%20d ns" % (srcaddr[0], srcaddr[1], msg.originateNanos)) self.log.debug("Response :"+str(reply)+"\n") if self.followup: self.log.debug("Followed by:"+str(followupReply)+"\n") else: raise ValueError("Wall clock server received non request message")
def handle(self, socket, data, srcaddr): recv_ticks, tickrate = self.clock.ticks, self.clock.tickRate msg = WCMessage.unpack(data) reply = msg.copy() if msg.msgtype == WCMessage.TYPE_REQUEST: reply.receiveNanos = recv_ticks * 1000000000 / tickrate if self.followup: reply.msgtype = WCMessage.TYPE_RESPONSE_WITH_FOLLOWUP else: reply.msgtype = WCMessage.TYPE_RESPONSE reply.setPrecision(self.precision if self.precision is not None else self.clock.dispersionAtTime(recv_ticks)) reply.setMaxFreqError(self.maxFreqErrorPpm if self. maxFreqErrorPpm is not None else self.clock. getRootMaxFreqError()) reply.transmitNanos = self.clock.ticks * 1000000000 / tickrate socket.sendto(reply.pack(), srcaddr) if self.followup: followupReply = reply.copy() followupReply.transmitNanos = self.clock.ticks * 1000000000 / tickrate followupReply.msgtype = WCMessage.TYPE_FOLLOWUP socket.sendto(followupReply.pack(), srcaddr) self.log.debug("Received :" + str(msg) + "\n") self.log.info( "Responding to request from %s port %d with originate time=%20d ns" % (srcaddr[0], srcaddr[1], msg.originateNanos)) self.log.debug("Response :" + str(reply) + "\n") if self.followup: self.log.debug("Followed by:" + str(followupReply) + "\n") else: raise ValueError("Wall clock server received non request message")
def test_simpleParse(self): payload = "\x00\x01\x05\x00\x00\x01\xf4\x00\xaa\xbb\xcc\xdd\x3b\x9a\xc9\xff\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8" m = WCMessage.unpack(payload) self.assertEquals(m.msgtype, WCMessage.TYPE_RESPONSE) self.assertEquals(m.precision, 5) self.assertEquals(m.maxFreqError, 500 * 256) self.assertEquals(m.originateNanos, 2864434397999999999) self.assertEquals(m.receiveNanos, 2000) self.assertEquals(m.transmitNanos, 3000)
def testSmokeTestCreate(self): m = WCMessage(WCMessage.TYPE_REQUEST, 1, 256, 2, 3, 4) self.assertEqual(m.msgtype, WCMessage.TYPE_REQUEST) self.assertEqual(m.precision, 1) self.assertEqual(m.maxFreqError, 256) self.assertEqual(m.originateNanos, 2) self.assertEqual(m.receiveNanos, 3) self.assertEqual(m.transmitNanos, 4) self.assertEqual(m.originalOriginate, None)
def test_simpleParseWithUnusualOriginate(self): payload = "\x00\x01\x05\x00\x00\x01\xf4\x00\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8" m = WCMessage.unpack(payload) self.assertEquals(m.msgtype, WCMessage.TYPE_RESPONSE) self.assertEquals(m.precision, 5) self.assertEquals(m.maxFreqError, 500 * 256) self.assertEquals(m.originalOriginate, (0xaabbccdd, 0xeeff1122)) self.assertEquals(m.receiveNanos, 2000) self.assertEquals(m.transmitNanos, 3000)
def test_simpleParse(self): payload = "\x00\x01\x05\x00\x00\x01\xf4\x00\xaa\xbb\xcc\xdd\x3b\x9a\xc9\xff\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8" m=WCMessage.unpack(payload) self.assertEquals(m.msgtype, WCMessage.TYPE_RESPONSE) self.assertEquals(m.precision, 5) self.assertEquals(m.maxFreqError, 500*256) self.assertEquals(m.originateNanos, 2864434397999999999) self.assertEquals(m.receiveNanos, 2000) self.assertEquals(m.transmitNanos, 3000)
def test_simpleParseWithUnusualOriginate(self): payload = "\x00\x01\x05\x00\x00\x01\xf4\x00\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8" m=WCMessage.unpack(payload) self.assertEquals(m.msgtype, WCMessage.TYPE_RESPONSE) self.assertEquals(m.precision, 5) self.assertEquals(m.maxFreqError, 500*256) self.assertEquals(m.originalOriginate, (0xaabbccdd, 0xeeff1122)) self.assertEquals(m.receiveNanos, 2000) self.assertEquals(m.transmitNanos, 3000)
def test_encodeMaxFreqError(self): self.assertEquals(WCMessage.encodeMaxFreqError(50), 12800) self.assertEquals(WCMessage.encodeMaxFreqError(1900), 486400) self.assertEquals(WCMessage.encodeMaxFreqError(0.01), 3) self.assertEquals(WCMessage.encodeMaxFreqError(28), 7168) self.assertEquals(WCMessage.encodeMaxFreqError(100000), 25600000) self.assertEquals(WCMessage.encodeMaxFreqError(0), 0)
def test_decodeMaxFreqError(self): self.assertEquals(WCMessage.decodeMaxFreqError(12800), 50 ) self.assertEquals(WCMessage.decodeMaxFreqError(486400), 1900 ) self.assertEquals(WCMessage.decodeMaxFreqError(3), 0.01171875) self.assertEquals(WCMessage.decodeMaxFreqError(7168), 28 ) self.assertEquals(WCMessage.decodeMaxFreqError(25600000), 100000 ) self.assertEquals(WCMessage.decodeMaxFreqError(0), 0 )
def test_decodeMaxFreqError(self): self.assertEquals(WCMessage.decodeMaxFreqError(12800), 50) self.assertEquals(WCMessage.decodeMaxFreqError(486400), 1900) self.assertEquals(WCMessage.decodeMaxFreqError(3), 0.01171875) self.assertEquals(WCMessage.decodeMaxFreqError(7168), 28) self.assertEquals(WCMessage.decodeMaxFreqError(25600000), 100000) self.assertEquals(WCMessage.decodeMaxFreqError(0), 0)
def test_decodePrecision(self): self.assertEquals(WCMessage.decodePrecision(-128), 2**-128) self.assertEquals(WCMessage.decodePrecision(-16), 2**-16) self.assertEquals(WCMessage.decodePrecision(127), 2**127) self.assertEquals(WCMessage.decodePrecision(-10), 2**-10) self.assertEquals(WCMessage.decodePrecision(-9), 2**-9)
def test_encodePrecision(self): self.assertEquals(WCMessage.encodePrecision(2**-128), -128) self.assertEquals(WCMessage.encodePrecision(0.00001), -16) self.assertEquals(WCMessage.encodePrecision(2**127), 127) self.assertEquals(WCMessage.encodePrecision(0.0007), -10) self.assertEquals(WCMessage.encodePrecision(0.001), -9)
def test_simplePayload(self): m=WCMessage(WCMessage.TYPE_RESPONSE, 5, 7680, 1000, 2000, 3000) payload=m.pack() self.assertEqual(payload, "\x00\x01\x05\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8")
def algorithmWrapper(dest,clock,algorithm): """\ UdpRequestResponseClient handler function that wraps up the act of sending and receiving a WallClockMessage. Also handles the optional "follow-up" response type of message. If a response is received that indicates a follow-up is due, it will also wait for the follow-up. However the total time it will wait since the original request will not exceed the specified timeout. If only a response-promising-follow-up is received then that is what shall be returned. In turn, you plug an algorithm into it that is also a generator function. However that algorithm need only yield the timeout, and the return value will be a dict containing 2 candidate objects. One in nanoseconds, and the other the same but converted to tick units according to the tick rate of the supplied clock object. If timeout occurs the return value is None instead of a dict. Arguments: dest = ("<ip-addr>",port) clock = clock that will be used for tick rate conversion algorithm = a generator that yields to request a WallClock request be sent, and supplies the timeout for waiting for a response as the yield value then expects to be sent the candidate object representing the result of the request response Example algorithm: def algorithm(): timeoutSecs=0.2 while True: candidate=(yield timeoutSecs) if candidate is not None: print "Candidate received! ",candidate["ticks"] else: print "Timeout" print "Now waiting 1 second" time.sleep(1) """ try: # hand control to the algorithm. when it wants a request sent, it will # return and supply the timeout for waiting for a response timeoutSecs = algorithm.next() while True: # assemble a request reqMsg=WCMessage(WCMessage.TYPE_REQUEST, 0, 0, clock.nanos, 0, 0) toSend=reqMsg.pack(), dest # we'll send the request then seek the best quality response # until timeout, or terminating early if we get a quality > 2 # response (meaning that it was a non-followed-up response or a # follow-up response). A lower quality response is one where # a follow-up is expected or if it related to a previous request # this design means it will pick the best response, but in the # absence of a reasonable one, it will still make do with whatever # it can get (e.g. a response relating to an earlier request) responseQuality = -999 responseMsg = None responseRecvNanos = None timeoutBy = time.time() + timeoutSecs remainingTime = timeoutBy - time.time() while responseQuality < 3 and remainingTime > 0: # wait for a response. if first time round, send the request too latestResponse,src = (yield toSend,remainingTime) toSend=None # note when response was received latestResponseNanos=clock.nanos # did we get a response? did it come from the server we sent # the request to? if latestResponse is not None and src == dest: # assess the response and work out if better than any previous # response we're received latestResponseMsg = WCMessage.unpack(latestResponse) newQuality=calcQuality(reqMsg, latestResponseMsg) if newQuality >= responseQuality: responseQuality=newQuality responseMsg=latestResponseMsg responseRecvNanos=latestResponseNanos # work out how long left until timeout remainingTime=timeoutBy - time.time() if responseMsg is not None: candidate=Candidate(responseMsg,responseRecvNanos) candidate={"nanos":candidate, "ticks":candidate.toTicks(clock)} else: # no message, no candidate candidate=None # pass the result to the algorithm, and wait for it to return when it # next wants a request to be sent (again supplying the response timeout) timeoutSecs=algorithm.send(candidate) except StopIteration: pass
def test_decodePrecision(self): self.assertEquals(WCMessage.decodePrecision(-128), 2**-128) self.assertEquals(WCMessage.decodePrecision(-16), 2**-16 ) self.assertEquals(WCMessage.decodePrecision(127), 2**127 ) self.assertEquals(WCMessage.decodePrecision(-10), 2**-10 ) self.assertEquals(WCMessage.decodePrecision(-9), 2**-9 )
def test_encodePrecision(self): self.assertEquals(WCMessage.encodePrecision(2**-128), -128) self.assertEquals(WCMessage.encodePrecision(0.00001), -16 ) self.assertEquals(WCMessage.encodePrecision(2**127), 127) self.assertEquals(WCMessage.encodePrecision(0.0007), -10 ) self.assertEquals(WCMessage.encodePrecision(0.001), -9 )
def algorithmWrapper(dest, measureClock, algorithm): """\ UdpRequestResponseClient handler function that wraps up the act of sending and receiving a WallClockMessage. Also handles the optional "follow-up" response type of message. If a response is received that indicates a follow-up is due, it will also wait for the follow-up. However the total time it will wait since the original request will not exceed the specified timeout. If only a response-promising-follow-up is received then that is what shall be returned. In turn, you plug an algorithm into it that is also a generator function. However that algorithm need only yield the timeout, and the return value will be a :class:`~dvbcss.protocol.wc.Candidate` object in units of nanoseconds. If timeout occurs the return value is None instead of a dict. :param dest: ("<ip-addr>",port) The destination address of the server (to which the requests should be sent) :param measureClock: The :mod:`~dvbcss:Clock` from which the readings are taken (being `t1` and `t4` in the resulting candidate) :param algorithm: A generator that yields to request a WallClock request be sent, and acts on the responses. The generator function you provide, should use `yield` as follows: * to pass the timeout for waiting for a response as the yield value * to receive a :class:`~dvbcss.protocol.wc.Candidate` object representing the response. Example algorithm: .. code-block:: python def algorithm(): timeoutSecs=0.2 while True: candidate=(yield timeoutSecs) if candidate is not None: print "Candidate received! ",candidate else: print "Timeout" print "Now waiting 1 second" time.sleep(1) sysClock = SysClock() wallClock = CorrelatedClock(sysClock, tickRate=1000000000) algWrapper = algorithmWrapper(destIpPort, sysClock, wallClock, algorithm()) """ try: # hand control to the algorithm. when it wants a request sent, it will # return and supply the timeout for waiting for a response timeoutSecs = algorithm.next() while True: # assemble a request reqMsg = WCMessage(WCMessage.TYPE_REQUEST, 0, 0, measureClock.nanos, 0, 0) toSend = reqMsg.pack(), dest # we'll send the request then seek the best quality response # until timeout, or terminating early if we get a quality > 2 # response (meaning that it was a non-followed-up response or a # follow-up response). A lower quality response is one where # a follow-up is expected or if it related to a previous request # this design means it will pick the best response, but in the # absence of a reasonable one, it will still make do with whatever # it can get (e.g. a response relating to an earlier request) responseQuality = -999 responseMsg = None responseRecvNanos = None timeoutBy = time.time() + timeoutSecs remainingTime = timeoutBy - time.time() while responseQuality < 3 and remainingTime > 0: # wait for a response. if first time round, send the request too latestResponse, src = (yield toSend, remainingTime) toSend = None # note when response was received latestResponseNanos = measureClock.nanos # did we get a response? did it come from the server we sent # the request to? if latestResponse is not None and src == dest: # assess the response and work out if better than any previous # response we're received latestResponseMsg = WCMessage.unpack(latestResponse) newQuality = calcQuality(reqMsg, latestResponseMsg) if newQuality >= responseQuality: responseQuality = newQuality responseMsg = latestResponseMsg responseRecvNanos = latestResponseNanos # work out how long left until timeout remainingTime = timeoutBy - time.time() if responseMsg is not None: candidate = Candidate(responseMsg, responseRecvNanos) else: # no message, no candidate candidate = None # pass the result to the algorithm, and wait for it to return when it # next wants a request to be sent (again supplying the response timeout) timeoutSecs = algorithm.send(candidate) except StopIteration: pass
def test_simplePayloadOverridingOriginate(self): m=WCMessage(WCMessage.TYPE_RESPONSE, 5, 12160, 1000, 2000, 3000, (0xaabbccdd, 0xeeff1122)) payload=m.pack() self.assertEqual(payload, "\x00\x01\x05\x00\x00\x00\x2f\x80\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x00\x00\x00\x00\x00\x00\x07\xd0\x00\x00\x00\x00\x00\x00\x0b\xb8")