• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

Görtzeljs findet Frequenzen nicht

xorg1990

New member
Hi, ich benötige für mein QRSSB Projekt eine Tone Erkennung, das macht man normalerwise mit den Görtzel Algorithmus
https://en.wikipedia.org/wiki/Goertzel_algorithm

Passenderweise gibt es sowas in JS schon.
https://github.com/Ravenstine/goertzeljs

Problem ist ich blicke da nicht ganz durch.
Ich möchte nach den Frequenzen 700,800 und 900 Hz suchen.
Erhalten tue ich in Goerzel.energies ein Object mit:
{
700 : float
800 : float
900 : flaot
}

Jetzt ist die frage wie groß müssen die Werte sein das man sagen kann 800Hz true??
Nutze ich den peak filter erhallte ich willkürlich true oder false aus Ausgabe. Obwohl nichts an die Soundkarte angeschlossen ist, erseiden ich Setze den sensitivity Wert auf 100.
Gebe ich ein 800Hz Sinus in die Soundkarte rein ändert sich daran nichts es ist einfach nur willkürlich.

Das mit den downsapling verstehe ich auch nicht so recht
The downsampleRate value decides how much the buffers are downsampled(by skipping every Nth sample). Default setting is 1.
See dtmf.js on how samples can be downsampled.
Habe ich gemacht.


In goertzeljs L116 steht
downSampledBufferLength = bufferLength / downSampleRate;
https://github.com/Ravenstine/goertzeljs/blob/master/build/goertzel.js#L116

Ja was nun? Ist nun ein Downsample Faktor gemeint ( z.b Faktor 2(Hälfte weniger) oder eine SampleRate zb 8000 Hz??

Geschrieben steht auch:
var buffer = [...]; // array of int samples
#
Wenn der Algorithmus integer verlangt dann hätte ich doch die Umwandlung in processSample gemacht, ohne das der zu nutzende User was davon mitbekommt

Meine Test Implementierung sieht so aus:
Code:
<!DOCTYPE html>
<html>

<head>
	<title>görtzel_test</title>
	<script src="goertzel.js"></script>
<script type="text/javascript">
function run(){

	function calcGoertzelRates(AudioRate){
		switch(AudioRate){
			case 22050 : return [Math.round(AudioRate/10),10];
			break;
			case 88200 : return [Math.round(AudioRate/10),10];
			break;
			case 48000 : return [Math.round(AudioRate/10),10];
			break;
			case 24000 : return [Math.round(AudioRate/10),10];
			break;
			case 96000 : return [Math.round(AudioRate/24),24];
			break;
			case 192000 : return [Math.round(AudioRate/40),40];
			break;
			default: return [Math.round(AudioRate/10),10];//44100Hz
		}
	}

	var scriptNodes = {};
			var keep = (function() {
				var nextNodeID = 1;
				return function(node) {
					node.id = node.id || (nextNodeID++);
					scriptNodes[node.id] = node;
					return node;
				};
			}());
	navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
	var audioCtx = new(window.AudioContext || window.webkitAudioContext || mozAudioContext || oAudioContext || msAudioContext)();
	var processor = keep((audioCtx.createScriptProcessor || audioCtx.createJavaScriptNode).call(audioCtx, 8192, 1, 1));
	var targetFrequencies = [700,800,900];
	var Fs = audioCtx.sampleRate;
	var GoertzelRate = calcGoertzelRates(Fs)[0];
	var GoertzelRateFactor = calcGoertzelRates(Fs)[1];
	var goertzel = new Goertzel({
    						frequencies: targetFrequencies,
    						sampleRate: GoertzelRate
  			});


function detectTones(buffer){
	Goertzel.Utilities.eachDownsample(buffer, GoertzelRateFactor, (function() {
      return function(sample, i, downSampledBufferLength) {
        var windowedSample = Goertzel.Utilities.exactBlackman(sample, i, downSampledBufferLength);
        goertzel.processSample(windowedSample);
      };
    })());
}

function audioProcess(e){
				var input = e.inputBuffer.getChannelData(0);
				var output = e.outputBuffer.getChannelData(0);
				for(var i=0;i<input.length;i++){
					output[i]=input[i];
				}
				detectTones(input);
				if(Object.values){
				var peak = Goertzel.Utilities.peakFilter(Object.values(goertzel.energies), 1)
				}else{
					var dataArray = new Array;
					for(var o in goertzel.energies) {
					    dataArray.push(goertzel.energies[o]);
					}
					var peak = Goertzel.Utilities.peakFilter(dataArray, 1)
				}

				console.log(goertzel.energies, peak)
				goertzel.refresh();
		}

var onRecordFail = function(e) {
		processor.disconnect();
		console.log(e);
		alert("Error getting audio: "+e.message);
	}

		processor.onaudioprocess = audioProcess;
		processor.connect(audioCtx.destination);

function init() {
			console.log(Fs);
			if(navigator.getUserMedia) {
				navigator.getUserMedia({ video: false, audio: true }, function(stream) {
					//fix https://bugzilla.mozilla.org/show_bug.cgi?id=934512
					window.source = audioCtx.createMediaStreamSource(stream);
					window.source.connect(processor);
					}, onRecordFail);
			}
};
init();
}
</script>
	</head>
	<body onload="run()">
	</body>
	</html>

Zum testen nehme ich Online Tone Generator - Free, Simple and Easy to Use.
Gehe dann mit eine klinke zu klinke kabel vom Laptop zum PC.
 
Ich habe noch mal ein zweites beispiel gemacht mit eine Oszillator node und 2 slider'n.
Das eigenartige ist je mehr man sich der gesuchten Frequenz nähert um so kleiner werden die Werte.:confused:

Die werte die ausgerechtet werden sind auch komnisch 5.34554 ... 7.6546574.
Da die Samples normiert sind auf -1 bis 1 dürften die Werte der Magnitude auch nicht über 1 gehen.
Bin mir aber nicht so sicher bei dem was da ich sage:panda:


Code:
<!DOCTYPE html>
<html>

<head>
	<title>görtzel_test</title>
	<script src="goertzel.js"></script>
<script type="text/javascript">
function run(){

	var sensinp = document.querySelector('#sens');
	var sensout = document.querySelector('#sens-value');
	var Freqinp = document.querySelector('#Freq');
	var Freqout = document.querySelector('#Freq-value');

	function calcGoertzelRates(AudioRate){
		switch(AudioRate){
			case 22050 : return [Math.round(AudioRate/10),10];
			break;
			case 88200 : return [Math.round(AudioRate/10),10];
			break;
			case 48000 : return [Math.round(AudioRate/10),10];
			break;
			case 24000 : return [Math.round(AudioRate/10),10];
			break;
			case 96000 : return [Math.round(AudioRate/24),24];
			break;
			case 192000 : return [Math.round(AudioRate/40),40];
			break;
			default: return [Math.round(AudioRate/10),10];//44100Hz
		}
	}

	var scriptNodes = {};
			var keep = (function() {
				var nextNodeID = 1;
				return function(node) {
					node.id = node.id || (nextNodeID++);
					scriptNodes[node.id] = node;
					return node;
				};
			}());
	navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
	var audioCtx = new(window.AudioContext || window.webkitAudioContext || mozAudioContext || oAudioContext || msAudioContext)();
	var processor = keep((audioCtx.createScriptProcessor || audioCtx.createJavaScriptNode).call(audioCtx, 8192, 1, 1));
	var targetFrequencies = [700,800,900];
	var Fs = audioCtx.sampleRate;
	var s = 1, x=0, F = 100;
	var osc = audioCtx.createOscillator();
	osc.type = 'sine'; 
	osc.frequency.value = F;
	var GoertzelRate = calcGoertzelRates(Fs)[0];
	console.log(GoertzelRate)
	var GoertzelRateFactor = calcGoertzelRates(Fs)[1];
	var goertzel = new Goertzel({
    						frequencies: targetFrequencies,
    						sampleRate: GoertzelRate
  			});


function detectTones(buffer){
	Goertzel.Utilities.eachDownsample(buffer, GoertzelRateFactor, (function() {
      return function(sample, i, downSampledBufferLength) {
        var windowedSample = Goertzel.Utilities.exactBlackman(sample, i, downSampledBufferLength);
        goertzel.processSample(windowedSample);
      };
    })());
}

function audioProcess(e){
				var input = e.inputBuffer.getChannelData(0);
				var output = e.outputBuffer.getChannelData(0);
				for(var i=0;i<output.length;i++){
					output[i]= input[i];
				}
				detectTones(output);
				if(Object.values){
				var peak = Goertzel.Utilities.peakFilter(Object.values(goertzel.energies), s)
				}else{
					var dataArray = new Array;
					for(var o in goertzel.energies) {
					    dataArray.push(goertzel.energies[o]);
					}
					var peak = Goertzel.Utilities.peakFilter(dataArray, s)
				}
					console.log(goertzel.energies, peak)
				
				goertzel.refresh();
		}

var onRecordFail = function(e) {
		processor.disconnect();
		console.log(e);
		alert("Error getting audio: "+e.message);
	}

		processor.onaudioprocess = audioProcess;
		osc.connect(processor);
		processor.connect(audioCtx.destination);
		osc.start();

function init() {
			console.log(Fs);
			if(navigator.getUserMedia) {
				navigator.getUserMedia({ video: false, audio: true }, function(stream) {
					//fix https://bugzilla.mozilla.org/show_bug.cgi?id=934512
					window.source = audioCtx.createMediaStreamSource(stream);
					window.source.connect(processor);
					}, onRecordFail);
			}
};

sens.oninput = function(){
	s = parseFloat(sens.value);
	sensout.innerHTML = s;
	}

Freqinp.oninput = function(){
	F = parseFloat(Freqinp.value);
	osc.frequency.value = F
	Freqout.innerHTML = F;
	}	
//init();
}
</script>
	</head>
	<body onload="run()">
		PeakFilter<input id="sens" type="range" min="0" max="100" step="0.05" value="1">
		<span id="sens-value">1</span></p><br>
		Frequency<input id="Freq" type="range" min="0" max="3000" step="10" value="100">
		<span id="Freq-value">100</span></p><br>
	</body>
	</html>
 
Naja, der Goertzel Algorithmus macht genau das was ich will er findet eine Frequenz, der Fourier analysiert ein ganzes Spektrum. Mann muss immer den Array Index für den entsprechenden Ton errechen.
Die FFT ist auch nicht Herz genau. Ein "Frequenz Eimerchen" kann auch mal 2 Herz betragen.
Da es kein fixed sampleRate gibt:mad: bei der Web Audio Api, ist das alles etwas schwierig.

Mittlerweile hab ich das Problem gefunden. Es liegt an den Blackman Window, nehme jetzt das Hamming.
goertzel.refresh(); muss auch immer aufgerufen werden sonnst kommt gülle raus.

Der Peak Filter ist aber für die Katz, der funzt nicht. Habe mir meinen eigenen geschrieben. Die Werte sind anscheid nochmal normiert komme bei Maximaler Amplitude nicht über 0.28f.

Habe mir einen Slider gemacht der von 0 bis 1 fährt mit step 0.02, der Rest ist klar.

Es gibt allerdings ein neues Problem. Wenn der Empfänger mit seinem Transceiver nur 50Hz daneben liegt, dann bringt mir der beste goertzel nix. Es ist halt die Frage ob der Peak filter ausreicht oder ob man eine art Frequenz Korrektur benötigt.

Sollte der Peak filter ausreichen, weiß ich nicht ob sich aus den Datenström wider eine Menschliche Stimme rekonstruieren lässt, da ich zuvor mit 6500 Hz mische und nur das obere Seitenband aussende.

Bei der SSB Modulation gibt es kein Trägersignal:
Obwohl die Amplitude des SSB-Signals gewisse Ähnlichkeit mit der modulierenden Niederfrequenz aufweist, kann SSB nicht mit einem Hüllkurvendemodulator demoduliert werden. Der Grund ist das Fehlen des Referenzsignals („Trägerfrequenz“).
https://de.wikipedia.org/wiki/Einseitenbandmodulation#Demodulation

Das Prinzip einer Frequenz Kalibrierung wird hier gut beschreiben:
Frequency Calibrator

Das wird mich noch eine weile beschäftigen.
 
Wenn er jetzt funktioniert... ich würde trotzdem mal testen, was da schneller ist. Ich würde auf den nativen FFT tippen.

- - - Aktualisiert - - -

Um einen Frequenzshift ist mit dem FFT dann auch kein großes Problem: einfach einen größeren bereich im Frequenzspektrum durchsuchen und nicht nur eine Frequenz.
 
kkapsner schrieb:
Wenn er jetzt funktioniert... ich würde trotzdem mal testen, was da schneller ist. Ich würde auf den nativen FFT tippen.
Ja funktioniert jetzt, man muss nur auf die reinfolge achten. Zuerst das görtzel energeis Object durchiterrieren(kann man das so schreiben), dann refresh und zu letzt processSample aufrufen.
Allerdings ist das sowas von empfindlich. Habe so ein alten Philips PM 5109 RC Generator
Wenn ich da 700Hz eindrehe kommen aber nicht genau 700Hz raus der görtzel greift nicht. Ich kann zwar jetzt via peak Filter die Amplituden unempfindlicher stellen aber irgendwann ist der Signal Rausch Abstand gleich 0 und man hat nur noch true als Ergebnis.


Die native FFT ist mit Sicherheit schneller, aber mir gefällt das Analyser Object überhaupt nicht, da bei einer FFT size von 2048 Schluss ist. Ich hätte gerne bis 524288.

Gerade bei Hohen Abtastraten ist das schlecht.
Bei 192000Hz hast du ein FFT bin width von 93.75 Hz
192000/2048 = 93.75 HZ

Ist die Abtastrate geringer ist dafür die Berechnung schneller 1/Bin width.
FFT Information

Beim Görtzel weiß ich das nicht genau.

Warum man bei der Nativen FFT nicht auf einen großeren Input wert gesetzt hat?
 
Sehe gerad da ist was falsch, in der Web Audio Spezifikation steht:
The size of the FFT used for frequency-domain analysis. This must be a power of two in the range 32 to 32768, otherwise an IndexSizeError exception must be thrown. The default value is 2048. Note that large FFT sizes can be costly to compute.
https://www.w3.org/TR/webaudio/#attributes-24
32768!! Damit kann man arbeiten.

Bei mozilla steht geschrieben:
The fftSize property's value must be a non-zero power of 2 in a range from 32 to 2048; its default value is 2048.
https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/fftSize

Eins noch:
Wie bereche man von den Goertzel energies die db
War es 20*Math.log10() oder 10*Math.log10().
Bei Magnitude eigentlich 10*Math.log10().
 
Zuletzt bearbeitet:
Aus einem Signal alleine kann man keine dB berechnen... da es eine relative Skala ist, brauchst du immer eine Referenz.
 
Hä what? Reverenz??

Na klar geht..., über den Root Mean Square:
javascript - Is there a way get something like decibel levels from an audio file and transform that information into a json array? - Stack Overflow
var decibel = 20 * (Math.log(rms) / Math.log(10));

Wäre so zusagen bei der Amplitude so, also Time Domain:
signal processing - How can I calculate audio dB level? - Stack Overflow

Hm, bei der Magnitude anscheid auch:
https://www.rohde-schwarz.com/us/fa...db-and-phase-in-degrees.-faq_78704-30465.html


Bei einem Silder(der von 0 bi 1 geht) ist es nun wider 10*Math.log10()
html5 - Web Audio Api working with Decibels - Stack Overflow

Bin mir ziemlich sicher mal gelesen zu haben das man bei Magnitude 10*log10 rechnen muss.

Ist das Ergebnis vom Görtzel überhaut in form einer Magnitude? Es gibt ja anscheint auch kein imaginär Anteil.
 
Zuletzt bearbeitet:
tsseh schrieb:
hatten wir alles schon mal. du musst das auch verstehen und nicht nur wild zusammenwürfeln.
Ja ich weiß das wir das schon mal hatten wusste nur den Beitrag nicht mehr wie merkst Du dir sowas?
In Diesem Fall war ich nur verunsichert weil nirgends steht was von 10*log10 im falle einer Magnitude.
Siehe rohde-schwarz link.

Wild zusammenwürfeln tue ich auch nix, ich hab halt nicht studiert woher soll ich den wissen. Dafür habe ich doch euch:p. Habe nach der 10ten einen vernünftigen Beruf gelernt und mich da immer weiter hochgearbeitet. Habe meine Vorarbeiter gemacht, das reicht. Eigentlich wollte ich Polier aber nee das machst du nur dem dummen Maxen.

Mittlerweile bin ich viel weiter als vor 2,3 Jahren noch, da wusst ich nix war ein Kaltstart.
-Lebenslauf ende.
 
Zurück
Oben