Ennek a kis python kódnak az elődjét eredetileg jelfeldolgozási algoritmusok kipróbálgatására-fejlesztgetgetésére szántam, melynek célja egy esetleges későbbi mikrovezérlős kézi T/S-mérő fejlesztésében egyfajta előre dolgozás lett volna. Végül oda fejlődött a kis kódocska, hogy már önállóan kezelte a hangkártyát, azaz algoritmus szimuláció helyett már igazi méréseket tudott végezni. Kipróbálva a python kódot Raspberry Pi 4-esen, azon is probléma nélkül futott és tette a dolgát, az USB-n rácsatlakoztatott E-MU 0202 és a Steinberg UR22 hangkártyákkal is! Ezzel a korábbi mikrokontrolleres ötletet akár fel is válthatja egy R-Pi-s megvalósítás. Némi kódtisztítás, kommentbeszúrás után kvázi "kiadható" formát kapott a programkód.
Amire jelenleg szerényen ugyan, de képes: Lekalibrálja a mérőrendszert, megméri a bekötött hangszóró (vagy egyéb dolog, pl. váltóalkatrész) impedanciamenetét a beállított frekvenciasávban és beállított felbontásban, menti a kapott eredményt egy ZMA fileba, valamint fel is rajzolja azt egy grafikonba, hogy azért valamit lássunk is belőle.
Ami a lehetséges jövőjét illeti: ebből is lehet dobozolható impedancia és T/S paraméter mérőt készíteni, sőt mivel sokkal erősebb hardver, mint egy sima 32 bites mikrokontroller, így akár egy teljes komplett mérőrendszert is lehetne készíteni, 3D nyomtatt házban, reflexiómentesen beépített mikrofonnal (hangnyomás-mérőkhöz hasonlóan a mikrofonnál elkeskenyedő ház) akár az akusztikai méréseket is tudhatná. Saját kijelzés és kezelőszervekkel nem kellene monitor és billentyűzet hozzá, de akár hálózaton is el lehetne érni egy webalapú kezelőfelületen pl. egy tablettel vagy mobiltelefonnal az R-Pi-be feltelepített Apache szerver jóvoltából wifi hálózaton keresztül. A lehetőségeknek mint látható, csak a képzelet szab határt!
Miért is kell nekünk ez a Python alapú fapados mérőprogram, mikor ott vannak a jó kis bevált szoftvereink? (Atra, LspLAB, stb.)
Hát, lássunk pár érvet:
- Látjuk hogyan működik, hogyan végzi a jelfeldolgozást, hogyan számol, tehát tanulni tudunk belőle!
- Bele is tudunk nyúlni, átírni, megnézni úgy mi történik, azaz tanulni tudunk belőle!
- Beleírhatunk egyedi perverziókat is, azaz 100%-ban testreszabható a mérési mód
- A Python platformfüggetlen nyelv, és a választott kiegészítők is (beleértve a hangkártya kezelését is) platformfüggetlenek, gyakorlatilag a kódnak tök mindegy, hogy Windowson, vagy Linuxon indítod-e el (olyannyira, hogy módosítás nélkül viheted át egyikről a másikra!)
- A kód tesztelten működik Raspbery Pi 4-esen, mely megnyitja a lehetőséget egy hasonló projekt előtt, mint amit a bevezetőben említettem, azaz kicsi, kompakt, hordozható, saját kijelző és kezelőfelülettel rendelkező T/S kéziműszer irányába. Az R-Pi-n vannak GPIO lábak, tudnak vezérelni dolgokat (LED-eket, reléket, nyomógombokat leolvasni), van SPI, I2C busz, amin tud kezelni kijelzőt, és van I2S busza, amivel tud kezelni audio DAC és ADC chipeket! Bár én USB hangkártyát kötöttem rá (EMU 0202, Steinberg UR22), de lehet venni illetve készíteni saját DAC/ADC modult is hozzá. Vagy még az is játszhat, hogy csak ADC-t kap, és a DAC helyett egy DDS függvénygenerátorral állítunk elő mérőjelet, ugyanis SPI buszon képes lehet egy ilyen IC-t vezérelni.
Mérési módszerek
A kód az un. stepped sine, azaz lépcsőzetes szinuszjeles mérést alkalmazza. Azonban pár mondatban összefoglalnám, milyen egyéb mérési módok jöhetnének még számításba. A stepped sine még a szimulációs cél miatt lett elsősorban választva, mivel a mikrokontrolleres eszközben egyedül ezt lehet reálisan megvalósítani a gyakorlatilag zéró memóriaigénye és a szintén közel zéró számításigénye végett. A lehetséges alternatívák amúgy a következők:
-
Stepped (lépcsőzetes) szinusz
Lépcsőzetesen növekvő szinusz csomagok, lényegében diszkrét jelcsomagokkal végigmérjük a frekvenciasávot adott nagyságú ugrásokkal. A mérési idő több perc is lehet (tipikusan kb 3-5, de akár 10-20 perc, vagy még több is, ha nagyon feltoljuk a felbontást, azaz nagyon kicsiket lépkedünk, vagy nagyon alacsony frekvenciáról (pl. 1-2Hz) indítjuk a mérést) A feldolgozás szinusz és koszinusz jellel történő korreláción alapul. Ezzel csak harmonikussal gerjesztünk, és a visszamért jelben csak az alapharmonikust mérjük, így eliminálja a felharmonikusokat és zaj nagy részét, ezért nem hamisítja a zaj és torzítás (nemlineáris viselkedés) meg a mérést. Továbbá a harmonikus gerjesztés miatt csak a harmonikus torzítást hozza ki a mérendő rendszerből, a többi torzításfajtát nem, de ezt is eliminálja. Nagyon pontos, nem igényel előre bufferelést, az adatok akár röptében is feldolgozhatók, minimális számítási kapacitást igényel. Hosszú szinusz sweep- (folyamatosan és lassan (akár percekben mérhető) változó szinuszjel.
Feldolgozása itt is korreláció alapú, FFT-hez túl nagy adatmennyiséget jelentene. Pontossága és számítási igénye megegyezik az előbbivel. Precíz "lejátszás/felvétel" együttfutást (gyakorlatilag zéró latency-t igényel a hangkártyától) Emiatt PC-n hangkártyával nem egyszerű eset.Rövid szélessávó jelek (zaj, gyors szinusz sweep, multitone, multisweep stb.)
Kifejezetten FFT alapú mérés, rövid szélessávú jellel (kvázi minden frekvencia benne van). Maga a jel lehet ilyen-olyan zaj, gyors szinusz sweep, különféle un. multitone, multisweep stb. Sweep esetén korreláció nem alkalmazható, ahhoz túl gyorsan változik a frekvencia. A mérés felbontása a jel hosszától függ, az FFT miatt nyers mivoltában lineáris felbontást eredményez. Pl. 1sec hosszú mérőjel 1Hz frekenciafelbontást tesz lehetővé, 1Hz az első spektrumvonal, és ennek egész számú többszörösein jön a többi. Ez a felbontás a 10-100Hz tartományban (ami a T/S paraméterek számításához is kell) nem mindig elég, ellenben meg a 10kHz környékén meg már irreálisan sok. A kapott nyers FFT rettenesen ronda szokott lenni, sok-sok jelfeldolgozási "kozmetikázás" kell neki, hogy jól is nézzen ki (FFT ablakolás, frekvencia-tartományban simító szűrések (átlagoló, medián stb))
Én személy szerint a T/S paraméterek mérésekhez jobban favorizálom a stepped szinusz módot, alacsony frekvenciákon jobb felbontást ad, mint az FFT-s, nem kell félni, hogy a kozmetikázás hamisít a mért értékeken (bár az ismert mérőprogramok igen jól használják ezeket a dolgokat, hogy ne legyen hamis az eredmény), ill a periódikus gerjesztés nem hoz ki a mérendő rendszerből intermodulációs (IM) és tranziens intermodulációs (TIM) torzításokat, amiket aztán az FFT nem tud eliminálni a THD-val egyetemben. Itt csak a THD torzítást és zaj játszik, amit viszont a korreláció eliminál (mivel csak az alapharmonikussal számol, azt korreláltatja ki a jelből).
Mérési elrendezés
A mérési séma a úgy néz ki, hogy PC vagy R-Pi, arra USB-n keresztül van csatlakoztatva egy hangkártya. A hangkártya bal kimenetét bekötjük egy egyszerű, alacsony és lehetőleg szimmetrikus tápfeszről táplált végfokozatra. Hi-fi erősítők alkalmazásával nagyon kell vigyázni, mivel azokban 30-50V tápfesz is lehet, és kb ugyanekkora csúcsjel jelenhet meg annak kimenetén, ami a hangkártya LINE bemenetén már károsodást okozhat! Sok egyszerű végfok vásárolható KIT-ben, ezek közül is a TDA2030 IC-vel rendekezőket tudom javasolni, mivel ennek az IC-nek (ha nem is egy HIFI világbajnok) de 140kHz a sávszélessége, vagyis a 192kHz hangkártyák kb 80-90kHz felső tartományát is gond nélkül átviszi.
A mérőkapcsolás a hangszóró és azzal sorbakötött -jelen esetben 15 Ohm- mérőellenállás. (nevezhetjük még soros ellenállásnak vagy sense ellenállásnak) Ez egy feszültségosztót képez, a mérés pedig az osztó bemenetét, és az osztási csomópontot érinti. Előbbi a referencia jel, és ez a jobb csatornába megy, másik a mért jel, ami a bal csatolnába megy vissza a hangkártyába. Ezeket a jeleket GND nélkül csak a melegpontra kell bekötni, vagy ha használunk árnyékolt kábelt is, akkor az árnyékoló harisnyát csak a hangkártya felöl kössük be! Ne csináljunk földhorkot, pláne hogy nagyáramú földhurok jönne létre, ha a hangszóró föld felöli (végfok kimeneti GND) csomópontjához kötnénk! Ezeket a földeket külön PGND-nek jelölöm, hogy ezek teljesítmény földek (azaz nagyáramú). A felüsztség kivezérlés lényeges, alapszabálynak tekinthető, hogy legalább akkora jel kerüljön a hangszóróra, hogy annak membránja alacsony frekvencián szemmel láthatóan mozgásba jöjjön (legalább 1-2mm), de ne legyen olyan nagy, hogy a tekercs kimenjen a homogén mágneses térből, azaz a hangszóró Xlin tartományán belül maradjon. Hangszórón mérve kb 200-300mVeff ideális, ami a végfokon 1Veff kivezérlés mellett kb meg is valósul egy 8 Ohmos hangszórón.
A környezet telepítése
Mielőtt futtatod a kódot, elő kell készíteni neki a terepet! Én ezt most a R-Pi estére mutatom be, de pl. egy Windows alapú PC-n se lesz ez nagyon más. R-Pi esetén először is telepíteni kell egy Raspbian OS-t egy SD kártyára. Ezt nem részletezem, találsz róla infót, vagy úgy veszel R-Pi-t, hogy kérsz hozzá egy előtelepített SD kártyát is. A Raspbianba előre fel van telepítve a Python3, így azzal nincs teendőnk, ill. gyakorlatilag minden ismert Linux rendszerrel ugyanez a helyzet. Windows esetében telepíteni kell a Python3-at, ami szintén nem nagy kihívás egy általános PC használó számára. Itt talán annyi megjegyzést tennék, hogy a telepítő ablakon lesz egy checkbox, ami azt kérdezi, hogy a rendszer path útvonalakhoz adja-e hozzá a python program útvonalát, ezt mindenképpen be kell jelölni! Következő lépés, hogy néhány Python plugint telepíteni kell. R-Pi-n nyissunk egy terminált (pl. a Ctrl + Alt + T billentyűkombó) és szépen egymás után írjuk be ezeket a parancsokat:
sudo apt install python3-scipy
sudo apt install python3-matplotlib
sudo apt install python3-soundfile
sudo apt install python3-pyaudio
pip3 install sounddevice
Windowson a fenti összetevők konzolos ablakba beírt pip-el is települtek, a példa legutolsó sora szerint. A sorrendre vigyázni kell, mert függőségi viszonyok is vannak köztük! (pl. a sounddevice csak akkor települ, ha van pyaudio)
UPDATE:2022.12.29
Időközben egy Windows 10 telepítés után kipróbáltam a fenti folyamatot végigművelni, és legnagyobb sajnálatomra nem működött a program. Sajnos mind a Python verzió, de főleg a kiegészítők verziói nagyon kihatnak a Pythonban
fejlesztett programok működésére, így most megosztom azt a konfigot, amivel a működés hibátlan:
Python verzió: 3.8.10 (64bites Windows)
Kiegészítők verziói:
matplotlib 3.2.1
numpy 1.24.1
PyAudio 0.2.13
scipy 1.9.3
sounddevice 0.4.5
soundfile 0.11.0
Ebből most csak a matplotlib volt hunyó, aminek kényszeríteni kellett a verzióját is, és ehhez így kellett kiadni a pip parancsot:
pip install matplotlib==3.2.1
A program paraméterezése
A program elején változókba van minden szükséges paraméter megadva. Ezeket a kódban kell átírni, rámenteni, majd lehet újra futtatni a kódot. A R-Pi-ben én a Geany programot használom editornak, ez elő van telepítve a Raspbianban. Ennek van egy papírrepülő gombja, amit megnyomva ráment a filera és futtatja terminálablakban.
Egy gyors megjegyzés: Bár Raspbianban nem volt probléma, de más asztali Linuxnál tapasztaltam olyat, hogy a a Geany futtatáskor Python 2-vel akarta futtatni a kódot. A Python 2 és Python 3 nem teljesen kompatibilis egymással, és az összetevőket is külön telepíti a két rendszernek. Ilyen esetben a Geany programban állítsuk be a python3-at alapértelmezett Python interpreternek a következőképpen: Menüben elnavigálunk a Build vagy Összeállítás / a Build- vagy Összeállítási parancsok megadása menüpontba. A felugró ablakban a Python parancsok rész Compile és a Futtatási parancsok / Execute részben is a python szót cseréljük le python3-ra! Ilyen esetben, amikor Python 2 és Python 3 is van a gépen, a pip-et is célszerű pip3-nak írni, hogy biztosan a Python 3-hoz telepítse a kívánt összetevőt a rendszer!
A program elején az import szekció után struktúráltan sorakoznak a paraméteres konstansok:
|
CALIB_MODE |
True=kalibrálunk, False=mérünk |
|
START_FREQUENCY |
ettől a frekvenciától... |
|
END_FREQUENCY |
...eddig a frekvenciáig mérünk |
|
FREQ_STEP_PER_OCT |
frekvencia felbontás (ennyi mérési érték oktávonként) |
|
MIN_SIGNAL_PERIODS |
legalább ennyi periódisból álljon egy jel (alacsony frekvenciákon hosszú mérőjeleket eredményes, egy 2Hz-es mérőjel esetén, aminek 0.5s a periódisideje és egy 10-es értékkel pl. ez 5 szekundum! |
|
MIN_SIGNAL_TIME |
legalább ennyi idejű egy mérőjel. Szekundumban van, a 0.4-es érték 400msec-nek felel meg! |
|
MIN_SILENCE_PERIODS |
A jel előtt és mögött csend szakaszt szúrunk be, hogy a hangeszköz különféle késései, valamint a jel mögötti esetleges tranziens is benne legyen a felvételben. Ez a paraméter azt jelenti, hogy legalább ennyi periódusideig legyen ez a csend. |
|
MIN_SILENCE_TIME |
Mint az előbbi, csak minimum időben. Itt is, alacsony frekvenciákon a megadott periódusszám, magas frekvenciákon a megadott idő számít. |
|
TIME_OFFSET |
Amennyiben ez az érték 0, úgy a mérőjel és a két csend szakasz szimmetrikus. 0-tól eltérő értékkel a jel tologatható eőre hátra a csend-el határolt részeken. (A -0.04s (40msec) pl. nagyjából kompenzálja a Steinberg UR22 hangkártya késését.) |
|
VOLUME |
A kimenőjel digitális nagysága 1 esetében "full scale" azaz teljes nagyságú. ]0...1] intervallumban lehet megadni, 0.1 alá (ez -20dB-nek felel meg) ne menjünk! |
|
R_SENSE |
A mérőkör soros (érzékelő, sense) ellenállásának pontos mért értéke (Ezt neked kell beálítanod, a kódban az enyém szerepel!) |
|
USE_WINDOWING |
Ablakolás alkalmazása. Ablakfüggvényt leginkább FFT és FIR szűrőknél használunk. Az ablakfüggvény tulajdonképpen egy burkológörbét ad a feldolgozandó jelnek, amivel mintegy felkeveri és lekeveri azt, a szélein lehalkítja. Itt nem a már felvett és feldolgozás előtt álló jelet ablakoljuk meg, hanem a kimenőjelet. Ezzel elkerülhetű, hogy a hangszórón pattogások legyenek, ill. a jel indítási/befejezési tranziensek is kisebbek lesznek. |
|
USE_CALIB_VALUE |
Kalibrálás üzemmódban a programkód kalibrál, majd azt lementi egy calibrate.npy file-na. Mérés üzemmódban ha ez a változó True, akkor betölti ezt a file-t és alkalmazza a kalibrációs értékeket. |
|
USE_SAVE_WAV |
Mérőjelek (generált és visszamért) wav file-ba mentése egy py_records nevű alkönyvtárba. Debug célokat szolgál, az össze freknecián, amin csak mérünk kimenti wav-ba a jeleket. Időzítések beállítása, tranziensek megfigyelése, hanghibák (pl. ha azt halljuk, hogy pattog vagy ciripel a hang mérés alatt) |
|
USE_REPLAY |
Szintén debug célból, visszajátszik minden felvett jelet |
|
DEVICE_NAME |
Itt van pár kikommentelt is, a hangeszköz nevének megadása, ez alapján próbálja kiválasztani a program a hangkártyát. |
|
FS |
Sampling frekvency, avgy samle rate, ahogy tetszik. Hardveres és szoftveres mintavételi frekvencia. Célszerűen a hangkártya maximális képességeire legyen beállítva. |
Első körben egy kalibrációt csináljunk, illetve itt állítsuk be a mérési frekvencia tartományt és lépésközt! Fontos, hogy a kalibráció mindig az itt beírt freki beállításoknak felel csak meg, ha más beállítás kell, akkor ahhoz új kalibrációt kell csinálni! Nyilván ezt lehetne javítani, de itt most a faék egyszerűség a fontosabb, de aki kicsit tud programozni, könnyen beleírhat egy jobb, de értelemszerűen bonyolultabb megoldást. Kezdő frekinek 1Hz alatti értéket nem érdemes beírni, itt már maga az ADC vág, van bennük DC mentesítő felülátereszteő szűrő. Felső frekvenciának a mintavételezési freki felénél kisebb érték legyen megadva, különben alias spektrumtartományba menyünk! Alacsony frekvenciákon (beállítástól függően) egy csomag időtartama több másodperc is lehet, így a kb 50Hz alatti frekvenciák mérése nagyon lassacskán megy, a túl alacsony alsó határ nagyon elhúzhatja a teljes mérés időtartamát, nálam volt, hogy egy alkalommal 23 percig kalibrált, majd szintán 23 percig mért! A túl finom felbontás (pl. 1/48,1/64 stb) szintén rendkívül hosszúra nyúltja a mérési időt. Ezekkel a beállításokkal játszadozni kell.
A kalibráció hardveres beállítása abból áll, hogy nyitva hagyjuk a mérőkapcsolat. Néhol ilyenkor egy kapcsolóval direkbe átkapcsolnak, azaz áthidalják a soros mérőellenállást, de ez nem helyes mód, ugyanis ilyenkor a hangkártya bemeneti impadanciáján nem történik feszültségesés. Fontos, hogy ne hidaljuk át a mérőellenállást, csakis ekkor kalibrálunk végtelenre! Nyilván, ez csak akkor számottevő, ha kicsi, pl. csak 10kOhm a bemeneti impedancia a hangkártyán.
Ezután be kell még állítani, hogy melyik hangkártyát használja a rendszer, ehhez előbb inítsuk el a progamot:
Nyilván itt még hiára kiáll, mert nem találta meg a beállított kártyát. Úgy van megírva a kód, hogy minden esetben végiglistázza a hangeszközöket. Ez lehet hogy csak 3 elemű, de a Win 10-nél pl. ki se fért a képernyőn, vissza kellett görgetni. Copyzzuk le a képernyőről a szöveges részt, és írjuk be a kód DEVICE_NAME sztringjébe. Előfordul, hogy később más hw számon látja majd ugyanezt a Linux, akkor át kell javítani csak azt az egy számot, vagy ismét arra tudok buzdítani, hogy programozzuk le, hogy ezt a karaktert ne nézze! Ezekután el kell indulnia a kódnak, és végig kell mennie a frekvenciuákon:
Látni fogjuk a képenyőn, hogy az egyes frekvenciákon mekkora eltérést mér a két csatorna között dB-ben és fázisban. A végén ezt lementi a calibrate.npy fileba, majd amikor visszaállítjuk a CALIB_MODE-ot Falsra, vagy nem kalibrációban indítjuk a programot, akkor ebből a fileból fogja visszatölteni a kalibrációs értékeket. A filbe csak a mért értékeket menti, frekenvia értéket nem! (egyszerűség miatt) Ezért fontos, hogy ne változzon a frekvencia-kiosztás a kalibráció és azt azt követő mérések között.
A következő lépés, hogy bekötünk valamilyen mérendő hangszórót a méricske mérőpontjaiba. Elindítjuk a programot, és szépen végigmér, majd a végén felmutat nekünk egy mérési grafikont:
A programkód letöltése
A programkódot feltettem a Letöltések menübe, ill. erről a linkről is letöltheted!
A teljes programkód:
Egy kis előretekintő...
Ilyen verzió is készült: (Python nyelv, GUI, érintőkijelző, Raspberry PI, USB hangkártya)
Sajnos ennek a programnak a kódját egyelőre nem fogom közzétenni, mivel ez csak egy próbálkozás volt, nem is túl átlátható és -annak ellenére, hogy próbáltam OOP-nek megfelelően mindent osztályokba szervezni- eléggé spagetti kód jellege lett. Később talán egy letisztultabb, funkciókban befejezett változat lesz belőle...
Windows ASIO driver aktiválás Python sounddevice-ban (Update 2024.12.28)
Hozzá kell adni a windows %PATH% környezeti változóhoz a portaudio dll-jeit tartalmazó könyvtárat. A Python többnyire a c:\Users\felhasználóneved\AppData\Local\Programs\Python\ könyvtárba települ, amin belül a Lib\site-packages\_sounddevice_data\portaudio-binaries\ könyvtárat kell az útvonalakhoz hozzáadni, amit a sysdm.cpl startmenüre gépelésével, majd a felugró ablakban a Speciális fül Környezeti változók gombon érhetünk el, az újabb alakban pedig a Path és Szerkesztés gomb, majd Új és beillesztkük az útvonalat. Ahhoz viszont, hogy a Pythonban tényleg lehessen ASIO-n keresztül sztrímelni, a következőt kell még beállítani, de ezt minden python futtatáskor, tehát bele kell írni a programunkba:
import os
if os.name=='nt':
os.environ['SD_ENABLE_ASIO']='1'
Ezután meg fog jelenni az ASIO lehetőség is az eszközök listázásakor.