Hallo zusammen,
ich habe einen UKW-Superhet-Empfänger gebaut, welcher in das Basisband
runtermischt (also wirklich auf 0Hz). Dieses Signal möchte ich mit einem
ADC im Mikrocontroller abtasten und im Mikrocontroller demodulieren.
Das demodulierte Signal soll dann über einen DAC wieder ausgegeben
werden und (verstärkt) auf einen Lautsprecher gegeben werden.
Die Hardware steht schon und funktioniert: Wenn ich statt ADC das Signal
mit der PC-Soundkarte aufnehme, dann kann ich mit GNU-Radio demodulieren
und es funktioniert!
Ebenso funktioniert der ADC und DAC schon: Wenn ich die Samples, die vom
ADC kommen direkt über den DAC wieder ausgeben, dann liegen die Signale
(im Oszi) schön übereinander. Der ADC läuft mit 1MSPS. Der
Mikrocontroller ist ein STM32F767 der mit 216MHz läuft.
Nur mit dem FM-Demodulieren klappt es im Mikrocontroller nicht so.
Immer dann, wenn der ADC ein sample gesamplet hat, wird ein Interrupt
aufgerufen, in welchem dieser Code läuft:
1
uint32_tadc_value_processed=(uint32_t)(((((int32_t)adc_value)-1209)*8)+2048);// remove offset and adjust scale
Die Funktion arccos() holt aus einer Lookup-Table die Arccosinus-Werte.
Ihr Eingangsbereich ist 0..4095 und ihr Rückgabewert liegt im Bereich
0..2047 (Datentyp int32_t).
Das Ergebnis ist ein Pfeifen (das schon mal zum Rythmus der Musik im
Radio passt). Was ist das Problem, warum funktioniert dies hier nicht?
Was könnte ich verbessern?
Vielen Dank schon mal für Hilfe!
Das ist FM, eine bestimmte Frequenz am ADC soll also einer bestimmten
Amplitude hinter deinem DAC entsprechen.
Du solltest also die Frequenz am Eingang bestimmen und dann da
entsprechend eine Spannung am Ausgang ausgeben. Das ist ja linear, also
kannst du sagen z. B. 0Hz am Eingang sind 0V am Ausgang. 1kHz am Eingang
ist 1V am Ausgang. Eben je nach Frequenzbereich und Spannungsamplitude
die du gerne hättest.
Ja wie misst man die Frequenz am Eingang? Z. B. indem man die Zeit
zwischen zwei Nulldurchgängen misst.
Moin,
Georg B. schrieb:> (also wirklich auf 0Hz)
So richtig ganz echt wirklich - also mit Nachregelung des LO? Oder nur
eher so ungefaehr 0Hz - dann musst du noch per "Derotator" das letzte
bisschen Offsetfrequenz entfernen.
Gruss
WK
Zum Nachdenken:
Ein HF-Träger der FM-moduliert ist und auf 0 Herz heruntergemischt
wird erzeugt als Augenblickswert auch negative Frequenzen, die
müssen auch irgendwo hin. Das ergibt beim Demodulieren dann was?
Georg B. schrieb:> in welchem dieser Code läuft:
Du erwartest nicht wirklich, daß ich mich da durch deine Quelle
durchwurschtele - gelle?
Also, die NF bei FM ergibt sich durch die erste Ableitung des
Empfangsvektors.
Du hast (hoffentlich) 2 Werte pro Sample vorrätig: I und Q.
Das ist dein Empfangsvektor in kartesischen Koordinaten. Jetzt mußt du
selbigen in Polarkoordinaten wandeln. Dazu gibt es den CORDIC (hin und
zurück, gelle..). Die Vektorlänge ist der AM-Anteil bzw. die Feldstärke
und den Winkel mußt du nun differenzieren. Also die Differenz zwischen
dem vorherigen Winkel und dem aktuellen Winkel bilden. Das ist erstmal
deine NF - aber beaufschlagt mit dem DC-Anteil, den du als "AFC"
benutzen könntest. Du mußt also deine Winkeldifferenz noch
hochpaßfiltern, um den DC-Anteil rauszuwerfen.
W.S.
>>>Das Ergebnis ist ein Pfeifen (das schon mal zum Rythmus der Musik im
Radio passt). Was ist das Problem, warum funktioniert dies hier nicht?
<<<<
Das funktioniert nirgendwo, weil die Umkehrfunktion des cos, arccos,
nicht eindeutig ist. Du schaust für jeden Abtastwert was der zugehörige
Winkel ist, da gibts aber unendlich viele und Du weißt nicht welcher
paßt.
>>>Was könnte ich verbessern?
Wende ein Standardverfahren zur FM Demodulation an.
https://en.wikipedia.org/wiki/Demodulation#FM_radio
erste Möglichkeit: Du mischt mit einem Sinus und einem Cosinus herunter
( Quadratur Demodulator), die I/Q Komponenten sind eine komplexe Zahl,
die Demodulation ist die Bestimmung des Winkels zwischen zwei
aufeianderfolgenden, das ist der Inhalt von Quadsignals.pdf oder von
https://mriquestions.com/uploads/3/4/5/7/34572113/quad_signals_tutorial-lyons.pdf
.
Zur Not kann man das einfach runtergemischte Signal durch ein
Hilbert-Filter ( Quadratur Filter ) schicken, das eine 90°
phasenverschobene Komponente nachträglich erzeugt, war hier auch schon
mal fett Thema.
zweite und dritte Möglichkeit sind eine PLL und ein digitales Filter auf
der Flanke, so haben das die Alten noch analog gemacht.
math rulez!
Cheers
Detlef
Na du misst die Zeit zwischen zwei Nulldurchgängen. Oder aber zu zählst
die Anzahl der Nulldurchgänge die in einem bestimmten Zeitintervall
auftreten.
Ich würde ersteres machen.
Hallo zusammen,
vielen Dank für die Antworten und nach einiger Recherche der angegebenen
Links bin ich nun auch soweit, dass ich es mit I und Q versuchen will.
Dazu habe ich meine Hardware so geändert, dass der 2. LO nicht mehr auf
0Hz herunter mischt, sondern auf 60kHz.
Im Mikrocontroller habe ich einen Timer, der mir ein Signal theta
liefert, sodass sin(theta) und cos(theta) verwendet werden können, um
das vom ADC kommende Signal auf I und Q herunterzumischen.
Der Code sieht nun so aus:
Folgendes habe ich mir dabei gedacht:
Per DMA wird das ADC-Sample in die Variable "adc_value" geschrieben.
Einen vorhandenen Offset entferne ich darauf hin und bringe das Signal
auf den Bereich -2047..2048, damit ich mit Integern rechnen kann.
Die Funktion sine_cosine(...) holt aus einer Lookup-Tabelle die zum
aktuellen theta passende Werte für Sinus und Cosine. (Das funktioniert
auch, habe mal den Sinus und Cosinus über den DAC ausgegeben und das
passt sogar).
Danach wird per Multiplikation heruntergemischt sodass man I und Q
erhält.
Nun versuche ich nach diesem Algorithmus:
https://www.embedded.com/dsp-tricks-frequency-demodulation-algorithms/
die FM zu demodulieren... Leider funktioniert das nun gar nicht mehr,
deshalb habe ich mit ein paar Faktoren (K, P und M) herumgespielt, aber
es kommt nun nur noch Rauschen aus dem Lautsprecher...
Hat jemand einen Tipp, was ich noch versuchen könnte?
Viele Grüße,
Georg
Georg B. schrieb:> Folgendes habe ich mir dabei gedacht:> Per DMA...
Solange du kein wirklich tragfähiges Konzept hast, ist es zwecklos,
Geschütze wie DMA aufzufahren.
Georg B. schrieb:> Hat jemand einen Tipp, was ich noch versuchen könnte?
Ja.
Dazu brauchst du nicht einmal zu löten, sondern kannst das am PC in
aller Ruhe per Software tun: das Ganze simulieren.
1. Senderseite:
a) Nimm dir ein nicht allzu langes Stück WAV her, irgend ein Jingle
deiner Wahl, ca. 1..3 Sekunden lang und der Einfachheit halber nur mono
in 22.050 kHz Samplerate.
b) Schreib dir ein Stückchen Programm, das WAV Input auf ne HF per FM
aufmoduliert, also wieder der Einfachheit halber auf 1 MHz (also
wenigstens 2 MHz Samples). Jaja, das gibt ne schön große Datei -
entweder als schlichter Text von Samplewerten im Wertebereich -0.99999..
bis +0.99999.. oder z.B. als 16 Bit Words. Ist deine Wahl. Der Hub, den
du anwenden willst, ist auch deine Wahl.
So, du hast nun eine Datei, die - wenn man sie senden würde, deinen
Jingle auf 1 MHz abstrahlen würde.
2. Empfängerseite:
a) Schreib dir ein Stückchen Programm, das ein HF-Signal per I/Q-Mischer
auf null herabmischt. Als LO-Frequenz solltest du aber nicht 1 MHz
nehmen, das wäre unrealistisch. Kannst du für's erste machen, aber dann
solltest du auch mal mit 0.99995 MHz und 1.00005 MHz mischen, um zu
sehen, was dann passiert. Merke: einen absoluten Gleichlauf zwischen
Sender und Empfänger gibt es nie wirklich. Also muß der Empfänger damit
auch klar kommen. Diese Programmstück sollte auch gleich das Dezimieren
erledigen. Wie weit du dezimieren mußt, wirst du ebenfalls ausprobieren
müssen.
So, nun hast du eine weitere Datei mit I/Q-Wertepaaren.
b) Schreibe dir jetzt deine Programmstückchen, die das FM-Demodulieren
bewerkstelligen sollen - zum Ausprobieren, bis du den richtigen
Algorithmus gefunden hast.
W.S.
Georg B. schrieb:> Dazu habe ich meine Hardware so geändert, dass der 2. LO nicht mehr auf> 0Hz herunter mischt, sondern auf 60kHz.
So richtig ganz echt wirklich - also mit Nachregelung des LO? Oder nur
eher so ungefaehr 60kHz - dann musst du noch per "Derotator" das letzte
bisschen Offsetfrequenz entfernen.
SCNR,
WK
Hallo,
das System holt seinen Takt zwar aus einem Quarzofen, aber nachgeregelt
wird leider noch nicht. Ist das so kritisch?
Ok, ich werde das rausfinden, indem ich es so wie von W.S. vorgeschlagen
mal simuliere. Vielen Dank für die Tipps schon mal!
Da wird gerade dabei sind, sei mir ein Frage zum Verständnis von
I/Q-Signalen gestattet (bin ziemlicher Newbie in dem Bereich):
Hab ich das richtig verstanden, dass ich ein direkt gesampeltes Signal
(z.B. mit 1MHz) in I- und Q-Signale splitten kann, indem ich aus jedem
4er-Block den ersten Sample als I-Sample nehme, den zweiten als Q und
den 3. und 4. wegschmeiße? Die I/Q-Samples wären dann mit je 250kHz
gesampelt (mit 90° Phasenverschiebung), die Nutzbandbreite halbiert
(wegen der weggeschmissenen Samples). Soweit korrekt?
Weiter: Kann ich die dritten und vierten Samples negieren und ebenfalls
benutzen (also I,Q,-I,-Q), um die Bandbreite zu erhalten?
(Hab mir das aus der Hardware von einfachen SDR-Receivern hergeleitet;
kann sein, dass ich total auf dem Holzweg bin.)
>>>>>>>>>>>>>>>>>>>>>>
Der Code sieht nun so aus:
int32_t adc_value_processed = (uint32_t)((((int32_t)adc_value) -
((int32_t)1209)) * ((int32_t)1));
// get sine and cosine values
int32_t sine_value, cosine_value;
sine_cosine(&sine_value, &cosine_value); // -2047..2048
// downsample (which yields I and Q)
int32_t I = cosine_value * adc_value_processed; // -4192256..4194304
int32_t Q = sine_value * adc_value_processed; // -4192256..4194304
<<<<<<<<<<<<<<<<<<<<<<<<
So geht das nicht, Du muss zweimal runtermischen mit sin und cos und mit
zwei ADCs sampeln. Wenn Deine hardware das nicht kann wirds delikat, wie
ich schrub braucht man dann einen Hilbert Filter:
Beitrag "Breitband-NF-Phasenschieber, DSP mit AVR"Beitrag "Phasenschieber 90 Grad"
Cheers
Detlef
Ich schrieb:
> ... Soweit korrekt?
Ich beantworte es selbst: Nein. Habe nicht beachtet, dass die
Mixer-/IQ-Demodulator-Frequenz nichts mit der Samplefrequenz zu tun hat.
Was ich da mache, wäre ein weiteres Mischen mit Fsample/4 (mit
IQ-Output).
W.S. schrieb:> Georg B. schrieb:>> Folgendes habe ich mir dabei gedacht:>> Per DMA...>> Solange du kein wirklich tragfähiges Konzept hast, ist es zwecklos,> Geschütze wie DMA aufzufahren.
Das finde ich nicht. Denn das hier:
Georg B. schrieb:> Der ADC läuft mit 1MSPS
in Kombination mit dem:
> Immer dann, wenn der ADC ein sample gesamplet hat, wird ein Interrupt> aufgerufen
dürfte alleine durch die Interrupt-Last schon eine ganz ordentliche
Prozessorlast erzeugen. Da erscheint mir DMA durchaus sehr sinnvoll.
>von foobar (Gast)>29.03.2020 22:56>Da wird gerade dabei sind, sei mir ein Frage zum Verständnis von>I/Q-Signalen gestattet (bin ziemlicher Newbie in dem Bereich):
Lies mal das hier:
https://de.wikipedia.org/wiki/I%26Q-Verfahren
W.S. schrieb:> Georg B. schrieb:>> Folgendes habe ich mir dabei gedacht:>> Per DMA...>> Solange du kein wirklich tragfähiges Konzept hast, ist es zwecklos,> Geschütze wie DMA aufzufahren.
DMA ist kein Geschütz sondern Handwerkszeug wie ein UART.
Sowas muss sitzen ;)
foobar schrieb:> Hab ich das richtig verstanden, dass ich ein direkt gesampeltes Signal> (z.B. mit 1MHz) in I- und Q-Signale splitten kann, indem ich aus jedem> 4er-Block den ersten Sample als I-Sample nehme, den zweiten als Q und> den 3. und 4. wegschmeiße? Die I/Q-Samples wären dann mit je 250kHz> gesampelt (mit 90° Phasenverschiebung), die Nutzbandbreite halbiert> (wegen der weggeschmissenen Samples). Soweit korrekt?
Nein.
Sondern nur so einigermaßen.
Der Knackpunkt ist, daß I und Q eigentlich zum exakt gleichen Zeitpunkt
gehören sollen - und eben nicht zu zwei verschiedenen Zeitpunkten.
Wenn du also ein bereits digital vorliegendes Signal per I/Q-Mischer
heruntermischen willst, dann multipliziere jedes Sample mit dem Sinus
und dem Cosinus deines digitalen LO's und du erhältst damit deine beiden
Ergebnisse I und Q. Da du für die weitere Verarbeitung die hohe
Anfangs-Samplerate normalerweise nicht brauchst, kannst du dann I und Q
gleichermaßen auf eine niedrigere Samplerate dezimieren. Z.B. per
Hogenauer.
Rolf M. schrieb:> dürfte alleine durch die Interrupt-Last schon eine ganz ordentliche> Prozessorlast erzeugen. Da erscheint mir DMA durchaus sehr sinnvoll.
Das ist Quatsch. Bedenke doch mal, daß DMA lediglich Daten von A nach B
schaufeln kann. Aber derartige Daten sollen nicht geschaufelt, sondern
verarbeitet werden. Das kann DMA eben nicht, sondern nur ein
Rechenwerk wie die CPU oder wenns für die zu schnell wird, dann eben ein
Rechenwerk in einem FPGA, was dadurch schneller wird, daß man dort
vieles parallel machen kann, was die CPU nur sequentiell tun kann.
Mich regen solche Gedankenlosigkeiten auf. Da hat jemand mal gehört, daß
DMA eben schnell ist, also muß DMA auch die richtige Medizin sein, wenn
es heißt, daß es hie und da eben schnell zugeht.
W.S.
> Wenn du also ein bereits digital vorliegendes Signal per I/Q-Mischer> heruntermischen willst, dann multipliziere jedes Sample mit dem Sinus> und dem Cosinus deines digitalen LO's und du erhältst damit deine beiden> Ergebnisse I und Q.
Was ich gemacht habe, war ein weiteres Mischen mit Fsample/4. Dabei
läuft der Sinus/Cosinus in 90°-Schritten und jeweils einer wird 0 (der
andere 1 oder -1) - dadurch entsteht das [I,Q,-I,-Q]-Muster, also aus
S=[a,b,c,d] wird I=[a,0,-c,0] Q=[0,b,0,-d].
Muß mir jetzt nur noch überlegen, was das für das Ergebnisspektrum
bedeutet (ist für mich alles Neuland) ;-)
W.S. schrieb:> Rolf M. schrieb:>> dürfte alleine durch die Interrupt-Last schon eine ganz ordentliche>> Prozessorlast erzeugen. Da erscheint mir DMA durchaus sehr sinnvoll.>> Das ist Quatsch. Bedenke doch mal, daß DMA lediglich Daten von A nach B> schaufeln kann. Aber derartige Daten sollen nicht geschaufelt, sondern> verarbeitet werden. Das kann DMA eben nicht, sondern nur ein> Rechenwerk wie die CPU>> Mich regen solche Gedankenlosigkeiten auf. Da hat jemand mal gehört, daß> DMA eben schnell ist, also muß DMA auch die richtige Medizin sein, wenn> es heißt, daß es hie und da eben schnell zugeht.
Du schnallst es einfach nur wieder nicht.
So ein IRQ hat ordentlich was an Overhead.
Das senkt die Zeit, die die CPU für die Berechnung selbst hat.
Wenn die Daten auf "magische weise" im RAM per DMA auftauchen hat man
diese Rechenzeit frei.
So kann man sich einen Chunk an Daten per DMA schaufeln und dann gibts
nur einen IRQ "Chunk fertig".
Auf dem wird dann gerechnet.
Auf einem F405 auf 168MHz hab ich da mal experimentiert.
Eine LED blinkt vor sich hin im busyloop und bei 1MSPS per IRQ vom
internen ADC blinkte die dann sichtbar langsamer.
Das ist nicht zu unterschätzen!
> Bedenke doch mal, daß DMA lediglich Daten von A nach B> schaufeln kann. Aber derartige Daten sollen nicht geschaufelt, sondern> verarbeitet werden. Das kann DMA eben nicht,
Ich denke, Rolf spielte eher auf den per-Datum-Overhead an. Eine
Millionen Interrupts pro Sekunde wird jede CPU an ihre Grenzen bringen
(selbst ohne "Verarbeitung"). Üblicherweise kann man die Daten im Batch
auch effizienter bearbeiten (gemeinsamer Setup, Vector-Units, etc).
Deshalb sammelt man solch große Datenmengen per DMA ein und lässt sie
dann blockweise von der CPU verarbeiten.
W.S. schrieb:> Rolf M. schrieb:>> dürfte alleine durch die Interrupt-Last schon eine ganz ordentliche>> Prozessorlast erzeugen. Da erscheint mir DMA durchaus sehr sinnvoll.>> Das ist Quatsch. Bedenke doch mal, daß DMA lediglich Daten von A nach B> schaufeln kann. Aber derartige Daten sollen nicht geschaufelt, sondern> verarbeitet werden. Das kann DMA eben nicht, sondern nur ein> Rechenwerk wie die CPU oder wenns für die zu schnell wird, dann eben ein> Rechenwerk in einem FPGA, was dadurch schneller wird, daß man dort> vieles parallel machen kann, was die CPU nur sequentiell tun kann.
Natürlich verarbeitet DMA die Daten nicht. Wie kommst du darauf, ich
würde so einen Blödsinn glauben? Aber DMA kann die Daten sammeln, so
dass der Prozessor einen ganzen Block auf einmal verarbeiten kann, statt
für jedes einzelne Sample eine Million mal pro Sekunde unterbrochen zu
werden und jedesmal separat zur ISR springen und die ausführen zu
müssen. Gerade bei so hohen Datenraten ist deshalb die Benutzung von DMA
eigentlich Standard. Man will dabei möglichst viel der Rechenkapazität
für die eigentliche Rechenaufgabe zur Verfügung haben, statt schon das
meiste davon für das hin- und herspringen zwischen Interrupt und
Hauptprogramm zu verpulvern.
foobar schrieb:> Ich denke, Rolf spielte eher auf den per-Datum-Overhead an.
Das denke ich auch - aber das ist eigentlich nur Kosmetik.
Denk doch mal an nen FIR-Filter mit - sagen wir mal - 200 Taps und das
2x (I und Q), und anschließend noch der Demodulator, also oftmals ein
Cordic. Da fällt das Sichern der Register nicht ins Gewicht, wenn man
keinen Unfug treibt, und wenn die Hardware ordentlich gestaltet ist.
An so einer Stelle kommt jedoch der Groll hoch, was die Chiphersteller
sich gelegentlich geleistet haben. Völlig hirnrissige Cores sieht man
z.B. bei einigen STM32-Typen, wo zum I2S umgebaute SPI-Cores die Daten
nur 16 bittig empfangen/senden und obendrein keinerlei Fifo haben. Ein
Sample dort per CPU abzuholen oder zu senden, würde 4 Interrupts/Sample
bedeuten. Grausam.
Nochwas: Um die Algorithmen (speziell Filter) zu beschleunigen, werden
die Samples normalerweise 2x abgelegt. Der Ringpuffer für die Samples
ist dabei 2x so lang wie das Filter. Jedes Sample wird dann bei Stelle x
im Filter und zusätzlich bei x+Filterlänge abgelegt. Dadurch kann der
Filter durchbrettern, ohne sich um das Umschlagen des Index im
Samplepuffer kümmern zu müssen. Das ist wesentlich für die
Signalverarbeitung, wenn man diese auf einem Cortex erledigen will. Bei
den DSP von AD ist das kein Problem, die haben intelligentere
Adreßgeneratoren für das Ausführen des Filters. Aber ob so etwas ein DMA
Core leisten kann, ist ja doch recht fraglich.
W.S.
Hallo zusammen,
bin nun einen etwas anderen Weg gegangen:
Per Ethernet habe ich die ADC-Samples an den PC geschickt und als
.csv-Datei abgespeichert.
Die Samplerate musste ich auf 500kSamples/s reduzieren, aber dann habe
ich immer noch ca. 8-fache Überabtastung.
Der zweite LO war wieder so eingestellt, dass ich auf 50kHz, nicht auf
0Hz herunter mische.
Die Datei mit den Samples habe ich mal angehängt, kann mir vielleicht
jemand helfen es zu demodulieren?
Das Problem das ich nämlich habe ist folgendes:
Ich bin nicht sicher, ob mein Demodulationsalgorithmus das Problem ist,
oder ob mit meiner Hardware/Antenne, etc... etwas nicht stimmt.
Über Hilfe würde ich mich sehr freuen!
Vielen Dank an alle!
'Wire to wire' von Razorlight sagt Shazam. Kenn ick nich.
Das ist quick and dirty über ein 'analytisches Signal', das würde auch
mit einem Hilbert Filter gehen. Deine samples sind ok.
Cheers
Detlef
clear
%load c:\Matlab\MYFILES\adc_samples_final.csv
%save c:\interim\ma.mat
load c:\interim\ma.mat
sam =adc_samples_final;
sam=sam(2:end); %gerade Anzahl
sam=sam-mean(sam); % DC raus
spsam=fft(sam);
n=length(sam);
spsam((n/2+1):end)=0; % Analytisches Signal basteln
spsam(1)=0; %DC nochmal raus
anasam=ifft(spsam);
diffangle=angle(anasam(2:end)./anasam(1:end-1)); % Winkeldifferenzen
diffangle(find(diffangle> 1.5))=[]; %Artefakte raus
diffangle(find(diffangle<-1.5))=[];
diffangle=diffangle-mean(diffangle); %DC raus
ton=decimate(diffangle,round(500000/8192)); % resample auf 8192er Audio
return
Hallo Detlef,
wow, ich bin beeindruckt! Vielen Dank für das Code-Snipet!
Es scheint mir so, dass ich bei DSP einiges nachzuholen habe, aber jetzt
ist die Motivation da, es zu lernen.
Damit ich auch etwas beitragen kann, stelle ich meine "Firmware" für den
Mikrocontroller im Anhang zur Verfügung, vielleicht hilft es jemandem,
der auch etwas per Ethernet übertragen möchte. Ist komplett selbst
gebastelt (und natürlich nur quick and dirty!). Aber als Anregung kann
es vielleicht jemandem dienen. Das Programm sendet die Daten "einfach"
in Ethernet-Frames, ohne TCP/IP oder UDP oder soetwas zu benutzen.
Empfangen kann man die Frames mit tcpdump oder Wireshark (oder einem
kleinen C-Programm). Das war für mich halt die einfachste Methode, die
Samples so schnell aus dem Mikrocontroller in den PC zu bekommen, denn
z.B. UART ist zu langsam und mit "nur" 512K RAM kann ich auch nicht
viele Samples im Mikrocontroller halten.
Werde nun aber weiter versuchen, direkt auch dem Mikrocontroller zu
demodulieren. Mal so ein Hilbert-Transformator versuchen zu verstehen
und zu implementieren. Beruflich komme ich aus dem Analogbereich und
Hobbymäßig mache ich manches mit FPGA und Mikrocontroller. Darum brauche
ich bei DSP etwas Hilfe.
Vielen Dank an alle!
Bringt es was, wenn du "diffangle" mit einer genaueren Winkelfunktion
berechnest ? ( arctan ?)
Dein DAC hat 12 Bit? Vielleicht gibt's Quantisierungsfehler in deinen
Rechnungen, die für das Rauschen sorgen.
Hi Georg,
die Demodulation mit einem Hilbert-Pärchen geht genausogut, siehe
angehängtes Matlab script. Das Hilbert-Pärchen ist aus
www.mikrocontroller.net/topic/308292#new
Man benötigt 32 multiply/accumulate pro sample ( die beiden filter sind
spiegelsymmetrisch), bei 500ks/s macht das 16MMACs/s, das sollte ein ARM
schaffen. Ausserdem hast Du mit 8fach Überabtastung ja noch Luft.
Zu Deinem Program:
- Offline ausprobieren. Du hast die samples und kannst in C auf dem PC
spielen.
- Die Koeffizienten Deines Filters sind nicht korrekt, woher hast Du
die, Du teilst bei der Berechnung einmal durch 0.
Ein Hilbert Filter sind in der Regel zwei Filter, Inphase Anteil und
Quadratur Anteil, deswegen Hilbert Pärchen.
-
for(uint32_t i = (N_STORE - 1); i >= 1 ; i--)
{ samp_store[i] = samp_store[i - 1]; }
Du hast keine Zeit samples im Speicher rumzurangieren. Du greifst über
Pointer auf die samples zu, die bleiben am Platz. Die angegebene Filter
sind symmetrisch, das nutzt Du auf jeden Fall aus, mit nem Pointer ist
das Fummelei aber notwendig.
-
int32_t diffangle = ( (K*Q*I_old) - (K*I*Q_old) ) / ((I*I) +
(Q*Q));
Du brauchst den Quotienten (I+i*Q)/(I_old+i*Q_old), und davon nur den
Winkel, also wie schnell sich der Zeiger zwischen zwei samples
fortbewegt hat. Da muss irgendwo noch 'atan' auftauchen. Falle: I steht
für Inphase (Realteil), i für sqrt(-1).
Ich werde die Demodulation mit einer PLL probieren.
math rulez!
Cheers
Detlef
Detlef _. schrieb:> die Demodulation mit einem Hilbert-Pärchen geht genausogut, siehe> angehängtes Matlab script. Das Hilbert-Pärchen ist aus>> www.mikrocontroller.net/topic/308292#new>> Man benötigt 32 multiply/accumulate pro sample ( die beiden filter sind> spiegelsymmetrisch), bei 500ks/s macht das 16MMACs/s, das sollte ein ARM> schaffen. Ausserdem hast Du mit 8fach Überabtastung ja noch Luft.
Ok, werde ich mir mal anschauen.
>> Zu Deinem Program:>> - Offline ausprobieren. Du hast die samples und kannst in C auf dem PC> spielen.
Ja, so habe ich es nun gemacht.
>> - Die Koeffizienten Deines Filters sind nicht korrekt, woher hast Du> die, Du teilst bei der Berechnung einmal durch 0.
Oha, stimmt, das muss ich mir nochmals genau anschauen. Danke für den
Hinweis.
>> Ein Hilbert Filter sind in der Regel zwei Filter, Inphase Anteil und> Quadratur Anteil, deswegen Hilbert Pärchen.
Ich habe es mir so gedacht: Durch die Faltung mit der Impulsantwort der
Hilbert-Transformation bekomme ich die Q-Komponente. Aber die Berechnung
hat ja ein paar Takte gedauert. Deshalb habe ich als I-Komponente nicht
das aktuelle Sample, sondern "ein älteres", verzögertes Sample benutzt.
Funktioniert ziemlich gut!?
>> -> for(uint32_t i = (N_STORE - 1); i >= 1 ; i--)> { samp_store[i] = samp_store[i - 1]; }> Du hast keine Zeit samples im Speicher rumzurangieren. Du greifst über> Pointer auf die samples zu, die bleiben am Platz. Die angegebene Filter> sind symmetrisch, das nutzt Du auf jeden Fall aus, mit nem Pointer ist> das Fummelei aber notwendig.
Das habe ich mir sogar auch schon gedacht. Die CPU ist aktuell ungefähr
80% ausgelastet. Deshalb habe ich noch keine Optimierungen in diese
Richtung unternommen, steht aber auch jeden Fall auf der TODO-Liste.
>> -> int32_t diffangle = ( (K*Q*I_old) - (K*I*Q_old) ) / ((I*I) +> (Q*Q));>> Du brauchst den Quotienten (I+i*Q)/(I_old+i*Q_old), und davon nur den> Winkel, also wie schnell sich der Zeiger zwischen zwei samples> fortbewegt hat. Da muss irgendwo noch 'atan' auftauchen. Falle: I steht> für Inphase (Realteil), i für sqrt(-1).
Das "( (K*Q*I_old) - (K*I*Q_old) ) / ((I*I) + (Q*Q));" beinhaltet
den atan, wie hier auf Seite 72, Formel 32 gezeigt:
http://openwebrx.org/bsc-thesis.pdf
Danke nochmals für die Anregungen!
Hi,
Georg B. schrieb:>> ->> int32_t diffangle = ( (K*Q*I_old) - (K*I*Q_old) ) / ((I*I) +>> (Q*Q));>>>> Du brauchst den Quotienten (I+i*Q)/(I_old+i*Q_old), und davon nur den>> Winkel, also wie schnell sich der Zeiger zwischen zwei samples>> fortbewegt hat. Da muss irgendwo noch 'atan' auftauchen. Falle: I steht>> für Inphase (Realteil), i für sqrt(-1).> Das "( (K*Q*I_old) - (K*I*Q_old) ) / ((I*I) + (Q*Q));" beinhaltet> den atan, wie hier auf Seite 72, Formel 32 gezeigt:> http://openwebrx.org/bsc-thesis.pdf
Interessante Idee, kannte ich nicht. Funktioniert so mittelmäßig, habe
ich ausprobiert. Vielleicht kan man da aber noch was dran drehen.
Die Demodulation mit einer PLL geht auch ganz gut, siehe angehängtes
file, Shazam erkennt den Song immer noch. PLL wird die Möglichkeit mit
der minimalen Anzahl Multiplikationen pro sample sein: eine für den
Phasenvergleicher und eine für den VCO, der Rest sind shifts und
Additionen.
Bei der PLL muss man bißchen fummeln, das ist nicht so unabhängig von
den Amplituden und der ZF wie die anderen Methoden.
Cheers
Detlef
Hallo Detlef,
cool, Danke für den Beispielcode!
Jetzt habe ich ja viele Anregungen, um den Algorithmus noch weiter zu
verbessern.
Ich melde mich, wenn sich noch etwas ergibt.
Gruß,
Georg