Hat hier jemand einen Uno und ein DCF77 Modul?

Wie einige hier sicher schon mitbekommen haben entwickle ich seit ~9 Monaten eine rauschresistente DCF77 Uhr. Aktueller Stand ist hier: http://blog.blinkenlight.net/experiments/dcf77/the-clock/. Jetzt packe ich das gerade in eine Library um es einfacher für Projekte anderer Leute verfügbar zu machen. So weit so gut. Nur der Code ist im Moment auf Arduinos mit Quarz optimiert und der Uno hat einen Resonator. Da ich mit Blinkenlighties eingedeckt bin habe ich bisher keinen Uno gekauft. Wozu auch, der Blinkenlighty kann ja mehr und hat einen Quarz :wink:

Deshalb kann ich nicht testen wie schlecht die Resonatoren wirklich sind. Um das auszuprobieren haben ich folgenden kleinen Sketch geschrieben:

//
//  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();
     }  
}

Mit einem Blinkenlighty sieht der Output an einem DCF77 Module dann etwa wie angehängt aus. Das Log wird erzeugt, daß pro Minute 1000 Samples gezogen werden und je 10 Samples zusammengefasst werden. Von einem + Zeichen zum nächsten sind also genau 100 Millisekunden. Wenn man in den angehängten Log schaut, dann sieht man, danß nach 346s die Phase bei etwas +200ms ist und nach 6511s bei etwa +400ms ist. Ergebnis: innerhalb von 6511s-346s = 6516s ist der Blinkenlighty 0,2s zu schnell. Oder 0.2s/6516s = 0.000030694 = 30.7 ppm zu schnell.

Jetzt die eigentliche Frage: wer hat hier einen Uno und ein DCF77 Modul und würde den Sketch mal auf dem Uno für 2 Stunden laufen lassen und mir das Log zur Verfügung stellen?

      340, +---------+---------3XXXXXXXXXXXXXXXXXXX6---------+---------+---------+---------+---------+---------
      341, +---------+---------3XXXXXXXXXX2--------+---------+---------+---------+---------+---------+---------
      342, +---------+---------4XXXXXXXXXXXXXXXXXXXX3--------+---------+---------+---------+---------+---------
      343, +---------+---------3XXXXXXXXXX1--------+---------+---------+---------+---------+---------+---------
      344, +---------+---------2XXXXXXXXXXXXXXXXXXXX2--------+---------+---------+---------+---------+---------
      345, +---------+---------5XXXXXXXXXXXXXXXXXXXX2--------+---------+---------+---------+---------+---------
      346, +---------+---------+XXXXXXXXXX2--------+---------+---------+---------+---------+---------+---------
      347, +---------+---------3XXXXXXXXXX---------+---------+---------+---------+---------+---------+---------
      348, +---------+---------+XXXXXXXXXX2--------+---------+---------+---------+---------+---------+---------
      349, +---------+---------3XXXXXXXXXX8--------+---------+---------+---------+---------+---------+---------
      350,
     6500, +---------+---------+---------+---------4XXXXXXXXXX3--------+---------+---------+---------+---------
     6501, +---------+---------+---------+---------4XXXXXXXXXX2--------+---------+---------+---------+---------
     6502, +---------+---------+---------+---------4XXXXXXXXXXXXXXXXXXXX---------+---------+---------+---------
     6503, +---------+---------+---------+---------2XXXXXXXXXXXXXXXXXXXX---------+---------+---------+---------
     6504, +---------+---------+---------+---------3XXXXXXXXXXXXXXXXXXX8---------+---------+---------+---------
     6505, +---------+---------+---------+---------2XXXXXXXXXXXXXXXXXXXX2--------+---------+---------+---------
     6506, +---------+---------+---------+---------3XXXXXXXXXX1--------+---------+---------+---------+---------
     6507, +---------+---------+---------+---------3XXXXXXXXXXXXXXXXXXX9---------+---------+---------+---------
     6508, +---------+---------+---------+---------2XXXXXXXXXX2--------+---------+---------+---------+---------
     6509, +---------+---------+---------+---------2XXXXXXXXXXXXXXXXXXX9---------+---------+---------+---------
     6510, +---------+---------+---------+---------4XXXXXXXXXXXXXXXXXXX9---------+---------+---------+---------
     6511, +---------+---------+---------+---------+XXXXXXXXXXXXXXXXXXXX---------+---------+---------+---------
     6512, +---------+---------+---------+---------1XXXXXXXXX9---------+---------+---------+---------+---------
     6513, +---------+---------+---------+---------2XXXXXXXXX8---------+---------+---------+---------+---------
     6514, +---------+---------+---------+---------2XXXXXXXXXXXXXXXXXXXX---------+---------+---------+---------
     6515, +---------+---------+---------+---------4XXXXXXXXXXXXXXXXXXXX---------+---------+---------+---------
     6516, +---------+---------+---------+---------2XXXXXXXXXX4--------+---------+---------+---------+---------
     6517, +---------+---------+---------+---------4XXXXXXXXXXXXXXXXXXXX1--------+---------+---------+---------
     6518, +---------+---------+---------+---------2XXXXXXXXXX2--------+---------+---------+---------+---------
     6519, +---------+---------+---------+---------2XXXXXXXXXX2--------+---------+---------+---------+---------
     6520, +---------+---------+---------+---------1XXXXXXXXXXXXXXXXXXX9---------+---------+---------+---------

DCF77_log.txt (820 KB)

Nachtrag: ich kenne den Beitrag hier http://forum.arduino.cc/index.php?topic=60662.0. Und selbstverständlich - wenn jemand einen Uno und einen Frequenzzähler oder ein Oszi hat, dann nehme ich auch direkt die gemessenen Frequenzen :)

Habe hier einen Uno der ersten Revision und mein Oszilloskop gibt gemessen am Resonator 15,93 MHz aus.

[edit] Der 16MHz-Quarz des Atmega8U2 hingegen steht wie festgenagelt auf glatten 16 MHz. :wink: Gemessen mit einem UNI-T 2052 (nur 50MHz, 1 GS/s).

[edit]² Vergleichsmessung zum Arduino Leonardo hinzugefügt.

Uno.BMP (98 KB)

Leonardo.BMP (98 KB)

150, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3------+---4XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      151, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX9-------+---------+--6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      152, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX6------+---------+--6XXXXXXXXXXXXXXXXXXXXXXXXXXXXX5XXXXXX
      153, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX7-------+--49XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      154, XXXXXXXXXXXXXXXX987XXXXXXXXXXXXXXXXXXXXXXXX-------+--5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      155, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4-------+-1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      156, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8-------+--6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX9XXXXXXXXX7XXXXX
      157, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2-------+--3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      158, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX7-------+---------+-1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      159, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX9XXXXX4------+---------+-5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      160, XXXXXXXXXXXXXXXXXXX8XXXXXXXXXXXXXXXXXXXXXX4-------+-8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      161, XXXXXXXXXXX7-9XXXXXXXXXXXXXXXXXXXXXXXXXXX7--------+--8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      162, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX8--------+---------+-2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      163, XXXXXXXXXXXXXXXXXXX69XXXXXXXXXXXXXXXXXXXX4--------+-4XXXXXXXXX7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      164, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2-------+-5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      165, XXXXXXXXXXXXXXXXXXXXXX3XXXXXXXXXXXXXXXXXX7--------+---------+1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      166, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX---------+---------+4XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      167, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX7--------+---------+7XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      168, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX7---------+-9XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      169, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1--------1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      170, XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX97XXXXXX5---------+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Das ist wohl nicht das, was du haben möchtest, die X und die - sind bei mir invertiert. Ist ein Pollin-Modul, kannst du dein Programm dafür umschreiben? Wollte ungern noch einen Negator zusammenbasteln.

Ganz unterschiedlich. Bei Zimmertemperatur gibt es gewaltige Streuungen.

Ich habe irgendwann schon mal meine Resonator-Boards im Vergleich mit einer DS1307 getestet. Anbei habe ich mal ein Logfile mit dem (bei weitem!) am weitesten abweichenden Board erstellt. Sind deutlich unter 2 Stunden, aber bei der großen Frequenzabweichung dürftest Du auch so schon was sehen.

Das DCF-Modul ist wohl ca. 20 Jahre alt von Conrad.

Meine übrigen Resonator-Boards liegen bei ungefähr einem viertel bis einem drittel der Abweichung von dem Board, dessen Logfile ich drangehängt habe.

dcf77test.zip (19 KB)

jurs: Das DCF-Modul ist wohl ca. 20 Jahre alt von Conrad.

Meine übrigen Resonator-Boards liegen bei ungefähr einem viertel bis einem drittel der Abweichung von dem Board, dessen Logfile ich drangehängt habe.

Sieht ja meinem Ergebnis recht ähnlich, also auch mit getauschten "X" und "-". Soweit ich weiß, hat das DCF-Modul zwei Ausgänge. Wenn du den anderen nimmst, hast du die Datenstruktur, die analog zu Udos ist.

@Jurs: Super! Danke. Ich dachte ja schon, daß die Resonatoren schwach sind. Aber das so im Log zu sehen ist schon was anderes.

@STH77, entweder Du setzt const uint8_t dcf77_inverted_samples = 1; von 1 auf 0 oder Du schickst mir den Log so. Die Drift kann man auch an einem invertierten Log sehen. Nur mal so zum vergleich wie das bei Dir aussieht würde mich schon interessieren.

Ach Mensch, da gibt es ja die passende Änderungsmöglichkeit. Habe ich gestern gar nicht mehr wahrgenommen. Werde ich nachher mal testen.

Hätte ich.

An welchen Pin muss ich das DCF-77 Modul hängen? A5?

da_user:
An welchen Pin muss ich das DCF-77 Modul hängen? A5?

Ja.

Ich habe hier nochmal eine zweite Messreihe, allerdings nicht mit einem UNO-kompatiblen Board, sondern mit einem Sainsmart MEGA2560 R3 gemacht und mit einem DCF-Modul von Pollin.

Irgendwie sieht das so aus, als wenn der noch mehr Zeit verliert, obwohl ich eigentlich im Gedächtnis hatte (per DS1307-Vergleich), dass dieser eigentlich genauer ticken sollte als der TinyOS UNO, dessen Logfile ich heute Vormittag gepostet hatte.

Ob mir da wohl mein Gedächtnis einen Streich spielt, sich das Timing durch Temperaturänderung oder Bauteilealterung stark verändert hat, meine damalige Methode zum Ausmessen des Board-Timings inkorrekt war, oder ist Dein Test-Sketch so auf den UNO optimiert, dass auf einem MEGA prinzipbedingt eine höhere Abweichung vom Solltakt festgestellt wird als auf einem UNO Board?

dcf77test_mega.zip (23.5 KB)

So, habe dann auch noch ein paar Aufzeichnungen mit dem Arduino Uno gemacht, siehe Dateianhang.

DCF77_sth77.txt (552 KB)

Danke Jurs + STH für die Messungen.

@Jurs: was die Genauigkeit angeht: es kann schon sein, daß der Uno mal genauer und mal ungenauer als Dein Mega läuft. Der Punkt ist, daß Oszillatoren abhängig von Alterung, Temperatur, Vibrationen, Lage, Spannung und sonst noch alles mal so mal so schneller oder langsamer laufen. Bei Resonatoren ist der Effekt stärker. Mein Messsketch ist auf genau 16 MHz Zieltakt eingestellt und die Zeitreferenz ist DCF77. Wenn da was wegläuft, dann ist das der Systemtakt. Was man daraus lernt ist, daß man Resonatoren nicht mit einem konstanten Offset kompensieren kann :(

Ich hab mal für ein paar Stunden gemessen. Datei im Anhang (hoffe ich).

und die Zeitreferenz ist DCF77.

Laut Wikipedia liegt die Ungenauigkeit bei klassischen DCF77-Empfängern bei 100ms,…?

log.txt (3.41 MB)

Erst einmal Danke für den Log.

Du meinst den Absatz:

Auch mit optimierten Dekodieralgorithmen und keiner zu starken Bandbegrenzung im Empfangsfilter liegt die zeitliche Unsicherheit, mit der der exakte Beginn der amplitudenmodulierten Sekundenmarken erkannt werden kann, bei wenigstens etwa 100 Mikrosekunden. Haushaltsübliche Funkuhren setzen schmalbandige Empfänger ein (mit 10 Hz Bandbreite) und können daher den Sekundenbeginn nur auf 0,1 s genau feststellen.

Das stimmt so nur bedingt. Erstens hat jede Uhr zunächst das Problem der Laufzeit des Zeitsignals vom Empfänger. Je nach Abstand zur Antenne kommen da selbst in Deutschland auch mal mehr als 1ms zusammen.

Der Witz beim DCF77 ist aber vor allem die Langzeitkonstanz

Das Trägersignal von 77,5 kHz ist in Frequenz und Phasenlage mit der steuernden primären Atomuhr synchronisiert und besitzt deshalb nur geringe Abweichungen von der Sollfrequenz. Über einen Tag sind dies weniger als relativ 2 · 10?12, im Mittel über 100 Tage um weniger als relativ 2 · 10?13. Es kann somit auch ohne Auswertung der Zeitinformation als Eichfrequenz für sehr genaue Hoch- und Niederfrequenzgeneratoren benutzt werden.

Achso... kurz gesagt:

Wenn die Zeit wirklich um -100ms abweicht, dann weicht sie die nächsten 100 Tagen auch ziemlich genau die gleichen 100ms ab.

Alles klar.

Ja, allerdings nur wenn wir von ms reden. Wenn Du es ganz genau wissen willst schaust Du hier: http://www.hopf.com/de/dcf77-gps_de.html. Man beachte: es wird nichts zur Langzeitgenauigkeit gesagt. Das liegt daran, daß per Definition dei Langzeitgenauigkeit “perfekt” ist. Denn DCF77 überträgt die amtliche Uhrzeit.

Hallo,
ich habe 2 Logs angehängt

Uno.log Uno in SMD MIT Quartz ( mein Uno hat einen 16MHZ Quartz )
Leonardo.log Leonardo auch mit Quartz

Der Leonardo Quartz scheint genauer zu stimmen, denn die Drift ist nicht so gross wie beim UNO, wie man von den Log Daten auf
die Abweichung des Quatrzes vom Sollwert kommt, ist mir allerdings nicht klar. Wäre nett, wenn du das nochmal erläutern könntest.

Grüsse
wega52

P.S. Habe das 2. Log ergänzt, war beim 1. Versuch irgendwie verloren gegangen

Leonardo.log (483 KB)

Uno.log (1.36 MB)

Hmm, ich sehe nur einen Log. Wo ist der andere?

Was die Logs angeht: das Ziel ist es den lokalen Takt gegen DCF77 zu vergleichen. Dazu wird alle 16 000 Takte 1 Sample vom Receiver gezogen. Je 10 Samples werden addiert und das Ergebnis ausgegeben. Wenn die lokale Takterzeugung wirklich mit 16 Mhz läuft, vergeht immer GENAU eine Sekunde für 100 Takte. Deshalb erscheint dann die Flanke des DCF77 Signals immer an der gleichen Stelle.

Wenn der lokale Takt aber schneller oder langsamer ist, dann braucht es für die 1000 Samples weniger oder mehr Zeit und die Flanke verschiebt sich. Die Frage ist wie schnell und wie rechnet man das um? Ist aber gar nicht schwer. Eine komplette Sekunde Phasenverschiebung bedeutet 16000000 Takte verloren oder dazugewonnen. Man muss also nur schauen wie lange es dauert um 1s Versatz zu bekommen. Nehmen wir mal an 1000s. Dann würde ich pro Sekunde 16000000/1000=16000 Takte danebenliegen. Oder eben die Frequenz ist nicht 16 000 000 Hz sondern 16 000 000 +/- 16 000 Hz. Je nachdem wer schneller ist, der lokale Takt oder DCF77.