Forum: Digitale Signalverarbeitung / DSP / Machine Learning Digitaler Lock-In-Verstärker. Wo wieviel Bit?


von Gerald G. (gerald_g)


Lesenswert?

Hallo,

ich möchte aus einem Signal die Amplitude und Phase der Sinusschwingung 
bekommen. Hierfür programmiere ich ein Terasic DE4 Board mit einem 
Stratix GX IV FPGA.
Der Vorgang ist im groben klar:
Nutze den 50 MSPS ADC und nehme das Signal mit 14bit auf. Multipliziere 
den Wert mit dem Sinus/Cosinus des Referenzsignals.
Tiefpassfiltere (IIR) und dezimiere (FIR) das Signal (jeweils für 
Sinus(Cosinus). Wiederhole letzte Schritte so oft wie nötig.

Das grobe habe ich gänzlich verstanden, nur bei den Feinheiten hapert 
es.
Ich messe das Signal mit 14bit. Dies entspricht 2Volt Peak2Peak.
Mit wieviel Bit rechne ich weiter? Das Sinussignal erzeuge ich im FPGA. 
Dieses gebe ich auch auf einen 14Bit DAC. Deshalb liegt es ja nahe den 
Sinus auch mit 14Bit zu erzeugen und zu benutzen. Nach dem 
Multiplizieren habe ich ja bereits 28 Bit. Wie weit macht es Sinn hier 
zu runden? Die DSP Slices können 18Bitx18Bit multiplizieren. Da ich 
danach wieder multiplizieren muss (Filter) wären 18 Bit gut. Ich kann 
alerdings nicht abschätzen wie genau gerundet werden muss.
Bei den nachfolgenden Filtern weiß ich das leider auch nicht. Gut, beim 
IIR Tiefpass würde ich einfach mit 18Bit Koeffizienten multiplizieren. 
Doch beim Decimator würde ich sogar 1.5Bit an Genauigkeit hinzugewinnen.

Also konkret: Wieviel Bit nehme ich bei den Koeffizienten (einmal bei 
Sinus, einmal bei den Filtern) und wo Runde ich am besten, um am 
wenigsten Verlust der Genauigkeit zu haben (bzw. wo lohnt es sich mit 
mehr Bit weiter zu rechnen).

Danke schon mal.

Hier ist schonmal der VHDL wie er im Prinzip funktioniert:
1
library IEEE;
2
use IEEE.std_logic_1164.ALL;
3
use IEEE.numeric_std.ALL;
4
5
entity lockin is 
6
port(  
7
  clock_in : in std_logic;
8
  enable : in std_logic;
9
  reset : in std_logic;
10
  frequency_in : in std_logic_vector(31 downto 0);
11
  adc_in : in std_logic_vector (13 downto 0);
12
  sinus_in : in std_logic_vector (13 downto 0);
13
  cosinus_in : in std_logic_vector (13 downto 0);
14
  clock_out : out std_logic;
15
  I_out : out std_logic_vector (17 downto 0);
16
  Q_out : out std_logic_vector (17 downto 0)
17
);
18
end lockin;
19
20
architecture behave of lockin is
21
22
component iir_lp
23
port(
24
      clk                             :   IN    std_logic; 
25
         clk_enable                      :   IN    std_logic; 
26
         reset                           :   IN    std_logic; 
27
         filter_in                       :   IN    std_logic_vector(17 DOWNTO 0); -- sfix18_En17
28
         filter_out                      :   OUT   std_logic_vector(17 DOWNTO 0)  -- sfix18_En12
29
         );
30
END component;
31
32
component deci
33
   PORT( clk                             :   IN    std_logic; 
34
         clk_enable                      :   IN    std_logic; 
35
         reset                           :   IN    std_logic; 
36
         filter_in                       :   IN    std_logic_vector(17 DOWNTO 0); -- sfix18_En17
37
         filter_out                      :   OUT   std_logic_vector(17 DOWNTO 0)  -- sfix18_En9
38
         );
39
40
END component;
41
42
43
component demodulate --get sin and coine in and multiply by adc value
44
generic ( sine_width : integer :=14);
45
port (--clock_in : in std_logic;
46
    fromadc_in : in Std_logic_vector (13 downto 0); 
47
    sine_in : in std_logic_vector (sine_width -1 downto 0); -- as 2s complement
48
    cosine_in : in std_logic_vector (sine_width -1 downto 0);
49
    output_i : out std_logic_vector (17 downto 0); --in*cos
50
    output_q : out std_logic_vector (17 downto 0) --in*sin
51
);
52
end component;
53
54
--signal adc_value : std_logic_vector (13 downto 0);
55
--signal sine_value : std_logic_vector (sine_width -1 downto 0);
56
--signal cosine_value : std_logic_vector (sine_width -1 downto 0);
57
signal multiplied_sine : std_logic_vector (17 downto 0);
58
signal multiplied_cosine : std_logic_vector (17 downto 0);
59
signal filtered1_sine : std_logic_vector (17 downto 0);
60
signal filtered1_cosine : std_logic_vector (17 downto 0);
61
signal filtered2_sine : std_logic_vector (17 downto 0);
62
signal filtered2_cosine : std_logic_vector (17 downto 0);
63
signal filtered3_sine : std_logic_vector (17 downto 0);
64
signal filtered3_cosine : std_logic_vector (17 downto 0);
65
signal decimated1_sine : std_logic_vector (17 downto 0);
66
signal decimated1_cosine : std_logic_vector (17 downto 0);
67
signal decimated2_sine : std_logic_vector (17 downto 0);
68
signal decimated2_cosine : std_logic_vector (17 downto 0);
69
signal decimated1_clock : std_logic;
70
signal decimated2_clock : std_logic;
71
signal counter1 : integer range 0 to 7;
72
signal counter2 : integer range 0 to 7;
73
--signal filtered2_sine : std_logic_vector (17 downto 0);
74
--signal filtered2_cosine : std_logic_vector (17 downto 0);
75
76
begin
77
d1 : demodulate port map(adc_in, sinus_in, cosinus_in, multiplied_cosine, multiplied_sine);
78
tp1cos : iir_lp port map (clock_in, enable, reset, multiplied_cosine, filtered1_cosine); -- filtered cos
79
tp1sin : iir_lp port map (clock_in, enable, reset, multiplied_sine, filtered1_sine); -- filtered sin
80
deci1 : deci port map (clock_in, enable, reset, filtered1_cosine, decimated1_cosine);
81
deci2 : deci port map (clock_in, enable, reset, filtered1_sine, decimated1_sine);
82
tp2cos : iir_lp port map (decimated1_clock, enable, reset, decimated1_cosine, filtered2_cosine); -- filtered cos
83
tp2sin : iir_lp port map (decimated1_clock, enable, reset, decimated1_sine, filtered2_sine); -- filtered sin
84
deci3 : deci port map (decimated1_clock, enable, reset, filtered2_cosine, decimated2_cosine);
85
deci4 : deci port map (decimated1_clock, enable, reset, filtered2_sine, decimated2_sine);
86
tp3cos : iir_lp port map (decimated2_clock, enable, reset, decimated2_cosine, filtered3_cosine); -- filtered cos
87
tp3sin : iir_lp port map (decimated2_clock, enable, reset, decimated2_sine, filtered3_sine); -- filtered sin
88
89
process (clock_in)
90
begin --process
91
if rising_edge(clock_in) then
92
  if (frequency_in (31 downto 29) = "000") then
93
    I_out <= filtered1_cosine;
94
    Q_out <= filtered1_sine;
95
    clock_out <= clock_in;
96
  elsif (frequency_in (28 downto 26)="000") then
97
    I_out <= filtered2_cosine;
98
    Q_out <= filtered2_sine;
99
    clock_out <= decimated1_clock;
100
  else
101
    I_out <= filtered3_cosine;
102
    Q_out <= filtered3_sine;
103
    clock_out <= decimated2_clock;
104
  end if;
105
end if;
106
end process;
107
108
process (clock_in)
109
begin --process
110
  if rising_edge(clock_in) then
111
    counter1 <=counter1+1;
112
    if (counter1=7) then
113
      counter2 <= counter2+1;
114
      decimated1_clock <= not decimated1_clock;
115
    end if;
116
    if (counter2=7) then
117
      decimated2_clock <= not decimated2_clock;
118
    end if;
119
  end if;
120
end process;
121
122
end behave;

von Fitzebutze (Gast)


Lesenswert?

Hi,

erst mal ne andere Frage: Warum verwendest du dafür nicht einen simplen 
Goertzel-IIR-Filter? Da gibt es verschiedene "Sliding"-Varianten, mit 
denen man die Phase bestimmen kann. Ich verstehe nicht ganz, warum du 
den Aufwand mit dem externen DAC betreibst, aber vielleicht musst du da 
noch mehr zu deinem Quellsignal erläutern.

Ansonsten lässt sich die Frage nicht so einfach aus dem Geratewohl 
beantworten, du müsstest schon eine Abschätzung des Fehlers und des 
Rauschens machen (und auch dran denken, dass gerne mal die LSB nicht 
echt, sondern korreliert 'rauschen'). D.h. Gauss'sche 
Fehlerfortpflanzung für die gesamte Verarbeitungskette, daraus ergibt 
sich dann das grösste sinnvolle Maschinen-Epsilon (als Anzahl 
Nachkomma-Bits). Hast Du das Ganze schon simuliert?

von Gerald G. (gerald_g)


Lesenswert?

Hi,
mit dem DAC gebe ich das Signal nach außen in das Device under Test und 
das Signal was zurück kommt messe ich mit dem ADC.
Soweit ich weiß ist das "Lock-in-Prinzip" deutlich besser geeignet um 
kleine Signale im Rauschen zu finden.
Das ganze habe ich bereits simuliert, doch nut mit Floatzahlen. Dabei 
ging es auch nur um das Prinzip, ob es so funktioniert wie ich es 
möchte. Doch mit der Genauigkeit bei Festkomma habe ich mich zwar 
außeinander gesetzt, doch schon bei einer Multiplikation hört das 
Verständnis auf, denn eigenlich bräuchte man dort auch wirklich alle 
Bits. Wie schlimm es ist, wenn ich Runde und anschließend sowieso nur 
mit 18 Koeffizienten rechne, kann ich schlecht abschätzen.
Literaturempfehlungen nehme ich natürlich auch gerne an.

edit:
Habe gerade einen ähnlichen Verstärker gefunden, den es so zu kaufen 
gibt. Dort wird angegeben:
A/D Converter Dynamik:  14 bits bei 40 MHz
Frequenzgenerator Grundfrequenz:  80 MHz
Frequenzgenerator Frequenzdynamik:  32 bit
Frequenzgenerator Signalform-Look-up Tabellenlänge:  4096
Frequenzgenerator Phasenauflösung:  0,088 °
Lock-In Verstärker Dynamik:  28 bit
Lock-In Verstärker Filter Dynamik:  54 bit
PI Regler Integrator Dynamik:  30 bit

Weiß jemand was mit "Filter Dynamik" gemeint ist?

: Bearbeitet durch User
von Fitzebutze (Gast)


Lesenswert?

Hi,

> mit dem DAC gebe ich das Signal nach außen in das Device under Test und
> das Signal was zurück kommt messe ich mit dem ADC.

Achso, du guckst dir eine "Response" an. Damit wird alles etwas klarer.

> Soweit ich weiß ist das "Lock-in-Prinzip" deutlich besser geeignet um
> kleine Signale im Rauschen zu finden.

Ja, in dem Fall denke ich, macht das Sinn. Vergiss meine dumme Nachfrage 
:-)


> Das ganze habe ich bereits simuliert, doch nut mit Floatzahlen. Dabei
> ging es auch nur um das Prinzip, ob es so funktioniert wie ich es
> möchte. Doch mit der Genauigkeit bei Festkomma habe ich mich zwar
> außeinander gesetzt, doch schon bei einer Multiplikation hört das
> Verständnis auf, denn eigenlich bräuchte man dort auch wirklich alle
> Bits. Wie schlimm es ist, wenn ich Runde und anschließend sowieso nur
> mit 18 Koeffizienten rechne, kann ich schlecht abschätzen.
> Literaturempfehlungen nehme ich natürlich auch gerne an.
>

Ich kann die Blackfin instruction reference empfehlen, da gibt's auch 
einen Passus über Fixpunkt-Arithmetik.
http://www.analog.com/media/en/dsp-documentation/processor-manuals/Blackfin_pgr_rev2.2.pdf

Wenn dein Signal recht klein ist, brauchst du ev. doppelte Bitbreite und 
musst die 18x18 Multiplier kaskadieren. Braucht aber so gut wie nur 
Logik (und ev. etwas Pipelining).

> edit:
> Habe gerade einen ähnlichen Verstärker gefunden, den es so zu kaufen
> gibt. Dort wird angegeben:
> A/D Converter Dynamik:  14 bits bei 40 MHz
> Frequenzgenerator Grundfrequenz:  80 MHz
> Frequenzgenerator Frequenzdynamik:  32 bit
> Frequenzgenerator Signalform-Look-up Tabellenlänge:  4096
> Frequenzgenerator Phasenauflösung:  0,088 °
> Lock-In Verstärker Dynamik:  28 bit
> Lock-In Verstärker Filter Dynamik:  54 bit
> PI Regler Integrator Dynamik:  30 bit
>
> Weiß jemand was mit "Filter Dynamik" gemeint ist?

Ich denke mal, das ist dann die interne Bitbreite, mit der der IIR 
Filter rechnet bzw. akkumuliert. Wenn du nicht an 18x18 DSP-Slices 
sparen musst, würde ich einfach mal mit einer ähnlichen internen 
Bitbreite draufhauen, zumindest hinter dem Mischer (ersten Multiplikator 
von Eingangs mit dem "Fehlersignal"), d.h. du hast rechnerisch 36 bit am 
Ausgang. Der Akkumulator, den du dann im IIR dazupackst, dürfte etwas 
breiter werden, damit's keinen Overflow gibt. Wie du normierst, ist ja 
dir überlassen, aber in dem Fall kannst du ja, da sowieso nur 14 Bit 
reinkommen, ein 4.14-Fractional-Format wählen, dann hast du etwas 
"headroom", und es werden nicht unnötige Bits, die kaum noch für die 
Rundung relevant sind, am unteren Ende "hinzuerfunden".
In Octave/Matlab gibt es Fixpunkt-Toolboxen, anstatt sich mit Gauss 
auszutoben, kannst du natürlich auch mal empirisch einige Grenzszenarios 
durchexerzieren. Dein IIR wird ja nicht gleich seine Pole am 
Einheitskreis liegen haben, oder? (Dann bist du die 
Quantisierungsprobleme und hässliche Dithering-Workarounds schon mal 
los)

von Gerald G. (gerald_g)


Lesenswert?

Fitzebutze schrieb:
> Wenn du nicht an 18x18 DSP-Slices
> sparen musst

1288 Stück habe ich davon :D
Habe gerade nachgeschaut, die sind sogar so designed dass man mit 4 
Stück davon 36x36 Bit multiplizieren kann :)
Ich denke das werde ich mal nach gusto ausreizen. Deswegen habe ich ja 
so ein Flaggschiff. reduzieren kann man immer noch.

Also ich würde einfach mal so vorgehen:
14 Bit von ADC mal 14 Bit Sinus
-> erhalte 28 Bit.
IIR-Tiefpassfilter mit 14 Bit Koeffizienten (sah ich der Simulation ganz 
gut aus)
-> Runde auf 28 Bit
FIR Decimation mit 14 Bit Koeffizienten
-> Runde auf 30 Bit (Hier erhalte bei M=8 ich echte 1.5 LSB hinzu)
IIR Tiefpass mit 14 Bit Koeffizienten
-> Runde auf 30 Bit
FIR Decimation mit 14 Bit Koeffizienten
-> Runde auf 32 Bit (Hier erhalte bei M=8 ich echte 1.5 LSB hinzu)
IIR Tiefpass mit 14 Bit Koeffizienten
-> Runde auf 32 Bit
FIR Decimation mit 14 Bit Koeffizienten
-> Runde auf 34 Bit (Hier erhalte bei M=8 ich echte 1.5 LSB hinzu)

Für die Zwischenergebnisse würde ich die vollen Bit ausnutzen.


Fitzebutze schrieb:
> Dein IIR wird ja nicht gleich seine Pole am
> Einheitskreis liegen haben, oder?

Nein, deswegen kaskadiere ich lieber ein paar Filter die nicht so tief 
filtern, und dezimiere dazwischen.

Mir wäre nur wichtig dass am Ende ca. 4 dieser Lock-Ins aufs FPGA 
passen, aber das sieht ja ziemlich gut aus.
Danke übrigens für den Link. Sehr informativ.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.