MODULE TeletextDecoder;
IMPORT
SYSTEM, TVDriver, TVChannels, KernelLog, Texts, Dates, Strings, UTF8Strings;
CONST
DEBUG = FALSE;
VtPageSize = 40*24;
FormattedPageSize = 41*24;
VbiLines = TVDriver.VbiMaxLines;
VbiLineSize = TVDriver.VbiLineSize;
VbiDataSize = TVDriver.VbiDataSize;
VbiBufferSize = TVDriver.VbiBufferSize;
VbiVT = {1};
VbiUndefined* = -1;
FpFac = 65536;
Red = 0;
Green = 1;
Blue = 2;
Yellow = 3;
Magenta = 4;
Cyan = 5;
White = 6;
Black = 7;
TYPE
TeletextSuite* = OBJECT
VAR
channel*: TVChannels.TVChannel;
pages- : ARRAY 800 OF TeletextPageSet;
next* : TeletextSuite;
PROCEDURE &Init*;
VAR
i : LONGINT;
BEGIN
FOR i := 0 TO 799 DO
NEW(pages[i]);
pages[i].data := NIL;
pages[i].next := VbiUndefined;
pages[i].prev := VbiUndefined
END
END Init;
PROCEDURE Count*() : LONGINT;
VAR
i, cnt : LONGINT;
pag, cur : TeletextPage;
BEGIN
FOR i := 0 TO 799 DO
IF (pages[i].data # NIL) THEN
pag := pages[i].data;
cur := pag;
INC(cnt);
WHILE cur.nextSub # pag DO
INC(cnt);
cur := cur.nextSub
END
END
END;
RETURN cnt
END Count;
PROCEDURE MakeEmptyPage(pageNum, subPage : LONGINT) : TeletextPage;
VAR
existingPage, newPage : TeletextPage;
newPageNumber : BOOLEAN;
next, prev : LONGINT;
BEGIN {EXCLUSIVE}
IF pages[pageNum].data = NIL THEN
NEW(pages[pageNum].data);
newPage := pages[pageNum].data;
newPage.nextSub := newPage;
newPage.prevSub := newPage;
newPageNumber := TRUE
ELSE
newPageNumber := FALSE;
existingPage := pages[pageNum].data;
IF subPage < existingPage.subPageNo THEN
NEW(newPage);
newPage.nextSub := existingPage;
newPage.prevSub := existingPage.prevSub;
newPage.prevSub.nextSub := newPage;
existingPage.prevSub := newPage;
pages[pageNum].data := newPage
ELSE
WHILE (existingPage.prevSub.subPageNo > subPage) DO
existingPage := existingPage.prevSub
END;
existingPage := existingPage.prevSub;
IF existingPage.subPageNo # subPage THEN
NEW(newPage);
newPage.nextSub := existingPage.nextSub;
newPage.prevSub := existingPage;
existingPage.nextSub := newPage;
newPage.nextSub.prevSub := newPage
ELSE
newPage := existingPage
END
END
END;
newPage.pageNumber := pageNum;
newPage.subPageNo := subPage;
newPage.Reset;
IF newPageNumber THEN
prev := (pageNum - 1);
WHILE (prev >= 0) & (pages[prev].data = NIL) DO
DEC(prev)
END;
IF prev >= 0 THEN
IF pages[prev].next # VbiUndefined THEN
pages[pageNum].next := pages[prev].next;
pages[pages[pageNum].next].prev := pageNum
ELSE
pages[pageNum].next := VbiUndefined
END;
pages[pageNum].prev := prev;
pages[prev].next := pageNum
ELSE
pages[pageNum].prev := VbiUndefined;
next := pageNum + 1;
WHILE (next < 800) & (pages[next].data = NIL) DO
INC(next)
END;
IF next < 800 THEN
pages[pageNum].next := next;
pages[next].prev := pageNum
ELSE
pages[pageNum].next := VbiUndefined
END
END
END;
RETURN newPage
END MakeEmptyPage;
END TeletextSuite;
TeletextPageSet* = OBJECT
VAR
next-, prev- : LONGINT;
data- : TeletextPage;
END TeletextPageSet;
TeletextPage* = OBJECT
VAR
nextSub-, prevSub- : TeletextPage;
pageNumber, subPageNo- : LONGINT;
text- : Texts.Text;
attributes-: ARRAY FormattedPageSize OF SHORTINT;
PROCEDURE &Init*;
BEGIN
NEW(text)
END Init;
PROCEDURE Reset;
VAR
i : LONGINT;
BEGIN
text.AcquireWrite;
text.Delete(0, text.GetLength());
text.ReleaseWrite;
FOR i := 0 TO VtPageSize-1 DO
attributes[i] := 0
END
END Reset;
END TeletextPage;
PageBuffer = OBJECT
VAR
vbi : VbiDecoder;
pageNo, subPageNo : LONGINT;
data : ARRAY VtPageSize OF CHAR;
language : LONGINT;
discardPage, suppressHeader, boxedContent : BOOLEAN;
packets: SET;
lastPacket: LONGINT;
PROCEDURE &Init*(vbi : VbiDecoder);
BEGIN
SELF.vbi := vbi
END Init;
PROCEDURE Reset(page, sub : LONGINT);
VAR
i : LONGINT;
BEGIN
pageNo := page;
subPageNo := sub;
FOR i := 0 TO VtPageSize-1 DO
data[i] := ' '
END;
language := 0;
discardPage := FALSE;
suppressHeader := FALSE;
boxedContent := FALSE;
packets := {};
lastPacket := -1
END Reset;
PROCEDURE SkipBlanks(VAR name : ARRAY OF CHAR; VAR startPos : LONGINT);
VAR
i : LONGINT;
BEGIN
i := startPos;
WHILE (i < LEN(name)-1) & ((name[i] < '0') OR (ORD(name[i]) > 150)) DO
name[i] := ' ';
INC(i)
END;
startPos := i
END SkipBlanks;
PROCEDURE IsDigit(ch : CHAR) : BOOLEAN;
BEGIN
RETURN (ch >= '0') & (ch <='9')
END IsDigit;
PROCEDURE SkipPageNum(VAR name : ARRAY OF CHAR; VAR startPos : LONGINT);
VAR
i : LONGINT;
BEGIN
i := startPos;
IF IsDigit(name[i]) & IsDigit(name[i+1]) & IsDigit(name[i+2]) & (name[i+3] = ' ') THEN
FOR i := startPos TO startPos + 2 DO
name[i] := ' '
END;
startPos := startPos + 4
ELSIF (name[i] = 'P') & IsDigit(name[i+1]) & IsDigit(name[i+2]) & IsDigit(name[i+3]) & (name[i+4] = ' ')THEN
FOR i := startPos TO startPos + 3 DO
name[i] := ' '
END;
startPos := startPos + 5
END
END SkipPageNum;
PROCEDURE MarkupPage;
VAR
text: ARRAY FormattedPageSize OF Texts.Char32;
fgColor, bgColor: ARRAY FormattedPageSize OF SHORTINT;
flashing, boxed: ARRAY FormattedPageSize OF BOOLEAN;
forceAlpha, isGraphics, holdGraphics, isDouble, hasDouble, conceal, isFlashing, separatedGraphics : BOOLEAN;
teletextPage: TeletextPage;
i, j, k, begin : LONGINT;
ch : LONGINT;
fg, bg, isBoxed : SHORTINT;
PROCEDURE CreateAttribute(fg, bg: SHORTINT; flashing, boxed: BOOLEAN) : SHORTINT;
VAR
attr: SHORTINT;
BEGIN
attr := fg + 8*bg;
IF flashing THEN
INC(attr, 64)
END;
IF boxed THEN
attr := -attr
END;
RETURN attr
END CreateAttribute;
BEGIN
IF (pageNo = 0) OR (vbi.currentSuite = NIL) THEN RETURN END;
teletextPage := vbi.currentSuite.MakeEmptyPage(pageNo-100, subPageNo);
FOR i := 0 TO FormattedPageSize-1 DO
text[i] := 0;
fgColor[i] := 0;
bgColor[i] := 0;
flashing[i] := FALSE;
boxed[i] := FALSE
END;
FOR i := 0 TO 7 DO
text[i] := ORD(' ');
fgColor[i] := White;
bgColor[i] := Black
END;
FOR i := 0 TO 23 DO
isGraphics := FALSE;
holdGraphics := FALSE;
separatedGraphics := FALSE;
isDouble := FALSE;
isFlashing := FALSE;
isBoxed := 0;
hasDouble := FALSE;
conceal := FALSE;
fg := White;
bg := Black;
FOR j := 0 TO 39 DO
ch := ORD (data[40*i + j]) MOD 128;
IF text[i*41 + j] = 0 THEN
CASE ch OF
9 : isFlashing := FALSE
| 12 : isDouble := FALSE
| 24 : conceal := TRUE
| 25 : separatedGraphics := FALSE
| 26 : separatedGraphics := TRUE
| 28 : bg := Black
| 29 : bg := fg
| 30 : holdGraphics := TRUE
ELSE
END;
forceAlpha := (ch >= 64) & (ch < 96);
IF boxedContent & (isBoxed < 2) THEN
text[i*41 + j] := 32
ELSIF (ch < 32) THEN
IF isGraphics & holdGraphics THEN
text[i*41 + j] := text[i*41 + j-1]
ELSE
text[i*41 + j] := 32
END
ELSIF conceal THEN
text [i*41 + j] := 32
ELSIF isGraphics & (~ forceAlpha) THEN
text [i*41 + j] := ch + 57312
ELSE
CASE ch OF
35 : text [i*41 + j] := nationals [language][0]
| 36 : text [i*41 + j] := nationals [language][1]
| 64 : text [i*41 + j] := nationals [language][2]
| 91 : text [i*41 + j] := nationals [language][3]
| 92 : text [i*41 + j] := nationals [language][4]
| 93 : text [i*41 + j] := nationals [language][5]
| 94 : text [i*41 + j] := nationals [language][6]
| 95 : text [i*41 + j] := nationals [language][7]
| 96 : text [i*41 + j] := nationals [language][8]
| 123 : text [i*41 + j] := nationals [language][9]
| 124 : text [i*41 + j] := nationals [language][10]
| 125 : text [i*41 + j] := nationals [language][11]
| 126 : text [i*41 + j] := nationals [language][12]
| 127 : text [i*41 + j] := 57440
ELSE
text [i*41 + j] := ch
END
END;
IF isDouble THEN
DoubleHeight (text, i*41 + j)
END;
IF fgColor [i*41 + j] = 0 THEN
fgColor [i*41 + j] := fg;
bgColor [i*41 + j] := bg;
flashing [i*41 + j] := isFlashing;
IF isBoxed > 1 THEN
boxed[i*41 + j-1] := TRUE;
boxed[i*41 + j] := TRUE
END;
IF isDouble & (i < 23) THEN
flashing [(i+1)*41 + j] := isFlashing;
IF isBoxed > 1 THEN
boxed[(i+1)*41 + j-1] := TRUE;
boxed[(i+1)*41 + j] := TRUE
END
END
END;
CASE ch OF
0 : isGraphics := FALSE; conceal := FALSE; fg := Black
| 1 : isGraphics := FALSE; conceal := FALSE; fg := Red
| 2 : isGraphics := FALSE; conceal := FALSE; fg := Green
| 3 : isGraphics := FALSE; conceal := FALSE; fg := Yellow
| 4 : isGraphics := FALSE; conceal := FALSE; fg := Blue
| 5 : isGraphics := FALSE; conceal := FALSE; fg := Magenta
| 6 : isGraphics := FALSE; conceal := FALSE; fg := Cyan
| 7 : isGraphics := FALSE; conceal := FALSE; fg := White
| 8 : isFlashing := TRUE
| 10 : DEC(isBoxed)
| 11 : INC(isBoxed)
| 13 : isDouble := TRUE; hasDouble := TRUE
| 16 : isGraphics := TRUE; conceal := FALSE; fg := Black
| 17 : isGraphics := TRUE; conceal := FALSE; fg := Red
| 18 : isGraphics := TRUE; conceal := FALSE; fg := Green
| 19 : isGraphics := TRUE; conceal := FALSE; fg := Yellow
| 20 : isGraphics := TRUE; conceal := FALSE; fg := Blue
| 21 : isGraphics := TRUE; conceal := FALSE; fg := Magenta
| 22 : isGraphics := TRUE; conceal := FALSE; fg := Cyan
| 23 : isGraphics := TRUE; conceal := FALSE; fg := White
| 27 :
| 31 : holdGraphics := FALSE
ELSE
END
END
END;
text [i*41 + 40] := Texts.NewLineChar;
fgColor[i*41 + 40] := White;
bgColor[i*41 + 40] := Black;
IF (i < 23) & hasDouble THEN
FOR j := 0 TO 39 DO
fgColor [(i+1)*41 + j] := fgColor [i*41 + j];
bgColor [(i+1)*41 + j] := bgColor [i*41 + j];
IF text [(i+1)*41 + j] = 0 THEN
text [(i+1)*41 + j] := 32
END
END
END
END;
text[FormattedPageSize-1] := 0;
teletextPage.text.AcquireWrite;
teletextPage.text.InsertUCS32 (0, text);
teletextPage.text.ReleaseWrite;
fg := White;
bg := Black;
begin := 0;
FOR i := 0 TO FormattedPageSize-1 DO
IF (bgColor[i] # bg) & (i # begin) THEN
fg := fgColor[i];
bg := bgColor[i];
begin := i
ELSIF (fgColor[i] # fg) & (text[i] > 32) & (i # begin) THEN
j := i;
REPEAT
DEC(j)
UNTIL (j < begin) OR (text[j] > 32);
INC(j);
IF j > begin THEN
FOR k := begin TO j-1 DO
fgColor[k] := fg
END;
begin := j
END;
fg := fgColor[i]
END
END;
FOR i := 0 TO FormattedPageSize-1 DO
teletextPage.attributes[i] := CreateAttribute(fgColor[i], bgColor[i], flashing[i], boxed[i])
END;
vbi.currentSuite.channel.cachingTime := Dates.Now()
END MarkupPage;
PROCEDURE DoubleHeight (VAR txt : ARRAY OF LONGINT; pos : LONGINT);
VAR
c : LONGINT;
BEGIN
c := txt [pos];
IF ((c >= 57344) & (c < 57376)) OR ((c >= 57408) & (c < 57440)) THEN
CASE ((c-57184) MOD 16) OF
0 : txt [pos] := 57344
| 1 : txt [pos] := 57349
| 2 : txt [pos] := 57354
| 3 : txt [pos] := 57359
| 4 : txt [pos] := 57360
| 5 : txt [pos] := 57365
| 6 : txt [pos] := 57370
| 7 : txt [pos] := 57375
| 8 : txt [pos] := 57408
| 9 : txt [pos] := 57413
| 10 : txt [pos] := 57418
| 11 : txt [pos] := 57423
| 12 : txt [pos] := 57424
| 13 : txt [pos] := 57429
| 14 : txt [pos] := 57434
| 15 : txt [pos] := 57439
END;
IF pos+41 >= FormattedPageSize THEN
RETURN
END;
CASE ((c-57184) DIV 4) OF
40 : txt [pos+41] := 57344
| 41 : txt [pos+41] := 57345
| 42 : txt [pos+41] := 57346
| 43 : txt [pos+41] := 57347
| 44 : txt [pos+41] := 57364
| 45 : txt [pos+41] := 57365
| 46 : txt [pos+41] := 57366
| 47 : txt [pos+41] := 57367
| 56 : txt [pos+41] := 57416
| 57 : txt [pos+41] := 57417
| 58 : txt [pos+41] := 57418
| 59 : txt [pos+41] := 57419
| 60 : txt [pos+41] := 57436
| 61 : txt [pos+41] := 57437
| 62 : txt [pos+41] := 57438
| 63 : txt [pos+41] := 57439
END
ELSIF pos+41 < FormattedPageSize THEN
CASE c OF
10 :
| 32 : txt [pos+41] := 32
ELSE
txt [pos+41] := 57347
END
END
END DoubleHeight;
END PageBuffer;
VbiDecoder* = OBJECT
VAR
vcd : TVDriver.VideoCaptureDevice;
tuner : TVDriver.TVTuner;
vbiBuffer : TVDriver.VbiBuffer;
chName- : ARRAY 17 OF CHAR;
extractName* : BOOLEAN;
dead : BOOLEAN;
parallel: BOOLEAN;
off, thresh : LONGINT;
line, spos : LONGINT;
buffers: ARRAY 8 OF PageBuffer;
freq : REAL;
vtstep, vcstep, vpsstep : LONGINT;
norm : LONGINT;
currentSuite : TeletextSuite;
PROCEDURE &Init*(vcd : TVDriver.VideoCaptureDevice);
VAR
i : LONGINT;
tuner : TVDriver.TVTuner;
BEGIN
IF vcd = NIL THEN
KernelLog.String("{TeletextDecoder.Init} Parameter vcd = NIL. Aborting.");
KernelLog.Ln;
RETURN
END;
vbiBuffer := vcd.GetVbiBuffer();
tuner := vcd.GetTuner();
currentSuite := SelectTeletextSuite(tuner.GetFrequency());
extractName := FALSE;
SetFreq(0, 0);
FOR i := 0 TO 7 DO
NEW(buffers[i], SELF)
END;
dead := FALSE
END Init;
PROCEDURE GetChannel*(): TVChannels.TVChannel;
BEGIN
RETURN currentSuite.channel
END GetChannel;
PROCEDURE Count*(): LONGINT;
BEGIN
RETURN currentSuite.Count()
END Count;
PROCEDURE ResetAll*;
VAR
i : LONGINT;
BEGIN
vbiBuffer.readPos := vbiBuffer.insertPos;
vbiBuffer.vbiSize := 0;
FOR i := 0 TO 7 DO
buffers[i].discardPage := TRUE
END;
chName := ""
END ResetAll;
PROCEDURE Stop*;
BEGIN {EXCLUSIVE}
dead := TRUE
END Stop;
PROCEDURE SetFrequency*(freq : LONGINT);
BEGIN
currentSuite := SelectTeletextSuite(freq);
ResetAll
END SetFrequency;
PROCEDURE UnHam(ch1, ch2 : CHAR; VAR error: BOOLEAN) : CHAR;
VAR
v1, v2: LONGINT;
BEGIN
v1 := unHamTab [ORD (ch1)];
v2 := unHamTab [ORD (ch2)];
IF (v1 = -1) OR (v2 = -1) THEN
IF DEBUG THEN
KernelLog.String("{ TeletextDecoder } Bad Hamming Code!"); KernelLog.Ln;
END;
error := TRUE;
RETURN 0FFX
ELSE
error := FALSE;
RETURN CHR(16*v2 + v1)
END
END UnHam;
PROCEDURE ExtractChannelName (rawName : ARRAY OF CHAR; language: LONGINT);
VAR
tmp: ARRAY 17 OF CHAR;
i, j, begin, end: LONGINT;
done : BOOLEAN;
PROCEDURE Is3Digits (pos: LONGINT) : BOOLEAN;
BEGIN
IF pos > 14 THEN RETURN FALSE END;
IF (rawName[pos] < '0') OR (rawName[pos] > '9') THEN RETURN FALSE END;
IF (rawName[pos+1] < '0') OR (rawName[pos+1] > '9') THEN RETURN FALSE END;
IF (rawName[pos+2] < '0') OR (rawName[pos+2] > '9') THEN RETURN FALSE END;
IF (rawName[pos+3] > ' ') THEN RETURN FALSE END;
RETURN TRUE
END Is3Digits;
PROCEDURE GetChar(ch: CHAR) : CHAR;
VAR
utf: LONGINT;
BEGIN
CASE ORD(ch) OF
35 : utf := nationals [language][0]
| 36 : utf := nationals [language][1]
| 64 : utf := nationals [language][2]
| 91 : utf := nationals [language][3]
| 92 : utf := nationals [language][4]
| 93 : utf := nationals [language][5]
| 94 : utf := nationals [language][6]
| 95 : utf := nationals [language][7]
| 96 : utf := nationals [language][8]
| 123 : utf := nationals [language][9]
| 124 : utf := nationals [language][10]
| 125 : utf := nationals [language][11]
| 126 : utf := nationals [language][12]
ELSE
utf := ORD(ch)
END;
IF utf < 256 THEN
RETURN CHR(utf)
ELSE
RETURN ch
END;
END GetChar;
BEGIN
FOR i := 0 TO 15 DO
IF ORD(rawName[i]) <= 32 THEN
tmp[i] := ' '
ELSE
tmp[i] := rawName[i]
END
END;
i := 0;
REPEAT
done := TRUE;
IF Is3Digits (i) THEN
begin := i;
end := i + 3
ELSIF ((tmp[i] = 'P') OR (tmp[i] = 'p')) & Is3Digits (i+1) THEN
begin := i;
end := i + 4
ELSE
done := FALSE
END;
IF ~done THEN
REPEAT
INC (i)
UNTIL (i >= 14) OR (rawName[i] = ' ');
INC (i)
END
UNTIL done OR (i >= 14);
IF done THEN
IF begin < 3 THEN
WHILE (end < 16) & (tmp[end] = ' ') DO
INC(end)
END;
i := 15;
WHILE (i >= 0) & (tmp[i] # ' ') DO
DEC (i)
END;
IF i < end THEN
i := 15
END;
FOR j := end TO i DO
CASE ORD(rawName[j]) OF
35,36,64,91,92,93,94,95,96,123,124,125,126: chName[j-end] := GetChar(rawName[j])
ELSE
chName[j-end] := tmp[j]
END
END;
chName[i-end+1] := 0X
ELSE
i := 0;
WHILE tmp[i] = ' ' DO
INC(i)
END;
FOR j := i TO begin-1 DO
CASE ORD(rawName[j]) OF
35,36,64,91,92,93,94,95,96,123,124,125,126: chName[j-end] := GetChar(rawName[j])
ELSE
chName[j-i] := tmp[j]
END
END;
chName[begin] := 0X
END
END;
Strings.Trim(chName, ' ');
UTF8Strings.ASCIItoUTF8(chName, chName)
END ExtractChannelName;
PROCEDURE DecodeVt(dat : ARRAY OF CHAR);
VAR
magPack, mag, pack : LONGINT;
pg : CHAR;
page, subPage, pTen, pUnit, designationCode, pageFunction, pageCoding : LONGINT;
sub1, sub2 : LONGINT;
chName : ARRAY 17 OF CHAR;
i, flg : LONGINT;
flags, bits : SET;
error: BOOLEAN;
BEGIN
magPack := ORD(UnHam(dat[3], dat[4], error));
IF error THEN
RETURN
END;
mag := magPack MOD 8;
pack := magPack DIV 8;
IF pack = 0 THEN
pg := UnHam(dat[5], dat[6], error);
IF error THEN
IF DEBUG THEN
KernelLog.String("Hamming error on page "); KernelLog.Int(buffers[mag].pageNo, 0)
END;
buffers[mag].discardPage := TRUE;
RETURN
END;
pTen := ORD(pg) DIV 16;
pUnit := ORD (pg) MOD 16;
page := mag*100 + pTen*10 + pUnit;
IF page < 100 THEN
page := page + 800
END;
IF (page # buffers[mag].pageNo) THEN
IF ~buffers[mag].discardPage THEN
buffers[mag].MarkupPage
END;
buffers[mag].Reset(page, subPage)
END;
IF (pg = 0FFX) OR (pTen > 9) OR (pUnit > 9) THEN
buffers[mag].discardPage := TRUE;
RETURN
END;
IF pg # 0FFX THEN
sub1 := ORD(UnHam(dat[7], dat[8], error));
IF error THEN
IF DEBUG THEN
KernelLog.String("Hamming Error on page "); KernelLog.Int(page, 0); KernelLog.Ln
END;
buffers[mag].discardPage := TRUE;
RETURN
END;
sub2 := ORD(UnHam(dat[9], dat[10], error));
IF error THEN
IF DEBUG THEN
KernelLog.String("Hamming Error on page "); KernelLog.Int(page, 0)
END;
buffers[mag].discardPage := TRUE;
RETURN
END;
subPage := 256*(sub2 MOD 64) + (sub1 MOD 128);
IF sub1 > 128 THEN
buffers[mag].Reset (page, subPage);
END;
IF (sub2 MOD 128) > 64 THEN
buffers[mag].boxedContent := TRUE
END;
IF sub2 > 128 THEN
buffers[mag].boxedContent := TRUE
END;
flg := ORD(UnHam(dat[11], dat[12], error));
IF error THEN
IF DEBUG THEN
KernelLog.String("Hamming Error on page "); KernelLog.Int(page, 0); KernelLog.Ln
END;
buffers[mag].discardPage := TRUE;
RETURN
END;
flags := SYSTEM.VAL(SET, flags);
IF 0 IN flags THEN
buffers[mag].suppressHeader := TRUE
END;
IF 3 IN flags THEN
KernelLog.String("Inhibit Display... Discarding Page"); KernelLog.Ln;
buffers[mag].discardPage := TRUE
END;
IF ~ (4 IN flags) THEN
parallel := TRUE
END;
buffers[mag].language := flg DIV 32;
IF DEBUG THEN
CASE buffers[mag].language OF
0: KernelLog.String(" Language: English")
| 1: KernelLog.String(" Language: French")
| 2: KernelLog.String(" Language: Swedish/Finnish/Hungarian")
| 3: KernelLog.String(" Language: Czech/Slovak")
| 4: KernelLog.String(" Language: German")
| 5: KernelLog.String(" Language: Portuguese/Spanish")
| 6: KernelLog.String(" Language: Italian")
| 7: KernelLog.String(" Language: <Undefined>")
END;
KernelLog.Ln
END;
IF (~ buffers[mag].suppressHeader) & (~ buffers[mag].discardPage) THEN
FOR i := 0 TO 39 DO
buffers[mag].data[i] := dat[i+5]
END
END;
buffers[mag].lastPacket := 0;
buffers[mag].packets := buffers[mag].packets + {0};
IF extractName THEN
FOR i := 0 TO 15 DO
chName[i] := CHR(ORD(dat[i+13]) MOD 128)
END;
ExtractChannelName (chName, flg DIV 32)
END
END
ELSIF (1 <= pack) & (pack < 24) THEN
IF (pack <= buffers[mag].lastPacket) OR (pack IN buffers[mag].packets) THEN
buffers[mag].discardPage := TRUE
ELSE
buffers[mag].lastPacket := pack;
buffers[mag].packets := buffers[mag].packets + {pack}
END;
IF ~ buffers[mag].discardPage THEN
FOR i := 0 TO 39 DO
buffers[mag].data[pack*40 + i] := dat[i+5]
END
END
ELSIF pack <= 25 THEN
ELSIF pack = 28 THEN
designationCode := unHamTab [ORD (dat[5])];
IF designationCode = -1 THEN
buffers[mag].discardPage := TRUE
ELSIF designationCode = 0 THEN
bits := SYSTEM.VAL(SET, dat[6]);
pageFunction := 0;
IF 2 IN bits THEN INC(pageFunction, 1) END;
IF 4 IN bits THEN INC(pageFunction, 2) END;
IF 5 IN bits THEN INC(pageFunction, 4) END;
IF 6 IN bits THEN INC(pageFunction, 8) END;
IF pageFunction # 0 THEN
buffers[mag].discardPage := TRUE
END;
pageCoding := ORD(dat[7]) MOD 8;
IF pageCoding # 0 THEN
buffers[mag].discardPage := TRUE
END
END
ELSIF (26 <= pack) & (pack <= 29) THEN
ELSIF pack = 31 THEN
END
END DecodeVt;
PROCEDURE SetFreq(f : REAL; n : LONGINT);
VAR
vtfreq : REAL;
vpsfreq : REAL;
vcfreq : REAL;
BEGIN
IF norm # 0 THEN
vtfreq := 5.72725
ELSE
vtfreq := 6.9375
END;
vpsfreq := 5;
vcfreq := 0.77;
norm := n;
IF f = 0 THEN
IF norm # 0 THEN
freq := 28.636363
ELSE
freq := 35.468950
END
ELSE
freq := f
END;
vtstep := ENTIER((freq/vtfreq)*FpFac + 0.5);
vpsstep := 2*ENTIER((freq/vtfreq)*FpFac + 0.5);
vcstep := ENTIER((freq/vcfreq)*FpFac + 0.5)
END SetFreq;
PROCEDURE AGC(pos, start, stop, step : LONGINT);
VAR
i : LONGINT;
min, max : LONGINT;
BEGIN
min := 255;
max := 0;
i := start;
WHILE i < stop DO
IF ORD(vbiBuffer.data[pos + i]) < min THEN
min := ORD(vbiBuffer.data[pos + i])
END;
IF ORD(vbiBuffer.data[pos + i]) > max THEN
max := ORD(vbiBuffer.data[pos + i])
END;
i := i + step
END;
thresh := (min + max) DIV 2;
off := 128 - thresh
END AGC;
PROCEDURE Scan(step, pos : LONGINT) : CHAR;
VAR
dat : SET;
j, ord : LONGINT;
BEGIN
dat := {};
FOR j := 7 TO 0 BY -1 DO
ord := ORD(vbiBuffer.data[pos + (spos DIV FpFac)]) + off;
IF (ord >= 128) & (ord < 256) THEN
dat := dat + { (7-j) }
END;
spos := spos + step
END;
RETURN SYSTEM.VAL(CHAR, dat)
END Scan;
PROCEDURE DecodeLine(pos : LONGINT);
VAR
i, p : LONGINT;
data : ARRAY 45 OF CHAR;
BEGIN
AGC(pos, 120, 450, 1);
p := 50;
WHILE (vbiBuffer.data[pos + p] < CHR(thresh)) & (p < 350) DO
INC(p)
END;
spos := p*FpFac + vtstep DIV 2;
data[0] := Scan(vtstep, pos);
IF (ORD(data[0]) DIV 2) = 42 THEN
data[1] := Scan(vtstep, pos);
IF data[1] = 0D5X THEN
spos := spos - 2*vtstep;
data[1] := 55X
END;
IF data[1] = 55X THEN
data[2] := Scan(vtstep, pos);
IF data[2] = 0D8X THEN
FOR i := 3 TO 44 DO
data[i] := CHR(ORD(Scan(vtstep, pos)) MOD 128)
END;
RETURN
ELSIF data[2] =27X THEN
FOR i := 3 TO 44 DO
data[i] := CHR(ORD(Scan(vtstep, pos)) MOD 128)
END;
DecodeVt(data);
RETURN
END
END
END
END DecodeLine;
PROCEDURE Decode;
VAR
readPos, curPos : LONGINT;
BEGIN
readPos := vbiBuffer.readPos;
IF vbiBuffer.vbiSize >= VbiDataSize THEN
FOR line := 0 TO 2*VbiLines-1 DO
curPos := readPos + line*VbiLineSize;
DecodeLine(curPos)
END
END
END Decode;
BEGIN {ACTIVE}
(* Wait until the initial teletext suite is selected *)
WHILE currentSuite = NIL DO
END;
IF vbiBuffer = NIL THEN
KernelLog.String("{TeletextDecoder} vbiBuffer is NIL!"); KernelLog.Ln
ELSE
KernelLog.String("{TeletextDecoder} Decoding started"); KernelLog.Ln;
dead := FALSE;
REPEAT
vbiBuffer.AwaitData;
(* AwaitData return after a 30sec. timeout. Decode only after non-timeouts *)
IF vbiBuffer.vbiSize > 0 THEN
Decode();
vbiBuffer.readPos := (vbiBuffer.readPos + VbiDataSize) MOD VbiBufferSize;
vbiBuffer.vbiSize := vbiBuffer.vbiSize - VbiDataSize
END
UNTIL dead
END;
KernelLog.String("{TeletextDecoder} Decoding stopped"); KernelLog.Ln
END VbiDecoder;
VAR
teletextSuites*: TeletextSuite;
unHamTab : ARRAY 256 OF LONGINT;
nationals : ARRAY 8 OF ARRAY 13 OF LONGINT;
PROCEDURE BuildTeletextSuites;
VAR
i: LONGINT;
suite: TeletextSuite;
BEGIN
teletextSuites := NIL;
FOR i := TVChannels.channels.GetCount()-1 TO 0 BY -1 DO
NEW(suite);
suite.channel := TVChannels.channels.GetItem(i);
suite.next := teletextSuites;
teletextSuites := suite
END
END BuildTeletextSuites;
PROCEDURE SelectTeletextSuite* (freq: LONGINT): TeletextSuite;
VAR
suite: TeletextSuite;
BEGIN
suite := teletextSuites;
WHILE (suite # NIL) & ((suite.channel.freq-10 > freq) OR (suite.channel.freq+10 < freq)) DO
suite := suite.next
END;
IF (suite = NIL) & DEBUG THEN
KernelLog.String("{TeletextDecoder} SelectTeletextSuite: Suite = NIL"); KernelLog.Ln
END;
RETURN suite
END SelectTeletextSuite;
PROCEDURE InitUnhamTab;
BEGIN
unHamTab [0] := -1; unHamTab [1] := -1; unHamTab [2] := 1; unHamTab [3] := 1;
unHamTab [4] := -1; unHamTab [5] := 0; unHamTab [6] := 1; unHamTab [7] := -1;
unHamTab [8] := -1; unHamTab [9] := 2; unHamTab [10] := 1; unHamTab [11] := -1;
unHamTab [12] := 10; unHamTab [13] := -1; unHamTab [14] := -1; unHamTab [15] := 7;
unHamTab [16] := -1; unHamTab [17] := 0; unHamTab [18] := 1; unHamTab [19] := -1;
unHamTab [20] := 0; unHamTab [21] := 0; unHamTab [22] := -1; unHamTab [23] := -1;
unHamTab [24] := 6; unHamTab [25] := -1; unHamTab [26] := -1; unHamTab [27] := 11;
unHamTab [28] := -1; unHamTab [29] := 0; unHamTab [30] := 3; unHamTab [31] := -1;
unHamTab [32] := -1; unHamTab [33] := 12; unHamTab [34] := 1; unHamTab [35] := -1;
unHamTab [36] := 4; unHamTab [37] := -1; unHamTab [38] := -1; unHamTab [39] := 7;
unHamTab [40] := 6; unHamTab [41] := -1; unHamTab [42] := -1; unHamTab [43] := 7;
unHamTab [44] := -1; unHamTab [45] := -1; unHamTab [46] := 7; unHamTab [47] := 7;
unHamTab [48] := 6; unHamTab [49] := -1; unHamTab [50] := -1; unHamTab [51] := 5;
unHamTab [52] := -1; unHamTab [53] := 0; unHamTab [54] := 13; unHamTab [55] := -1;
unHamTab [56] := 6; unHamTab [57] := 6; unHamTab [58] := -1; unHamTab [59] := -1;
unHamTab [60] := 6; unHamTab [61] := -1; unHamTab [62] := -1; unHamTab [63] := 7;
unHamTab [64] := -1; unHamTab [65] := 2; unHamTab [66] := 1; unHamTab [67] := -1;
unHamTab [68] := 4; unHamTab [69] := -1; unHamTab [70] := -1; unHamTab [71] := 9;
unHamTab [72] := 2; unHamTab [73] := 2; unHamTab [74] := -1; unHamTab [75] := -1;
unHamTab [76] := -1; unHamTab [77] := 2; unHamTab [78] := 3; unHamTab [79] := -1;
unHamTab [80] := 8; unHamTab [81] := -1; unHamTab [82] := -1; unHamTab [83] := 5;
unHamTab [84] := -1; unHamTab [85] := 0; unHamTab [86] := 3; unHamTab [87] := -1;
unHamTab [88] := -1; unHamTab [89] := 2; unHamTab [90] := 3; unHamTab [91] := -1;
unHamTab [92] := -1; unHamTab [93] := -1; unHamTab [94] := 3; unHamTab [95] := 3;
unHamTab [96] := 4; unHamTab [97] := -1; unHamTab [98] := -1; unHamTab [99] := 5;
unHamTab [100] := 4; unHamTab [101] := 4; unHamTab [102] := -1; unHamTab [103] := -1;
unHamTab [104] := -1; unHamTab [105] := 2; unHamTab [106] := 15; unHamTab [107] := -1;
unHamTab [108] := 4; unHamTab [109] := -1; unHamTab [110] := -1; unHamTab [111] := 7;
unHamTab [112] := -1; unHamTab [113] := -1; unHamTab [114] := 5; unHamTab [115] := 5;
unHamTab [116] := 4; unHamTab [117] := -1; unHamTab [118] := -1; unHamTab [119] := 5;
unHamTab [120] := 6; unHamTab [121] := -1; unHamTab [122] := -1; unHamTab [123] := 5;
unHamTab [124] := -1; unHamTab [125] := 14; unHamTab [126] := 3; unHamTab [127] := -1;
unHamTab [128] := -1; unHamTab [129] := 12; unHamTab [130] := 1; unHamTab [131] := -1;
unHamTab [132] := 10; unHamTab [133] := -1; unHamTab [134] := -1; unHamTab [135] := 9;
unHamTab [136] := 10; unHamTab [137] := -1; unHamTab [138] := -1; unHamTab [139] := 11;
unHamTab [140] := 10; unHamTab [141] := 10; unHamTab [142] := -1; unHamTab [143] := -1;
unHamTab [144] := 8; unHamTab [145] := -1; unHamTab [146] := -1; unHamTab [147] := 11;
unHamTab [148] := -1; unHamTab [149] := 0; unHamTab [150] := 13; unHamTab [151] := -1;
unHamTab [152] := -1; unHamTab [153] := -1; unHamTab [154] := 11; unHamTab [155] := 11;
unHamTab [156] := 10; unHamTab [157] := -1; unHamTab [158] := -1; unHamTab [159] := 11;
unHamTab [160] := 12; unHamTab [161] := 12; unHamTab [162] := -1; unHamTab [163] := -1;
unHamTab [164] := -1; unHamTab [165] := 12; unHamTab [166] := 13; unHamTab [167] := -1;
unHamTab [168] := -1; unHamTab [169] := 12; unHamTab [170] := 15; unHamTab [171] := -1;
unHamTab [172] := 10; unHamTab [173] := -1; unHamTab [174] := -1; unHamTab [175] := 7;
unHamTab [176] := -1; unHamTab [177] := 12; unHamTab [178] := 13; unHamTab [179] := -1;
unHamTab [180] := -1; unHamTab [181] := -1; unHamTab [182] := 13; unHamTab [183] := 13;
unHamTab [184] := 6; unHamTab [185] := -1; unHamTab [186] := -1; unHamTab [187] := 11;
unHamTab [188] := -1; unHamTab [189] := 14; unHamTab [190] := 13; unHamTab [191] := -1;
unHamTab [192] := 8; unHamTab [193] := -1; unHamTab [194] := -1; unHamTab [195] := 9;
unHamTab [196] := -1; unHamTab [197] := -1; unHamTab [198] := 9; unHamTab [199] := 9;
unHamTab [200] := -1; unHamTab [201] := 2; unHamTab [202] := 15; unHamTab [203] := -1;
unHamTab [204] := 10; unHamTab [205] := -1; unHamTab [206] := -1; unHamTab [207] := 9;
unHamTab [208] := 8; unHamTab [209] := 8; unHamTab [210] := -1; unHamTab [211] := -1;
unHamTab [212] := 8; unHamTab [213] := -1; unHamTab [214] := -1; unHamTab [215] := 9;
unHamTab [216] := 8; unHamTab [217] := -1; unHamTab [218] := -1; unHamTab [219] := 11;
unHamTab [220] := -1; unHamTab [221] := 14; unHamTab [222] := 3; unHamTab [223] := -1;
unHamTab [224] := -1; unHamTab [225] := 12; unHamTab [226] := 15; unHamTab [227] := -1;
unHamTab [228] := 4; unHamTab [229] := -1; unHamTab [230] := -1; unHamTab [231] := 9;
unHamTab [232] := -1; unHamTab [233] := -1; unHamTab [234] := 15; unHamTab [235] := 15;
unHamTab [236] := -1; unHamTab [237] := 14; unHamTab [238] := 15; unHamTab [239] := -1;
unHamTab [240] := 8; unHamTab [241] := -1; unHamTab [242] := -1; unHamTab [243] := 5;
unHamTab [244] := -1; unHamTab [245] := 14; unHamTab [246] := 13; unHamTab [247] := -1;
unHamTab [248] := -1; unHamTab [249] := 14; unHamTab [250] := 15; unHamTab [251] := -1;
unHamTab [252] := 14; unHamTab [253] := 14; unHamTab [254] := -1; unHamTab [255] := -1;
END InitUnhamTab;
PROCEDURE InitNationals;
BEGIN
nationals[0][0] := 163; nationals[0][1] := 36; nationals[0][2] := 64; nationals[0][3] := 8592;
nationals[0][4] := 189; nationals[0][5] := 8594; nationals[0][6] := 8593; nationals[0][7] := 35;
nationals[0][8] := 45; nationals[0][9] := 188; nationals[0][10] := 8741; nationals[0][11] := 190;
nationals[0][12] := 247;
nationals[1][0] := 233; nationals[1][1] := 239; nationals[1][2] := 224; nationals[1][3] := 235;
nationals[1][4] := 234; nationals[1][5] := 249; nationals[1][6] := 238; nationals[1][7] := 35;
nationals[1][8] := 232; nationals[1][9] := 226; nationals[1][10] := 244; nationals[1][11] := 251;
nationals[1][12] := 231;
nationals[2][0] := 35; nationals[2][1] := 164; nationals[2][2] := 201; nationals[2][3] := 196;
nationals[2][4] := 214; nationals[2][5] := 197; nationals[2][6] := 220; nationals[2][7] := 239;
nationals[2][8] := 233; nationals[2][9] := 228; nationals[2][10] := 246; nationals[2][11] := 229;
nationals[2][12] := 252;
nationals[3][0] := 35; nationals[3][1] := 367; nationals[3][2] := 269; nationals[3][3] := 357;
nationals[3][4] := 382; nationals[3][5] := 253; nationals[3][6] := 237; nationals[3][7] := 345;
nationals[3][8] := 233; nationals[3][9] := 225; nationals[3][10] := 277; nationals[3][11] := 250;
nationals[3][12] := 353;
nationals[4][0] := 35; nationals[4][1] := 36; nationals[4][2] := 167; nationals[4][3] := 196;
nationals[4][4] := 214; nationals[4][5] := 220; nationals[4][6] := 94; nationals[4][7] := 95;
nationals[4][8] := 176; nationals[4][9] := 228; nationals[4][10] := 246; nationals[4][11] := 252;
nationals[4][12] := 223;
nationals[5][0] := 231; nationals[5][1] := 36; nationals[5][2] := 161; nationals[5][3] := 225;
nationals[5][4] := 233; nationals[5][5] := 237; nationals[5][6] := 243; nationals[5][7] := 250;
nationals[5][8] := 191; nationals[5][9] := 252; nationals[5][10] := 241; nationals[5][11] := 232;
nationals[5][12] := 224;
nationals[6][0] := 163; nationals[6][1] := 36; nationals[6][2] := 233; nationals[6][3] := 176;
nationals[6][4] := 231; nationals[6][5] := 8594; nationals[6][6] := 8593; nationals[6][7] := 35;
nationals[6][8] := 249; nationals[6][9] := 224; nationals[6][10] := 242; nationals[6][11] := 232;
nationals[6][12] := 236;
nationals[7][0] := 35; nationals[7][1] := 164; nationals[7][2] := 64; nationals[7][3] := 91;
nationals[7][4] := 92; nationals[7][5] := 93; nationals[7][6] := 94; nationals[7][7] := 95;
nationals[7][8] := 96; nationals[7][9] := 123; nationals[7][10] := 166; nationals[7][11] := 125;
nationals[7][12] := 126;
END InitNationals;
BEGIN
IF TVChannels.channels.GetCount() # 0 THEN
BuildTeletextSuites
ELSE
KernelLog.String("{TeletextDecoder} No channels found");
KernelLog.Ln
END;
InitUnhamTab;
InitNationals
END TeletextDecoder.