How does the locking routine work?
==================================

Introduction
~~~~~~~~~~~~

Here a try to explain the workings of the locking code of cc2y4m-0.0.x.

Some theory 
~~~~~~~~~~~

A PAL frame consists of 625 lines, there are 25 frames in a second. The BTTV
samples 2268 pixels in every line. So this means that the sampling frequency is
625*25*2268 = 35437500 Hz and every frame consists of 625*2268 = 1417500 pixels
(this includes the invisibe pixels in the sideborders and in the vertical
blanking interval).

Suppose that some station exactly broadcasts at 25 frames/second. If the PLL of
the BTTV would be exactly tuned to 35437500 Hz, then the image would be
stationary (not roll).

The following two statements are very important, be sure to understand them.

- if the PLL of the BTTV would be tuned a bit too low, then the image
would roll to the LEFT.

- if the PLL of the BTTV would be tuned too high, then the image would
roll to the RIGHT.

I will illustrate the last statement with an example: Let's tune the PLL of the
BTTV to 35437600 Hz (a bit too high). Take some pixel in the tv signal as a
reference point. We now for sure that the same pixel will be rebroadcasted
every 0.04 (1/25) seconds. Because the PLL is 'wrong' we capture 1/25*35437600
= 1417504 pixels. This equals 625 lines and four pixels in our graphics memory
(1417504%2268=4). So the pixel (and therefore the whole image) moves right at a
speed of 4 pixels per frame.


Requirement for autosync
~~~~~~~~~~~~~~~~~~~~~~~~

A human can perform synchronisation by looking at the image and seeing if it is
stationary and if the position is correct (i.e. if the upper left corner of the
image is at the upper left color of the viewing window/monitor).

For a computer to perform autosync, it must be able to see (in a
computationally inexpensive manner) those two things.

The reference point that is used by cc2y4m can be characterized as follows:
'it is the point preceded by a black line of at least 50 pixels followed by
a white line with a length between 355 and 365 pixels.' 


Implementation
~~~~~~~~~~~~~~

The locking code is a state-machine with three states, it is implemented in
flutlock. In state 0, the code performs a global (across the whole image)
search to find the abovementioned reference point. If a candidate is found, the
state changes to 1. If the reference point is not found we stay in state 0. If
it is not found for a long time, the locking code complains and keeps on
trying.

In state 1 another global search is performed. If the search is successfull,
then the code assumes that the POSITION and the SPEED of the reference point
are known. The state is switched to 2. The code calculates a first guess
about the 'sampling frequency for which the image would be sationary'.

In state two, the code predicts where the sync point will occur, based on the
previous POSITION and SPEED and changes in the PLL frequency (see the example
below). The code verifies its prediction by looking for a black->white
transition around the predicted point. If the prediction turns out to be wrong
for a couple of times, we change back to state 0. If the prediction is good,
then the running average of the 'sampling frequency for which the image would
be stationary' is updated. Information about the current position of the sync
point is provided to the locking code by flutlock.

At this point the computer has expert knowledge of the location of the
reference point the effects of changing the PLL frequency.

The stuff that follows is simple:

-if the reference point is too low -> move the image up by changing the
vertical total to 624

-if the reference point is too high -> move the image down " " " 626

-if the reference point is left from the point where it should be -> change the
pll frequency to a frequency 'too high' so that the image starts moving to the
right

-if the refenence point is right from etc....

(these four things happen after the "if (lock->offset >= 0)" in lock_thread(),
the movement will be faster if the sync point is further off)

In summary: lock.c contains three important functions:

-refpoint(): this finds the location of the reference point. If the specified
search area is large, it performs a global search. Otherwise it just verifies
and updates the guessed location of the reference point.

-flutlock(): this tries to measure (using refpoint()) and predict the position
of the reference point and privide it to the caller

-lock_thread(): it takes the output from flutlock, if the output is valid (!=
-1) it adjusts the PLL and the vertical total so that the reference point moves
to the right location on the screen. (using 'common sense')


Example calculation
~~~~~~~~~~~~~~~~~~~

How does the program keep track of the reference point if the PLL frequency
changes?

The time at which the PLL frequency changes is known, it is at the moment
a v4l buffer is synced. The next buffer will be captured with the new frequency.

Assume we have two frames, frame0 and frame1. Frame0 is captured with a PLL of
35438000 Hz, frame1 with 35436700 Hz. The 'frequency at which the image would
be stationary' is 35437200Hz (slightly off spec, but we need to deal with it).
The location of the sync point in frame0 is 411000 'pixels after the PLL
change'. We now predict the location of the sync point in frame1.

The remaining 2268*625-411000 pixels of frame0 will be captured at a frequency
of 35438000 Hz, this will take 0.0284017157 seconds. Based on the exact
duration of frame, derived from the 'frequency at which the image would be
stable' is 625*2268/35437200 = 0.0400003386. So we have 0.0400003386 -
0.0284017157 = 0.0115986229 seconds to capture the pixels that appear before
the reference point in frame1. The amount of pixels is 0.0115986229 * 35436700
= 411017. So the new location of the reference point will be 411017; it has
shifted 17 pixels to the right.

This calcuation is performed in the two lines of code after 'case 2:' in
flutlock. It also takes into account the changing of the 'vertical total'
register of the BTTV.

There is one important subtlety: nothing is known about the amount of time
between a 'PLL change' and the aqcuisition of the first visible pixel. 
The code contains the macro INVIS to denote the amount of pixels
between a PLL change and the occurance of the first visible pixel. It is
an experimentally derived constant. A value of 70000 seems to work
rather nicely.


Interlacing
~~~~~~~~~~~

A PAL frame consists of two fields. The current autosync code looks only
at the first field. It would be better to look at both fields, but there
are currently some weird problems with interlacing..... so I forgot about
it for now. For the exact story of the buffer layout, see the comment
at the top of bttv.c.


The End
~~~~~~~

I hope that this text answers your questions about the locking code.
Feel free to ask for clarification and see lock.c for the implementation.

Rik Snel (snel@phys.uu.nl).

$Id: TECH 38 2004-06-24 19:00:24Z rsnel $

