Spektroszkóp C++/Qt - kérdések

Fórumok

Sziasztok!

Arra lenne szükségem, hogy egy hangkártya bemenetén kapott jelet valós időben tudjam kiolvasni egy programmal. Próbálkoztam guglizni rá, de nem jött olyan találat, ami nekem kell, ha van is, akkor az sajnos elvész az egyéb találatok között. Valamilyen doksit, leírást ha tudna valaki adni, azt nagyon megköszönném.

UPDATE: ez a része megoldódott, erre nem kérek több választ, köszi! :)

Hozzászólások

Milyen oprendszerre kell?

Biztos vannak platformfüggetlen libek is, de ha nem kell platformfüggetlennek lenni, akkor egyszerűbb lehet egy low-level API-t használni.

Windowson valami DirectSound-szerű API-t lehet a legcélszerűbb használni.

Linuxon hangból nagy káosz van, el kell dönteni, hogy pulseaudio, oss, Alsa, Jack, vagy valami egyébbel akarsz-e dolgozni.

Alsa körökben capture-nek hívják azt, amit keresel. Vannak fenn példaprogramok is az alsa oldalain. Az más kérdés, hogy nekem legutóbb nem működött úgy szó szerint, ahogy ott volt fenn... Átalakítás előtt meg meggondoltam magam, hogy mégsem akarok ilyet programozni. Ha sikerül működőt írnod és megosztod velünk, az király volna...

AlSA? Nézd meg az "arecord" esetleg az alsamixer forrását - kicsit hosszadalmasabb de ott van minden. Esetleg a forrásra ráereszted a doxygen -t az mindent összeszed és szép, html formában összerakja. A saját programocskáimat is így dokumentálom, nagyon hatékony.
Open source - ilyenkor jól jön ;)

* Én egy indián vagyok. Minden indián hazudik.

Az ALSA tényleg egy nagyon nehéz szülés, elvileg sokat tud, de szerintem nincs jól dokumentálva.

Ott van még az ősi OSS, ami még kernelből emulációval megy. Azt nagyon egyszerű programozni és egészen biztosan tud felvételt is, nézz utána, nagyságrendekkel könnyebb használni ilyen egyszerű célra, mint az ALSA-t. A többit nem ismerem...
--
http://www.open-st.eu

+1, feiratkozás. :D Jó megoldás érdekelne engem is.

subscribe
- - - - - - - - - - - - - - - - - - - - - - - - -
Fejlődőképes hiperláma, és okleveles érdekfeszítő
Aki érti a humort az mindent tud. Aki nem érti az viszont mindenre képes.

Esetleg a DarkIce forrását is megnézheted.

-----
"Én vagyok a hülye, hogy leállok magával vitatkozni."

Jack API erre van. (jack audio connection kit)

---------------------------------------------------------------------------------------
Unix is simple. It just takes a genius to understand its simplicity. — Dennis Ritchie

Az eredeti kérdést megoldottam, látom többeket is érdekel a megoldás. Mivel nem biztos, hogy tisztán fogalmaztam, a program azt csinálja, hogy adott ideig vesz fel a hangkártya bemenetről, és egyből integerként írja ki egy dat file-ba. Akinek ez kell, az írjon privátban, szívesen elküldöm.

Közben felmerült egy új probléma, nevezetesen a szálkezelés. Nem maga a megvalósítás, mert arra találtam jónak tűnő doksikat, a gondom ott kezdődik, hogy hol, melyik osztály indítson új szálakat.

A program egy gamma spektroszkóp lesz, a detektor jelét a hangkártyán olvassa be (mivel egy céleszköz milliós tétel lenne, meg azért nem adnak diplomát). Jelenleg úgy képzelem a programot, hogy van egy Control objektum, ami megadott paraméterekkel levezérli a mérést. A feladat egy adott minta felületének végigmérése, például végig kell menni egy 10x10-es négyzeten, ez ugye 100 mérés.

Ez a vezérlő pozícionálná a detektorfejet, indít egy adatrögzítést, majd amit van befejezett mérés el is indítja az adatok feldolgozását. Ezt szeretném több szálon megoldani, hogy amíg megy az adatrögzítés, addig is számoljon. Valószínűleg sokmagos vas lesz alatta, tehát egyszerre többet is számolhatna.

A problémám, hogy miket rakjak külön szálakba? Az egyik lehetőség, hogy a vezérlő kap egy külön szálat, és az hív meg új szálakat a számolásokhoz. Itt is van két lehetőség, egy külön vezérlő objektum a számolásokhoz, amely 2-3 új szálat nyit, amelyek számolnak a meglévő adatfájlokkal, és amit egy kész, indít egy újat. Vagy a mérés vezérlője indítson új szálakat a számolásoknak, csak ekkor mi van, ha adatrögzítés közben végez egy számolás? Esetleg ne a vezérlő, hanem csak az adott adatrögzítés legyen szálon, és közben figyeli a számolásokat?

Csak egy megjegyzés. Tényleg kell a közös memória terület? Ha anélkül megoldható akkor a thread lehet egy child. Én írtam mutithreaded programot Linux -ba és windows -ba. Jó-jó de ez egy ráerőltetett filozófia. Szigorúan véve akkor lehet (talán) jó, ha nagyobb méretű, közös adattömbök lennek. Viszont, amit megnyersz a réven elveszítheted a vámon - a thread szinkronizálás és a megbízhatóság. Használd a posix eszközöket pl. pipe mint interprocessz kommunikáció (esetleg UNIX domai9n socket, vagy akár sima TCP socket később a feladatot akár egy másik gép is végezheti, ennél gyorsabb nincs).

* Én egy indián vagyok. Minden indián hazudik.

Nem, a feldolgozó megkap egy filenevet, és utána azzal ellesz magában. Ezért is kérdeztem, mert úgy indultam neki a témának, hogy google, és a pthread-nél lyukadtam ki. Annyi kommunikáció kell összesen, hogy a szál vagy akármi visszajelezzen, ha végzett, és a vezérlő indíthatja a következőt. Mondjuk ez meg a másik, van értelme a processzor által kezelt szálak számánál maximalizálni az egyszerre futó számításokat, vagy egyszerűbb, ha amint kész az adatfelvétel, indul is a számítás, és akár 50-100 is fut egyszerre? ÉS akkor még a visszajelzés sem kéne. Sajnos arról még körülbelül sincs infóm, hogy meddig tart egy adatfelvétel, és meddig a feldolgozása. Több gép biztos nem lesz a feladatra.

Szerintem a thread -nek akkor látom igazi értelmét, ha a memóriában van az adat. Nem tudom a hangkártya mintáinak beolvasását, de gondolom az is egy fájl, ha így van próbáld ki a liboop -ot, az maximáslisan kihasználja a rendszer IO sebességét, és mondhatni párhuzamosan olvasol és feldolgozol.
A threadek számát 50 -re hát, ilyet nem csináltam és nem is igen mernék. Ez majdnem úgy hangzik mint a rekurzió.

* Én egy indián vagyok. Minden indián hazudik.

Na végül is pthread-el csináltam, mert mégis csak kell egy közös változó, de csak az egyik írja, a többi olvassa, tehát nem vészes. Egyelőre egy-egy külön szálat csináltam a beolvasásnak és a feldolgozásnak, az időviszonyok és a vas függvényében majd a feldolgozó csinálhat többet, vagy maga az algoritmus lesz többszálas, de lehet nem lesz rá szükség.

Mi történik ekkor:

Egyik szál:

a=5;
b=a+7;

Másik szál:

a=4;

Mi van, ha a másik szál akkor kotorászik a-ba, amikor még a b=a+7 összeadás nem történt meg? Na mi? b-ben 11-lesz. Míg első esetben 12. Ez azért van, mert az egyik szálban történő műveletbe beleszakíthat a másik. Azt hívjuk atomi műveletnek, amibe a másik nem szakíthat bele. Remélem, elég világos, nem vagyok jó magyarázó...

Egyetlen közös map van, amiben szerepel a file előre legenerált neve és egy bool változó, hogy a file írása befejeződött-e. Ezt csak a felvevő szál írhatja, a feldolgozó ha épp nincs mit számolnia, akkor egy while ciklussal figyeli, hogy mikor válik a köveztkező file feldolgozhatóvá.

Akkor ha jól értem a magyarázatod, a korábbi kérdés arra vonatkozott, hogy megváltozik-e a program működése attól függően, hogy mikor piszkál bele az író szál. Azt hiszem, nyugodtan mondhatom, hogy nem.

Az még előfordulhat, hogy egy ilyen történik:

kezdetben: a.b=1 és a.c=3;

a.b=4; <-- Ha ez után bejön a másik szál?
a.c=5; <-- Ha ez után bejön a másik szál?

Másik szál:

eredmeny=a.b+a.c

Attól függően, hogy hol jön be a másik szál, lehet az eredmény 4, 7, 9 (ha jól néztem).

Pedig az egyik csak olvasta.

Szóval ilyenekre figyelni.

Ha a program az esetek 99 %-ában működik, de néha nem, simán lehet, hogy ilyen versenyhelyzet a hiba. És ennek gusztustalan a debuggolása (szakdolgozat meg egyéb nálam ilyenekről szólt. Poén, de nagy szívás tud lenni)


void Control::processControl()
{
for (map::iterator i = isTheFileFinished.begin(); i != isTheFileFinished.end(); i++) {
while((*i).second != true) sleep(1);
calculate((*i).first);
}
}

Ennyi maga a kód, szerintem ez olyan faék egyszerűségű, hogy ilyen gond nem lehet, de ugye én nem programozó vagyok. A későbbiekre nézve köszi az észrevételeket!

"wachag" fordítása tökéletes és szemléletes.
Ököl szabály, közösen használt (bármi módon) változót védeni kell - számtalan módon, kiszámíthatatlan hibákat okozhat, ezen hibák felderítése hihetetlenül nehéz, a debugger az ilyen hibákhoz szinte használhatatlan. Az ilyen dolgokat nem szabad félvállról venni, roppant fájdalmas hibákat okozhat.
Az ilyen változókhoz a pthread-ben mutex -et lehet használni (windows -ban a CriticalSession is megteszi).

* Én egy indián vagyok. Minden indián hazudik.

Büszke vagyok magamra :-)

Egyébként a debug főleg azért rossz, mert általában a hibajelenség úgy lép elő, hogy valami időzítéses dolog történik (magyarul rosszkor rossz helyen történő kotorászás van). És az a program egyszerű léptetésénél (próbáltatok egyszerre 64 szálat debugolni? Élvezet :-) ) pont nem jön össze.

Egyébként egészen megdöbbentő, hogy hányféleképpen lehet ezeket az egyszerű dolgokat (mutex, message box, shared memory, stb.) megvalósítani... Linux alatt foglalkozgattam vele, lenyűgöző, hány STABIL és SZABVÁNYOS (tényleg az) API van rá...

Mindazon által, én nem javaslom, hogy ezt használjuk. A Unix/Linux "szabványos" eszköze a folyamat a thread az ms -ből van áthozva, és mint mondtam, az igazi ereje akkor mutatkozik meg ha közös memória felületen lehet/kell dolgozni. Elvileg, kicsit kevesebb erőforrást visz el (alapvetően memória) mint a processz, cserébe instabil és rosszul "pucolható" (NT4 -ben a rossz thread lebontás lerombolja a memóriát). Kilométeres, szinkronozott "roll back" -et kellhet hozzá írni.
Ha még ez is kevés, akkor gondoljatok az apache2 -re - ha php akkor prefork!

* Én egy indián vagyok. Minden indián hazudik.

Pár problémát azért látnék ebben a filozófiában...

Ha nekem kéne csinálni, akkor legfeljebb 2 szálon dolgoznék, az egyikkel mérek, a másikkal adatfeldolgozom az előző mérést.

Amúgy az adatgyűjtésedet nem akasztja meg a taskok illetve threadek közti váltás? Azért kérdem, mert lövésem nincs a hangkártya programozásról, nem tudom, hogy mennyire független az adatfolyam a userspace dolgaitól.

Én csináltam egy jó kis mérődrivert a hangkártyabemeneten.
+-1 Voltot mértem rajta két csatornán. Valahol megvan még a
cucc, csak pont most dobom szép a hálózatot, install közepén
vagyok, legalább 4 napig még nem nagyon tudok foglalkozni a
dologgal.

> Sol omnibus lucet.

Szia!

Javaslom ezt a figyelmedbe:
http://burningsmell.org/sdl_audioin/
Elvileg platformfüggetlen, de linuxon OSS-t használ.
Az OSS egyébként sokkal szabványosabb az ALSA-nál. Aztán találtam hozzá valahol ALSA modult is, de linket nem tudok most adni.
Abból meg lehetett érteni az alsa működését is.

Azt remélem tudod, hogy a hangkártya bemenete AC csatolt! Ne érjen meglepetés, hogy egy konstans ráadott feszültségnél nulla lesz a jel.

Úgy ahogy mondta - a bemenet egy kondenzátorral kezdődik, azaz csak a potenciál változását fogja érzékelni - ha rákötsz egy lapos elemet, láthatnál egy szép nagy tranzienst, aztán semmi, ha leveszed megint tranziens aztán semmi.

* Én egy indián vagyok. Minden indián hazudik.

A jel generátor, ahogy a neve is mondja, jelet generál - valamilyen hullám formát készít (pl szinusz, háromszög, meander stb.), akár modulálva (általában szinusz, amplitúdó, frekvencia, vagy fázis, de az impulzus szélesség is modulálható).
Ha hang, akkor ez hangfrekvenciás jelgenerátor, azzal jó lesz (csak az egyenáramú eltolást ne próbáljátok és a frekvencia tartomány sem lehet túl széles, mondjuk 20Hz ... 50 KHz)

* Én egy indián vagyok. Minden indián hazudik.

Parancssorból már egész szépen működik, most ahhoz a ponthoz érkeztem el, hogy grafikus felületet kellene hozzá írni. Qt-ben álltam neki, qt creatort használok. Amivel már elakadtam egy ideje, hogy hogyan tudok "kézzel megírt" widgetet elhelyezni designer nézetben, vagy designer nézetben lepakolt widgetet a .cpp fileban testre szabni. Ha lepakolok egy widgetet és adok neki valamilyen nevet, majd arra hivatkozva használnám, akkor a fordító panaszkodik, hogy nincs deklarálva. Ha deklarálom, majd azon a néven kiteszem designerben, akkor meg a beállításaim nem alkalmazza rá.

Van erről egész korrekt leírás valahol, de igazából felesleges vacakolás szerintem. Én meg szoktam elégedni azzal, hogy az UI szerkesztőben valahol elhelyezek egy QLayoutot (pl QVBoxLayout) és az ablak kódjában az UI inicializálás után

layoutneve->addWidget(énwidgetem);

parancs segítségével bepasszírozom oda :)

Nem nagyon akarja az igazságot, most már annyi helyről összeolvastam mindent, hogy valószínű valami triviális hülyeség:


ui->setupUi(this);
QLabel *imageLabel = new QLabel;
QImage image(":/image.jpg");
imageLabel->setPixmap(QPixmap::fromImage(image));
layout->addWidget(imageLabel);

A designerben átneveztem layout-ra. A hiba:

12: error: invalid use of member (did you forget the ‘&’ ?)
12: error: base operand of ‘->’ is not a pointer

A 12 sor az ahol hozzáadnám a widgetet a layouthoz.

Újabb problémám akadt a qt-vel. Hozzá kell adnom a -lasound flaget, hogy beforgassa az alsa libeket is. Több netes leírást is találtam, hozzáadtam a .pro filehoz a QMAKE_CXXFLAGS += -lasound részt. Erre nem működött. További guglizásra azt találtam, hogy a .pro file-on futtassam a qmake-t. Megtörtént, a makefile-ba be is került az opció:

CXXFLAGS = -m64 -pipe -lasound -O2 -Wall -W -D_REENTRANT $(DEFINES)

viszont továbbra sem működik. A fájlokat, amely használja, egy az egyben a működő, leforduló konzolos alkalmazásból vettem át, az nem lehet a gond. Mit hagytam ki?

Ennyi elég is volt, simán build után egyből jó volt. De hogy ne legyen teljes az örömöm, újabb gondom van.

A MainWindow class a fő ablak, és a Control class-nak, ami a parancssoros alkalmazásban vezérli az egész programot átadtam a pointerét, hogy ha a gui változó true, akkor tudja manipulálni az ablakot. A Control.h-ba includeoltam a MainWindow.h-t, ekkor boldogság van, lefordul. Utána beraktam a Control.h-t a mainwindow.h-ba, hogy tudjak csinálni Control objektumot, ekkor viszont azt írja, hogy a control.h-ban nincs dekralálva a mainwindow. A probléma okát sejtem, hogy előbb kerül bele a mainwindow-ba a control, mint fordítva, de a megoldást nem :)

Sziasztok, újra elakadtam.

Mivel elég nehézkes volt a parancssoros alkalmazást átültetni grafikus felületre, ezért nekiálltam a megfelelő függvények átemelésével újraírni. Lényegében sikerült is, de egy pici problémám van.

Úgy működne, hogy rögzít egy másodpercnyi adatot, ezt feldolgozza, gnuplottal elkészíti a hisztogramot, majd megjeleníti az elkészült képet, így másodpercenként frissülő adatokat látnék. Azonban a gui-n bármilyen változás csak azután áll be, hogy teljesen lefut az adatrögzítés.


void MainWindow::startRecording() {

itt vegyesen változóbeállítások a számításokhoz és parancsok a gui-nak, pl a mérés nevének és idejének kiírása és fileműveletek

hangkártya inicializálása

for (int i = 0; i < captureTime; i+= 1) {

ez a ciklus annyiszor fut le, ahány másodpercre van állítva a mérés ideje, itt sorrendben:
adatfelvétel;
adatfeldolgozás;
kép frissítése;

}

A probléma az, hogy a gui-n a változások csak azután jelennek meg, hogy a ciklus teljesen lefut, beleértve a ciklus előtt szereplő név és idő kiírását is. A mérés végén rendben megjelenik a végső kép és a mérés neve, ideje.

OK, csak vigyázz, ha túl gyakran hívod meg, jelentősen lassítja a programodat vagy a GUI sebességét, ha ez zavaró kezd lenni, akkor nincs más választásod mint külön szálban kezelni a számítást. (Illetve ott van még a QtConcurent, de még macerásabb lehet jól használni mint a QThread-et.)

Igen, észrevettem, ha 0,1 másodpercenként frissítettem az adatokat, akkor az egy perces mérés 1 perc 15 másodpercig futott :) Most egy másodpercen van (eredetileg is ennyit akartam), itt ez már lecsökken 1-2 másodpercre (vagy ha számolunk 1,5-re), ami azt jelenti, hogy 2,5%-al növeli meg a mérési időt. Mivel jellemzően egy ilyen mérés inkább órás nagyságrendbe esik, nyugodtan feljebb vihetem ezt az értéket, úgyse fogják végig figyelni, és akkor elhanyagolható a lassulás, de még a 2,5% is az. Valószínűleg ezt is beveszem a beállítások közé, parasztvakításnak jó lesz a 0,1 :)

Szia!

Először is köszönöm a tanácsokat.

Ezen felbuzdulva kimértem, 6-7%-al tart tovább a mérés, mint maga a nyers adatfelvétel. Közben a gui használható, bár jelenleg semmi olyan funkció sincs, ami ezt indokolná, és nem is nagyon tudnék ilyet találni. Kimértem azt is, hogy ha csak egyszer frissül a gui (az előzőnél másodpercenként négyszer), akkor az összes feldolgozás és a gui egyszeri frissítése a mérési idő 0,6%-át tette ki (azt hiszem egy öt perces mérés volt). A gép több órás méréseket fog végrehajtani, ehhez a tíz másodperces vagy ritkább frissítés is bőven elég, tehát nem oszt, nem szoroz.

A másik ok sokkal prózaibb, hogy végül is miért nem csinálom meg többszálasra: a jelenlegi felvevőgépen nem hogy több mag, de több szál sincs. Ez később változhat, akkor még lehet, hogy visszatérek a dologra, illetve ha az algoritmus a fejlesztés során jelentősen belassul.