CSS-WC Clients

Modules: dvbcss.protocol.client.wc | dvbcss.protocol.client.wc.algorithm

The WallClockClient class provides a complete Wall Clock Client implementation. It is used in conjunction with an algorithm for adjusting a clock object so that it mirrors the server’s Wall Clock.

Using a Wall Clock Client

Recommended simplest use is to instantiate a WallClockClient and provide it with an instance of the LowestDispersionCandidate algorithm to control how it updates a clock.

A simple example that connects to a wall clock server at 192.168.0.115 port 6677 and sends requests once per second:

from dvbcss.clock import SysClock as SysClock
from dvbcss.protocol.client.wc import WallClockClient
from dvbcss.protocol.client.wc.algorithm import LowestDispersionCandidate

sysClock=SysClock()
wallClock=CorrelatedClock(sysClock,tickRate=1000000000)

algorithm = LowestDispersionCandidate(wallClock,repeatSecs=1,timeoutSecs=0.5)

bind = ("0.0.0.0", 6677)
server = ("192.168.0.115", 6677)

wc_client=WallClockClient(bind, server, wallClock, algorithm)
wc_client.start()

# wall clock client is now running in the background

After the start() method is called, the WallClockClient runs in a separate thread and execution continues.

WallClockClient is used by providing it with an object implementing the clock synchronisation algorithm. The Wall Clock client handles the protocol interaction (sending the requests and parsing the responses) at the times specified by the algorithm and passes the results of each request-response measurement (a Candidate) to the algorithm. The algorithm then adjusts the clock.

_images/wc-client-clock-model.png

Changed in version 0.4: The measurement process involves taking a reading from the local clock when the request is about to be sent, and when the response is received. This measurement is taken from the parent of the clock you provide. The candidate represents a possible relationship between that (parent) clock and the Wall Clock of the server given the results of the request-response measurement. The algorithm processes this and makes a decision as to the Correlation that is to be used.

Although the WallClockClient class does not require the tickrate of the Wall Clock to be 1 tick per nanosecond, it is recommended to set it as such. This is because the next step (using the CSS-TS protocol) assumes a Wall Clock ticking at this rate.

Algorithms

The algorithm object determines when (and how often) a WallClockClient sends CSS-WC protocol requests and processes the results of requests and responses to adjust the clock object representing the Wall Clock.

Dispersion algorithm

The LowestDispersionCandidate algorithm is the recommended algorithm. It uses the candidate with the lowest dispersion.

Simple algorithm

The MostRecent algorithm is a simple naive algorithm that uses the most recent candidate irrespective of its quality (e.g. how long the round-trip time of the measurement was)

Writing new algorithms

An algorithm is an object that has the following method:

.algorithm(self)[source]

A python generator function that yields whenever it wants a Wall Clock protocol measurement to be taken:

candidateOrNone = yield timeoutSecs

The yield must pass a timeout in seconds. This is the maximum amount of time the Wall Clock client will wait for a response to its request before timing out.

Changed in version 0.4: The yield statement will return either None or a Candidate object representing the result of the measurement.

The algorithm can then use the Candidate object in its algorithm for estimating the wall clock (and in the case of most practical implementations: adjusting a clock object).

Here is an example of a simple naive algorithm that adjusts a CorrelatedClock object using the most recent measurement, irrespective of influencing factors such as previous measurements or network latency (round trip time). It makes requests at most once per second and has a timeout on waiting for responses of 0.5 seconds:

class NaiveAlgorithm(object):

    def __init__(self,clock):
        self.clock = clock
    
    def algorithm(self):
        while True:
            candidate=(yield 0.5)
            if candidate is not None:
                self.clock.correlation = candidate.calcCorrelationFor(self.clock)
                time.sleep(1.0)

Composable Filter and prediction algorithm

There is also a simple modular framework for building up an algorithm out of two parts:

  • Filters - processes measurement candidates, determining whether to reject them.
  • A Predictor - takes the measurement candidates outputted from the filtering step and use them to adjust the clock.

Use the FilterAndPredict() function to compose zero, one or more Filters, and a Predictor into an algorithm that can be used with a WallClockClient.

Changed in version 0.4: When using this algorithm, you provide a CorrelatedClock object to it, whose parent is the clock given to the WallClockClient. This algorithm controls the CorrelatedClock object by settings its correlation property to that returned by the predictor. The parent of this clock is the clock used by the WallClockClient in generating the measurement candidates. So the job of the predictor is to determine the best correlation for modelling the relationship between that (parent) clock and the wall clock of the server.

Here is a simple example that uses a simple predictor and a round-trip-time threshold filter:

from dvbcss.clock import CorrelatedClock, SysClock
from dvbcss.protocol.client.wc.algorithm import FilterAndPredict, FilterRttThreshold, PredictSimple
from dvbcss.protocol.client.wc import WallClockClient

sysClock = SysClock(tickRate=1000000000)
wallClock = CorrelatedClock(parentClock=sysClock, tickRate=1000000000)

filters = [ FilterRttThreshold(thresholdMillis=10.0) ]
predictor = PredictSimple(wallClock)

algorithm = FilterAndPredict(wallClock, repeatSecs, timeoutSecs, filters, predictor)

bind = ("0.0.0.0", 6677)
server = ("192.168.0.115", 6677)

wc_client=WallClockClient(bind, server, wallClock, algorithm)
wc_client.start()

The Clock object given to the algorithm must be CorrelatedClock whose parent is the clock object provided to the WallClockClient object.

Round-trip time threshold Filter

The FilterRttThreshold class implements a filter that eliminates any candidate where the round-trip time exceeds a threshold.

Lowest dispersion candidate filter

The FilterLowestDispersionCandidate class implements a filter that eliminates any candidate if it does not have a lower dispersion that any candidate that came before it.

Simple Predictor

The PredictSimple class is a simple predictor that uses the candidate most recently provided to it and directly transforms that into a dvbcss.clock.Correlation.

Writing your own Filter

You can write your own Filter by creating a class with the following method defined:

.checkCandidate(self, candidate)[source]
Parameters:candidate – A (dict) containing two Candidate objects representing the result of the measurement in units of ticks (dict key “ticks”) and units of nanoseconds (dict key “nanos”):

Writing your own Predictor

You can write your own Predictor by creating a class with the following methods defined:

.addCandidate(self, candidate)[source]
param candidate:
 A (dict) containing two Candidate objects representing the result of the measurement in units of of nanoseconds.

This method is called whenever a measurement candidate resulting from a request-response measurement survives the filtering process.

.predictCorrelation(self)[source]
Returns:A Correlation

The returned Correlation must represent the relationship between the clock and its parent, such that the clock becomes an estimate of the server’s Wall Clock. This must be in the correct units (tick rate) for the clock and its parent.

Classes

WallClockClient

class dvbcss.protocol.client.wc.WallClockClient((bindaddr, bindport), (dstaddr, dstport), wallClock, wcAlgorithm)[source]

Simple object implementing the client side of the CSS-TS protocol.

Use by initialising and supplying with an algorithm object and Clock object.

The Candidate objects provided to the algorithm represent the relationship between the parent of the provided clock and the server’s Wall Clock.

The algorithm is then expected to set the dvbcss.clock.Correlation of the clock object such that it becomes an estimate of the server’s Wall Clock.

It is recommended to use the LowestDispersionCandidate algorithm.

Initialisation takes the following parameters:

Parameters:
  • (bindaddr,bindport) – (str, int) A tuple containing the IP address (as a string) and port (as an int) to bind to to listen for incoming packets
  • (dstaddr,dstport) – (str, int) A tuple containing the IP address (as a string) and port (as an int) of the Wall Clock server
  • wallClock – (clock) The local clock that will be controlled to be a Wall Clock. Measurements will be taken from its parent and candidates provided to the algorithm will represent the relationship between that (parent) clock and the server’s wall clock.
  • wcAlgorithm – (algorithm) The algorithm for the client to use to update the clock.
algorithm = None[source]

(read only) The algorithm object being used with this WallClockClient

start()[source]

Call this method to start the client running. This function call returns immediately and the client proceeds to run in a thread in the background.

Does nothing if the client is already running.

stop()[source]

Call this method to stop the client running. Returns once the client has stopped.

If the client is not running then nothing happens and this call returns immediately.

Dispersion algorithm

class dvbcss.protocol.client.wc.algorithm.LowestDispersionCandidate(clock, repeatSecs=1.0, timeoutSecs=0.2, localMaxFreqErrorPpm=None)[source]

Algorithm that selects the candidate (request-response measurement result) with the lowest dispersion.

Dispersion is a formal measure of the error bounds of the Wall Clock estimate. This value is the sum of possible error due to:

  • measurement precision limitations (at both client and server)
  • round-trip time
  • maximum potential for oscillator frequency error at the client and at the server server This grows as the candidate (that was used to most recently adjust the clock) ages.

Note

The Clock object must be the same one that is provided to the WallClockClient, otherwise this algorithm will not synchronise correctly.

The tick rate of the Clock can be any tick rate (it does not have to be one tick per nanosecond), but too low a tick rate will limit clock synchronisation precision.

There is a stub callback function provided that you can override (e.g. by subclassing) that will be called whenever the clock is adjusted:

The arguments to this method provide details of when the adjustment took place and what adjustment was made. It also reports the dispersion before and after the adjustment and gives information needed to extrapolate future dispersions. You can use this, for example, to record the clock dispersion over time.

Initialisation takes the following parameters:

Parameters:
  • clock – A Correlated object representing that will be adjusted to match the Wall Clock.
  • repeatSecs – (float) The rate at which Wall Clock protocol requests are to be sent (in seconds).
  • timeoutSecs – (float) The timeout on waiting for responses to requests (in seconds).
  • localMaxFreqErrorPpm – Optional. Override using the getRootMaxFreqError() of the clock as the max freq error of the local clock, and instead use this value. It is the clock maximum frequency error in parts-per-million
getCurrentDispersion()[source]
Returns:Current dispersion at this moment in time in units of nanoseconds.
getWorstDispersion()[source]

Returns the worst (greatest) dispersion encountered since the previous time this method was called.

The first time it is called, the value reported will be very large, reflecting the fact that initially dispersion is high because the client is not yet synchronised.

Returns:dispersion
onClockAdjusted(timeAfterAdjustment, adjustment, oldDispersionNanos, newDispersionNanos, dispersionGrowthRate)[source]

This method is called immediately after a clock adjustment has been made, and gives details on how the clock was changed and the effect on the dispersion.

Parameters:
  • timeAfterAdjustment – The wall clock time (in ticks) immedaitely after the adjustment took place
  • adjustment – The amount by which the wall clock instaneously changed (in ticks)
  • oldDispersionNanos – The dispersion (in nanoseconds) just prior to adjustment
  • newDispersionNanos – The dispersion (in nanoseconds) immediately after adjustment
  • dispersionGrowthRate – The rate at which dispersion will continue to grow.
To calculate a future dispersion at time T:
futureDispersion = newDispersionNanos + (t-timeOfAdjustment) * dispersionGrowthRate

This is a stub for this method. Sub-classes should implement it.

Most recent measurement algorithm

class dvbcss.protocol.client.wc.algorithm.MostRecent(clock, repeatSecs=1.0, timeoutSecs=0.2)[source]

Simple (naive) Wall Clock client algorithm.

Crash locks the supplied clock based on the offset observed in the result of every successful request/response.

Note

The Clock object must be the same one that is provided to the WallClockClient, otherwise this algorithm will not synchronise correctly.

The tick rate of the Clock can be any tick rate (it does not have to be one tick per nanosecond), but too low a tick rate will limit clock synchronisation precision.

Initialisation takes the following parameters:

Parameters:
  • clock – A CorrelatedClock object representing that will be adjusted to match the Wall Clock.
  • repeatSecs – (float) The rate at which Wall Clock protocol requests are to be sent (in seconds).
  • timeoutSecs – (float) The timeout on waiting for responses to requests (in seconds).
getCurrentDispersion()[source]

Calling this method will always result in ValueError being raised.

Raises:ValueError – This algorithm does not model clock synchronisation accuracy.

Filter and Prediction composable algorithms

Filters

class dvbcss.protocol.client.wc.algorithm._filterpredict.FilterRttThreshold(thresholdMillis=1.0)[source]

Simple filter that rejects all candidates where round trip time exceeds a specified threshold.

Parameters:thresholdMillis – (float) The threshold to use (in milliseconds)
class dvbcss.protocol.client.wc.algorithm._filterpredict.FilterLowestDispersionCandidate(clock)[source]

Simple filter that will reject a candidate unless its dispersion is lower than that currently being used by the clock.

Note that at initialisation, the clock’s dispersion is forced to +infinity.

Parameters:clock – A CorrelatedClock object representing that will be adjusted to match the Wall Clock.

Predictors

class dvbcss.protocol.client.wc.algorithm._filterpredict.PredictSimple(clock)[source]

Simple naive predictor that chooses the correlation represented by the most recent candidate

Parameters:clock – The 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.

Functions

Filter and Prediction algorithm creator

dvbcss.protocol.client.wc.algorithm.FilterAndPredict(clock, repeatSecs=1.0, timeoutSecs=0.2, filters=[], predictor=None)[source]

Combines zero, one or more Filters and a Predictor and returns an algorithm for a WallClockClient.

Parameters:
  • clock – A CorrelatedClock object that will be adjusted to match the Wall Clock.
  • repeatSecs – (float) The rate at which Wall Clock protocol requests are to be sent (in seconds).
  • timeoutSecs – (float) The timeout on waiting for responses to requests (in seconds).
  • filters – (list of Filters) A list of zero, one or more Filters
  • predictor – (Predictor) The Predictor to use, or None to default to PredictSimple

This algorithm controls the CorrelatedClock object by settings its correlation property to that provided by the predictor.

The parent of this clock is the clock used by the WallClockClient in generating the measurement candidates. So the job of the predictor is always to estimate the Correlation needed by the clock.

Requests are made at the repetition rate specified. If a response is received within the timeout period it is then it is transformed into a measurement candidate and passed to the filters. Filters are applied in the order you list them. If a candidate survives filtering, then it is passed to the predictor. Every time a candidate is provided to the predictor, the correlation returned by the predictor replaces the previous correlation.

Note

The Clock object must be CorrelatedClock whose parent is the clock object that is measured when generating the candidates.

The tick rate of the Clock can be any tick rate (it does not have to be one tick per nanosecond), but too low a tick rate will limit clock synchronisation precision.

Candidate quality calculator

This function is used internally by the WallClockClient class.

dvbcss.protocol.client.wc.algorithm.calcQuality(reqMsg, respMsg)[source]

Generate measure of how good the response was. Quality < 0 means response corresponded to a different request.

Quality = 3 or 4 means it was a response for which no follow-up is expected or a follow-up response.

Quality = 2 means it was a response for which a follow-up is expected