Source file: /~heha/hs/Funkuhr.zip/src/mtDekoder.c

/********************************
 * Projekt: Funkuhr DCF77	*
 * Wetter-Region-Zuordnung	*
 * und Verwaltung des Caches	*
 * (Kein Entschlüsseln hier!)	*
 ********************************/

#include "Funkuhr.h"

/* Extrahiert aus den (bis zu 10 zurückliegenden) Telegrammdaten
   einen 3-Minuten-Block und liefert diesen nach <data>
   Rückgabe: Negativer Fehlerkode, sonst 0
 -1: Nicht genügend Daten
 -2: Fehler im 2. Datenblock
 -3: Inkonsistente Minute (die anderen Zeitangaben werden hier nicht ausgewertet)
 -4: Falsche Minute, Rest=1, versuche es mit --MinuteZurueck
 -5: Falsche Minute, Rest=0, versuche es mit 2x --MinuteZurueck
*/
static int Dreierblock(BYTE MinuteZurueck, U64 data[3]) {
 ULONG Index,m;
 if (!MinuteZurueck && Empfang.Sek<15) return -1;
 if (Empfang.Anzahl<MinuteZurueck+3) return -1;	// Nicht genügend Daten
 Index=Empfang.Index-MinuteZurueck;	// "letztes" Telegramm
 while (Index>=MINU) Index+=MINU;
 data[2].ull=Empfang.Data[Index];
 Index--; if (Index>=MINU) Index+=MINU;	// "mittleres" Telegramm
 data[1].ull=Empfang.Data[Index];
 if (Empfang.Okay[Index]!=0x7FFFFFFFFFFFFFF) return -2;	// Fehler im 2. Datenblock (zu hart?)
 m=((DWORD)Empfang.Data[Index])>>21;
 if (CountLsbBits(m,8)&1) return -3;	// falsche Parität
 m=GetBCD((BYTE)(m&0x7F),0,59);
 if (m==0xFF) return -3;		// falsche Ziffern
 switch (m%3) {
  case 0: return -5;	// Falsche Minute
  case 1: return -4;
 }
 Index--; if (Index>=MINU) Index+=MINU;
 data[0].ull=Empfang.Data[Index];
 return 0;
}
/*
==== Alarmblock (aus Wikipedia) ====
Bei Bit 0 == 1 und Bit 7 == 1 ist's ein Katastrophenwarnsignal für Deutschland mit folgenden Bits:
1. Minute:  A D1 D2 P1 D3 P2 P3    A  D1  D2 P1  D3 P2 P3
2. Minute: D4 D5 D6 D7 D8 D9 P4  D10 D11 D12 P5 D13 P6 P7
3. Minute: Wie 2. Minute
 dekodiert
 Empfang.Data[Empfang.Index-MinuteZurueck]
 Empfang.Data[Empfang.Index-MinuteZurueck-1]
 Empfang.Data[Empfang.Index-MinuteZurueck-2]
 auf einen Alarmblock
 Voraussetzung:
 * Mindestens diese drei Telegramme vorhanden, das oberste zu mindestens 15 Sekunden
 * Empfang.Data[Empfang.Index-MinuteZurueck-1] muss eine bestimmte Minute ergeben: Minute%3==2
 * Datenbits müssen gleich sein und Prüf-Bits stimmen
 Bei Fehler ist der Rückgabewert negativ
 -1..-5: Siehe Dreierblock()
 -7: Kein Alarmblock
 -8: Gleichbits ungleich
 -9: Prüfbits falsch (Wie ist die Prüfvorschrift?)
*/
int AlarmDek(BYTE MinuteZurueck) {
 int k=0xD1034;	// 1 = P-Bits, 0 = D-Bits
 int i,r,ret=0;
 U64 data[3];
 BYTE lo,hi;
 DWORD da;
 i=Dreierblock(MinuteZurueck,data);
 if (i<0) return i;
 lo=data[0].b[0]>>1;
 hi=data[0].b[1]&0x7F;
 if (!(lo&hi&1)) return -7;	// kein Alarmblock
 if (lo!=hi) return -8;		// Ungleiche Gleichbits
 if (((da=(WORD)data[1].Lo)^data[2].Lo)&0x7FFE) return -8;
 r=lo>>1|(da&0x7FFE)<<6;
 da=lo=0;
// jetzt in r:
// PPDPDDD PDDDDDD PPDPDD
// 76D5CBA 4987654 323121
 for (i=0; i<20; i++,r>>=1,k>>=1) {
  if (k&1) {
   lo>>=1; if (r&1) lo|=0x40;	// Hier kommt ein Prüfbit
  }else{
   da>>=1; if (r&1) da|=0x1000;	// Hier kommt ein Datenbit
  }
 }
// Jetzt im Ergebnis:
// 00000000 0PPPPPPP 000DDDDD DDDDDDDD
 return MAKELONG(da,lo);
}

// MJD = Modifiziertes Julianisches Datum, Tage ab 1.1.1601, 00:00 Uhr
// (auch ANSI-Datum, COBOL-Datum)
// <saveindex> ist ein fortlaufender Index, geeignet für 24000 Jahre …
bool BuildCipher(BYTE MinuteZurueck, BYTE cipher[10], DWORD*saveindex) {
/* MinuteZurueck = 0 meint aktuelle Minute
 Ein gültiger Chiffre erfordert:
 + Fehlerfreier Empfang der 0. Minute (Minutenangabe % 3 == 1)
 + Fehlerfreier Empfang der 1. Minute (Minutenangabe % 3 == 2)
 + Fehlerfreier Empfang der Folgeminute bis zum 15. Bit
 + Zeitangaben der 0. und 1. Minute zusammenhängend
 *saveindex%480 = Tabellen-Index
 *saveindex/480 = MJD
Index	Empfangszeit					Region	Inhalt
	MESZ		MEZ		UTC
  0- 59	00:00-02:59	23:00-01:59	22:00-00:59	0-59	Heute T (+ Nacht **)
 60-119	03:00-05:59	02:00-04:59	01:00-03:59	0-59	Heute N (+ Tag **)
120-179	06:00-08:59	05:00-07:59	04:00-06:59	0-59	Morgen T (+ Nacht **)
180-239	09:00-11:59	08:00-10:59	07:00-09:59	0-59	Morgen N (+ Tag **)
240-299	12:00-14:59	11:00-13:59	10:00-12:59	0-59	Übermorgen T (+ Tag **)
300-359	15:00-17:59	14:00-16:59	13:00-15:59	0-59	Übermorgen N (+ Nacht **)
360-419	18:00-20:59	17:00-19:59	16:00-18:59	0-59	Tag4 T (+ Nacht)
420-449	21:00-22:29	20:00-21:29	19:00-20:29	60-89	Morgen T (+ Nacht)
450-479	22:30-23:59	21:30-22:59	20:30-21:59	60-89	Übermorgen T (+ Nacht)
** = Tag- bzw. Nachtinformation (allgemeines Wetter) doppelt (redundant) vorhanden

So wird's zu 80 Bits zusammengesetzt:
Zielbyte Zielbits #Bits Minute	Quellbits (0-basiert)
0         0.. 5    6	0	2..7	Wetter
0,1       6..11    6	0	9..14	Wetter
1,2,3    12..25   14	1	1..14	Wetter
3,4      26..39   14	2	1..14	Wetter
5        40..46    7	1	21..27	Minute, BCD, lässt bei Division durch 3 immer Rest 2
5        47        1	-		immer Null
6        48..53    6	1	29..34	Stunde, BCD
6        54..55    2	-		immer Null
7        56..61    6	1	36..41	Tag, BCD
7        62..63    2	-		immer Null
8        64..68    5	1	45..49	Monat, BCD
8        69..71    3	1	42..44	Wochentag
9        72..79    8	1	50..57	Jahr, BCD
Der originale HKW581-IC will 82 Bit und wirft die beiden Alarmbits der Minute 0 heraus.
 */
 MYSYSTEMTIME st;
 U64 ft[2];
 ULARGE_INTEGER *w=(ULARGE_INTEGER*)cipher;	// Typecast, sollte der Optimierer rauswerfen
 ULONGLONG ll;
 bool mesz;
 BYTE Index;
 DbgPrintf(("%s(%d) : BuildCipher(%d,%p,%p)\n",__FILE__,__LINE__,MinuteZurueck,cipher,saveindex));
 if (Empfang.Anzahl<=MinuteZurueck) return false;	// chancenlos!
 Index=Empfang.Index-MinuteZurueck-1;	// Perfekte Annahme: "mittleres" Telegramm
 while (Index>=MINU) Index+=MINU;
 if (Empfang.Okay[Index]!=0x7FFFFFFFFFFFFFF) return false;
 if (!DecodeTime(Empfang.Data[Index],&st)) return false;
 switch (st.bMinute%3) {
  case 1: {	// Telegramm der Minute Null
   if (MinuteZurueck<2) return false;		// Folgetelegramme fehlen
  }break;
  case 2: {	// Telegramm der Minute Eins (am günstigsten)
   if (Empfang.Anzahl<=MinuteZurueck) return false;
   Index--;
  }break;
  default: {	// Telegramm der Minute Zwei
   if (Empfang.Anzahl<=MinuteZurueck+1) return false;
   Index-=2;
  }
 }
 if (Index>=MINU) Index+=MINU;
 if (Empfang.Okay[Index]!=0x7FFFFFFFFFFFFFF) return false;
 if (!DecodeTime(Empfang.Data[Index],&st)) return false;	// ggf. nochmal, geht ja schnell
 w->LowPart=(DWORD)Empfang.Data[Index]>>2;		// Startbit und erstes Wetterbit weg
 w->LowPart=w->LowPart&0x3F | w->LowPart>>1&0xFC0;	// Bit 7 weg, bleiben 6+6=12 Bits
 Index++;
 if (Index>=MINU) Index-=MINU;
 SystemTimeToFileTime(&st.st,&ft[0].ft);
 if (Empfang.Okay[Index]!=0x7FFFFFFFFFFFFFF) return false;
 ll=Empfang.Data[Index];
 if (!DecodeTime(ll,&st)) return false;
 SystemTimeToFileTime(&st.st,&ft[1].ft);
 if (ft[0].ull+600000000!=ft[1].ull) return false;	// nicht zusammenhängend!
 w->LowPart|=(DWORD)ll<<11&0x3FFF000;	// 14 weitere Bits, zusammen 26
 Index++;
 if (Index>=MINU) Index-=MINU;
 if (~(DWORD)Empfang.Okay[Index]&0x7FFF) return false;	// 15 Bits müssen OK sein
 w->HighPart=0;
 w->QuadPart|=(ULONGLONG)((DWORD)Empfang.Data[Index]>>1)<<26;	// 14 Bits dazu: 40 Bits
 ll>>=17;     mesz=(bool)((BYTE)ll&0x01);
 ll>>=4; cipher[5]=(BYTE)ll&0x7F;	// Minute, BCD, Parität weg
 ll>>=8; cipher[6]=(BYTE)ll&0x3F;	// Stunde, BCD, Parität weg
 ll>>=7; cipher[7]=(BYTE)ll&0x3F;	// Tag, BCD
 ll>>=6; cipher[8]=(BYTE)ll<<5 | (BYTE)ll>>3;	// Wochentag, Monat (vertauscht!)
 ll>>=8; cipher[9]=(BYTE)ll;		// Jahr
 if (saveindex) {
  if (!mesz) ft[1].ull+=(ULONGLONG)60*60*10000000;	// MESZ draus machen
  ft[1].ull-=1200000000;		// 2 Minuten abziehen (sollte unnötig sein)
  ft[1].ull/=3*60*10000000;		// in 3-Minuten-Schritten, passt in ein DWORD
  *saveindex = ft[1].dw[0];
 }
 return true;
}

// Liefert Zeitstempel eines Cache24-Eintrags in 3-Minuten-Schritten
// -1 wenn kein Eintrag vorhanden
// <*j3m> = Zeit in 3-Minuten-Schritten, 0 wenn uninitialisiert, erspart andauernde Neuberechnungen
int GetT3M(int index, int*j3m) {
 int t3m;
 DWORD v=Cache24[index];	// Vorhersage-Wert
 if (!v) return -1;
 if (!*j3m) {
  U64 ft;
  ft=GetSystemFileTime(false);
  ft.ull/=3*60*10000000;	// 3-Minuten-Schritte, Ergebnis passt in DWORD
  *j3m=ft.Lo+2*20;		// MESZ
 }
 t3m=((v>>24)&0x7F)*480+index;	// Zeitstempel des Index', auf eine 128-Tage-Periode begrenzt
 t3m+=*j3m/(480*128)*(480*128);	// ganze 128-Tage-Perioden addieren
 if (t3m>*j3m) t3m-=(480*128);	// Ergebnisse in der Zukunft (= Überläufe) korrigieren
 return t3m;
}

// Liefert Alter eines Cache24-Eintrags in 3-Minuten-Schritten
// -1 wenn kein Eintrag vorhanden
// <*j3m> = Zeit in 3-Minuten-Schritten, 0 wenn uninitialisiert, erspart andauernde Neuberechnungen
int GetAge(int index, int*j3m) {
 int t3m=GetT3M(index,j3m);
 if (t3m>=0) t3m=*j3m-t3m;
 if (t3m>480*4) DbgPrintf(("GetAge: Uralt-Eintrag v[%d] = %X, Alter = %d h\n",index,Cache24[index],t3m/20));
 return t3m;
}

// Sichert zu überschreibenden Cache24-Eintrag auf „aktuellere“ Tagesposition
// (horizontale Verschiebung) - erfordert Fälschung (Erhöhung) des MJD-Eintrags!
static void MoveCacheEntry(int i) {
 DWORD bv,v=Cache24[i];
 int bi;
 if (!v) return;	// frei, nichts tun
 if (i>=420) {
  bi=i-30;
  if (bi<420) return;
 }else{
  bi=i-120;
  if (bi<0) return;
 }
 bv=Cache24[bi];
 if (!bv || (long)((v&0x7F000000)-(bv&0x7F000000))>=0) {
  MoveCacheEntry(bi);		// Rekursion!
  Cache24[bi]=(v+0x1000000)|0x80000000;	// umsetzen und kennzeichnen der Fälschung
 }
}

// Berechnet aus <idx> (0..479):
// LOBYTE(LOWORD(result)) = Tag(0) / Nacht(1)
// HIBYTE(LOWORD(result)) = Zeitraum (2 oder 4)
// LOBYTE(HIWORD(result)) = Vorhersageregion 0..89
// HIBYTE(HIWORD(result)) = Gültigkeitsbereich (0..3)
DWORD IndexToRegion(int idx) {
 union{
  DWORD d;
  BYTE b[4];
 }ret;
 div_t d;
 if (idx>=420) {
  d=udiv(idx-420,30);
  ret.b[0]=0;		// nur Tag
  ret.b[1]=2;		// 2-Tages-Prognose
  ret.b[2]=60+d.rem;	// 60..89
  ret.b[3]=1+d.quot;	// 1 oder 2 Tage voraus (nehme ich mal an!)
 }else{
  d=udiv(idx,60);
  ret.b[1]=4;		// 4-Tages-Prognose
  ret.b[2]=d.rem;	// 0..59
  ret.b[0]=d.quot&1;	// Tag/Nacht
  ret.b[3]=d.quot>>1;	// 0(heute)..3(über-übermorgen)
 }
 return ret.d;
}

// Schreibt Cache24-Eintrag, aktualisiert vorhergehendende Einträge,
// löscht veraltete Einträge, zz. ohne Aktualisierung der Registry
void SetCache24Entry(DWORD saveindex, DWORD result) {
 int i;
 div_t d=udiv(saveindex,480);	// d.quot = heutiges Julianisches Datum (mit MESZ:00:00 wechselnd)
 MoveCacheEntry(d.rem);
 Cache24[d.rem]=result|d.quot<<24;
 for (i=0; i<480; i++) {
  BYTE r=(BYTE)(IndexToRegion(i)>>24);
  if ((BYTE)(d.quot-(Cache24[i]>>24))>r) Cache24[i]=0;
 }
}

int GetHeuteMJD(bool bLocalTime) {
 U64 ft;
 ft=GetSystemFileTime(bLocalTime);	// Aktuellen Tag beschaffen - Was tun wenn Datum falsch?
 if (!bLocalTime) ft.ll+=(__int64)2*3600*10000000;
#ifdef _M_AMD64
 ft.ull/=(__int64)24*3600*10000000;
#else
 ft.ull>>=14;
 ft.ull/=0x324A9A7;		// Heute: Julianisches Datum, ganze Tage
#endif
 return ft.Lo;
}

// Führt zwei Caches zusammen, zz. aber nur „vertikal“, ohne Verschieben von Vorhersagen innerhalb Cache24
int Cache24Merge(const DWORD other[480]) {
 int heuteMJD=(BYTE)GetHeuteMJD(false);
 int i;
 int count=0;
 for (i=0; i<480; i++) {
  int cacheage=Cache24[i]?(BYTE)(heuteMJD-(Cache24[i]>>24)):256;
  int otherage=other[i]  ?(BYTE)(heuteMJD-(other[i]>>24))  :256;
  if (otherage<8) {
   if (cacheage>otherage) {		// Bei Gleichheit nichts tun
    Cache24[i]=other[i];
    cacheage=otherage;
    count++;
   }else if (cacheage==otherage && Cache24[i]!=other[i]) {
    DbgPrintf(("Cache24Merge: Konflikt bei Index %d, %X!=%X\n",i,Cache24[i],other[i]));
   }
  }
  if (cacheage>=8) Cache24[i]=0;	// Total alte Einträge löschen (was bei cacheage==256 nichts tut)
 }
 if (count) DbgPrintf(("Cache24Merge: %d Einträge überschrieben\n",count));
 return count;
}

/***********************************
 * Format der dechiffrierten Daten *
 ***********************************
So wie es wirklich ist, nicht wie von Meteotime dargelegt.

Die angebenen String-IDs bestehen aus durch "\0" getrennten Substrings.
Der erste ist der printf-Formatstring, die anderen die Variablen für ein "%s" darin.

T und N:
Bit 3:0 = Wetter {0} Tag (redundant für Region 0..59, Tag 1..3)
Bit 7:4 = Wetter {1} Nacht (gleiche Redundanz)

Wert	Wetter-Symbol {String51-Nr.}	wenn Schweres Wetter {String52-Nr.}
----------------------------------------------------------------------------
0	(nie gesehen)			{1} -
1	{2} sonnig / {3} klar (nachts)	{2} große Hitze (tags), {1} - (nachts)
2	{4} leicht bewölkt		{1} -
3	{5} vorwiegend bewölkt		{1} -
4	{6} bedeckt			{1} -
5	{7} Wärmegewitter		{1} -
6	{8} starker Regen		{3} extreme Niederschläge
7	{9} Schneefall			{4} starke Niederschläge
8	{10} Nebel			{5} dichter Nebel, Sichtweite unter 50 m
9	{11} Schneeregen		{4} starke Niederschläge
10	{12} Regenschauer		{6} kurze, starke Niederschläge
11	{13} leichter Regen		{1} -
12	{14} Schneeschauer		{4} starke Niederschläge
13	{15} Frontengewitter		{7} starke Gewitter
14	{16} Hochnebel			{1} -
15	{17} Schneeregenschauer		{4} starke Niederschläge

T: Bit 15 gelöscht: Bit 11:8 = Extreme Wetterformen (ungesichert)

Wert	Bedeutung {String52-Nr.}
----------------------------------------------------------------------------
0	-
1	Schweres Wetter		TAG+NACHT	Text siehe oben
2	Schweres Wetter		TAG		Text siehe oben
3	Schweres Wetter		    NACHT	Text siehe oben 
4	{8} Sturm		TAG+NACHT
5	{8} Sturm		TAG 
6	{8} Sturm		    NACHT
7	{9} Böen		TAG 
8	{9} Böen		    NACHT
9	{10} Eisregen {15}VormitTAG
10	{10} Eisregen {16}NachmitTAG
11	{10} Eisregen		    NACHT
12	{11} Feinstaub		TAG+NACHT
13	{12} Ozon		TAG+NACHT
14	{13} Radiation		TAG+NACHT
15	{14} Hochwasser		TAG+NACHT

T: Bit 15 gesetzt: Bit 9:8 = Relatives Vormittagswetter (nie gesehen)

Wert	Bedeutung {String54-Nr.}
-----------------------
0	{1} gleiches Wetter
1	{2} Sprung 1 (unklar, was das bedeuten soll!)
2	{3} Sprung 2
3	{4} Sprung 3

T: Bit 15 gesetzt: Bit 11:10 = Sonnenscheindauer (nie gesehen)

Wert	Bedeutung {String55}
-----------------------
0	0 - 2 h
1	2 - 4 h
2	5 - 6 h
3	7 - 8 h

T: Bit 14:12 = Niederschlagswahrscheinlichkeit (ungesichert)

Wert	Bedeutung {String56}
-----------------------
0	0 %
1	15 %
2	30 %
3	45 %
4	60 %
5	75 %
6	90 %
7	100 %

T, N: Bit 21:16 = Temperatur, T enthält Tages(höchst?)temperatur, N enthält Nacht(tiefst?)temperatur

Wert	Bedeutung {String57}
-----------------------
0	< -21 °C
1..62	-22 °C + Wert
63	> 40 °C

N: Bit 15 gelöscht: Bit 11:8 = Windrichtung (nicht definiert bei Windstärke = 0) (ungesichert)

Wert	Bedeutung {String58-Nr.}
-----------------------
0	{1} N
1	{2} NO
2	{3} O
3	{4} SO
4	{5} S
5	{6} SW
6	{7} W
7	{8} NW
8	?	{9} wechselnd	(nie gesehen)
9	?	{10} Föhn
10	NO	{11} Biese
11	N	{12} Mistral
12	S	{13} Scirocco
13	W	{14} Tramont
14	-
15	-

N: Bit 15 gelöscht: Bit 14:12 = Windstärke (ungesichert)

Wert	Windstärke/bft {String59-Nr.}
-----------------------
0	0	{1} Windstille	(nie gesehen)
1	0-2	{2}
2	3-4	{3}
3	5-6	{4}
4	7	{5} steifer Wind
5	8	{6} stürmischer Wind
6	9	{7} Sturm
7	>=10	{8} schwerer Sturm

N: Bit 15 gesetzt: Umschaltung?? (nie gesehen)

*/
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded