Flytande medelvärde i avr

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Flytande medelvärde i avr

Inlägg av Nisse »

Jag roar mig med att räkna pulserna från ett GM-rör med hjälp av en Atmega88.
Det jag gör är att visa antalet pulser per sekund samt räkna fram och visa ett flytande medelvärde över 5 sekunder.

Medelvärdesbildningen funkar enligt följande:

temp = medel * 4
temp = temp + nuvärde
medel = temp / 5

Min kod:

Kod: Markera allt

rol		avg
rol		avg
add		avg,	cps 
mov		dd8u,	avg
ldi		dv8u,	5
rcall	div8u
mov		avg,	dres8u
koden för att dividera är tagen ur Atmels Appnote 200

Kod: Markera allt

div8u:
sub		drem8u,	drem8u
ldi		temp,	9
d8u_1:
rol		dd8u
dec		temp
brne	d8u_2
ret
d8u_2:
rol		drem8u	
sub		drem8u,	dv8u
brcc	d8u_3
add		drem8u,	dv8u
clc
rjmp	d8u_1
d8u_3:
sec
rjmp	d8u_1
Min fråga är om det finns något annat/enklare sätt att uppnå samma sak?


Och för att det verkar vara obligatoriskt med bilder :lol:
Bild

Dippen i mitten på diagrammet visar när jag tog bort strålkällan från GM-röret. Det är en svag strålkälla och GM-röret går på knapp halvfart.
Bild
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

Ett enkelt sätt där du slipper division och multiplikation är att ha en buffer som är en 2 potens lång 2^N (t.ex. 4, 8, 16 osv).

Summerar du sedan ihop värdena och kaller det S och skiftar summan N steg så får du medelvärdet eftersom du då i praktiken dividerar men antalet värden.

För att sedan uppdatera med ett nytt värde drar du ifrån det äldsta värdet i buffern och ersätter med det nya som också läggs till summan.

På så vis kan du göra ett mycket effektivt glidande medelvärde.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Jo, det där med en buffert är något jag också funderat på. Men det blir inte riktigt som jag vill ha det. Visst det är väl igentligen det rätta sättet att få fram ett riktigt flytande medelvärde. Men det jag är ute efter är just det som man får med funktionen:

temp = medel * 4
temp = temp + nuvärde
medel = temp / 5

Har inget bra namn på det. Men det är ju ett annat typ av medelvärdesbildande.
Den metoden ger ett snabbare svar på plötsliga förändringar än vad ett äkta glidande medelvärde gör.

Mvh
Nisse
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Eller vänta nu..... Tänker jag helt fel :? Får exprimentera lite.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

En buffert blir ju inte så tokigt heller. Men jag gillar inte riktigt hur plötsliga spikar påverkar medelvärdet över en längre tid än med den andra metoden. Men å andra sidan få jag väl köra lite längre buffer. Behöver en svarstid runt 8-10 sekunder. Det borde också vara tillräckligt för att jämna ut den statistiska variationen från strålkällan.

Mvh
Nisse

Bild
Bild
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Uj vad jag håller låda alldeles själv :lol: Kanske är att likställa med att tänka högt....

Nu är bufferten 8 värden stor.
Resultatet blir inte så tokigt. Kör med det tillsvidare.
Nu måste jag börja fundera kring kompensering för strålkällans halveringstid. (kanske inte just den strålkälla jag testar med nu då den har 465 år halveringstid).

Kod: Markera allt

.equ	avg1 =	0x0100
.equ	avg2 =	0x0101
.equ	avg3 =	0x0102
.equ	avg4 =	0x0103
.equ	avg5 =	0x0104
.equ	avg6 =	0x0105
.equ	avg7 =	0x0106
.equ	avg8 =	0x0107

Kod: Markera allt

lds		temp,	avg2
sts		avg1,	temp
lds		temp,	avg3
sts		avg2,	temp
lds		temp,	avg4
sts		avg3,	temp
lds		temp,	avg5
sts		avg4,	temp
lds		temp,	avg6
sts		avg5,	temp
lds		temp,	avg7
sts		avg6,	temp
lds		temp,	avg8
sts		avg7,	temp
sts		avg8,	cps

ldi		avg,	0

lds		temp,	avg1
add		avg,	temp
lds		temp,	avg2
add		avg,	temp
lds		temp,	avg3
add		avg,	temp
lds		temp,	avg4
add		avg,	temp
lds		temp,	avg5
add		avg,	temp
lds		temp,	avg6
add		avg,	temp
lds		temp,	avg7
add		avg,	temp
lds		temp,	avg8
add		avg,	temp

ror		avg
clc
ror		avg
clc
ror		avg
clc
Bild
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

Nisse, namnet på din metod är rekursivt medelvärde. Nackdelen med den är att felaktiga värden lever kvar mycket längre i medelvärdet, medan med buffern så lever det bara kvar så länge som buffern är lång.

Buffermetoden behöver ju bara en addition, en subtraktion och några bitskift (för division med 2^N) oavsett hur lång buffern är.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

En addition... Hmmmm, fundera, fundera... Aha, nu är jag med på hur du menar i ditt första inlägg.

Då skall jag lura ut hur man håller ordning på vilket som är det äldsta värdet utan att flytta runt dom som jag gör nu :?
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Kanske en ringbuffert, där du flyttar runt pekaren till det äldsta/nyaste elementet snarare än elementen själva?

Jag funderar på din metod att läsa av GM-röret. Är det en fördel att verkligen mäta antalet utslag per sekund jämfört med frekvensen (perioden) på utslagen?
Senast redigerad av $tiff 21 oktober 2006, 14:47:18, redigerad totalt 1 gång.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Då skall jag lura ut hur man håller ordning på vilket som är det äldsta värdet utan att flytta runt dom som jag gör nu

Skitenkelt... :-)

En circulär buffert med *en* pekare.

Varje nytt värde sätts bara in där pekaren "pekar" och stega sedan fram pekaren inför nästa position.
Om pekeran har passerat "slutet", laddas den bara om med start-adressen.
(Man behöver alltså aldrig "plocka bort" något "äldsta" värde, eftersom det nya värdet ju skrivs in på samma position...)
Och det behövs inte *två* pekare, så klart, eftersom bufferten "sitter ihop" så att säga. Äldsta värdet är samma address dr
det nya värdet ska läggas in...

Tiden (d.v.s antal instruktioner) för att uppdatera bufferten blir i princip den samma oavsett längd på bufferten.
(För att vara exakt, så blir det i snitt *mindre* processortid/nytt värde ju längre bufferten är,
eftersom "end-of-buffer" kommer med sällan... :-) )

Ett sätt att göra summeringen mer effektiv, är att spara föregående summa,
sedan först dra ifrån det äldsta värdet (d.v.s där pekaren pekar!), sedan
uppdatera bufferten och slutligen lägga till det nya värdet till summan.
Man kan se det som att man sparar summan av alla värden mellan mellan
det nya och det äldsta värdet, det finns ju ingen anledning att summera dessa gång på gång... :-)

På så sätt blir summaberäkningen också oberoende av längden på bufferten !

Att ha en statisk buffert där man hela tiden flyttar alla värden från ena änden till den andra är ingen bra lösning
(eller i alla fall ingen *snygg* lösning :-) ).

EDIT : Fanken också, missagde $tiff med en minut, skulle ha skrivit lite kortare... :-)

EDIT2 : Vid en närmare läsning av Andex inlägg, så är det ju i princip samma sak som jag beskriv ovan, men lite "pratigare"... :-)
Ursäkta...
Senast redigerad av sodjan 21 oktober 2006, 14:51:54, redigerad totalt 1 gång.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Är det en fördel att verkligen mäta antalet utslag per sekund jämfört med frekvensen (perioden) på utslagen?

Radioaktiva sönderfall sker helt slumpmässigt, perioden mellan *två* sönderfall säger i princip ingenting om den totala aktiviteten.
Den måste summeras över en (lämplig) mätperiod.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Hoppsan, här tog det fart....

Jag har faktiskt medans ni alla skrev här snickrat ihop just en ringbuffert:

Kod: Markera allt

ld		temp,	X
sub		sum,	temp
st		X+,		cps
add		sum,	cps
cpi		XL,		buff_length
brne	_timer1
ldi		XL,		avg_buff_L
_timer1:
mov		avg,	sum

ror		avg
clc
ror		avg
clc
ror		avg
clc
problemet som det ser ut just nu är att summan liksom aldrig hinner "byggas upp".
Ska ta en klurare på det.

Jösses vad jag lär mig saker idag. Eller snarare återupptäcker kunskaper. Indirekt adressering och liknande är det snart 15 år sedan jag använde och då på en z80.

Tack för alla svaren, låt dom gärna fortsätta komma. Det här är lärorikt.

> Är det en fördel att verkligen mäta antalet utslag per sekund jämfört med frekvensen (perioden) på utslagen?

Precis som sodjan skriver. vanligaste tiden är just 1 sekund, därav enheten CPS = Counts Per Second. Sedan jämnar man ut det med ett medelvärde, men presenterar det fortfarande som CPS.

Mvh
Nils
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Vad har du för intervall för ett mätvärde, summan resp avg ?
Deet ser ut som om du kör med 8-bitars-matte för allt.
Du måste ha en summa som klarar <max värdet> x <buffert storlek>.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Stämmer att jag kör med 8-bitar rakt över. Bufferten är 8 värden lång vilket gör att jag klarar max 32 pulser per sekund. Det är dubbelt så mångs som behövs.

Kanske nästa revision använder scintillationsdetektor och då behöver jag räkna betydligt fler pulser. Men å andra sidan så är den typen av detektor betydligt dyrare. Hmmm, men man kanske skulle plocka fram en och labba lite ändå, de är ju mycket känsligare.... Näää, en sak i taget.

mvh
Nils
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Inlägg av Nisse »

Den smarta lösningen med att spara summan för att kunna dra ifrån det gamla värdet och sedan lägga till det nya ger mig problem.

Summan hinner ju (som jag konstaterat tidigare) aldrig byggas upp till summan av de åtta värdena i bufferten. Summan blir alltså alltid lika med sista värdet.

Hur kommer jag runt det?

Och för att ni gillar bilder:
(notera den lilla plastpåsen)

Bild
Skriv svar