Forum: PC-Programmierung 1100hz signal über 500 ms erzeugen


von Karsten (Gast)


Lesenswert?

Hi an alle,

wie kann ich unter C/C++ ein Sinusignal mit 1100Hz erzeugen über eine 
Länge von 500ms. Dieses soll so erzeugt werden das ich es blockweise in 
30ms Blöcken rausziehen kann. Hierfür würde ich den STL - Vector nutzen 
wollen.
Das Signal muss als 16bit Signal also short erzeugt werden.

Mein momentaner Versuch klappt leider nicht so ganz...es kommt kein 
sauberes Signal heraus.

Hier mal mein Code.
1
double dCosFreq = cos(2*M_PI*m_dFreq/m_nSampleRate); //SampleRate ist 8000hz und m_dFreq meine 1100Hz
2
double dCosFreq2 = 2 * dCosFreq;
3
double dCosFreq2A = cos(2*(2*M_PI*m_dFreq/m_nSampleRate));
4
double dSinFreq = sin(2*M_PI*m_dFreq/m_nSampleRate);
5
for(int x = 0; x < m_nNumSamples; x++) //m_nNumSamples ist Samplerate/500ms ergibt also die Anzahl der benötigten 30ms Blöcke
6
{
7
   for(int i = 0; i < m_nFrameSize; i++)
8
   {
9
      if(i > 1)
10
      {
11
         m_pBuffer[i] =  static_cast<short>(16383*(dCosFreq2 * m_pBuffer[i-1] - m_pBuffer[i-2]));
12
      }
13
      else if (i > 0)
14
      {
15
         m_pBuffer[i] =  static_cast<short>(16383*(dCosFreq2 * m_pBuffer[i-1] - dCosFreq));
16
      }
17
      else
18
      {
19
         m_pBuffer[i] =  static_cast<short>(16383*(dCosFreq2 * dCosFreq - dCosFreq2A));
20
      }
21
   }
22
   m_pSampleArray.push_back(m_pBuffer);
23
}

Ich mach sicher alles falsch:D

Gruß
Karsten

von Karl H. (kbuchegg)


Lesenswert?

Karsten schrieb:

> Mein momentaner Versuch klappt leider nicht so ganz...es kommt kein
> sauberes Signal heraus.

Wie gibst du denn das Signal aus?
Dort wird eher das Problem liegen und nicht bei der Generierung.

>    m_pSampleArray.push_back(m_pBuffer);

Den vector hast du bei der Initialisierung schon auf die zu erwartende 
Größe gebracht?

von Karsten (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wie gibst du denn das Signal aus?

Das Signal wird nochmal in µLaw umgewandelt und dann übers Netz Versand.

Karl heinz Buchegger schrieb:
> Den vector hast du bei der Initialisierung schon auf die zu erwartende
> Größe gebracht?

Nein der stl::vector ist da doch dynamisch.

von Rolf Magnus (Gast)


Lesenswert?

Karsten schrieb:
> Hi an alle,
>
> wie kann ich unter C/C++

C oder C++? "C/C++" gibt es nicht.

> ein Sinusignal mit 1100Hz erzeugen über eine Länge von 500ms. Dieses
> soll so erzeugt werden das ich es blockweise in 30ms Blöcken rausziehen
> kann. Hierfür würde ich den STL - Vector nutzen wollen.

Also C++.

> //m_nNumSamples ist Samplerate/500ms ergibt also die Anzahl der
> benötigten 30ms Blöck

Die Rechnung verstehe ich nicht. Die Anzahl der Blöcke ergibt sich aus 
500ms/30ms.

Übrigens: Wo wird da dein eigentlicher Sinus berechnet? Innerhalb der 
Schleife wird nirgends eine Sinus-Funktion aufgerufen. Stattdessen 
nimmst du zum Berechnen eines Wertes immer den letzten Wert, 
multiplizierst ihn mit ca. 1,3 und ziehst dann den vorletzten Wert davon 
ab.

von Karl H. (kbuchegg)


Lesenswert?

Karsten schrieb:
> Karl heinz Buchegger schrieb:
>> Wie gibst du denn das Signal aus?
>
> Das Signal wird nochmal in µLaw umgewandelt und dann übers Netz Versand.

Vielleicht hab ich dich auch misverstanden.
Was genau meinst du mit
"es kommt kein sauberes Signal heraus."


> Nein der stl::vector ist da doch dynamisch.

Mein Fehler. Du stopfst ja sowieso das Feld als ganzes in den vector 
rein (wozu eigentlich das zusätzliche Feld? mach doch vor den Schleifen 
den vector gleich um die richtige Anzahl größer und schreib die Werte 
gleich bei der Berechnung gleich in den vector)


Deine Berechnung .... sieht eigenartig aus.

von Karsten (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Die Rechnung verstehe ich nicht. Die Anzahl der Blöcke ergibt sich aus
>
> 500ms/30ms.

Hehe...da hast du recht...falsch gedacht.

Rolf Magnus schrieb:
> Wo wird da dein eigentlicher Sinus berechnet?

Stimmt...gute Frage???:D

Karl heinz Buchegger schrieb:
> wozu eigentlich das zusätzliche Feld?

So kann ich am Ende den Vector blockweise auslesen und brauch nur immer 
nen offset mit übergeben wenn ich den nächsten Block brauche.

Karl heinz Buchegger schrieb:
> Deine Berechnung .... sieht eigenartig aus

Inwieweit eigenartig?

von Karl H. (kbuchegg)


Lesenswert?

Karsten schrieb:

> Karl heinz Buchegger schrieb:
>> wozu eigentlich das zusätzliche Feld?
>
> So kann ich am Ende den Vector blockweise auslesen und brauch nur immer
> nen offset mit übergeben wenn ich den nächsten Block brauche.

Vergiss es.
Ohne Datentypen muss man raten und ich hab offenbar falsch geraten.


> Karl heinz Buchegger schrieb:
>> Deine Berechnung .... sieht eigenartig aus
>
> Inwieweit eigenartig?

Weil ich innerhalb der Schleife einen Sinus erwartet hätte.

Wenn ich eine Tabelle erstellen will, in der alle Quadrate der Zahlen 
von 0 bis 100 enthalten sind, dann habe ich

     for( i = 0; i < 100; ++i )
       tabelle[i] = i * i;

das Quadrat kommt in der Schleife vor

Will ich eine Tabelle aller Wurzeln von 0 bis 100

     for( i = 0; i < 100; ++i )
       tabelle[i] = sqrt( i );

die Wurzel kommt innerhalb der Schleife vor

Will ich eine Tabelle aller Sinuswerte von 0 bis 100

     for( i = 0; i < 100; ++i )
       tabelle[i] = sin( i );

logischerweise kommt dann der Sinus in der Tabelle vor. Und das sehe ich 
bei dir nicht. Gut, die 100 muss man jetzt natürlich so anpassen, dass 
sich beim Abspielen dieses Samples in einer bestimmten Zeit die 
gewünschte Freuenz ergibt. Aber nichts desto trotz hätte ich da einen 
sin innerhalb der Schleife erwartet.

OK. Jetzt gibt es natürlich bei Quadraten und Wurzeln die Möglichkeit, 
dass man sich den unmittelbar nächsten Wert mit dem vorhergehenden 
berechnen kann und so nicht durch die Quadrat/Wurzelberechnung durch 
muss. Vielleicht hast du ja auch so etwas für einen Sinus gefunden, 
daher hab ich eigenartig gesagt und nicht falsch. Denn irgendwas 
berechnest du mit dem jeweiligen Vorgängerwert. Nur was?

von Karsten (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> logischerweise kommt dann der Sinus in der Tabelle vor. Und das sehe ich
>
> bei dir nicht.

Ja da hast du vollkommen recht...da hab ich irgendwie nen ganz groben 
Denkfehler weiß aber gerade nicht wie ich den Sinus da jetzt korrekt 
unterbringe???

Aber die Berechnungen mit dem Cosinus sind doch soweit erstmal richtig 
oder?
Ich beschäftige mich noch nicht so lange mit dem Thema.
Wie würde man das denn normalerweise machen?


Ich komm gerad irgendwie nicht drauf???

von Karl H. (kbuchegg)


Lesenswert?

Karsten schrieb:

> Denkfehler weiß aber gerade nicht wie ich den Sinus da jetzt korrekt
> unterbringe???

Das allgemeine Schema hab ich dir ja schon aufgezeigt.

> Aber die Berechnungen mit dem Cosinus sind doch soweit erstmal richtig
> oder?

Das kann ich nicht sagen, weil die noch unverständlicher sind.

> Ich beschäftige mich noch nicht so lange mit dem Thema.
> Wie würde man das denn normalerweise machen?
>
>
> Ich komm gerad irgendwie nicht drauf???

Dann solltest du darüber mal nachdenken.

WEnn du eine Tabelle haben willst, die 100 Einträge hat und in der eine 
komplette Sinusschwingung 1 mal darin enthalten ist, wie machst du das?

Du rechnest dir aus, um wieviel der Winkel (von dem du den Sinus nehmen 
wirst) von einem Schritt zum nächsten zunehmen muss, sodass der Winkel 
in 100 Schritten einen Vollkreis (=360 Grad) überstreicht.

Das Winkelinkrement ist daher   2*PI / 100

Eine Schleife

     winkel = 0.0;
     inkrement = 2*PI / 100;

     for( i = 0; i < 100; ++i ) {
       tabelle[i] = sin( winkel );
       winkel += inkrement;
     }

erzeugt daher eine Tabelle mit 100 Werten, wobei das die Werte 1 
Sinusschwingung sind.

Sollen in denselben 100 Werten 2 komplette Schwingungen untergebracht 
werden, dann muss der gedankliche Winkel nicht von 0 bis 360 Grad 
verändert werden, sondern von 0 bis 720 Grad. Denn 720 Grad sind 2 
Umdrehungen.

     winkel = 0.0;
     inkrement = 2 * 2*PI / 100;

     for( i = 0; i < 100; ++i ) {
       tabelle[i] = sin( winkel );
       winkel += inkrement;
     }

Bei der berechnung des Inkrements wird also die Anzahl der zu 
erzeugenden Schwingungen eingehen.

Das andere was variiert werden kann ist natürlich die Tabellenlänge. 
Wenn die Tabelle nicht 100 Einträge sondern 500 umfassen soll, dann wird 
sich das
 * bei der Berechnung des Inkrements
 * in der Steuerung der for-Schleife bemerkbar machen

Inkrement ist auch klar. Wenn ich 500 Meter zurücklegen soll und dafür 
300 Schritte machen darf, dann kann ich kleinere Schritte machen als 
jemand der dieselbe Strecke mit nur 100 Schritten zurücklegen soll.


Vielleicht solltest du auch erst mal mit ein paar Testprogrammen 
anfangen und dir die erzeugten Werte erst mal auf dem Monitor per printf 
ausgeben lassen. Müssen ja nicht gleich 1100 Herz und eine Tabellenlänge 
von 8000 Samples sein. Mit kleineren Werten sieht man das auch schon 
sehr gut und man erkennt auch gleich viel besser wie die mathematischen 
Zusammenhänge sind.
Was gerade bei Schwingungen immer sehr gut kommt, ist wenn man sich die 
Werte dergestalt ausgibt, dass der Wert in eine Anzahl Leerzeichen 
übersetzt wird und dahinter dann ein *.
Dann sieht man nämlich auch auf dem Monitor sehr schön, wie sich der 
Sinus in den Daten abbildet ohne dass man lange auf Zahlen starren muss


                               *
                                      *
                                          *
                                            *
                                          *
                                      *
                               *
                        *
                     *
                   *
                     *
                        *
                               *


Das wäre 1 Sinusschwingung, umgesetzt in einer Tabelle mit 13 Einträgen

(Macht man heutzutage eigentlich keine Experimente mehr? Als 
Programmierneuling haben wir Stunden damit verbracht, solche Kurven zu 
generieren. Verschiedene Kurvenformen, Achsen, Achsbeschriftungen .... 
alles nur mit printf und zeilenweiser Ausgabe)

von mano (Gast)


Lesenswert?

ich würde es so schreiben

1
#define COUNT_MAX 2000
2
3
double f = 1100;       // Freq. vom Sinus
4
double fs = 4000;      // Abtastfreq.
5
double Ts = 1/fs;
6
double amp = 1.0;     // Verstärkung
7
8
double sinusPoints[COUNT_MAX];
9
10
for(int i = 0; i < COUNT_MAX; ++i) {
11
    sinusPoints[i] = amp * sin(2*pi*f*i*Ts);
12
}

von Karsten (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Vielleicht solltest du auch erst mal mit ein paar Testprogrammen
>
> anfangen und dir die erzeugten Werte erst mal auf dem Monitor per printf
>
> ausgeben lassen. Müssen ja nicht gleich 1100 Herz und eine Tabellenlänge
>
> von 8000 Samples sein. Mit kleineren Werten sieht man das auch schon
>
> sehr gut und man erkennt auch gleich viel besser wie die mathematischen
>
> Zusammenhänge sind.

Da werd ich mich am WE mal genauer mit beschäftigen...danke für die 
unterstützenden Hilfen.

Dank auch mano.

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.