/********************************
* 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)
*/
Vorgefundene Kodierung: ANSI (CP1252) | 4
|
|