Pages: 1 2 [3] 4 5 6   Go Down
Author Topic: Binäre DCF77 Uhr mit exponentiellem Filter und Blinkenlighty  (Read 18846 times)
0 Members and 1 Guest are viewing this topic.
Germany S-H
Offline Offline
Faraday Member
**
Karma: 146
Posts: 3031
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo, ich bin fleißiger Mitleser, allerdings beruflich bedingt noch nicht viel zum Testen meiner DCF-Module (von Pollin und Conrad) gekommen. Ein 2-Kanal-50MHz-Oszilloskop hätte ich, wenn es konkrete Signalverläufe (z.B. beide Module an jeweils einem Kanal) darzustellen gilt, könnte ich die wohl auch liefern.

Meine damaligen Erkenntnisse habe ich hier festgehalten: http://sth77.blogspot.de/2012/06/projekt-analoguhr-teil-3.html Auf den viel zu kleinen Bildern erkennt man den Signalverlauf nicht besonders gut, das Video im Vollbildmodus scheint mir da besser geeignet.

Hallo, danke fürs Posten! Die von Dir gezeigten Signalverläufe passen eher zu dem, was mein Conrad-Modul auch liefert:

- Einschwingfehler bzw. "Prellen": Beim Wechsel des Pegels kann es sein,  dass ganz kurze Zeit danach der Pegel noch ein- oder mehrmals ganz kurz zurückschwingt und den Pegel mehrmals wechselt, bevor der Pegelwechsel endgültig vollzogen ist.

- Spikes: Kurze, oft nur einzelne Störungen während ein Pegel gesetzt ist, die nach wenigen Millisekunden wieder verschwinden

Das von Udo in Bild http://blinkenlightblog.files.wordpress.com/2012/11/c05_simple_pulse_train_noise_60_1000.png gezeigte Störverhalten, mit ständigen und extrem kurzfristigen Pegelwechseln als ein dem Signal überlagertes "stochastisches Rauschen mit hoher Frequenz" kann ich für keines meiner Module nachvollziehen.

Anyway, wer selber Testen möchte, für den habe ich mein DCF-Modul-Testprogramm nochmal etwas weiter aufgebohrt.

Mit demselben Sketch kann man nun durch Setzen eines #define Statements sowohl zwei DCF-Module anschließen und deren Signale gegeneinander vergleichen. Oder man kann ein DCF-Signal vom DCF-Modul gegen das per Softwarefilter gefilterte Signal vergleichen.

Folgende Daten werden angezeigt (erste vier Spalten):
A/B: Signale der beiden DCF-Module, oder ein DCF-Modul und dessen gefiltertes Signal
0/1: Es wurde ein 0-Bit oder 1-Bit erkannt
xxx: Zahl zwischen 50 und 250 mit der erkannten Dauer des Bits
Die vierte Spalte gibt die Anzahl der fehlerhaften (<50ms) Impulse an, die zuvor seit dem letzten gültigen Bit aufaddiert wurden

Folgende Fehler im Signalverlauf werden angezeigt:
- Summe der kurzen Pegelfehler (<50ms Pegelwechsel) zwischen zwei korrekten Impulsen (50 ... 250 ms)
- Bitfehler '#': Derselbe Eingang liefert zwei Bits nacheinander, das Bit des anderen Eingangs fehlt dazwischen
- Bitfehler '*':  Ein Eingang liefert innerhalb von weniger als 500ms ein anderes Bit als der andere Eingang
  (wobei aber nicht sicher ist, dass der mit * gekennzeichnete Eingang tatsächlich das falsche Bit geliefert hat)
- SYNC-Fehler: Ein Eingang liefert für mehr als 1500 ms überhaupt kein Bit (normal beim Minuten-Marker zur vollen Minute)

Die fünfstellige Zahl sind die letzten fünf Ziffern des millis() Timers

Beispiel:
B 0 124   0 24197
A 0 139   0 25168
B 1 178   0 25257*
A 1 218   1 26236
A 0 122   8 27148#
B 0 121   0 27197 SYNC
A 0 128   0 28149
B 0 129   0 28199

Erklärung:
In Sekunde 25xxx hat das B-Modul ein 1-Bit geliefert, aber das A-Modul ein 0-Bit, daher Bitfehler *

Bei Timer 27148 hat das A-Modul zweimal hintereinander gefeuert, daher Bitfehler #

Bei Timer 27197 tritt ein Sync-Fehler an Modul B auf, da das vorherige Bit von Modul B älter als 1500ms ist
(Modul B hatte davor zuletzt bei Timer 25257 gefeuert, und in Sekunde 26xxx gar nicht)

Na ja, das ist das beste, was ich als Auswertung herausholen konnte. Die Messung und Ausgabe der Werte verfälscht natürlich immer etwas das, was eigentlich genauestens vermessen werden soll. So dauert die Formatierung mit "printf" recht lange, Pegelwechsel werden also automatisch etwas "entprellt", ohne dass dies gewollt ist. Dadurch sieht das Signal des Conrad-Moduls etwas besser aus als es tatsächlich ist.

So wie das Programm unten einkopiert ist, dient es zum Testen von Udos Exponentialfilter.
Einfach DCF-Modul an Pin-2 hängen und Sketch laufen lassen mit Ausgabe über den seriellen Monitor.

Was man deutlich sieht: Der Exponentialfilter verzögert das DCF-Signal am gefilterten (B) Ausgang um ca. 50 ms.

Eine Verbesserung des Signals durch den Filter kann ich allerdings nicht feststellen, weder an einem mittelstark noch an einem stark gestörten Signal. Sobald der Störpegel hoch genug ist, dass die "High"-Pegel von Störsignalen zerhackt werden, so dass das Conrad-Modul keine einwandfreien Signale mehr liefert, ist auch das gefilterte Signal fehlerhaft (Sync-Fehler beim ungefilterten und beim gefilterten Signal in derselben Sekunde).

Nur sehr selten läßt sich durch das Filter eine gute Fehlerkorrektur erkennen wie hier:
A 0  70   1 68087
B 0  70   0 68137
A 0  51   0 69069
A 0 108   0 69186#
B 1 165   0 69233*
A 0  89   0 70087
B 0  90   0 70138

In Sekunde 69xxx feuert das Conrad-Modul zwei gültige Bits (51 und 108ms lang) in derselben Sekunde (Bitfehler #), aber das gefilterte Signal gibt nur ein einziges Bit aus, und zwar ein langes 1-Bit statt zweier kurzer 0-Bits, daher Bitfehler * angezeigt. Da man weiß, dass im DCF-Protokoll nur ein Bit pro Sekunde übertragen wird, kann man also davon ausgehen, dass das am gefilterten Ausgang(B) gelieferte 1-Bit korrekt ist und die zwei davor am A-Ausgang signalisierten 0-Bits falsch.

Im Schnitt ist auf dem gefilterten Signal zwar weniger Gezappel durch weniger Flankenwechsel, aber dabei gehen praktisch genau so viele Bits von Sekundenpulsen verloren wie bei der Auswertung des ungefilterten Signals. Denn das Gezappel durch Flankenwechsel tritt bei gestörtem Signal nicht gleichverteilt über das gesamte Signal auf, sondern bevorzugt um die Zeit herum, um die tatsächlich ein Flankenwechsel stattfindet. Und das läßt sich eigentlich auch durch ein einfaches Entprellen des Signals von wenigen ms gut herausfiltern.


Was ergeben Eure Tests?

Also meine anfängliche Euphorie über den Exponentialfilter und dass damit das Signal viel besser aussieht, konnte ich durch meine Messungen nun nicht mehr bestätigen. Zwar sind gefilterte Signale immer mindestens 50 ms lang, während ungefilterte Signale oft sehr zappelig sein können, aber da die Zappeligkeit fast nur um den eigentlichen Flankenwechsel herum auftritt, ist eigentlich auch das Originalsignal gut auswertbar.
« Last Edit: December 09, 2012, 03:48:31 pm by jurs » Logged

Germany S-H
Offline Offline
Faraday Member
**
Karma: 146
Posts: 3031
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hier das Testprogramm zum vorherigen Beitrag:

Code:

/*************************************************************************
 Testprogramm zum Testen von DCF-Modulen und DCF-Softwarefiltern
 
 Es können entweder zwei DCF-Module gegeneinander getestet werden
 Dazu die zwei Dateneingänge mit den Pins DCF_A_MODULE und DCF_B_MODULE verbinden
 und die Zeile "#define B_IS_FILTERED_A" auskommentieren
 
 Oder es kann ein DCF-Modul gegen einen Softwarefilter getestet werden
 Beispielhaft ist der hier vorgestellte Filter eingebaut:
 http://arduino.cc/forum/index.php?topic=135153.0
 Dazu das DCF-Modul an den Pin DCF_A_MODULE anschliessen und
 die Zeile "#define B_IS_FILTERED_A" in den Code einfügen
 (Der Pin DCF_B_MODULE bleibt dann frei.)
  
**************************************************************************/

// Pin fuer DATA-Ausgang des DCF-Moduls
#define DCF_A_MODULE 2

// Pin fuer DATA-Ausgang des zweiten DCF-Moduls oder den Software-Filter
#define DCF_B_MODULE 3

// die nachfolgende Zeile auskommentieren, wenn zwei Module getestet werden sollen
#define B_IS_FILTERED_A
// (wenn "#define B_IS_FILTERED_A" gesetzt ist, den Pin für das DCF_B_MODULE freilassen!)

// legt fest, an welchem Pin eine LED blinken soll
#define LED 13

#ifdef B_IS_FILTERED_A
  #include <MsTimer2.h>
#endif

char error[]="SYNC";
char noerror[]="";

void setup() {
  Serial.begin(9600);
  Serial.println();
  pinMode(DCF_A_MODULE, INPUT);
  pinMode(DCF_B_MODULE, INPUT);
  pinMode(LED, OUTPUT);
#ifdef B_IS_FILTERED_A
  pinMode(DCF_B_MODULE, OUTPUT);
  MsTimer2::set(1, low_pass_filter);
  MsTimer2::start();
#endif
}

#ifdef B_IS_FILTERED_A
void low_pass_filter() {
    // http://en.wikipedia.org/wiki/Low-pass_filter#Continuous-time_low-pass_filters
    // I will use fixed point arithmetics with 5 decimals
    const uint16_t decimal_offset = 10000;
    static uint32_t smoothed = 0*decimal_offset;
    const uint32_t input = digitalRead(DCF_A_MODULE) * decimal_offset;
//    const uint32_t input = analogRead(dcf77_analog_sample_pin)>200? decimal_offset: 0;
    // compute N such that the smoothed signal will always reach 50% of
    // the input after at most 50 samples (=50ms).
    // N = 1 / (1- 2^-(1/50)) = 72.635907286
    const uint16_t N = 72;
    smoothed = ((N-1) * smoothed + input) / N;
    // introduce some hysteresis
    static uint8_t square_wave_output = 0;
    if ((square_wave_output == 0) == (smoothed >= decimal_offset/2)) {
        // smoothed value more >= 50% away from output
        // ==> switch output
        square_wave_output = 1-square_wave_output;
        // ==> max the smoothed value in order to introduce some
        //     hysteresis, this also ensures that there is no
        //     "infinite memory"
        smoothed = square_wave_output? decimal_offset: 0;
      digitalWrite(DCF_B_MODULE, square_wave_output);   //changed jurs
    }
}
#endif


boolean
  BdcfState,AdcfState,
  lastBdcfState=false,
  lastAdcfState=false;

byte
  AdcfBitval,BdcfBitval;

long
  looptime,
  lastAdcfH,lastAdcfL,
  lastBdcfH,lastBdcfL,
  AdcfShortPulses,BdcfShortPulses,
  AdcfLastPulse,BdcfLastPulse,
  AdcfHighcycle, BdcfHighcycle;

void loop() {
//  looptime=millis()%100000L; // last 5 digits
  looptime=millis();
  AdcfState=digitalRead(DCF_A_MODULE);
  BdcfState=digitalRead(DCF_B_MODULE);
  digitalWrite(LED,BdcfState);
  CheckForHighCycle(looptime, AdcfState, lastAdcfState, AdcfHighcycle, lastAdcfH, lastAdcfL, AdcfShortPulses);
  CheckForHighCycle(looptime, BdcfState, lastBdcfState, BdcfHighcycle, lastBdcfH, lastBdcfL, BdcfShortPulses);
  printIfValidPulse('A', looptime, AdcfHighcycle, AdcfLastPulse, AdcfShortPulses, AdcfBitval, BdcfBitval, BdcfLastPulse);
  printIfValidPulse('B', looptime, BdcfHighcycle, BdcfLastPulse, BdcfShortPulses, BdcfBitval, AdcfBitval, AdcfLastPulse);
}

void printIfValidPulse(char c, long ltime, long &Highcycle, long &LastPulse, long &ShortPulses, byte &Bitval, byte OtherBitval, long LastOtherPulse)
{
  char *msg;
  char bitError;
  char buf[32];
  if ((Highcycle>=50)&&(Highcycle<=250))
  {
   // Falls mehr als 1500 ms kein Impuls ==> SYNC (Minutenimpuls oder Sync-Error)
   if (looptime>LastPulse+1500) msg=error; else msg=noerror;
   // > 150 ms HIGH => 1-Bit, 50...150 ms HIGH => 0-Bit
   if (Highcycle>150) Bitval=1; else Bitval=0;
   // Biterror falls innerhalb der letzten 500 ms ein anderes Bit am anderen Eingang gemeldet wurde
   bitError=' ';
   // Biterror erkennen, falls weniger als 500 ms vorher ein anderes Bit am anderen Eingang erkannt wurde
   if ((looptime<LastOtherPulse+500)&&(Bitval!=OtherBitval)) bitError='*';
   // Biterror erkennen, falls der letzte Pulse vom selben Eingang kam
   if (LastPulse>LastOtherPulse) bitError='#';

   LastPulse=looptime;
   sprintf(buf,"%c %d %3ld %3ld %05ld%c%s", c, Bitval, Highcycle, ShortPulses,looptime%100000L,bitError,msg);
   Serial.println(buf);
   Highcycle=0;
   ShortPulses=0;
 }
}

void CheckForHighCycle(long looptime, boolean &State, boolean &lastState, long &Highcycle, long &lastH, long &lastL, long &ShortPulses)
{
  if (State!=lastState)
  {
    if (State==LOW)
    {
      Highcycle=looptime-lastH;
      if (Highcycle<50) ShortPulses++;
      lastL=looptime;
    }
    else lastH=looptime;
    lastState=State;
  }
}

Logged

Germany
Offline Offline
Jr. Member
**
Karma: 0
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Interessanter Beitrag! Bin aber leider ebenfalls aus Zeitgründen noch nicht zum Testen gekommen. Hoffentlich schaffe ich das noch vor den nächsten Semesterferien  smiley-eek-blue

Aber eine Frage kommt mir doch auf:
Du tastest mit einer Frequenz von 1 kHz ab. Fehlt da nicht noch irgendeine Form von analogem Tiefpass vor der digitalisierung, damit höherfrequente Störungen nicht in den niederfrequenten Teil des Spektrums rutschen?
Soetwas haben wir jedenfalls zum Thema Analog-Digitalwandlung in Verbindung mit digitaler Regelung in einer Vorlesung gehört...
Logged

Offline Offline
Edison Member
*
Karma: 21
Posts: 1419
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich habe am Wochenende auch mal den DCF-Empfänger aus dem Conrad-Wecker ausgeschnitten. Die beiden Leiterbahnen die zum MC des Weckers gingen habe ich auf zwei digitale Eingänge gelegt. Einer liefert eine 1, der andere eine 0, daher vermute ich mal Signal und invertiertes Signal. Allerdings habe ich bisher noch keinerlei Änderungen an den Ausgängen feststellen können, ich muss mir noch eine bessere Position für den Aufbau suchen, zur Zeit sind da zu viele Netzteile und Monitore in der näheren Umgebung.
Logged

Germany
Offline Offline
Edison Member
*
Karma: 47
Posts: 2320
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Die beiden Leiterbahnen die zum MC des Weckers gingen habe ich auf zwei digitale Eingänge gelegt. Einer liefert eine 1, der andere eine 0, daher vermute ich mal Signal und invertiertes Signal. Allerdings habe ich bisher noch keinerlei Änderungen an den Ausgängen feststellen können, ich muss mir noch eine bessere Position für den Aufbau suchen, zur Zeit sind da zu viele Netzteile und Monitore in der näheren Umgebung.
Es ist zwar richtig, dass die DCF-Module von Conrad auch einen invertierten ausgang haben, so recht kann ich mir das bei einer Funkuhr jedoch nicht vorstellen. Da halte ich es eher für einen Eingang, um das Modul zu Stromsparzwecken ein- und ausschalten zu können.
Die Wikipedia schreibt im Funkuhr-Artikel dazu:
Quote
Obwohl das Zeitsignal kontinuierlich gesendet wird, wird es aus Stromspargründen oft nur ab und zu zum Nachstellen abgefragt. Bei Uhren, die mit größeren Zellen betrieben werden, ist ein Empfang jede volle Stunde üblich, bei Armbanduhren mit Knopfzellen oder mit Solarzellen nur einmal pro Tag, meistens zwischen 2:00 und 4:00 Uhr morgens.
Einen entsprechenden Eingang haben bspw. auch die Pollin-Module.
Logged

Mein Arduino-Blog: http://www.sth77.de/ - letzte Einträge: Teensy 3.0 - Teensyduino unter Window 7 - Teensyduino unter Windows 8

Offline Offline
Edison Member
*
Karma: 21
Posts: 1419
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hmm, guter Tipp. Bei den Pollin-Modulen wird der ON/OFF Eingang glaub ich auf Masse gezogen, um das Modul zu aktivieren. Ich werde das heute Abend mal testen.
Logged

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3482
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Du tastest mit einer Frequenz von 1 kHz ab. Fehlt da nicht noch irgendeine Form von analogem Tiefpass vor der digitalisierung, damit höherfrequente Störungen nicht in den niederfrequenten Teil des Spektrums rutschen?

Wieso sollte es einen analogen Tiefpass brauchen? Der exponentielle Filter ist doch ein Tiefpass. "Analoge" Filter sind nicht auf magische Weise besser.
Logged

Check out my experiments http://blog.blinkenlight.net

Germany
Offline Offline
Jr. Member
**
Karma: 0
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Kann der Digitale Filter denn die Aliaseffekte ausgleichen, die BEIM Abtasten entstehen?
Logged

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3482
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

And1G: ich dachte Du hast eine Vorlesung zu dem Thema gehört. Wieso fragst Du dann mich? Und vor allem wieso stellst Du Dich dumm? Selbstverständlich kann überhaupt kein Filter Alias Effekte ausgleichen die ins Passband rutschen. Die Frage ist nur: treten die tatsächlich auf? in meinem Fall also: gibt es Überlagerungen die nahe bei 500 Hz oder einer Oberwelle davon sind? Bei einem Empfänger der eine Bandbreite von wohel eher <300 Hz hat? Ich würde sagen nein. Wenn Du aber anderer Meinung bist, dann belege das doch bitte etwas genauer. Auf die Begründung bin ich mal gespannt.
Logged

Check out my experiments http://blog.blinkenlight.net

Germany
Offline Offline
Jr. Member
**
Karma: 0
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Warum ich frage? Na weil meine bisherigen Kenntnisse etwas anderes sagen als das was du realisiert hast. Da wundert man sich und stellt dann halt eine Frage, ganz normaler Vorgang. Es hätte ja durchaus sein können, dass irgendetwas da zwischendurch eingekoppelt wird.
Kein Grund sich gleich angegriffen zu fühlen oder so genervt zu reagieren.
Logged

0
Offline Offline
Faraday Member
**
Karma: 23
Posts: 3482
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Dir ist aber schon klar, daß meine Implementation funktioniert? Von daher ist die Frage

Quote
Kann der Digitale Filter denn die Aliaseffekte ausgleichen, die BEIM Abtasten entstehen?

schon etwas offensiv. Dass dich das wundert war aus der Frage nicht zu ersehen. Die hat sich vom Ton eher angehört wie: "Du hast nur Glück und weisst vermutlich nicht was Du tust". Hast Du Dir die Quellen auf meiner Seite angesehen. Ich habe die gelesen und ausreichend weit verstanden.
Logged

Check out my experiments http://blog.blinkenlight.net

NRW
Offline Offline
Sr. Member
****
Karma: 2
Posts: 372
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich habe mal eine blöde Frage.
Ich experimentiere gerade mit dem Filter rum und hätte aber gerne den I2C frei.
Deshalb wollte ich den Eingangspin für das DCF77-Modul ändern.
Ich dachte dies geht hier:
Code:
const uint8_t dcf77_sample_pin = 19; // A5
19 = Analog 5
Wenn ich, sagen wir mal Analog 1 haben will:
Code:
const uint8_t dcf77_sample_pin = 15; // A1
Aber dann macht er nichts mehr.

Wo denke ich falsch?
Logged

Germany S-H
Offline Offline
Faraday Member
**
Karma: 146
Posts: 3031
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Aber dann macht er nichts mehr.

Wo denke ich falsch?

Wenn Du Dich auf den Original-Code beziehst:

> const uint8_t dcf77_sample_pin = 19; // A5
> const uint8_t dcf77_analog_sample_pin = 5;
...
>    //const uint32_t input = digitalRead(dcf77_sample_pin) * decimal_offset;
>    const uint32_t input = analogRead(dcf77_analog_sample_pin)>200? decimal_offset: 0;

Deklariert sind sowohl dcf77_sample_pin als auch dcf77_analog_sample_pin.

Die auskommentierte Codezeile bezieht sich auf dcf77_sample_pin und die ausgeführte Codezeile aber auf dcf77_analog_sample_pin.

Wenn Du den Code so verwendest, mußt Du entweder
- die Deklaration für dcf77_analog_sample_pin ändern.
oder
- die Codezeile verwenden, in der dcf77_sample_pin verwendet wird und die andere auskommentieren.
Logged

NRW
Offline Offline
Sr. Member
****
Karma: 2
Posts: 372
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Jetzt wo Du es sagst.  smiley-eek

Hab ich nicht gesehen.

Danke!
Logged

NRW
Offline Offline
Sr. Member
****
Karma: 2
Posts: 372
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich glaube ich werde das nie lernen mit den Variablen Typen.
Ich versuche gerade das Funkuhrsignal in eine RTC zu schreiben.
von der Funkuhr bzw der Time.h bekomme ich ja :
Code:
sekund() minute() hour() year() month() day()
Das sind int wenn ich mich nicht irre. Diese werden mir auch schön im seriellen Monitor angezeigt:
Quote
17.12.2012 16:31:02

So die RTC (ich habe hier eine softI2C) nimmt die Daten richtig an wenn ich sie so schreibe:
Code:
uint8_t d[4];
d[3]=0x12; // Jahr
d[2]=0x12; // Monat
d[1]=0x16; // Tag
d[0]=0x01; // Wochentag
Das ist HEX-Schreibweise wenn ich mich nicht irre.
Wenn ich jetzt einfach die ints versuche zu übergeben sieht meine ausgelesene RTC so aus;
Quote
RTC: 11.0C.2001 01:1F:01
Das ist irgendwie falsch!
Gehe ich recht in der Annahme das ich aus dem int ein HEX machen muss?
Und wenn ja, wie?
Logged

Pages: 1 2 [3] 4 5 6   Go Up
Jump to: