Go Down

Topic: Binäre DCF77 Uhr mit exponentiellem Filter und Blinkenlighty (Read 20850 times) previous topic - next topic

Udo Klein

#60
Feb 01, 2013, 06:28 pm Last Edit: Feb 02, 2013, 12:50 pm by Udo Klein Reason: 1
Die Feldstärke in DB zu ermitteln ist mit den einfachen Modulen nicht möglich. Die finale Ausbaustufe meiner Uhr wird aber die Qualität des dekodierten Signals ermitteln. Aktueller Stand ist, daß ich jetzt auch die Sekunden dekodieren kann. Ich bin exakt im Plan ;)

http://blog.blinkenlight.net/2013/02/01/the-second-decoder/
Check out my experiments http://blog.blinkenlight.net

Udo Klein

Die nächste Ausbaustufe ist fertig und dokumentiert: http://blog.blinkenlight.net/2013/04/01/what-time-is-it/. http://blog.blinkenlight.net/experiments/dcf77/decoding-time-data/. Die aktuell in Entwicklung befindliche Ausbaustufe hat auch die Sommerzeitumstellung richtig hinbekommen, ist aber noch nicht fertig dokumentiert :)
Check out my experiments http://blog.blinkenlight.net

jurs

#62
Apr 02, 2013, 12:58 pm Last Edit: Apr 02, 2013, 06:00 pm by jurs Reason: 1

Die nächste Ausbaustufe ist fertig und dokumentiert: http://blog.blinkenlight.net/2013/04/01/what-time-is-it/. http://blog.blinkenlight.net/experiments/dcf77/decoding-time-data/. Die aktuell in Entwicklung befindliche Ausbaustufe hat auch die Sommerzeitumstellung richtig hinbekommen, ist aber noch nicht fertig dokumentiert :)


Bekommst Du am Ende eigentlich mit Deinem DCF-Sketch den Speicher eines UNO voll, oder bleibt noch ein Rest an Speicher für was anderes übrig?  ;)

Irgendwie verstehe ich das mit dem Hamming-Code nicht ganz. Einerseits wird das DCF-Signal nur mit einem einfachen Parity-Code gesendet, und Du konstruierst aus erwarteten Daten (bei bereits korrekt laufender Uhr?) und eintreffenden Daten ein Scoring, mit dem Du auch bei ständig falschen Parity-Codes aus der Abfolge mehrer Minuten nacheinander trotzdem die richtigen Werte auslesen willst?

Irgendwie wird mir das alles zu hoch, was Du da machst.  :smiley-eek-blue:

[Edit] Jetzt habe ich mir mal den Sketch heruntergeladen und getestet: Huch, der ist ja gar nicht so groß?!
Und die exponentielle Filterung mit Sampling des Eingangssignals ist auch gar nicht mehr drin?!

Und wie lange braucht der Sketch, um die Uhrzeit zu bekommen? Habe hier mal gerade mit meinem Pollin-Modul und leichten Störungen getestet. Die "Quality"-Zeilen (die mir nichts sagen), habe ich mal dazwischen entfernt.

Dein Sketch fängt an mit:
Decoded time: ??:??:00
Wechselt nach einer Weile auf die richtige Sekunde:
Decoded time: ??:??:20
Decoded time: ??:??:21
Decoded time: ??:??:22
Decoded time: ??:??:23
...

Irgendwann wird wohl Stunde und Minute zu 00 und 00 erkannt (was nicht korrekt ist, Sekunde stimmt):
Decoded time: 00:00:26
Decoded time: 00:00:27
Decoded time: 00:00:28
Decoded time: 00:00:29
Decoded time: 00:00:30
Decoded time: 00:00:31

Dann wird die Stunde wieder unbekannt, die Minute zählt hoch (immer noch falsch), die Sekunde stimmt weiterhin:
Decoded time: ??:01:00
Decoded time: ??:01:01
Decoded time: ??:01:02
Decoded time: ??:01:03
Decoded time: ??:01:04
Decoded time: ??:01:05
Decoded time: ??:01:06
Decoded time: ??:01:07

Dann wird die Stunde wieder (falsch) zu 00 erkannt, die Minute unbekannt, die Sekunde stimmt weiterhin:
Decoded time: 00:??:20
Decoded time: 00:??:21
Decoded time: 00:??:22
Decoded time: 00:??:23
Decoded time: 00:??:24
Decoded time: 00:??:25
Decoded time: 00:??:26

Dann kommt wieder die Minute (Stunde falsch, Minute geht um wenige Minuten falsch):
Decoded time: 00:22:05
Decoded time: 00:22:06
Decoded time: 00:22:07
Decoded time: 00:22:08
Decoded time: 00:22:09
Decoded time: 00:22:10
Decoded time: 00:22:11
Decoded time: 00:22:12

Dann merkt er wieder, dass mit der Minute was nicht stimmt:
Decoded time: 00:??:14
Decoded time: 00:??:15
Decoded time: 00:??:16
Decoded time: 00:??:17
Decoded time: 00:??:18
Decoded time: 00:??:19

Nach ca. 15 Minuten stimmen dann Minuten und Sekunden:
Decoded time: 00:31:01
Decoded time: 00:31:02
Decoded time: 00:31:03
Decoded time: 00:31:04
Decoded time: 00:31:05

Na ja.

Udo Klein

Naja, am Ende wird die Uhr wohl so 22kBytes verpulvern. Das ist ziemlich wenig im Vergleich zur Leistungsfähigkeit. Einfache DCF77 Uhren (insbesondere alle kommerziellen) Uhren werden ja nicht einmal mit 1 Bitfehler pro Minute fertig. Meine Uhr wird locker mit mehr als 15 Bitfehler pro Minute fertig.

Das mit dem Hamming Code ist so, daß es keinen "Hamming Code" gibt. Es gibt eine "Hamming Metrik". Im Prinzip ist es ganz einfach. Stell Dir vor jede Minute würde das gleiche Signal gesendet. Dann wäre der Durchschnitt der empfangenen Werte eine naheliegende Schätzung für die richtige Zeit. Jetzt ändert sich die Zeit aber jede Minute. Was aber gleich bleibt ist der Zeitverlauf. Ich muß also nur die Phase erkennen. D.h. für verschiedene Startzeiten ergeben sich verschiedene aber jeweils vorbestimmte Zeitverläufe. Ich muß also nur den "naheliegendsten" Zeitverlauf bestimmen. "Naheliegend" zum empfangenen Signal eben. Die Frage ist wie man "Nähe" bei Signalen misst --> da kommt die Hamming Metrik ins Spiel. Das ist alles.

Probleme Dir dabei macht vermutlich, daß Dir nie jemand verraten/gezeigt hat, daß man "Abstand" auch anders festlegen kann als "euklidischer Abstand im 3-dimensionalen Raum".

Und ich will das nicht nur können, meine Uhr funktioniert bereits bestens. Wie gesagt, 15 Bitfehler pro Minute (und zwar JEDE Minute) sind kein besonders großes Problem. Bei 10 Bitfehlern pro Minute braucht die Uhr weniger als 15 Minuten um die korrekte Zeit zu ermitteln UND danach dauerhaft auf die Sekunde verriegelt zu bleiben.
Check out my experiments http://blog.blinkenlight.net

Udo Klein

Der neueste Stand meiner DCF77 Uhr ist fertig und dokumentiert: http://blog.blinkenlight.net/2013/06/01/what-time-and-date-is-it/ bzw. http://blog.blinkenlight.net/experiments/dcf77/decoding-everything/. Die Uhr kann jetzt "alles" ausser den Wetterdaten mit Fehlerkorrektur dekodieren. Als nächstes kommt dann die lokale Uhr für komplette Signalverluste dran.
Check out my experiments http://blog.blinkenlight.net

Serenifly

1. Gibt es das irgendwo als fertige Bibliothek in einer eigenen Klasse mit Header? :)

2. Was ist denn mit dem Reichelt Modul?

http://www.reichelt.de/Bausaetze/DCF77-MODUL/3/index.html?;ACTION=3;LA=446;ARTICLE=57772;GROUPID=5727;artnr=DCF77+MODUL

Sowie ich das sehe ist das ja ein halber Dekoder, der schon die amplitudenmodulierten Rechteck-Impulse liefert. Das würde ja mit keiner der Standardbibliotheken ohne Modifikation funktionieren. Reicht es da einfach die Analogdigital-Wandlung zu überspringen und einen Schritt später einzusteigen, oder soll ich lieber mein Glück bei Conrad versuchen?

Udo Klein

1) Nein, das ist aber auch nicht schwer falls das jemand haben will. Solange das Teil aber noch in Entwicklung ist mach ich keine Library draus. Ich will auf meiner Webseite nun mal Sketches am Stück haben. Lokal bei mir gibt es das schon als Library. Aber wie gesagt, solange das Teil nicht endgültig fertig ist nicht.

2) Klar geht das, warum auch nicht? Der "halbe Decoder" nennt sich "Demodulator" und das ist genau das was alle billigen Module tun.
Check out my experiments http://blog.blinkenlight.net

Serenifly

Ok, ich dachte das Conrad-Modul gibt nur das rohe Analogsignal aus, weil da kaum Elektronik drauf ist, während das Reichelt Modul so einen IC-Knuppel hat :)

Udo Klein

Nächste Runde: http://blog.blinkenlight.net/2013/07/01/keeping-time-no-matter-what/ bzw. http://blog.blinkenlight.net/experiments/dcf77/local-clock/ --> Jetzt überbrückt meine Uhr auch Totalausfälle. In der Zwischenzeit wird auf den Quarzoszillator zurückgegriffen. Falls nur noch der Takt verfügbar ist aber die Daten nicht mehr gelesen werden können nimmt die Uhr den Takt weiter aus dem DCF77 Signal.
Check out my experiments http://blog.blinkenlight.net

Udo Klein

Jetzt ist mein DCF77 Decoder in der Endausbaustufe angekommen: http://blog.blinkenlight.net/2013/08/01/the-real-thing/, http://blog.blinkenlight.net/experiments/dcf77/the-clock/.

Jetzt wird das DCF77 Signal auch noch lokal synthetisiert um bei Signalverlust schneller wieder eine Phasenverriegelung hinzubekommen. Außerdem gibt es jetzt verschiedene Anzeigemodi.
Check out my experiments http://blog.blinkenlight.net

Hi Udo,

Ich versuch deinen DCF77 in meiner Wordclock Projekt zu verwenden, doch leider bekomme ich das ganze nicht mit dem Reichelt DCF Modul zum Laufen.
Die normale DCF Library hat nach 45 Minuten einen Lock gehabt, allerdings erst nachdem ich die Split time reduziert habe (default ist 180, (130+230)/2:
Code: [Select]
#define DCFSplitTime 130 // Specifications distinguishes pulse width 100 ms and 200 ms. In practice we see 130 ms and 230
Das Reichelt Modul scheint etwas zu kurze Pulse auszugeben.
So sieht der Output des DCFPulseLenght examples aus der DCF library aus:
Code: [Select]
Cycle: 994 Pulse :61
Cycle: 1019 Pulse :144
Cycle: 985 Pulse :61
Cycle: 995 Pulse :168
Cycle: 1018 Pulse :152
Cycle: 993 Pulse :58
Cycle: 1005 Pulse :155
Cycle: 1000 Pulse :56
Cycle: 997 Pulse :63
Cycle: 1010 Pulse :152
Cycle: 991 Pulse :56
Cycle: 1001 Pulse :65
Cycle: 1013 Pulse :152
Cycle: 986 Pulse :167
Cycle: 1016 Pulse :50
Cycle: 990 Pulse :166
Cycle: 1009 Pulse :53
Cycle: 993 Pulse :164
Cycle: 1006 Pulse :59
Cycle: 1002 Pulse :163
Cycle: 1002 Pulse :48
Cycle: 1006 Pulse :57
Cycle: 996 Pulse :63
Cycle: 1001 Pulse :62
Cycle: 1005 Pulse :56
Cycle: 995 Pulse :67
Cycle: 1007 Pulse :157
Cycle: 993 Pulse :170
Cycle: 1002 Pulse :170


Wie muss ich deinen Dekoder anpassen um mit den kürzeren Pulsen klarzukommen?

Grüße,
Michael

Udo Klein

Welchen Code verwendest Du? Den des "Binary Clock" oder den des "The Clock" experiments?

Leider sind die Tools aus der DCF Lib wenig aussagekräftig. Laß mal mit dem Tool hier:

Code: [Select]

//
//  www.blinkenlight.net
//
//  Copyright 2013 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/


const uint8_t dcf77_analog_sample_pin = 5;
const uint8_t dcf77_sample_pin = A5;  // == D19 for standard Arduinos
const uint8_t dcf77_inverted_samples = 1;
const uint8_t dcf77_analog_samples = 1;

const uint8_t dcf77_monitor_pin = A4; // == D18 for standard Arduinos

const uint8_t lower_output_pin = 2;
const uint8_t upper_output_pin = 17;


void stopTimer0() {
    // ensure that the standard timer interrupts will not
    // mess with msTimer2
    TIMSK0 = 0;
}

ISR(TIMER2_COMPA_vect) {
    process_one_sample();
}

void initTimer2() {
    // Timer 2 CTC mode, prescaler 64
    TCCR2B = (1<<WGM22) | (1<<CS22);
    TCCR2A = (1<<WGM21);
   
    // 249 + 1 == 250 == 250 000 / 1000 =  (16 000 000 / 64) / 1000
    OCR2A = 249;
   
    // enable Timer 2 interrupts
    TIMSK2 = (1<<OCIE2A);
}

uint8_t sample_input_pin() {
    const uint8_t sampled_data = dcf77_inverted_samples ^
        (dcf77_analog_samples? (analogRead(dcf77_analog_sample_pin) > 200):
                                digitalRead(dcf77_sample_pin));

    digitalWrite(dcf77_monitor_pin, sampled_data);
    return sampled_data;
}

void led_display_signal(const uint8_t sample) {
    static uint8_t ticks_per_cycle = 12;
    static uint8_t rolling_pin = lower_output_pin;
    static uint8_t counter = 0;

    digitalWrite(rolling_pin, sample);
       
    if (counter < ticks_per_cycle) {
        ++counter;
    } else {
        rolling_pin = rolling_pin < upper_output_pin? rolling_pin + 1: lower_output_pin;
        counter = 1;
        // toggle between 12 and 13 to get 12.5 on average
        ticks_per_cycle = 25-ticks_per_cycle;
    }
}

const uint16_t samples_per_second = 1000;
const uint8_t bins                = 100;
const uint8_t samples_per_bin     = samples_per_second / bins;

volatile uint8_t gbin[bins];
boolean samples_pending = false;

void process_one_sample() {
    static uint8_t sbin[bins];
   
    const uint8_t sample = sample_input_pin();
    led_display_signal(sample);

    static uint16_t ticks = 999;  // first pass will init the bins   
    ++ticks;

    if (ticks == 1000) {
        ticks = 0;
        memcpy((void *)gbin, sbin, bins);
        memset(sbin, 0, bins);
        samples_pending = true;     
    }

    sbin[ticks/samples_per_bin] += sample;   
}

void setup() {
    Serial.begin(115200);
    Serial.println();

    pinMode(dcf77_sample_pin, INPUT);
    digitalWrite(dcf77_sample_pin, HIGH);

    pinMode(dcf77_monitor_pin, OUTPUT);
   
    for (uint8_t pin = lower_output_pin; pin <= upper_output_pin; ++pin) {
        pinMode(pin, OUTPUT);
        digitalWrite(pin, LOW);
    }

    initTimer2();
    stopTimer0();
}


int32_t sign(int32_t value) {
    return (value>0) - (value<0);
}

void loop() {
    static int64_t count = 0;
    uint8_t lbin[bins];   
   
    if (samples_pending) {     
        cli();
        memcpy(lbin, (void *)gbin, bins);
        samples_pending = false;
        sei();
       
        ++count;
        // ensure the count values will be aligned to the right
        for (int32_t val=count; val < 100000000; val *= 10) {
            Serial.print(' ');
        }
        Serial.print((int32_t)count);
        Serial.print(", ");       
        for (uint8_t bin=0; bin<bins; ++bin) {
            switch (lbin[bin]) {
                case  0: Serial.print(bin%10? '-': '+'); break;
                case 10: Serial.print('X'); break;
                default: Serial.print(lbin[bin]);
            }
        }
        Serial.println();
     } 
}


einen Log erzeugen und schick häng den Output von einer gesamten Stunde an Deine Antwort.
Check out my experiments http://blog.blinkenlight.net

Hi Udo,

mit folgenden Einstellungen:
Code: [Select]
const uint8_t dcf77_analog_sample_pin = 5;
const uint8_t dcf77_sample_pin = A5;  // == D19 for standard Arduinos
const uint8_t dcf77_inverted_samples = 1;
const uint8_t dcf77_analog_samples = 0;

kommt dieses Ergebnis raus:
Code: [Select]

        1, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        2, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        3, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        4, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        5, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        6, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        7, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        8, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
        9, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       10, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       11, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       12, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       13, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       14, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       15, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       16, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       17, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       18, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       19, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       20, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       21, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       22, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       23, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       24, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       25, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       26, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       27, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       28, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       29, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       30, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       31, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       32, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------
       33, +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------


Das ändert sich auch nach langer Zeit nicht.

Mit dem vollständig gleichen HW aufbau kommt das DCFPulseLenght example aus der DCF77 lib zu dem Ergebnis in meinem obigem Post. Natürlich nach der Änderung auf
Code: [Select]
#define DCF77PIN 19

Umstellen von const uint8_t dcf77_inverted_samples = 1; auf const uint8_t dcf77_inverted_samples = 0; in deinem Code führt zu Zeilen voll mit X.

Also muss da irgendwo was faul sein.
Hab das ganze mit zwei Arduino Uno getestet.
Mit dem Micro läuft es ja nicht so direkt da der keinen Timer2 hat.
Muss mich mal ein wenig in die Timer einlesen und dann mit dem Micro und Timer4 mal testen.

Grüße,
Michael


Udo Klein

So wie das aussieht kommt beim Arduino kein Signal an. Hast Du das Modul am richtigen Pin angeschlossen?

Wenn ja, kannst Du irgendwie nachmessen ob da wirklich ein Signal ankommt? Da Du mit dem anderen Programm der DCF Lib ja Ergebnisse hattest würde ich auf ein Problem mit der Pinbelegung tippen. Nicht jedes der Programme verwendet die gleichen Pins. Also besser nochmal genau nachschauen.
Check out my experiments http://blog.blinkenlight.net

Hi Udo,

ich denke ich muss mich noch mal genauer mit dem Modul beschäftigen.
Google meint auch es sei wohl manchmal etwas zickig...

Danke erst mal.
Ich melde mich wieder wenn ich neue Erkenntnisse habe.

Grüße,
Michael

Go Up