Zahlenbereich bei Addition

#3278914
Lesenswert?

Hallo

Ich versuche ein Mittelwertfilter zu implementieren und bin mir dabei 
nicht im Klaren, wie gross ich meine Signale machen muss.

Das Eingangssignal kommt von einem ADC und hat eine Breite von 14 Bit. 
Dieses wird danach in einen Integer gewandelt, welcher im Bereich 
-20'000...20000 angelegt ist. "AdcInBufxDS" hat also den Bereich 
-20'000...20000.

Wie kann ich dafür sorgen, dass es keinen Überlauf währen der Addition 
gibt?

Ich könnte natürlich den Bereich des AdcInBufxDS auf +-200000 setzen, 
doch dies scheint mir unschön.

Die Division "/8" ist im Moment eine genügend gute Näherung an "/10" und 
viel einfacher.
1
  FIR_ff : process(ClkxCI, RstxRBI)
2
  begin
3
    if RstxRBI = '0' then
4
      for c in 0 to 9 loop
5
        AdcInBufxDS(c) <= 0;
6
      end loop;
7
    elsif rising_edge(ClkxCI) then
8
      AdcInBufxDS(9) <= AdcInBufxDS(8);
9
      AdcInBufxDS(8) <= AdcInBufxDS(7);
10
      AdcInBufxDS(7) <= AdcInBufxDS(6);
11
      AdcInBufxDS(6) <= AdcInBufxDS(5);
12
      AdcInBufxDS(5) <= AdcInBufxDS(4);
13
      AdcInBufxDS(4) <= AdcInBufxDS(3);
14
      AdcInBufxDS(3) <= AdcInBufxDS(2);
15
      AdcInBufxDS(2) <= AdcInBufxDS(1);
16
      AdcInBufxDS(1) <= AdcInBufxDS(0);
17
      AdcInBufxDS(0) <= to_integer(unsigned(DataInxDI)) - 8192;
18

19
      DataInDecxDO <= NextDataInDecxS;
20
    end if;
21
  end process FIR_ff;
22

23
  FIR_comb : process(AdcInBufxDS)
24
  begin
25
    NextDataInDecxS<= (AdcInBufxDS(9) + AdcInBufxDS(8) + AdcInBufxDS(7) + AdcInBufxDS(6) + AdcInBufxDS(5) + AdcInBufxDS(4) + AdcInBufxDS(3) + AdcInBufxDS(2) + AdcInBufxDS(1) + AdcInBufxDS(0)) /8;
26
  end process FIR_comb;
#3279703
Lesenswert?

Also verstehe ich die korrekt:

Die Rechnung funktioniert auf der Registergrösse der Operanden und nicht 
des Resultats.
Ich muss die "AdcInBufxDS" mit dem grossen Bereich initialisieren, damit 
die Rechnung korrekt funktioniert. Den "NextDataInDecxS" kann ich jedoch 
auf den kleinen Bereich setzen.
#3279731
Lesenswert?

So müsste es gehen:
1
    SumxD           <= AdcInBufxDS(9) + AdcInBufxDS(8) + 
2
                       AdcInBufxDS(7) + AdcInBufxDS(6) + 
3
                       AdcInBufxDS(5) + AdcInBufxDS(4) + 
4
                       AdcInBufxDS(3) + AdcInBufxDS(2) + 
5
                       AdcInBufxDS(1) + AdcInBufxDS(0);
6
    NextDataInDecxS <= SumxD(18 downto 3); -- /8

NextDataInDecxS ist dann wieder ein 16-bit-signed.

Simulieren müsste Aufschluss geben.
#3279735
Lesenswert?

Clemens S. schrieb:
> Moritz A. schrieb:
>> Du kannst schon vor der Addition jeden Wert durch 8 teilen. Dauert zwar
>> ein Moment länger, aber ich glaube nicht dass die Routine hier
>> zeitkritisch ist.
>
> und den rest auch aufsummieren, zum schluss dividieren und dazuzählen,
> dann ist es sogar genau.

Da er achtelt statt zehntelt ist es mit der Genauigkeit hier offenbar 
sowieso nicht so kritisch, daher habe ich den Rest großzügig liegen 
gelassen.
Gast #3279736
Lesenswert?

ähm... Mittelwert ?

Du addiert 10 Zahlen und dividierst durch 8 ??? hä?

Warum nicht 8 Addieren und durch 8 Dividieren. Dann ist es ein 
Mittelwert.


btw... dieser 10fach addieren wird verdammt langsam. Stichwort 
Pipelining.
btw... Division durch acht ist ein Shift, schreib's auch so hin.

gruß
#3279743
Lesenswert?

Moritz A. schrieb:
> Da er achtelt statt zehntelt ist es mit der Genauigkeit hier offenbar
> sowieso nicht so kritisch, daher habe ich den Rest großzügig liegen
> gelassen.

Das eine hat mit dem anderen nichts zu tun. Du verlierst nicht mehr an 
Genauigkeit, wenn Du leicht anders skalierst, falls in der 
darauffolgenden Rechnung die absolute Amplitude keine Rolle spielt.
#3279767
Lesenswert?

Genau, die Division "/8" ist lediglich eine Skalierung, da es bei der 
späteren Berechnung nicht wirklich auf die absolute Amplitude ankommt 
ist dies vertretbar. Und die Division durch 8 (Ja ich schreibe Division, 
weil es ja nicht wirklich eins reiner shift ist, wegen des signed) ist 
viel einfacher als eine Division durch 10.

Bezüglich Verarbeitungszeit sehe ich überhaupt kein Problem. Ein 
FIR-Filter (also hier das Moving Average Filter) hat nunmal eine 
Verzögerung der "halben Ordnung" dies kann auch durch ein Piplining 
nicht verbessert werden. Oder sehe ich dies etwa falsch?

Die Ungenauigkeit durch die Division ist vertretbar und macht eine 
spätere Implementation auf einem günstigen ASIC-CPLD einfacher.
#3279840
Lesenswert?

R. K. schrieb:
> Oder sehe ich dies etwa falsch?

Der Pipelining-Aufwand kommt dann noch oben drauf. Eine "billigere" 
Lösung wäre die Summe zu speichern und jeweils den neuen Wert addieren 
und den ältesten zu subtrahieren. Du kommst so mit 2 Addern aus 
(vergibst Dir aber die Möglichkeit, die Taps in Zukunft zu gewichten).

> Und die Division durch 8 ist viel einfacher als eine Division durch 10.

Falls Du es trotzdem mal genau brauchst: Division durch eine Konstante 
ist nicht so schlimm, das kannst Du immer auf eine Division durch 2^N 
und eine Multiplikation reduzieren...
Moderator (Firma: Titel) Persönliche Seite #3280018
Lesenswert?

Moritz A. schrieb:
> Du kannst schon vor der Addition jeden Wert durch 8 teilen. Dauert zwar
> ein Moment länger, aber ich glaube nicht dass die Routine hier
> zeitkritisch ist.
In Hardware ist eine Division durch eine Zweierpotenz einfach ein 
festes Umverdrahten. Das braucht überhaupt keine "Rechenzeit"...

Eine Division durch 10 könnte man auch mit einer Multiplikation mit 410 
und anschließendem Abschneiden der unteren 12 Bits hinreichend genau (1 
Promille Fehler) hinbekommen...
Gast #3280035
Lesenswert?

Lothar Miller schrieb:
> Eine Division durch 10 könnte man auch mit einer Multiplikation mit 410
> und anschließendem Abschneiden der unteren 12 Bits hinreichend genau (1
> Promille Fehler) hinbekommen...

Wo kriegt man eigentlich solche Näherungen her? Reines "Trickreich 
denken" oder gibt es da eine Übersicht über gängige Methoden?

Ich muß gerade an "fast inverse square root" 
(http://en.wikipedia.org/wiki/Fast_inverse_square_root) denken.
#3280044
Lesenswert?

Nicolas S. schrieb:
> Wo kriegt man eigentlich solche Näherungen her?

Ist überhaupt kein Voodoo, Du willst durch 10 teilen, sprich mit 0.1 
multiplizieren. Jetzt gehst Du wie folgt vor:

1. Multiplizieren mit 2^N * 0.1
2. Weglassen der hintersten N Bits (i.e. teilen durch 2^N)

Den Fehler errechnest Du zu

3. Err = 0.1 / (ganzzahlgerundet(2^N*0.1) / 2^N)

So und jetzt musst Du nur noch N bestimmen, so klein wie möglich 
(Aufwand), so gross wie nötig (Fehler).

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren