Zum Inhalt

Python

In diesem Abschnitt sollen einige für die Simulation und Datenverarbeitung relevante Konzepte angesprochen werden.

Hilfe zur allgemeinen Einführung in Python:

Echtzeit

Die Verarbeitung von Signalen setzt i.d.R. eine äquidistante Abgetastung (im Sinne der Signaltheorie bzw. der diskreten Regelungstechnik) voraus. Da die Übertragung von Datensätzen, die aus einer Abtastung stammen oftmal zeitlichen Schwankungen (Jitter) unterliegen, gibt man einem solchen Datensatz stets einen Zeitstempel (Timestamp) mit:

\[ x_T(kT) = sin(\omega \cdot T) \]

Zeitreihe:

\(kT\) Wert \(x\) Wert \(y\)
\(1 \cdot T\) \(x_1\) \(y_1\)
\(2 \cdot T\) \(x_2\) \(y_2\)
... ... ...
\(n \cdot T\) \(x_n\) \(y_n\)

Als json-Objekt:

[
    {
        ts : 1,
        x  : 0.1,
        y  : 10.1
    },
    {
        ts : 2,
        x  : 0.2,
        y  : 10.2
    },
]

time.sleep

Möchte man in Python eine zeitäquidistante Abarbeitung realisieren, dann kann mit den Standard-Bliotheken

  • time
  • datetime

mit Zeiten gerechnet werden. Folgende Beispiel-Skript gibt in einer Endlosschleife im Zeitraster \(T=1 s\) die aktuelle Zeit auf der Konsole aus.

time_loop.py

import time
from datetime import datetime

i = 0
while True:
    i +=1
    ts = datetime.now()
    print(f'({i}) {ts.strftime("%H:%M:%S:%f")}')
    time.sleep(1)  # waiting
Download: File

Konsolenausgabe:

timeloop_jitter

Diese kleine Beispiel zeigt das Problem auf: Die Abstastzeit ist nicht konstant, sondern läuft (in Abhängigkeit der momentanen CPU-Last) langsam (in der Größenordnung 1 %) weg.

Abhilfe schafft der Einsatz spezieller Bibliotheken:

APScheduler

Eine sehr weit verbreitete und mächtige Bibliothek ist der

Advanced Process Scheduler Doku

Konsole: Installation

pip install APScheduler

Das Anwendungskonzept gliedert sich:

  1. function "Job": periodisch auszuführender Job (Funktion)
  2. Management der Jobs

aps.py

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

sched = BlockingScheduler()

i=0

def job():
    global i
    i +=1
    ts = datetime.now()
    print(f'({i}) {ts.strftime("%H:%M:%S:%f")}')

sched = BlockingScheduler({'apscheduler.timezone': 'UTC'})
sched.add_job(job,'interval',seconds=1,id='runJob1') 

try:
    sched.start()
except (KeyboardInterrupt, SystemExit):
    pass   
Download: File

Konsolenausgabe:

timeloop_jitter

Diese kleine Beispiel zeigt die Lösung: Die Abstastzeit ist annähernd konstant und jittert um eine sehr kleinen Betrag. der APScheduler lässt die Job als Thread laufen. Mit dem APS lassen sich noch eine Vielzahl weiterer zeitgesteuerter Aufgaben realisieren: Datum-getriggerte Aufgaben, sogenannte Cron-Jobs. Mehr in der Dokumentation

toml-Dateien

Um variable Kopnfigurationsdaten von dem eigentlichen Programmcode zu trennen, ist es praktisch, diese Daten als ASCII-Konfigurationsfile zur Verfügung zu stellen. In Frage kommen verschiedene standardisierte Formate zur Abbildung strukturierter Daten in Frage:

Da tomlein sehr einfach zu handhabendes Format ist (und seit der Python-Version 3.12 standartmäßig verfügbar ist, soll es kurz vorgestellt werden.)

toml_1.py

import tomllib # py > 3.11
# import toml as tomllib # py <= 3.11  (pip install toml)

with open("cfg.toml", "rb") as f:
    data = tomllib.load(f)

for d in data["dev"]:
    print(d)

print(f'ip={data["web"]["ip"]} ,  Port={data["web"]["port"]} ')    
Download: File

python-benedict

Eine sehr mächtige Erweiterung stellt das Paket "python-benedict" zur Verfügung Link

Konsole: Installation

pip install python-benedict

"benedict" zielt mit seiner Klassen-Konzept auf eine Erweiterung der klassischen Python-Dictonaries. Elegant ist die Möglichkeit, eine toml-Datei einlesen und als benedict(dict)-Klasse nutzen zu können.

Zusätzlich lassen sich benedict-Daten auch schreiben (toml, yaml, csv, ...). Details sind in der Dokumentation beschrieben.

Beispiel

benedict_1.py

from benedict import benedict

# with open("cfg.toml", "rb") as f:
#     data = tomllib.load(f)
data = benedict("cfg.toml", format="toml")

# for d in data["dev"]:
for d in data.dev:
    print(d)

# print(f'ip={data["web"]["ip"]} ,  Port={data["web"]["port"]} ')    
print(f'ip={data.web.ip} ,  Port={data.web.port} ')    
Download: File

Hinweis: Die Unterschiede zur tomllib sind durch Auskommentierung hervorgehoben.