Category Archives: Programming

Hałas wentylatora czyli Szybka Transformata Fouriera (FFT) w domowych warunkach

Wstęp.

„back to basics” czyli mały powrót do tematyki moich studiów połączony z „za” dużą ilością wolnego czasu na urlopie.
Chłodząc się ostatnio za pomocą małego wiatraczka przypomniały mi się zajęcia na katedrze wibroakustyki poświęcone analizie hałasu oraz drgań od urządzeń mechanicznych – a zwłaszcza wentylatorów. Postanowiłem przeprowadzić mały pomiar a następnie przeprowadzić jego analizę. Oczywiście mówimy tu o totalnej amatorce – przecież nie dysponuję laboratorium – ale idea pozostaje ta sama.

Tor pomiarowy.

Jak już wspominałem nie dysponuję laboratorium (komora bezechowa, czułe mikrofony, oscyloskop) więc wszystko zostało sprowadzone do absolutnego minimum.

Tor pomiarowy wyglądał następująco:

Jak już widać, że za mikrofon posłużyła funkcja dyktafonu z telefonu komórkowego Nokia E5. Nagrałem 5s materiału w najlepszej jakości (jaką tylko oferował telefon).

Następnie rozpakowałem plik mp4 do wav i przeszedłem do analizy.

Obliczenia.

Mając już plik wav pozostała już szybka obróbka w Matlabie.

Pierwszy plik to tylko pomocnicza funkcja wyliczająca kilka podstawowych parametrów sygnału wejściowego.


function [suma,srednia,sr_geom, wariancja] = obliczenia(x)
%funkcja licząca sume elementow, srednia i wariancje
suma = 0;
suma_kw = 0;
sr_geom = 1;
for i=1:(length(x))
suma = suma + x(i);
sr_geom = sr_geom*((abs(x(i)))^(1/length(x)));
suma_kw = suma_kw + (x(i))^2;
end
srednia = suma/(length(x));
wariancja = suma_kw/(length(x) - 1);

 

Poniższy kod jest odpowiedzialny za całą resztę.


%Analiza danych z pliku wave

%Wczytanie pliku wave
[dane_wav,fs,format]=wavread('Wentylator.wav');

%wyrysowanie danych z pliku
wykresy_sygnalow=figure;
set(gcf,'Name', 'Wykresy wave');
plot(dane_wav);

%Wyliczanie podstawowych parametrow sygnalu
amplituda = (max(dane_wav) - min(dane_wav))/2;
maxim=max(dane_wav);
minim=min(dane_wav);
[suma srednia srednia_geom wariancja]=obliczenia(dane_wav);
wyniki(1) = maxim;
wyniki(2) = minim;
wyniki(3) = suma;
wyniki(4) = srednia;
wyniki(5) = srednia_geom;
wyniki(6) = wariancja;
wyniki(7) = amplituda;

%Wypisanie wynikow na ekran
wyniki'

%Wylicznie Szybkiej Transformaty Fouriera dla zadanego sygnalu
y1 = fft(dane_wav);
fs=2*fs;
for k=1:length(y1),
y1(k) = sqrt(real(y1(k))*real(y1(k))+imag(y1(k))*imag(y1(k)));
end;
zm = round(length(y1)/2);
y1 = y1(1:zm);
clear zm;
elementy = length(y1);
krok = (fs/2)/(elementy-1);
wfs1 = 0:krok:(fs/2);

%Wyrysowanie wykresu w dziedzinie czestotliwosci
wykresy_fft=figure;
set(gcf,'Name', 'Wykresy fft wave');
plot(wfs1,y1);

Pierwszy krok to wczytanie pliku wave do przestrzeni roboczej matlaba. Wykonujemy to za pomocą funkcjiwavread.

Następnie mamy wyświetlenie badanego sygnału na ekranie oraz wypisanie parametrów sygnału. Od linijki 28 następuje wyliczenie transformaty Fouriera-czyli reprezentacja sygnału w dziedzinie częstotliwości (linijka 29 to nic innego tylko wynik twierdzenia o próbkowaniu [tw. Kotielnikowa-Shannona / warunek Nyquista] ).

Na samym końcu następuje wyrysowanie wyników na ekran.

 Wyniki.

Sygnał wejściowy z pliku wave:

Analizowany sygnał w dziedzinie częstotliwości:

Oczywiście należy pamiętać jak prymitywny był tor pomiarowy i jak wiele”śmieci” zarejestrowaliśmy (turbulencje związane z obudową wentylatora, drgania poprzez luzy na konstrukcji, zakłócenia z zewnątrz) jednakże na podstawie uzyskanych wyników widać w jakim zakresie wentylator generuje hałas.

P.S.

Plik z nagraniem pracy wentylatora znajduje się tutaj:  Wentylator.

Visual Basic 2010 Express – dostęp do plików MS Excel

Na dzisiaj coś z innej beczki – podpinanie się przez własną aplikację w Visual Basic’u do pliku excel.

Na wstępie robimy drobny import:


Imports Excel = Microsoft.Office.Interop.Excel

Reszta idzie w ten sposób:


Dim ExcelApp As Excel.Application
Dim ExcelWorkBook As Excel.Workbook
Dim ExcelWorkSheet As Excel.Worksheet
ExcelApp = New Excel.Application()

OpenFileDialog1.ShowDialog()

ExcelWorkBook = ExcelApp.Workbooks.Open(OpenFileDialog1.FileName)
ExcelWorkSheet = ExcelWorkBook.Worksheets(1)

'Pokaz zawartosc komorki B2
MsgBox(ExcelWorkSheet.Cells(2, 2).value)

'Nadpisz zawartosc komorki B2
ExcelWorkSheet.Cells(2, 2) = "http://www.it-guy.pl"

ExcelWorkBook.Close()
ExcelApp.Quit()

Planarność grafów

Z okazji sesji poprawkowej zostałem poproszony o pomoc przy jednym zagadnieniu: wyznaczenie planarności grafów. Poniższy kod jest wynikiem tej pomocy (nie jest on może idealny, ale stanowi punkt startu dla bardziej rozbudowanego programu).


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<conio.h>

const int Max_rozmiar = 100;         // maksymalny rozmiar grafu

int graf[Max_rozmiar][Max_rozmiar]; //tablica polaczen pomiedzy
			   // wierzcholkami graf[x][y] = 1-jest
		       // graf[x][y] = 0 brak polaczenia

int stopien[Max_rozmiar];	// tablica zawiera stopnie wierzcholkow
int rozmiar;                // rozmiar grafu;

// Deklaracje funkcji

void losuj_graf(float stopien_pelnosci);  // losowo inicju polaczenia w grafie
void zeruj_graf();   // zeruje polaczenia w grafie
void wyswietl();	 // wyswietls polaczenia grafu na ekranie
int redukcja();		// usuwa z grafu wszystki krawedzie o
// stopniu mniejszym niz 3 gdy nie wplywaja one na planarnosc
int znajdzK5();		// znajduje podraf K5
int znajdzK33();    // znajduje podraf K3,3
int planarnosc();   // sprawdza czy graf jest planarny

// Definicje funkcji

void losuj_graf(float stopien_pelnosci){
	if ( (stopien_pelnosci >1 ) || (stopien_pelnosci <= 0 ))
	{
		printf("stopien pelnosci musi byc z przedzialu (0,1>\n");
		return;
	}
	int i,j;

	for(i=1;i<rozmiar;i++)
		for(j=0;j<i;j++)
			if ( rand() / (double)RAND_MAX < stopien_pelnosci) {graf[i][j] = 1;
			graf[j][i] = 1;
			stopien[i] += 1; // aktualizacja stopnia wierzcholka
			stopien[j] += 1;
			}
			else {
				graf[i][j] = 0;
				  graf[j][i] = 0;
			}
}

void zeruj_graf(){
	int i,j;
	for(i=0;i<Max_rozmiar;i++){
		stopien[i] = 0;
		for(j=0;j<Max_rozmiar;j++)
			graf[i][j] = 0;
	}

}

void wyswietl(){
	int i,j;
	if (rozmiar >20){
		printf("Rozmiar grafu wynosi %i i moze sie nie zmiescic",rozmiar);
		printf(" na erkranie,\ndlatego nie bedzie wyswietlony\n");
		return;
	}
	printf("Oto polaczenia w grafie.\n");
	printf("1 - istnieje polaczenie.\n");
	printf("0 - brak polaczenia.\n");
	printf("W nawiasie podano stopien wierzcholka.\n\n       ");

	for(i=0;i<rozmiar-1;i++) printf("%2i ",i+1);
	printf("\n");
	for(i=0;i<rozmiar;i++){
		printf("%2i(%2i) ",i+1,stopien[i]);
		for(j=0;j<i;j++)
			printf("%2i ",graf[i][j]);
		printf("\n");
	}

}

// funkcja usuwa z grafu wszystki wierzcholki stopnia 1 i 2
// poniewaz nie wplywaja one na planarnosc grafu
int redukcja(){
	// zliczanie ilosc polaczeń wierzcholka
	int i,j,k,l;
	int zmiany = 0; //zmienna czy funkcja zmienia graf
	for (i=0;i<rozmiar;i++){
		if (stopien[i] == 1)
		{

			k = 0;
			while (graf[i][k] == 0) k++; // znajdujemy jedynego sasiada
			// usuwamy polaczenia
			graf[i][k] = 0;
			graf[k][i] = 0;
			stopien[k] -= 1;
		}
		if (stopien[i] == 2){
			k=0;
			while (graf[i][k] == 0) k++;
			l=k+1;
			while (graf[i][l] == 0) l++;
			// usuwamy polaczenia

			graf[i][k] = 0; graf[k][i] = 0;
			graf[i][l] = 0; graf[l][i] = 0;

			// uaktualniamy stopien wierzcholkow
			if (graf[k][l] == 1) {
				stopien[k] -= 1;
				stopien[l] -= 1;
			}
			// dodajemy polaczenie
			graf[k][l] = 1; graf[l][k] = 1;

		}
		if (stopien[i] < 3)
		{
			// usuwamy wierzcholek z grafu
			// ostania kolomna zastepuje kolumne i
			for (j=0;j<rozmiar;j++) graf[i][j] = graf[rozmiar-1][j];
			// ostani wiersz zastepuje wiersz i
			for (j=0;j<rozmiar;j++) graf[j][i] = graf[j][rozmiar-1];
			stopien[i] = stopien[rozmiar-1];
			rozmiar -= 1;
			i -= 1;
			// zaznaczamy, ze dokonalismy zmian
			zmiany = 1;
		}

	}

	return zmiany;
}

int znajdzK5(){
	int i1,i2,i3,i4,i5;
	// przeszukujemy kolejno wszystkie piatki
	for (i1=0;i1<rozmiar-4;i1++)
	  for (i2=i1+1;i2<rozmiar-3;i2++)

		if (graf[i2][i1] == 1)
		for (i3=i2+1;i3<rozmiar-2;i3++)

		  if ((graf[i3][i1]==1)&&(graf[i3][i2]==1))
		  for (i4=i3+1;i4<rozmiar-1;i4++)

			if ((graf[i4][i1] == 1)&&(graf[i4][i2] == 1)&&(graf[i4][i3] == 1))
			for (i5=i4+1;i5<rozmiar;i5++)

				if ( (graf[i5][i1]==1)&&(graf[i5][i2]==1)&&
					(graf[i5][i3]==1)&&(graf[i5][i4] == 1) )
				{
				 return 1; // znaleziono podgraf K5
				}

	return 0;
}

int znajdzK33(){
	int i1,i2,i3;   // pierwsza trojka
	int j1,j2,j3;	// druga trojka
	for(i1=0;i1<rozmiar-2;i1++)
	 for(i2=i1+1;i2<rozmiar-1;i2++)
	  for(i3=i2+1;i3<rozmiar;i3++)
	  {
		for(j1=0;j1<rozmiar-2;j1++)
			if((j1!=i1)&&(j1!=i2)&&(j1!=i3))
			 if((graf[j1][i1]==1)&&(graf[j1][i2]==1)&&(graf[j1][i3]==1))

 			 for(j2=j1+1;j2<rozmiar-1;j2++)
			  if((j2!=i1)&&(j2!=i2)&&(j2!=i3))
			   if((graf[j2][i1]==1)&&(graf[j2][i2]==1)&&(graf[j2][i3]==1))

			   for(j3=j2+1;j3<rozmiar;j3++)
			    if((j3!=i1)&&(j3!=i2)&&(j3!=i3))
			     if((graf[j3][i1]==1)&&(graf[j3][i2]==1)&&(graf[j3][i3]==1))
				 {
					 return 1; // znaleziono podgraf K3,3

				 }
	  }

	return 0;
}

int planarnosc(){
	if (rozmiar <5) {
		printf("Graf jest planarny poniewaz po redukcji\notrzymano ");
		printf("graf o mniej niz 5 wierzcholkach\n");
		printf("wszystkie grafy o mniej");
		printf(" niz 5 wierzcholkach sa planarne\n");
		return 1;
	}
	if ( znajdzK5() == 1) {
		printf("Graf nie jest planarny poniewaz ");
		printf("istnieje podgraf homeorficzny z K5\n");
		return 0;
	}
    if ( znajdzK33() )
	{
		printf("Graf nie jest planarny poniewaz ");
		printf("istnieje podgraf homeorficzny z K3,3\n");
		return 0;
	}
	// Zgodnie z twierdzeniem kuratowskiego gdy nie znalezlismy podgrafow
	// k3,3 i k5 w zredukowanym grafie graf jest planarny
	printf("Graf jest planarny poniewaz ");
	printf("nie istnieje zaden podgraf homeorficzny z K3,3 lub K5\n");
	return 1;

}

void main(){
float p;  // prawdopodobienstwo
int koniec = 0;
char c;
//inicjacja generatora liczb losowych

srand( (unsigned)time( NULL ) );

while(koniec == 0)
{
	clrscr();    //czyszczenie ekranu
	rozmiar = 0;
	p = 0;
	zeruj_graf();
	// pobieramy dane grafu
	printf("Podaj rozmiar grafu (2-%d)\n",Max_rozmiar);
	scanf("%i",&rozmiar);
	while ( (rozmiar<2)||(rozmiar>Max_rozmiar) ){
		printf("Podales zly rozmiar grafu.");
		printf("Podaj rozmiar grafu (2-%d) jeszcze raz\n",Max_rozmiar);
		getchar();
		scanf("%i",&rozmiar);

	}
	printf("Rozmiar grafu: %d\n",rozmiar);
	printf("Podaj prawdopodobienstwo wystapienia krawedzi z przedzilu (0-1)\n");
	printf("Aby rozlosowac polaczenia grafu\n");
	scanf("%f",&p);
	while ( (p <= 0)||(p>1) ){
		printf("Podales zle wartosc prawdopodobienstwa.\nSprobuj jeszcze raz\n");
                getchar();
		scanf("%f",&p);
	}
	printf("Prawdopodobienstwo wystapienia krawedzi: %f\n",p);

	losuj_graf(p);  // losujemy polaczenia w grafie
	wyswietl();		// wyswietlamy graf o ile nie jest za duzy
	while (redukcja()); // usuwamy wszystkie wierzcholki stopnia 0,1,2
						// ktore nie maja wplywy na planarnos
	planarnosc();      // sprawdzamy plnarnosc
	printf("Czy kontynuowac ? (t/n)\n");
	getchar();
	c = getchar();
	if ( (c== 'n')||(c == 'N') ) koniec = 1;
}
}

VBA: Sumowanie kolumny danych o zmiennej długości serii

Ku pamięci:

Załóżmy sytuację, że mamy kolumnę danych liczbowych, której długość się zmienia (jakiś wyciąg z bazy danych, pomiary, cokolwiek…) i chcemy dokonać jakiś obliczeń matematycznych na tej serii. Oczywiście zawsze można „przelecieć” wszystko za pomocą pętli aż do znalezienia pustej komórki i przy okazji wyliczać sumę lub średnią. Kod trochę się skomplikuje, gdy będziemy chcieli policzyć coś bardziej zaawansowanego (odchylenie standardowe, kwantyle) – wtedy aż się prosi wykorzystać wbudowane funkcje Excel’a.

Pierwszym krokiem  będzie znalezienie pozycji ostatniego wiersza, który ma w sobie dane. Można to zrobić poniższą funkcją:


Function LastRowPosition() As Long
LastRowPosition = ActiveSheet.UsedRange.Row - 1 + ActiveSheet.UsedRange.Rows.Count
End Function

Następnie musimy się dobrać do odpowiedniej funkcji Excel’a (robimy to poprzez WorksheetFunction.[funkcja] gdzie jako parametr podajemy odpowiednio sprecyzowany zakres danych):


ActiveSheet.Cells(2, 3).Value = WorksheetFunction.Sum(Range("A1:A" &  LastRowPosition))

Własny kanał RSS w PHP

Kolejny, programistyczny, wpis „ku pamięci”.

Poniższe rozwiązanie generuje prosty kanał RSS, który jest bezbłędnie interpretowany przez większość czytników (wliczając Google Reader).

Plik db.php przechowuje ustawienie dostępu do bazy MySQL.


//db.php

<?php

$user 	= "username";
$pass 	= "password";
$db		= "localhost";
$dbname    = "dbname";
$polaczenie  = mysql_connect($db, $user, $pass) or die('Brak połączenia z serwerem MySQL. Błąd: '.mysql_error());
$baza 	 = mysql_select_db($dbname, $polaczenie);

?>

Plik rss.php jest odpowiedzialny za wygenerowanie odpowiedniego pliku XML – po zaimplementowaniu poniższego kodu wystarczy dodać http://www.twojastrona.pl/rss.php do czytnika.


//rss.php
<?php

header('Content-Type: application/xml; charset=utf8');
include('db.php');
$rss .= "<?xml version=\"1.0\" encoding=\"utf8\"?>\r\n";
$rss .= "<rss version=\"2.0\">\r\n";
$rss .= "<channel>\r\n";
$rss .= "<title>Tytył</title>\r\n";
$rss .= "<link>http://www.twojastrona.pl</link>\r\n";
$rss .= "<description>Opis......</description>\r\n";
$rss .= "<image>\r\n";
$rss .= "<title>Logo</title>\r\n";
$rss .= "<link>http://www.twojastrona.pl</link>\r\n";
$rss .= "<url>http://www.twojastrona.pl/logo.jpg</url>\r\n";
$rss .= "</image>\r\n";
echo $rss;
$db_query = 'SELECT * FROM blog ORDER BY id DESC';
mysql_query('SET character_set_connection=utf8');
mysql_query('SET character_set_client=utf8');
mysql_query('SET character_set_results=utf8');
$db_wynik = mysql_query($db_query) or die(mysql_error());
while($row = mysql_fetch_array($db_wynik))
{
echo '	<item>
<title>'.$row["tytul"].'</title>
<link>'.$row["link"].'</link>
<description>'.$row["wpis"].'</description>
</item>';
}
?>
</rss>
</xml>

Uwaga: Powyższy kod jest strasznie prymitywny, ale … działa.

Na koniec polecam bardziej eleganckie (dość rozbudowana klasa obsługująca kanały RSS napisana w PHP) rozwiązanie znalezione w sieci: Link