Taktgeber, NTP Sync, syncroner Start

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Hallo zusammen

Lange nicht mehr hier gewesen... Und gleiche eine Frage, inwieweit Python als Taktgeber taugt, der die Taktfolge nach einer Uhr stellen kann und sehr exakt ist...

Konkret:

Ich brauche für einen Versuch einen Taktgenerator, der im ersten Step auf der Konsole einen Takt anzeigen kann, später sollte das dann einen Ausgang steuern. Auf mindestens zwei getrennten Rechnern. Das ganze ist leider etwas kompliziert. Die Taktfolge ist nicht symetrisch, sondern kann sowas sein wie:
(Start)
1000 ms ON
500 ms OFF
7000 ms ON
2000 ms OFF
5000 ms ON
2000 ms OFF
(ab hier zurück auf Start)

Der Steuerrechner hat eine Uhr, die per DCF77 oder NTP die genaue Zeit hat. Für den Test soll es NTP sein. Das ist sicher noch einfach. Das Problem kommt jetzt. Wenn der Taktgenerator startet, soll der Takt auf diesem einen Rechner genauso syncron laufen, wie auf jedem weiteren Rechner mit dem selben Programm. Wie die Blinklichter auf den Windparks, die per DCF syncron sind. Nur hier in diesem Fall ist der Takt komplexer als nur an und aus und so eine Abfolge der ON und OFFS kann auch mal länger sein... Die beiden und mehrere Rechner haben aber keine Verbindung, ausser die genaue Zeit... Die Abweichung des Taktes zwischen den Rechnern darf 30ms nicht überschreiten.

Kann Python so genau schalten, und kann der Takt auch nach langer Laufzeit des Programm noch syncron bleiben? Es ist sicher nicht damit getan, einen Zählen des Prozessor als Zeitbasis nach dem syncronen Start zu nehmen. Das würde sicher auseinander laufen. Es muss also der Zyklus einer Taktfolge alle paar Minuten neu an die Zeitbasis angelehnt werden, denke ich.

Lösbar, oder ein unsinniges Vorhaben.? Danke für Anregungen...

--
Rüdiger
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du die "wall clock" nimmst, was einfach time.time sein sollte, und die Rechner alle per synchron sind - dann ist das trivial. Es bietet sich an, die Rechner auf UTC zu konfigurieren (so hast du keine Umschaltung Sommer/Winterzeit), und spaeter dann per DCF muss man das eben auch entsprechend machen. Alles was du tun musst ist eine Epoche festlegen, ab der die Sequenz berechnet wird. Kann auch einfach die Unix-Epoche sein. Womit dann einfach

Code: Alles auswählen

math.fmod(time.time(), <periode>)
den Offset in die Sequenz bestimt. Und dann einfach warten.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Epoche... Der berühmte 1.1.1970
Hört sich einfach an. Als weiss das Programm dann wo in der Abfolge es ist.?

Ja, das hatte ich vergessen. UTC ist die Zeit der Wahl.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Als Ergebnis der von mir gezeigten Berechnung bekommst du einen Wert, der zeigt, *wo* innerhalb deine Periode-Sekunden-langen Sequenz du bist. Also ja, damit weiss das Programm, wo es ist.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

OK, muss feststellen das meine ersten Gehversuche vor Jahren mit einem PI und ein paar GPIO mir hier nicht weiterhelfen. Die "periode" oben in spitzen Klammern ist diese Liste an ON und OFF Zeiten? Blätter gerade durch diverse Codebeispiele...
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Periode ist wie bei allem was periodisch ist die Laenge deiner Sequenz. Also die Summe aus deinen Zeiten, und das ausgedrueckt in Sekunden (weil das die Einheit von time.time() ist).
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Code: Alles auswählen

import math
import time
seconds = time.time()
print("Seconds since epoch =", seconds)	
position = math.fmod(time.time(), 17500)
print(position)
Das hab ich mal zum Test ausgeführt. Die Sekunden seit der Epoche werden angezeigt. Und was sagt mir nun das Ergebnis.? Das scheint
nicht durch 17500 geteilt zu sein... :)

Code: Alles auswählen

$ python ./takt.py 
('Seconds since epoch =', 1580840128.924031)
12628.924068
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe dir ja auch gesagt, dass du die Summe deiner in Millisekunden angegebenen Intervalle in SEKUNDEN ausdruecken musst. Und natuerlich kommt der Wert als ein Modulo von 17500 bei raus - was sonst sollte denn 12628.924068 sein?

So berechnet man an/aus. Der Rest sollte nun wirklich ein Kinderspiel sein:

Code: Alles auswählen

import time
import math
import bisect

SEQUENCE = [
            (1000, True),
            (500, False),
            (7000, True),
            (2000, False),
            (5000, True),
            (2000, False),
]

PERIOD = sum(t for t, _ in SEQUENCE) / 1000.0  # in seconds


def main():
    absolute_sequence = [0]
    for ts, _ in SEQUENCE:
        absolute_sequence.append(absolute_sequence[-1] + ts)
    del absolute_sequence[0]

    while True:
        time.sleep(.1)
        offset = int(math.fmod(time.time(), PERIOD) * 1000)
        pos = bisect.bisect_left(absolute_sequence, offset)
        print(offset, pos, SEQUENCE[pos][1])


if __name__ == '__main__':
    main()
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Mist, zu doof. Hab ich ja selber vorgegeben.
Danke, ich probier mal damit rum...
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Tolle Sache. Hab das Programm in zwei getrennte Terminals nacheinander gestartet, und die True/False Zeilen laufen parallel hoch. OK, klar. Ist ja die selbe Zeitbasis. Aber wenn NTP und Co seinen Job machen, sollte das grundsätzlich gehen.

Und bzgl. weglaufen. Es wird immer wieder neu ausgerechnet.? Alle 100 ms...
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Danke für die Unterstützung. Will das mal auf zwei Raspis laufen lassen und ne LED steuern. Das fällt dann mehr ins Auge, wenn das aus dem Takt läuft.

Schönen Abend noch...
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Hab noch ne Frage bzgl. der LED...

Die Ansteuerung der LED ist kein Problem, wenn ich die jeweiligen Zeilen auskommentiere, geht die auch an bzw. aus. Aber die Abfrage, die parallel zu der Ausgabe True oder False die LED setzen soll, geht aber nicht.
Das wird einfach ignoriert.
Geht das so nicht innerhalb dieser Schleife?

Code: Alles auswählen

mport time
import math
import bisect
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
GPIO.setup(40, GPIO.OUT)

SEQUENCE = [
            (1000, True),
            (500, False),
            (7000, True),
            (2000, False),
            (5000, True),
            (2000, False),
]


PERIOD = sum(t for t, _ in SEQUENCE) / 1000.0  # in seconds


def main():
    absolute_sequence = [0]
    for ts, _ in SEQUENCE:
        absolute_sequence.append(absolute_sequence[-1] + ts)
    del absolute_sequence[0]

    while True:
        time.sleep(.1)
        offset = int(math.fmod(time.time(), PERIOD) * 1000)
        pos = bisect.bisect_left(absolute_sequence, offset)
        print(offset, pos, SEQUENCE[pos][1])
    if SEQUENCE == True:
 	GPIO.output(40, GPIO.HIGH)
    else :
	GPIO.output(40, GPIO.LOW)

if __name__ == '__main__':
    main()

GPIO.cleanup()


Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Ich werde aus deiner Beschreibung des Problems nicht ganz schlau. Welche Zeile meinst du denn?
In der Zeile SEQUENCE == True prüfst du, ob das Objekt , das in den Namen "SEQUENCE" gebunden ist, den Wahrheitswert True hat. Das Objekt, dass du ein SEQUENCE gebunden hast, ist eine Liste. Ich bin mir ziemlich sicher, dass du das so nicht möchtest.

Edit: Und "in der Schleife" sehe ich auch keine Änderung zu deinem vorherigen Code.
Zuletzt geändert von sparrow am Mittwoch 5. Februar 2020, 11:58, insgesamt 1-mal geändert.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Ja, da hast Du Recht. In der Schleife wird immer ausgegeben, an welcher Stelle in dieser Sequenz die Berechnung rauskommt. Und im Falle von True soll der Ausgang auf High gehen, bzw. auf LOW bei False. Ich dachte ich kann die SEQUENCE als eine Art variable nehmen.!? War wohl Unsinn...
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Na, wenn das _in_ der Schleife ausgegeben wird, dann weißt du doch auch, wie man an den Wert kommt.
Ich würde dann aber auch _in_ der Schleife etwas damit machen. Momentan ist das nämlich nicht _in_ der Schleife.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

Der print Befehl gibt das ja periodisch aus. Endet damit die Schleife?

Sowas wie ein "Done" am Ende der Schleife gibt es bei Python wohl nicht...?!
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Es gibt ein ganz gutes Tutorial in der Python-Dokumentation, das einem die Grundlagen der Sprache recht gut vermittelt.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da die Schleife endlos ist, endet sie nicht. Und wenn eine Schleife endlos laeuft, dann ist es auch irrelevant welcher Code danach steht - der wird ja nie ausgefuehrt(*). Der GPIO-Code muss IN die Schleife, und warum Code in einer Schleife ist, und warum nicht, ist absolute Grundlage in Python. Ein paar Stunden mit dem Tutorial das sparrow nennt wirst du dir goennen muessen.


(*)Ausnahmen sind Ausnahmen. Aber die kommen hier nicht vor.
RudiOnTheAir
User
Beiträge: 21
Registriert: Montag 11. September 2017, 20:27

OK, schaue ich mir an.
Wie so oft, ist Zeit ein limitierender Faktor. Und ich hatte angenommen, das mein GPIO grundsätzlich laufen müsste, ich nur irgendwo ein Zeichen falsch gesetzt habe, oder so. Und dann neige ich dazu erstmal rumzuprobieren, was auch ab und zu klappt.
Aber eben nicht immer...
Antworten