Probleme mit Formatierter Ausgabe.

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.
Antworten
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

Hallo ich bin blutiger Anfänger in der Python programmierung.
Ich habe ein Programm zum Auslesen eines Vielfachmessgerätes umgebaut zum Auslesen der Feldstärke eines Kurzwellen-Empfängers.
Das Auslesen der Messwerte und Plotten funktioniert soweit alles.
Mein Problem ist dass ich die Ausgelesenen Messwerte ( 00 bis 22 )in S-Meter werte umwandeln will.
00 = S0
02 = S1
03 = S2
04 = S3
05 = S4
06 = S5
08 = S6
09 = S7
10 = S8
11 = S9
12 = S9+10
14 = S9+20
16 = S9+30
18 = S9+40
20 = S9+50
22 = S9+60
Geht das mit Formatierter Ausgabe? Ich kriege das nicht hin.
Jeder Hinweis ist erwünscht.
## Programm zur Steuerung des Transceivers Elad FDM-Duo
## Das Programm kann:
## 1.Frequenz setzen. OK
## 2.Die Modulationsart setzen.OK
## 3.Das S-Meter auslesen.OK
## 4.Verlauf der Messwerte des S-Meters grafisch darstellen. OK.
## Ich habe dieses Programm nicht selbst geschrieben,sondern von der University Reutlingen
## Fakultät TEC:Messtechnik mit Pythen heruntergeladen und geändert.
## Es war ursprünglich ein Programm zum Auslesen von Daten eines Vielfachmessgerätes.
##
## 29.04.2019 L.Holzer DF8LHO

#!/usr/bin/python3

import matplotlib.pyplot as plt
import tkinter as TK
import serial
from time import sleep

# Objekt fuer serielle Kommunikation
portDmm = serial.Serial()

# Tkinter Hauptfenster
root=TK.Tk()
root.title("Elad FDM-Duo")

# Eingabewerte durch Benutzer
comPortNo = TK.StringVar() # StringVar()-Objekt wird erzeugt und der Variablen comPortNo zugewiesen
comPortNo.set("COM11") # Initalwerte werden dem Objekt gegeben
numMeas = TK.IntVar() # IntVar()-Objekt wird erzeugt und der Variablen numMeas zugewiesen
numMeas.set(20) # Initalwerte werden dem Objekt gegeben Messungen
sampTime = TK.IntVar()
sampTime.set(1) #Periode(S)

# GUI Texte (macht hier Sinn, wenn andere Sprache noetig
comPortTxt='COM-Port:'
numMeasPerTxt='# Messungen / Periode (h):'
valTxt='Aktueller Messwert:'
unitTxt='Einheit'



# COM-Verbindung aufbauen
def connComPort(port):
print('connComPort()')
# Serielle Schnittstelle oeffnen. Muss vor GUI geschehen, damit dort Info ueber COM-Verbindungsstatus
try:
port.baudrate = 38400
port.port = comPortNo.get()
port.parity = serial.PARITY_NONE
port.stopbit = serial.STOPBITS_ONE
port.bytesize = serial.EIGHTBITS
port.timeout = 2
port.setDTR(True) # fuer Stromversorgung RS-232 Interface.
port.setRTS(False)
port.open()
print('COM-Verbindung OK')
comStatusLabel.configure(text='COM-Verbindung OK')
port.flushInput() # Seriellen Puffer leeren
except:
print('Keine COM-Verbindung')
comStatusLabel.configure(text='Keine COM-Verbindung')

# Call Back Funktion fuer Klicken des Buttons (in tkinter kann leider kein Parameter uebergeben werden)
def comConnButton():
print('comConnButton()')
connComPort(portDmm)

# Call Back Funktion fuer Klicken des Buttons (in tkinter kann leider kein Parameter uebergeben werden)
def singleMeasButton():
print('singleMeasButton()')
singleMeas(portDmm)

# Call Back Funktion fuer Klicken des Buttons (in tkinter kann leider kein Parameter uebergeben werden)
def multiMeasPlotButton():
multiMeasPlot(portDmm)

# Einzelmessung
def singleMeas(port):
print('Parameter setzen')
try:
#port.write(b'D')
#port.write(b"FA00000016400;") # Frequenz setzen (16,4 Khz)
port.write(b"FA00000023400;") # Frequenz setzen (23,4 Khz)
port.write(b"MD3;")#Einstellung CW+
#port.write(b"MD4;")#Einstellung FM
port.write(b"SMO;")#Einstellung S-Meter auslesen
result = port.read(8) # Daten aus dem Elad FDM-Duo auslesen (8 Byte)
if len(result) == 8: # Umwandlung in String und Formatierungszeichen entfernen, falls Datensatz konsistent
print(str(result)[7:9])# Nur den Aktuellen Messwert auslesen. ( 00006 )


val = str(result)[7:9] # Nur den Messwert auslesn. ( 00006 )
comStatusLabel.configure(text='Elad FDM-Duo OK') # Labeltext aktualisieren
else:
print('Kein passender Antwortstring vom Elad FDM-DUO (Elad FDM-DUO antwortet nicht).')
val = '00000' # Muss Zahl sein, falls Messreihe (wegen Berechnung Statistikparameter)
comStatusLabel.configure(text='Daten DMM inkonsistent')
except:
print('COM-Verbindung abgebrochen')
comStatusLabel.configure(text='COM-Verbindung abgebrochen')
val = '00000'
valLabel.configure(text=val)

return val

# Messreihe
def multiMeasPlot(port):
print('Messreihe')
results = list(range(0, numMeas.get())) # Liste fuer Messwerte, muesste eigentlich nicht initialisiert werden
times = [j * sampTime.get() for j in list(range(0, numMeas.get()))] # Messzeiten als "List Comprehension"
for i in range(0,numMeas.get(),1):
results = float(singleMeas(port))
print('Messreihe Messung ',i)
sleep(sampTime.get()) # Samplingperiode lang warten

xwerte = [j * sampTime.get() for j in list(range(0, numMeas.get()))] #range(10)messung beginnt erst bei 10
# plt.plot ([0,24,],[0,24,]
plt.plot(times, results)
plt.xlabel('Stunden')
plt.ylabel('S-Meter Anzeige')#Beschriftung y-Achse fest.

plt.title('Elad FDM-DUO 23.400 Khz DHO38 Germany ') #Überschrift Messplot
plt.xticks(xwerte) # x-Achsenbeschriftung fuer jeden Messwert
# plt.grid(True)# Gitterlinien können entfernt werden.
plt.show()


# Einzelne widgets: Hier werden die Buttons, Labels und Eingabefelder erzeugt und deren Layout
# festgelegt. Bei den Eingabefeldern (TK.Entry) werden die Variablen definiert.
comConnButton=TK.Button(root,text="COM-Schnittstelle verbinden",command=comConnButton,bg='light blue') # Button COM Verbinden
comStatusLabel=TK.Label(root,text='Keine COM-Verbindung',width=20,bg='light blue') # Status COM Verbindung. width ändert Breite des 1.Fensters

comInputLabel=TK.Label(root,text=comPortTxt,width=len(comPortTxt)) # Anzahl Messungen / Abtastperiode
comPortEnter=TK.Entry(root,textvariable=comPortNo,width=len(comPortTxt)) # Eingabemaske fuer COM-Port Nummer

numSampInputLabel=TK.Label(root,text=numMeasPerTxt,width=len(numMeasPerTxt))
numMeasEnter=TK.Entry(root,textvariable=numMeas,width=8) # Eingabemaske fuer Anzahl Messungen
sampTimeEnter=TK.Entry(root,textvariable=sampTime,width=8) # Eingabemaske fuer Abtastperiode

singleMeasButton=TK.Button(root,text="Parameter setzen",command=singleMeasButton) # Button Einzelmessung
multiMeasButton=TK.Button(root,text="Messreihe+Plot",command=multiMeasPlotButton) # Button Messreihe

valTextLabel=TK.Label(root,text=valTxt,width=len(valTxt)) # Aktueller Messwert
valLabel=TK.Label(root,text='-----',width=20) # Ausgabe aktueller Messwert





#Festlegung Layout der gesamten GUI: Tabellenanordnung der Widget TK.E = Textausrichtung East
comConnButton.grid(row=40,columnspan=3,sticky=TK.E+TK.W) #row=40 COM-Schnittstelle verbinden ist jetzt am unteren Ende.
comStatusLabel.grid(row=1,columnspan=3,sticky=TK.E+TK.W)#row=10 Keine Com Verbindung ist jetz am unteren Ende.

comInputLabel.grid(row=2,column=0,sticky=TK.E)#row stelle an der Com-Port steht
comPortEnter.grid(row=2,column=1,sticky=TK.W)

numSampInputLabel.grid(row=3,column=0,sticky=TK.E) #stelle an der Messunge /Periode steht
numMeasEnter.grid(row=3,column=1,sticky=TK.W)#stelle an der das Eingabefeld für Messungen steht
sampTimeEnter.grid(row=3,column=2,sticky=TK.W)#stelle an der das Eingabefeld für Periode steht.

singleMeasButton.grid(row=4,column=0,sticky=TK.E)#stelle an der Parameter setzen steht
multiMeasButton.grid(row=4,column=1,sticky=TK.W)#stelle an der Messreihe +Plott steht.

valTextLabel.grid(row=5,column=0,sticky=TK.E)#stelle an der Aktueller Messwert steht.
valLabel.grid(row=5,column=1,sticky=TK.W)#stelle an der Aktueller Messwert angezeigt wird.







# GUI starten
TK.mainloop()
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Da die Umwandlung nicht ganz regelmäßig ist, geht das wohl am besten über ein Wörterbuch.

Der Code enthält einige Fehler. Es werden nakte excepts benutzt, die eine Fehlersuche unmöglich machen. Es wird mit der Stringrepräsentation von Bytes gearbeitet, statt ordentlich zu Decodieren. Die Unmenge an globalen Variablen macht das Programm sehr unübersichtlich. Da die Messzeit beim Berechnen der Samplezeit berücksichtigt wird, ist diese falsch. So gut wie alle Kommentare sind nutzlos, weil sie nur das beschreiben, was ohnehin im Code steht. Die kryptischen Abkürzungen machen ein Lesen unnötig schwierig.
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

Hallo Sirius3 erstmal recht herzlichen Dank für die Antwort.
Also ich habe in meinem ersten Satz ja geschrieben dass ich blutiger Anfänger bin und ich seit 4Wochen versuche dieses Programm für meine Amateurfunkanwendung zum laufen zu bringen. Das Programm habe ich bei der Uni Reutlingen heruntergeladen,ich kann nichts darüber sagen.
So dann werde ich mal nach dem Begriff Wörterbuch suchen....
Ich kann nur hoffen dass noch mehr hilfsbereite Programmieren da sind schonst gibt dies ein Jahresprojeckt für mich. :lol:
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@VLF: Ergänzend zu dem was Sirius3 angemerkt hat: Die She-Bang-Zeile muss die allererste sein, sonst hat sie keine Wirkung. Mit `py.exe` ist das auch unter Windows wichtig.

Der Kommantarblock am Anfang würde sich als Docstring für das Modul eignen.

Datum und Autor des Moduls können als `__date__` und `__author__` nach den importen definiert werden. Die üblichen Dokumentationswerkzeuge, zum Beispiel `pydoc` aus der Standardbibliothek, übernehmen das von dort.

Damit die üblichen Dokumentationswerkzeuge (und auch einige andere) funktionieren, muss das Modul allerdings importierbar sein, ohne das mehr passiert als das Konstanten, Funktionen, und Klassen definiert werden. Anderer Code gehört nicht auf Modulebene. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

`tkinter` wird üblicherweise was `tk` importiert und nicht als `TK`. Auch wenn Module in gewisser Weise Konstanten sind, schreibt man die klein. Wie man fast alles klein_mit_unterstrichen schreibt. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Um Zuweisungen ausserhalb von Argumentlisten, binäre Operatoren, und nach Kommas, erhöhen Leerzeichen die Lesbarkeit.

Das mit den GUI-Texten an Namen binden für den Fall das man das Programm mal übersetzen möchte macht keinen Sinn. Dafür gibt es andere Mechanismen wie das `gettext`-Modul.

Dadurch das diese Variablen nicht dort definiert wurden wo sie auch tatsächlich verwendet werden, ist nicht aufgefallen das eine davon gar nicht verwendet wird.

Die Widgets erst in einem Block zu erzeugen um sie dann erst in einem weiteren Block anzuordnen zwingt einen tatsächlich jedes Widget an einen Namen zu binden, auch wenn man den ausser zum anordnen eigentlich gar nicht braucht.

Bei zwei der drei Schaltflächen hast Du die Schaltfläche an den gleichen Namen gebunden wie die `command`-Funktion die Du da zugeordnet hast. Das ist verwirrend. Funktionen und Methoden haben üblicherweise Namen die die Tätigkeit beschreiben die sie durchführen.

Für ``tk.E+tk.W`` gibt es eine eigene Konstante: `tk.EW`.

Warum hast Du alle `Entry`-Objeke `irgendwasEnter` gennant? Wobei letztendlich auch keines von denen einen Namen braucht.

Die Methoden von `Serial`-Objekte die nicht der Namenskonvention entsprechen, sollte man nicht mehr benutzen. Für die trivialen Getter/Setter-Methoden haben `Serial`-Objekte Properties und `flushInput()` heisst jetzt `reset_input_buffer()`. Voreinstellungen muss man nicht noch einmal setzen.

Das die Frequenz von 23,4 Khz drei mal im Programm in verschiedenen Formaten steht – kodiert für das Gerät, im Kommentar, und in einem Ausgabetext – ist unschön weil fehleranfällig. Wenn man das ändern will, muss man immer daran denken es überall gleich zu ändern.

Man sollte GUI und Programmlogik besser trennen. Das die Einzelmessung sowohl von einem Button aus aktiviert werden kann, und damit der Rückgabewert ins leere geht, als auch von der Messreihenfunktion als Funktion aufgerufen wird um an den Rückgabewert zu kommen, ist unsauber.

Den Tranceiver modelliert man am besten mit einer eigenen Klasse die komplett unabhängig von der GUI ist.

Bei den nackten ``except``\s die Sirius3 ja schon angesprochen hat, kann man mindestens in den GUI-Rückrufen einfach ein ebenfalls nacktes ``raise`` setzen. Das bricht das Programm nicht ab, weil die GUI-Hauptschleife diese Ausnahmen behandelt in dem sie ausgegeben, aber ansonsten ignoriert werden. Aber man hat zur Fehlersuche dann wenigstens die Ausnahme samt Traceback zum nachlesen.

In Python legt man keine Listen mit ”Dummy”-Werten an die dann der Reihe nach mit den tatsächlichen Werten initialisiert werden, sondern man erstellt eine leere Liste und hängt an die dann die Werte an die da drin sein sollen.

`times` und `xwerte` enthalten genau die gleichen Werte.

Es macht keinen Sinn aus dem `range()`-Objekt bei der Berechnung von `times` tatsächlich eine Liste zu machen.

Rückrufe in GUIs dürfen nur kurz etwas machen und müssen dann möglichst zügig wieder die Kontrolle an die GUI-Hauptschleife zurückgeben von der sie aufgerufen wurden. Das heisst weder `sleep()` noch blockierende Aufrufe wie das Anzeigen eines Plots kann man dort machen. Solange die Rückruffunktion blockiert, friert die GUI ein. Und das ist nicht nur ein Schönheitsproblem: Wenn die Tk-Hauptschleife nicht läuft, bedient die auch die Hauptschleife vom Betriebssystem nicht mehr, was auf einigen Betriebssystemen dann dazu führen kann, das der Benutzer informiert wird, dass die Anwendung nicht mehr reagiert und ob sie vom System beendet werden soll.

Das mit dem `sleep()` löst man in Tk entweder über die `after()`-Methode oder man muss sich mit dem `threading`-Modul herum schlagen – *und* der `after()`-Methode, denn aus einem anderen Thread heraus als dem in dem die Tk-Hauptschleife läuft, darf man nichts an der GUI verändern. Also verwendet man üblicherweise eine `Queue` zwischen den beiden Threads und fragt regelmässig mit `after()` den Inhalt ab.

Die Werte die die Rückruffunktionen brauchen kann man hier noch mit `functools.partial()` binden, aber schön ist anders. Für jedes nicht-triviale GUI-Programm braucht man objektorientierte Programmierung (OOP).

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
"""Programm zur Steuerung des Transceivers Elad FDM-Duo.

Das Programm kann:

1. Frequenz setzen. OK.
2. Die Modulationsart setzen. OK.
3. Das S-Meter auslesen. OK.
4. Verlauf der Messwerte des S-Meters grafisch darstellen. OK.

Ich habe dieses Programm nicht selbst geschrieben,sondern von der University
Reutlingen Fakultät TEC:Messtechnik mit Python heruntergeladen und geändert. Es
war ursprünglich ein Programm zum Auslesen von Daten eines Vielfachmessgerätes.
"""
import tkinter as tk
from functools import partial
from math import nan as NAN
from time import sleep

import matplotlib.pyplot as plt
import serial

__date__ = '2019-04-29'
__author__ = 'L. Holzer DF8LHO'


def open_port(port, port_var, com_status_label):
    try:
        port.baudrate = 38400
        port.port = port_var.get()
        port.timeout = 2        
        port.dtr = True  # Für Stromversorgung RS-232 Interface.
        port.rts = False
        port.open()
        com_status_label.configure(text='COM-Verbindung OK')
        port.reset_input_buffer()
    except:
        com_status_label.configure(text='Keine COM-Verbindung')
        raise


def do_single_measurement(port, com_status_label, value_label):
    # 
    # TODO Programmlogik und GUI sauber trennen.
    # 
    value = NAN
    try:
        # 
        # TODO Das die Frequenz einmal hier in dem Wert und einmal weiter
        #   unten in einem Text hart kodiert ist, ist unschön weil
        #   fehleranfällig.
        # 
        port.write(b'FA00000023400;')  # Frequenz setzen (23,4 Khz).
        port.write(b'MD3;')  # Einstellung CW+.
        port.write(b'SMO;')  # Einstellung S-Meter auslesen.
        result = port.read(8)
        if len(result) == 8:
            value = float(result[5:7])
            com_status_label.configure(text='Elad FDM-Duo OK')
        else:
            com_status_label.configure(text='Daten DMM inkonsistent')
    except:
        com_status_label.configure(text='Irgend ein Problem')
        raise
        
    value_label.configure(text=value)
    return value
    

def do_multiple_measurements(
    port, measurements_count_var, period_var, com_status_label, value_label
):
    # 
    # FIXME Das hier ist blockierend und muss in einer GUI anders gelöst werden.
    # 
    results = list()
    times = [i * period_var.get() for i in range(measurements_count_var.get())]
    for _ in times:
        results.append(
            do_single_measurement(port, com_status_label, value_label)
        )
        sleep(period_var.get())

    plt.plot(times, results)
    plt.xlabel('Stunden')
    plt.ylabel('S-Meter Anzeige')
    plt.xticks(times)
    plt.title('Elad FDM-DUO     23.400 Khz DHO38 Germany ')
    plt.show()
       

def main():
    port = serial.Serial()

    root = tk.Tk()
    root.title('Elad FDM-Duo')

    port_var = tk.StringVar(value='COM11')
    measurements_count_var = tk.IntVar()
    measurements_count_var.set(20)
    period_var = tk.IntVar()
    period_var.set(1)

    com_status_label = tk.Label(
        root, text='Keine COM-Verbindung', width=20, bg='light blue'
    )
    com_status_label.grid(row=1, columnspan=3, sticky=tk.EW)

    tk.Label(root, text='COM-Port:').grid(row=2, column=0, sticky=tk.E)
    tk.Entry(
        root, textvariable=port_var, width=8
    ).grid(row=2, column=1, sticky=tk.W)

    tk.Label(
        root, text='# Messungen / Periode (h):'
    ).grid(row=3, column=0, sticky=tk.E)
    tk.Entry(
        root, textvariable=measurements_count_var, width=8
    ).grid(row=3, column=1, sticky=tk.W)
    
    tk.Entry(
        root, textvariable=period_var, width=8
    ).grid(row=3, column=2, sticky=tk.W)

    single_measurement_button = tk.Button(root, text='Parameter setzen')
    single_measurement_button.grid(row=4, column=0, sticky=tk.E)
    multi_measurement_button = tk.Button(root, text='Messreihe+Plot')
    multi_measurement_button.grid(row=4, column=1, sticky=tk.W)

    tk.Label(
        root, text='Aktueller Messwert:'
    ).grid(row=5, column=0, sticky=tk.E)
    
    value_label = tk.Label(root, text='-----', width=20)
    value_label.grid(row=5, column=1, sticky=tk.W)

    tk.Button(
        root,
        text='COM-Schnittstelle verbinden',
        command=partial(open_port, port, port_var, com_status_label),
        bg='light blue',
    ).grid(row=6, columnspan=3, sticky=tk.EW)

    single_measurement_button['command'] = partial(
        do_single_measurement, port, com_status_label, value_label
    )
    multi_measurement_button['command'] = partial(
        do_multiple_measurements,
        port,
        measurements_count_var,
        period_var,
        com_status_label,
        value_label,
    )
    root.mainloop()


if __name__ == '__main__':
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

Hallo blackjeck,
auch dir recht herzlichen Dank für die sehr informativen und ausführlichen Erklärungen und das umarbeiten des Programms.
Es läuft ohne Probleme.Ich werde die Informationen in den nächsten Tagen (Wochen und Monaten )durcharbeiten soweit ich das als Anfänger verstehe. :roll:
Du hast die Anzeige der Messpunkte ausgebaut ,was mann sicherlich nicht braucht. Wenn ich aber zb. 20 Stunden (oder Wochen )messe dann habe ich da die Restlaufzeit gesehen.Also wenn man das wieder einbauen kann oder eine andere Anzeige dann hätte das etwas. :)
Ich habe mich als Anfänger an ein Projekt gewagt das mit um Hausnummern zu hoch ist.
Aber wenn ihr alle Fleißig mithelft kann daraus ein richtig gutes Programm für den Amateurfunk werden. :)

Ich habe mal noch eine ToDO Liste für mich gemacht.
1. S-Meter werte richtig anzeigen.
2. Gitterlinien richtig machen.
3.Messzeiten besser einstellbar machen ( Sekunden, Minuten,Stunden,Wochen)
4.Einbau eines Datum -Zeitstempels um später die Plotts genau zuordnen zu können.
5. Erweitern des Eingabefensters ( Frequenz einstellen und automatisch in die Überschrift übernehmen).

Es werden sicherlich noch weitere Wünsche auftreten.

Ich wünsche euch allen ein schönes Wochenende.
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

So jetzt habe ich mal ein Dictionarie geschrieben.
Es soll mir die Ausgelesenen Messwerte in echte S-Werte bzw.Absolute Pegel anzeigen.
Das Einbinden klappt aber nicht.Ich brauch mal wieder Hilfe. :roll:

Code: Alles auswählen

[Alles Auswählen]
#Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
#Type "help", "copyright", "credits" or "license()" for more information.
#!/usr/bin/env python3
"""Programm zur Steuerung des Transceivers Elad FDM-Duo.

Das Programm kann:

1. Frequenz setzen. OK.
2. Die Modulationsart setzen. OK.
3. Das S-Meter auslesen. OK.
4. Verlauf der Messwerte des S-Meters grafisch darstellen. OK.

Ich habe dieses Programm nicht selbst geschrieben,sondern von der University
Reutlingen Fakultät TEC:Messtechnik mit Python heruntergeladen und geändert. Es
war ursprünglich ein Programm zum Auslesen von Daten eines Vielfachmessgerätes.
"""
import tkinter as tk
from functools import partial
from math import nan as NAN
from time import sleep

import matplotlib.pyplot as plt
import serial

__date__ = '2019-04-29'
__author__ = 'L. Holzer DF8LHO'


def open_port(port, port_var, com_status_label):
    try:
        port.baudrate = 38400
        port.port = port_var.get()
        port.timeout = 2        
        port.dtr = True  # Für Stromversorgung RS-232 Interface.
        port.rts = False
        port.open()
        com_status_label.configure(text='COM-Verbindung OK')
        port.reset_input_buffer()
    except:
        com_status_label.configure(text='Keine COM-Verbindung')
        raise


def do_single_measurement(port, com_status_label, value_label):
    # 
    # TODO Programmlogik und GUI sauber trennen.
    # 
    value = NAN
    try:
        # 
        # TODO Das die Frequenz einmal hier in dem Wert und einmal weiter
        #   unten in einem Text hart kodiert ist, ist unschön weil
        #   fehleranfällig.
        # 
        port.write(b'FA00000023400;')  # Frequenz setzen (23,4 Khz).
        port.write(b'MD3;')  # Einstellung CW+.
        port.write(b'SMO;')  # Einstellung S-Meter auslesen.
        result = port.read(8)
        if len(result) == 8:
            value = float(result[5:7])

#Umsetzen des Ausgelesen Wertes in S-Meter werte und absolute Pegel mittels Dictionary.           
value = {"00" : "S0 -135dBm",
         "02" : "S1 -121dBm",
         "03" : "S2 -115dBm",
         "04" : "S3 -109dBm",
         "05" : "S4 -103dBm",
         "06" : "S5 -97dBm",
         "08" : "S6 -91dBm",
         "09" : "S7 -85dBm",
         "10" : "S8 -79dBm",
         "11" : "S9 -73dBm",
         "12" : "S9+10 -63dBm",
         "14" : "S9+20 -53dBm",
         "16" : "S9+30 -43dBm",
         "18" : "S9+40 -33dBm",
         "20" : "S9+50 -23dbm",
         "22" : "S9+60 -13dBm"}

#dictionaries {"value":value } 
#print(dictionaries["value"]["09"])

            com_status_label.configure(text='Elad FDM-Duo OK')
        else:
            com_status_label.configure(text='Daten DMM inkonsistent')
    except:
        com_status_label.configure(text='Irgend ein Problem')
        raise
        
    value_label.configure(text=value)
    return value
    

def do_multiple_measurements(
    port, measurements_count_var, period_var, com_status_label, value_label
):
    # 
    # FIXME Das hier ist blockierend und muss in einer GUI anders gelöst werden.
    # 
    
    results = list()
    times = [i * period_var.get() for i in range(measurements_count_var.get())]
    for _ in times:
        results.append(
            do_single_measurement(port, com_status_label, value_label)
        )
        sleep(period_var.get())
    
        
    plt.plot(times, results)
    plt.xlabel('Stunden')
    plt.ylabel('S-Meter Anzeige')
    plt.xticks(times)
    plt.title('Elad FDM-DUO     23.400 Khz DHO38 Germany ')
    plt.show()
       

def main():
    port = serial.Serial()

    root = tk.Tk()
    root.title('Elad FDM-Duo')

    port_var = tk.StringVar(value='COM11')
    measurements_count_var = tk.IntVar()
    measurements_count_var.set(20)
    period_var = tk.IntVar()
    period_var.set(1)

    com_status_label = tk.Label(
        root, text='Keine COM-Verbindung', width=20, bg='light blue'
    )
    com_status_label.grid(row=1, columnspan=3, sticky=tk.EW)

    tk.Label(root, text='COM-Port:').grid(row=2, column=0, sticky=tk.E)
    tk.Entry(
        root, textvariable=port_var, width=8
    ).grid(row=2, column=1, sticky=tk.W)

    tk.Label(
        root, text='# Messungen / Periode (h):'
    ).grid(row=3, column=0, sticky=tk.E)
    tk.Entry(
        root, textvariable=measurements_count_var, width=8
    ).grid(row=3, column=1, sticky=tk.W)
    
    tk.Entry(
        root, textvariable=period_var, width=8
    ).grid(row=3, column=2, sticky=tk.W)

    single_measurement_button = tk.Button(root, text='Parameter setzen')
    single_measurement_button.grid(row=4, column=0, sticky=tk.E)
    multi_measurement_button = tk.Button(root, text='Messreihe+Plot')
    multi_measurement_button.grid(row=4, column=1, sticky=tk.W)

    tk.Label(
        root, text='Aktueller Messwert:'
    ).grid(row=5, column=0, sticky=tk.E)
    
    value_label = tk.Label(root, text='-----', width=20)
    value_label.grid(row=5, column=1, sticky=tk.W)

    tk.Button(
        root,
        text='COM-Schnittstelle verbinden',
        command=partial(open_port, port, port_var, com_status_label),
        bg='light blue',
    ).grid(row=6, columnspan=3, sticky=tk.EW)

    single_measurement_button['command'] = partial(
        do_single_measurement, port, com_status_label, value_label
    )
    multi_measurement_button['command'] = partial(
        do_multiple_measurements,
        port,
        measurements_count_var,
        period_var,
        com_status_label,
        value_label,
    )
    root.mainloop()


if __name__ == '__main__':
    main()
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

Ich bekomme das einfach nicht hin im Programm.
Daher habe ich mir mal ein Testprogramm geschrieben mit zwei Dictionaries.
Das erste zum eingeben von abgespeicherten Frequenzen geht.
Das zweite zum umwandeln des ausgelesen Wertes in S-Meter-Werte bekomm ich einfach nicht hin.
Muss ich den ausgelesen Wert erst zwischenspeichern oder wo liegt das Problem.

CODE: ALLES AUSWÄHLEN
#Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
#Type "help", "copyright", "credits" or "license()" for more information.

# Programm zum setzen der Frequenz und umwandeln der S-Meter werte mittels Dictionarie 27.05.2019

import serial
ser = serial.Serial(
port='COM11',
baudrate = 38400,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout = 2)
key1 = {"0" : b"FA00000011905;",
"2" : b"FA00000014881;",
"3" : b"FA00000016400;",
"4" : b"FA00000017200;",
"5" : b"FA00000018100;",
"6" : b"FA00000018200;",
"7" : b"FA00000018300;",
"8" : b"FA00000018500;",
"9" : b"FA00000019580;",
"10" : b"FA00000019800;",
"11" : b"FA00000020270;",
"12" : b"FA00000020900;",
"13" : b"FA00000022100;",
"14" : b"FA00000023400;",
"15" : b"FA00000026700;",
"16" : b"FA00000029700;",
"17" : b"FA00000033300;",
"18" : b"FA00000037500;",
"19" : b"FA00000040450;",
"20" : b"FA00000060000;"}
ser.write(key1["14"]) # setzen der Frequenz mittels Dictionary
ser.write(b'MD3;') # setzen der Modulationsart
ser.write(b'SMO;') # auslesen des S-Meters
print(ser.readline()) # ausgelesener Wert zb. b'SM00006;'
key2 = {b'SM00000;' : 'S0 -135dBm',
b'SM00002;' : 'S1 -121dBm',
b'SM00003;' : 'S2 -115dBm',
b'SM00004;' : 'S3 -109dBm',
b'SM00005;' : 'S4 -103dBm',
b'SM00006;' : 'S5 -97dBm',
b'SM00008;' : 'S6 -91dBm',
b'SM00009;' : 'S7 -85dBm',
b'SM00010;' : 'S8 -79dBm',
b'SM00011;' : 'S9 -73dBm',
b'SM00012;' : 'S9+10 -63dBm',
b'SM00014;' : 'S9+20 -53dBm',
b'SM00016;' : 'S9+30 -43dBm',
b'SM00018;' : 'S9+40 -33dBm',
b'SM00020;' : 'S9+50 -23dbm',
b'SM00022;' : 'S9+60 -13dBm'}
print(key2[print(ser.readline())])
ser.close()
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, wenn Du einen Wert mehrfach verwenden willst, dann mußt Du ihn "zwischenspeichern". Den Rückgabewert von `print` als Schlüssel zu verwenden, ist aber wenig sinnvoll.
key1 und key2 sind schlechte Variablennamen. Was ist denn der Inhalt der Wörterbücher? Danach solltest Du sie auch benennen.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Beim ersten Wörterbuch ist eventuell auch fraglich ob das eines sein sollte. Mal von der Lücke bei der 1 abgesehen, sind das ja fortlaufend nummerierte Schlüssel (als Zeichenketten?), also vielleicht doch eher eine Liste.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

Hallo Sirius,hallo Blackjack

Ich glaube ich muss meine Fragen genauer stellen.
1. Kann ich die seriell ausgelesenen Daten zb. b'SM00004;' direkt in den Wert 'S3 -109dBm umwandeln und anzeigen?
2. Wenn das erste nicht geht wie kann ich den Wert speichern ,wieder aufrufen und dann umwandeln.

Im vorraus schon mal besten Dank
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie man ein Wörterbuch benutzt, hast Du doch schon an anderer Stelle gezeigt. Du darfst natürlich nicht mehrfach readline aufrufen, denn das erste mal, liest Du die Zeile und dann wartet Dein Gegenüber auf den nächsten Befehl. Müssen die Zeilen, die Du sendest, nicht mit einem Zeile-Ende-Zeichen abgeschlossen werden? Ebenso wird die gelesene Zeile eines enthalten, das Du per strip erst entfernen mußt.
VLF
User
Beiträge: 7
Registriert: Samstag 11. Mai 2019, 13:15

]So mein Testprogramm geht.
Er liest jetzt aus meinem Emfänger die Werte zb. S8 -79dBm aus.

Jetzt muss ich das ganze moch im Hauptprogramm ändern.
Das wird auch noch eine harte Nuss.

Wie lade ich denn einen Code hier richtig hoch ? Bin ich zu blöde?

Code: Alles auswählen

[/code

#Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
#Type "help", "copyright", "credits" or "license()" for more information.

# Programm zum setzen der Frequenz und umwandeln der S-Meter werte mittels Dictionarie   27.05.2019

import serial 
ser = serial.Serial(
    port='COM11',
    baudrate = 38400,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout = 2)
key1 = {"0" : b"FA00000011905;",
        "2" : b"FA00000014881;",
        "3" : b"FA00000016400;",
        "4" : b"FA00000017200;",
        "5" : b"FA00000018100;",
        "6" : b"FA00000018200;",
        "7" : b"FA00000018300;",
        "8" : b"FA00000018500;",
        "9" : b"FA00000019580;",
       "10" : b"FA00000019800;",
       "11" : b"FA00000020270;",
       "12" : b"FA00000020900;",
       "13" : b"FA00000022100;",
       "14" : b"FA00000023400;",
       "15" : b"FA00000026700;",
       "16" : b"FA00000029700;",
       "17" : b"FA00000033300;",
       "18" : b"FA00000037500;",
       "19" : b"FA00000040450;",
       "20" : b"FA00000060000;"}
ser.write(key1["14"])    # setzen der Frequenz mittels Dictionary  
ser.write(b'MD3;')       # setzen der Modulationsart    
ser.write(b'SMO;')       # auslesen des S-Meters

value_1 = (ser.readline().decode("ascii"))
#print(value_1)      

key2 = {'SM00000;' : 'S0   -135dBm',
        'SM00002;' : 'S1   -121dBm',
        'SM00003;' : 'S2   -115dBm',
        'SM00004;' : 'S3   -109dBm',
        'SM00005;' : 'S4   -103dBm',
        'SM00006;' : 'S5    -97dBm',
        'SM00008;' : 'S6    -91dBm',  
        'SM00009;' : 'S7    -85dBm',
        'SM00010;' : 'S8    -79dBm',
        'SM00011;' : 'S9    -73dBm',
        'SM00012;' : 'S9+10 -63dBm',
        'SM00014;' : 'S9+20 -53dBm',
        'SM00016;' : 'S9+30 -43dBm',
        'SM00018;' : 'S9+40 -33dBm',
        'SM00020;' : 'S9+50 -23dbm',
        'SM00022;' : 'S9+60 -13dBm'}
value=(key2[value_1]) 
#print(value_1) 
print(value)     
ser.close()
Antworten