Seite 1 von 1

Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 09:03
von LisaG.
Hallo,

ich habe ein etwas seltsames Problem. Ich möchte einen Kondensatorwert (c) anhand eines übergebenen Wertes (cload) berechnen. Der berechnete Wert soll mit einer Liste von Werten (self.basevalues) verglichen werden. Ausgewählt wird dann schließlich der Wert der am nähsten dranliegt.

Soviel zur Theorie. Wenn in ich dem Programm einen Wert cload von 19 übergebe, das Programm also wie folgt starte: "python programm.py -l 19", dann erhalte ich 30 pF als Wert den ich einsetzen soll und nicht 27 pF. Warum?

Liebe Grüße

Lisa

Code: Alles auswählen

#!/usr/bin/python

import argparse
import sys

class crystal():
	def __init__(self, cload, cstray):
		self.loadcap = cload   # The crystal's load capacitance (see datasheet)
		self.straycap = cstray # Stray capacitance on the PCB (approximately 5 pF)
		self.basevalues = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8,
				   2.0, 2.2, 2.4, 2.7,
				   3.0, 3.3, 3.6, 3.9, 
				   4.3, 4.7,
				   5.1, 5.6,
				   6.2, 6.8,
				   7.5,
				   8.2,
				   9.1]

	def calc(self):
		c = 2*(self.loadcap - self.straycap)
		return c

	def getFactor(self, c):
		if (c/10000 > 1):
			sys.exit('Not a realistic load capacitance.')
		elif (c/1000 >= 1):
			factor = 1000
		elif (c/100 >= 1):
			factor = 100
		elif (c/10 >= 1):
			factor = 10
		else:
			factor = 1
		return factor

	def fit(self, c):
		print c
		factor = self.getFactor(c)
		cbase = c/factor
		print 'cbase: %s' % cbase # DEDUG
		i = 0

		while 1:
			print 'i: %s' % i # DEDUG
			print 'cbase + i: %s' % (cbase + i) # DEDUG
			print 'cbase - i: %s' % (cbase - i)  # DEDUG
			if (cbase + i) in self.basevalues:
				return ((cbase + i) * factor) 
			if (cbase - i) in self.basevalues:
				return ((cbase - i) * factor)
			else:
				i += 0.1
		

if __name__ == '__main__':

        parser = argparse.ArgumentParser()
        parser.add_argument('-s', '--straycap', action='store', 
                            help='Stray capacitance in pF, standard is 5 pF.')
        parser.add_argument('-l', '--loadcap', action='store', 
                            help='Load capacitance in pF.')
        parser.add_argument('-d', '--different', action='store_true', 
                            help='Use different caps for C1 and C2.')

	results = parser.parse_args()

	if results.straycap:
		cstray = results.straycap
	else:
		cstray = 5.0
	if not results.loadcap:
		parser.print_help()
		sys.exit()

	if float(results.loadcap) <= cstray:
		sys.exit('Error: Check the crystal\'s datasheet for the right load capacitance.')
		
	mycrystal = crystal(float(results.loadcap), float(cstray))
	ccalc = mycrystal.calc()
	cfit = mycrystal.fit(ccalc)
	print "Calculated capacitor value is %s pF, use %s pF." % (ccalc, cfit)


Re: Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 09:24
von Barabbas
Hallo,

der Fehler steckt in der Methode "fit()". Hier mal eine Variante:

Code: Alles auswählen

        def fit(self, c):
                factor = self.getFactor(c)
                cbase = c/factor
                return min(self.basevalues, key=lambda x:abs(x-cbase))*factor
Besten Gruß,

brb

Re: Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 09:33
von LisaG.
Hallo Barabbas,

vielen Dank für deine Antwort, deine Version funktioniert super.

Ich wüsste trotzdem gerne warum meine Version nicht funktioniert.

Kann jemand helfen?

Liebe Grüße

Lisa

Re: Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 09:44
von BlackJack
@LisaG.: Ich würde auf Fliesskommazahlen als Ursache tippen. Wenn Du zum Beispiel 10 mal 0.1 aufaddierst, kommt da leider nicht 1 heraus. Denn 0.1 ist etwas grösser als das "echte" 1/10, die Summe von 10 davon ist dann allerdings wiederum etwas *kleiner* als die Summe von "echten" zehn 1/10:

Code: Alles auswählen

In [615]: 0.1
Out[615]: 0.10000000000000001

In [616]: sum(itertools.repeat(0.1, 10))
Out[616]: 0.99999999999999989
Die Freuden von Fliesskommadarstellungen in Rechnern. :-) Du kannst Fliesskommazahlen deswegen zum Beispiel nicht sicher auf Gleichheit testen:

Code: Alles auswählen

In [617]: sum(itertools.repeat(0.1, 10)) == 1
Out[617]: False

In [618]: sum(itertools.repeat(0.1, 10)) == 10 * 0.1
Out[618]: False
Die Dokumentation von Python hat etwas zu dem Thema: Floating Point Arithmetic: Issues and Limitations. Und dann gibt es zum Beispiel noch diese Seite hier: http://floating-point-gui.de/

Re: Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 10:18
von LisaG.
:-) Vielen Dank.

Gruß

Lisa

Re: Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 10:37
von BlackJack
@LisaG.: Allgemein noch zum Programm: `argparse` sollte sowohl Defaultwerte kennen, als auch das Umwandeln von Argumenten in andere Datentypen als `str` für Dich erledigen können. Mit dem Vorteil das Du Dich dann nicht mehr um eine entsprechende Fehlermeldung für den Benutzer kümmern musst, wenn der etwas eingibt, was keine Zahl ist.

Ich persönlich halte bei Kommandozeilen-APIs ja nichts von zwingend erforderlichen Optionen. Das ist ein Widerspruch in sich -- entweder es ist eine Option und damit optional, oder man muss es angeben. ``--loadcap`` würde ich dementsprechend als Argument und nicht als Option parsen lassen.

Anstelle von `sys.exit()` kann man im Hauptprogramm die `error()`-Methode vom `ArgumentParser` verwenden. Ausserhalb eines Hauptprogramms beziehungsweise der Benutzerinteraktion hat `sys.exit()` nichts verloren. Programmlogik die das Programm abbricht, kann man nicht verwenden wenn man etwas anderes als einen Programmabbruch machen möchte, sollte ein Fehler auftreten.

Der Quelltext hält sich nicht an die Empfehlungen in PEP 8 -- Style Guide for Python Code. Wenn Du zum Beispiel die Klasse mit einem Grossbuchstaben beginnen lässt, dann brauchst Du nicht so inhaltslose Vorsilben wie `my` um ein Exemplar davon zu benennen.

Allerdings sehe ich hier sowieso nicht warum diese Funktionen in einer Klasse stecken müssen. Die Klasse macht aus meiner Sicht keinen Sinn. Das hätte man mit Funktionen genau so gut ausdrücken können. Keine der drei Funktionen in der Klasse benutzt gemeinsame Daten mit einer der anderen Funktionen. `getFactor()` benutzt sogar gar keine Daten aus der Klasse, ist also wirklich nur rein formal eine Methode.

`getFactor()` kann man mit Hilfe von `math.log10()` wahrscheinlich kürzer beziehungsweise "geschlossener" schreiben:

Code: Alles auswählen

def get_factor(capacity):
    if not (0 <= capacity <= 10**5):
        raise ValueError('Unrealistic load capacitance.')
    try:
        result = 10**math.ceil(math.log10(capacity))
    except ValueError:
        result = 1      # In case of capacity == 0.
    return result
Wobei ich mich Frage ob die Obergrenze wirklich in der Funktion festgelegt werden sollte. Wären das ganz grundsätzlich unrealistische Werte mit denen man *niemals* rechnen würde, oder dient das hier nur dazu Fehleingaben vom Benutzer abzufangen?

Re: Problem mit einer Liste von Werten

Verfasst: Mittwoch 16. März 2011, 12:54
von LisaG.
Hi BlackJack,

vielen Dank für deine Anmerkungen, ich bin noch Anfänger wie du vielleicht bemerkt hast, deshalb nehme ich deine Tipps sehr gerne zu Herzen. Die Lastkapazität liegt normalerweise im Bereich 15 pF, oder 22 pF. Alles weit darunter oder darüber wäre also eher unrealistisch.

LG

Lisa