Zpracování signálu se zvukovou kartou - DirectSound DLL interface

[eng]

   Posledních pár let se pracovně zabývám vývojem digitálních vzorkovacích aparatur pro účely primární a sekundární metrologie elektrických veličin. V uplynulých zhruba 15ti letech probíhal/probíhá celkem intenzivní výzkum s cílem digitalizovat a především automatizovat analogové měřící aparatury, které prozatím v mnoha oblastech poskytují nejlepší nejistoty měření. Např. se v poslední době objevila celá řada algoritmů pro harmonickou analýzu signálů, takže se začaly vyvíjet digitální vzorkovací wattmetry, fázoměry, zkresloměry, digitální impedanční můstky a celá řada aparatur pro měření různých exotičtějších parametrů jako třeba flicker. Tyto systémy už dnes dosahují nejistot měření srovnatelných s původními analogovými sestavami.
   Drtivá většina z nich používá high-end digitizéry (ADC). Pro nižší kmitočty se nejčastěji používají unikátní multimetry HP/Agilent/Keysight/CoJáVímJakSeToBudeJmenovatZítra 3458A. Je to sice skoro 30 let stará mašina, ale neexistuje náhrada a shodnou okolností se kvůli RoHS paskvilu přestanou prodávat v EU a postupně i ve zbytku světa (bez náhrady). No snad to vyřeší čínské ručičky. :-) Tento DMM dokáže vzorkovat až 100 kSa/s a přitom má úroveň šumu pod -120 dBfs. Zároveň však při pomalém vzorkování dosahuje parametrů 8.5-místných DMM.
   Pro vyšší šířku pásma se pak používají vzorkovací karty jako třeba National Instruments 5922. Tato karta zvládá 500 kSa/s v rozlišení 24bitů a 15 MSa/s v rozlišení 16bitů. Efektivní rozlišení je pochopitelně nižší, ale i tak jsou to úctyhodné parametry. Jen má podstatně horší stabilitu citlivosti. 3458A drží v řádu ppm za rok, 5922 "lítá" po provedení self-kalibrace v desítkách ppm. Každopádně problém těchto digitizérů je cena, díky které jsou naprosto nedostupné pro jednotlivce, pro řadu firem i škol. Jen pro info zmíněné modely stojí přes 200 kKČ. Jsou samozřejmě i levnější varianty s nižšími vzorkovacími kmitočty, ale i tak je to raritní záležitost, takže cena nejde pod nějakých 20 kKČ. Teoreticky se taková věc dá i vyrobit na koleni s nějakým ADC, FPGA a pár operáky a někdy to má i smysl, ale udělat to pořádně si vezme mnoho času. A já nehodlám trávit čas výrobou komponent, protože pak jaksi nemám čas řešit ten samotný měřící systém.
   Nedávno jsem řešil problém digitálního vzorkovacího můstku pro vysoké impedance. Nabízela se sice možnost použít jako ADC a DAC něco od NI, třeba cDAQ systém, ale říkal jsem si, že by nebylo od věci zkusit klasickou zvukovou kartu, protože frekvenčně na to stačí a navíc by se takový systém dal používat i mimopracovně. :-)

1. Volba zvukové karty

   Volba karty byla celkem snadná záležitost. Potřeboval jsem něco externího s USB, aby se to dalo snadno galvanicky oddělit. To se vždycky hodí kvůli zemním smyčkám. Dál by to mělo umět 24bitů a především to musí používat tzv. "asynchronní režim". Tj. karta si řídí vzorkovací kmitočet sama podle interních hodin a PC jen doplňuje/vyčítá data z FIFO bufferů. Čili přesně, jako jsem to kdysi zkoušel s I2S. Toto je jediný režim, kdy je zajištěno synchronní vzorkování ADC a DAC. Existují ještě další režimy, kdy PC sype data vlastním tempem a karta nějak pochybně mění vzorkování nebo provádí resampling, ale to se samozřejmě použít nedá. Popravdě nevím, proč takovou hovadinu vůbec někdo vymyslel. Snad jen aby se vyhnul nějakým patentům, protože složitěji už by to asi fakt nešlo a o kvalitě zvuku ani nemluvě. Po troše googlení jsem zjistil, že jedna z mála recenzovaných karet za ještě rozumnou cenu je Creative X-FI HD USB. Od pohledu to dělal někdo, kdo o tom něco ví. Na DPS je oddělená analogová a digitální zem, dají se sehnat katalogové listy od DAC i ADC a dokonce tam je i zemní svorka. :-) To je tak trochu indikace, že to dělal někdo, kdo asi i ví, jak to použít. No a na bonus to není karta inzerovaná jako audiofilní/gamer produkt, takže je v celkem normálně hranatě vypadající pixle a né v nějaké hypermoderní předesignované kastli jako třeba model X7.

2. První testy - waveIn/waveOut

   Jako první jsem zkusil pro ovládání staré dobré in/out, tedy waveIn()/waveOut() samozřejmě (co jiného, že...). Bohužel už prostý poslech generované čisté sinusovky ukázal, že je něco hodně špatně. Bylo zřetelně slyšet, že se po začátku přehrávání několikrát skokově mění amplituda i kmitočet. Tak jsem trochu googlil a zjistil jsem, že od Windoze Vista M$ konečně zahodil prehistorický audio stack a nahradil ho něčím odpovídajícím 21 století. Palec hore za to, protože např. způsob směšování streamů s různým formátem byla katastrofa. Ale bohužel někdo něco podělal ve wrapper funkcích, které mají zajišťovat zpětnou kompatibilitu pro waveIn/waveOut rozraní. Sice to jakože něco dělá, ale na zpracování signálu se to už opravdu použít nedá. Různě po fórech na tento jev nadávalo už dost lidí a našel jsem i řadu "osvedčených" řešení, ale jak se asi dá čekat, nic nefunguje. Takže jsem se tedy rozhodl tyto 20+ let staré API opustit a použít něco novějšího. Kdyby někoho zajímaly detaily o novém audio stacku, tak je velmi pěkně vysvětlen zde:
https://channel9.msdn.com/Shows/Going+Deep/Vista-Audio-Stack-and-API

3. Další pokusy - DirectSound

   Nějak se mi nechtělo jít přímo ke zdroji a použít přímo nové WASAPI. Z dokumentace jsem zrovna moudrý nebyl a nějaké uspokojivé příklady jsem tehdy také nenašel. Takže jsem si řekl, že zkusím další nejjednodušší řešení a to je DirectSound. To má tu výhodu, že by to mělo fungovat i v XP. Sice jsem byl trochu skeptický, protože po zkušenostech s DirectDraw jsem očekával, že to budu řešit pěkně dlouho, ale ukázalo se to podezřele snadné. Na rozdíl od waveIn/WaveOut zcela odpadá ta známá onanie s několika buffery, které bylo třeba připravit, plnit, rotovat a zase "odpřipravit". DirectSound má proti tomu jen jeden kruhový buffer a uživatel prostě jen periodicky zapisuje (přehrávání) nebo čte data (záznam), aby nedošlo k přetečení (záznam) nebo podtečení (přehrávání). Toť vše. Docela se to blíží low level programování na MCU. No vlastně kdysi prý DirectSound umožňoval vrtat přímo do primárního bufferu, tj. do paměti karty, ale to už je samozřejmě pasé, protože dnes je DirectSound jen wrapper pro WASAPI, jestli jsem to dobře pochopil.
   Snad jediná zákeřnost je, že metoda GetCurrentPosition() u záznamu vrací dva pointery. A jak se asi dá očekávat, tak jejich význam je popsán někde úplně jinde, než v dokumentaci té metody. Takže po pročtení celé dokumentace jsem zjistil, že "pdwCapturePosition" je ten špatný. Je to pozice, kde data mohou, ale také ještě nemusí být platná, systém může stále zapisovat. Teprve ten druhý, tj. "pdwReadPosition" je ten, ze kterého se má číst. No nevím sice, k čemu je ten první, ale budiž...    Po pár pokusech s line-in/line-out smyčkou se zdá, že DirectSound API ani ve Windows 7 nikdo nezvoral. Signál vypadá čistě, nejsou patrné žádné skoky ve frekvenci ani amplitudě a ani není patrný "spectral leakage". Z toho plyne, že ADC a DAC jedou synchronně. Pár ukázkových měření je zobrazeno v následujících grafech.

Spektrum line-out/line-in smyčky při maximální amplitudě, 1 kHz, 48 kSa/s, 24bit, 48 kpt FFT. THD+N = 0.0047 %.
Obr. 3.1 - Spektrum line-out/line-in smyčky při maximální amplitudě, 1 kHz, 48 kSa/s, 24bit, 48 kpt FFT. THD+N = 0.0047 %.

Spektrum line-out/line-in smyčky při amplitudě -20 dBfs, 1 kHz, 48 kSa/s, 24bit, 48 kpt FFT. THD+N = 0.0082 %.
Obr. 3.2 - Spektrum line-out/line-in smyčky při amplitudě -20 dBfs, 1 kHz, 48 kSa/s, 24bit, 48 kpt FFT. THD+N = 0.0082 %.

4. DirectSound DLL interface

   Po prověření DS API funkcí jsem se rozhodl k tomu napsat nějakou knihovnu funkcí pro snadnější použití. Mé požadavky na funkce byly následující:

  • Přehrávání průběhu ve smyčce nebo jednorázově.
  • Opakovaně spustitelný záznam souběžně s přehráváním.
  • Záznam musí podporovat časové značky.
  • Musí to jít nějak rozumně použít v LabVIEW.

   První dva požadavky jsou snadné. Na to už knihovny existují. Bohužel časové značky jsou zřejmě pro většinu programátorů neznámá věc, takže mě psaná vlastní knihovny stejně neminulo. Časové značky jsou velmi užitečný nástroj např. pro měření více signálů v časovém multiplexu. Problém je ilustrován na obr. 4.1.

Použití časových značek u vzorkovacího fázoměru v režimu časového multiplexu.
Obr. 4.1 - Použití časových značek u vzorkovacího fázoměru v režimu časového multiplexu.

   Dva signály, A a B, jsou generovány dvěma střídavými zdroji. Oba jsou postupně měřeny společným ADC pomocí přepínače (multiplexeru). Je vidět, že oba signály mají sice stejnou fázi, ale vzhledem k tomu, že začátky vzorkování nejsou nijak synchronizovány se signály A a B, mají zaznamenané signály zjevně jinou fázi. Navíc při opakování měření bude tento fázový posuv jiný, protože interval mezi t2 a t4 je díky granularitě časovače systému a zpožděním různých API náhodný. Je tedy zřejmé, že pro určení fázového posuvu mezi signály je potřeba znát časový interval mezi t1 a t4. Měřit ho pomocí systémového časovače je nesmysl, protože časovač má mizerné rozlišení a navíc není nijak svázán s časovou základnou zvukové karty. Jedno z možných řešení je prostě vzorkovat kontinuálně od t1 do t5 a pak z té nudle vybrat dvě okna A a B. Vzhledem k tomu, že je mezi nimi známý počet vzorků a je známá i vzorkovací perioda TS, lze spočítat časový posuv a provést korekci podle:

\Delta\phi = \phi_B - \phi_A - 2 \pi f_x(t_4-t_1)

kde fx je kmitočet měřeného signálu a ΦA a ΦB jsou fáze signálů z oken A a B získané např. pomocí DFT. Toto řešení ovšem není příliš praktické pro složitější měřící sekvence, protože to znamená zaznamenat a uložit celkem značné množství dat, z čehož většina je často ustalování relé. To je jeden z důvodů, proč každý seriózní digitizér podporuje časové značky. Tj. současně se zaznamenaným průběhem vrací relativní čas prvního vzorku počítaný od nějaké společné události, např. resetu. Některé modely dokonce umožňují i absolutní časové značky odvozené od GPS, ale to už je jiná story. Výhodné na tomto řešení je, že časové značky jsou odvozené od stejných hodin, jako vzorkování, takže se dá říct, že jsou, vzhledem k zaznamenanému signálu, absolutně přesné. Tedy až na jitter.
   Emulace této funkce pomocí DirectSound je velmi snadná. Prostě začnu zaznamenávat signál okamžitě po otevření vstupního zařízení a pomocí permanentně běžícího vlákna s vysokou prioritou data vyčítám, ale zahazuji, dokud uživatel nevyžaduje záznam. Pak je začnu kopírovat do uživatelského bufferu a po dokončení záznamu je zase začnu zahazovat. Nicméně i když je vlákno zahazuje, tak počítá vzorky, takže v kterémkoliv okamžiku ví, kolik uplynulo času od resetu a může tudíž vrátit spolu se záznamem i časovou značku.

   Poslední požadavek na knihovnu byl trochu větší oříšek. Nejsem zrovna nadšenec do LabVIEW, ale bylo k dispozici na pracovišti, tak jsem s ním začal experimentovat. Po pár projektech jsem se tím naučil pracovat celkem efektivně a rychle a je pravda, že na jednoduché měřící smyčky, když je potřeba rychle něco zautomatizovat je to celkem pohodlné. U složitějších vzorkovacích systémů navíc LV používám jen k řízení přístrojů a jako GUI a všechno podstatné, tj. výpočty, případně stavový automat, který řídí třeba vyvažování impedančního můstku, běží v GNU Octave (open source alternativa Matlabu). To má proti použití black-box knihoven od LV tu výhodu, že ty výpočetní skripty jsou transparentní a mohu je "nakrmit" simulovanými daty a ověřit si tak, že počítají co mají. Případně mohu provést výpočet nejistot měření technikou Monte-Carlo. Dokonce jsme si k tomuto účelu s kolegou vyrobili interface, který umožňuje runtime výměnu dat mezi oběma prostředími podobně, jako interface pro Matlab, který je součástí LV. Konkrétně se jedná o knihovnu GOLPI - GNU Octave LabVIEW Pipe Interface [1].
   Výsledek je, že mám po letech práce řadu vzorkovacích měřících systémů, které jsou ale všechny od začátku prozřetelně napsané pro generalizované přístroje. Tj. stačí doplnit ovladače nového HW a mohou bez jakýchkoliv dalších změn pracovat třeba právě se zvukovou kartou za dva papíry místo profi digitizéru za čtvrt mega a pro některé aplikace nebude znatelný rozdíl. Jediný problém je, že volat přímo WINAPI s jejich perverzně složitými strukturami je v LV téměř nemožné. Dokonce i NI doporučuje si k tomuto účelu vyrobit DLL wrapper. :-) Tož jsem to tedy udělal a výsledek je celkem jednoduchá knihovna se základními funkcemi, které používají jen základní datové typy.

[1]Mašláň S., Šíra M. GOLPI - Gnu Octave to Labview Pipes Interface. November 2014.
https://decibel.ni.com/content/docs/DOC-35221, http://kaero.wz.cz/golpi.html


4.1. Knihovna DSDLL

   Celá knihovna DSDLL (hodně kreativní název...) je řešená jako klasická DLL. Obsahuje funkce pro výčet zařízení, přehrávání, záznam, konfiguraci a asynchronní zjišťování stavu. Až na jednu výjimku používá jen typy int32, uint32, double a pointery, takže ji lze snadno použít v LabVIEW. A když to jde v LV, tak to musí jít snad všude. Původně jsem to chtěl napsat ve starém dobrém Borland BDS2006 Turbo C++, ale nějak jsem zjistil, že to do Windoze 7 nedostanu, protože instalace vyžaduje nějakou prehistorickou verzi .NET, která jaksi pro Windoze 7 neňa. Řešit to ve virtuálu se mi nechtělo, takže jsem zkusil M$ Visual Studio 2013. Překvapivě to docela funguje a sžít se tím ani nebylo nijak zvlášť složité. No a na rozdíl od RAD Studia, jakožto nástupce Borlandu, je to free.

Plné zdrojové kódy a binárky (verze 1.0, 2016-08-20): dsdll_20160820.zip.

4.1.1 Licence

   Vzhledem k tomu, že se nejedná o nějakou převratnou věc, rozhodl jsem se knihovnu uvolnit pod GNU Lesser GPL, která je poněkud méně restriktivní, než klasická GNU GPL. Mimo jiné umožňuje linkovat DLL i do komerčních produktů. Demonstrační utilitky jsou uvolněny pod ještě méně restriktivní licencí WTFPL. To je sice tak trochu trolling, ale jinak se mi líbí ta jednoduchost. :-)

4.1.2 - Základní použití

Popis jednotlivých funkcí je přímo ve zdrojácích i hlavičkových souborech a použití by mělo být jasné z demonstračních prográmků, takže zde jen pár základních informací formou příkladů pro C/C++.

4.1.2.1 - Výčet DirectSound zařízení

   Následující kód umožňuje jednoduchý výčet dostupných DS zařízení. Nejdřív se naplní seznamy vstupních a výstupních zařízení voláním sb_enum(). Následně se pomocí sb_enum_get_device() získá GUID zvoleného zařízení. Pak lze seznamy zase smáznout voláním sb_enum_clear_list(). Získané GUID se pak použijí k samotnému otevření zařízení pomocí sb_open().

  // initialize empty lists of input/output devices
  TSDev dsolist = {0,0,NULL};
  TSDev dsilist = {0,0,NULL};

  // fill the lists with DirectSound devices
  sb_enum(&dsolist,&dsilist,NULL,NULL);

  // obtain the GUID of selected input/output devices, id = 0 is default device
  GUID outguid;
  GUID inpguid;
  sb_enum_get_device(&dsolist,output_device_id,&outguid,NULL,0);
  sb_enum_get_device(&dsilist,output_device_id,&inpguid,NULL,0);
  
  // clear the lists
  sb_enum_clear_list(&dsolist);
  sb_enum_clear_list(&dsilist); 
  
  // now the DirectSound can be initialized by calling sb_open() with the GUIDs
  sb_open(..., &inpguid, &outguid, ...);

4.1.2.2 - Otevírání a zavírání DirectSound zařízení

   DSDLL obsahuje jen jednu společnou funkci pro otevření vstupních a výstupních zařízení. V první řadě je třeba zvolit formát přehrávání a záznamu "fmt". Vzorkovací kmitočet je společný pro záznam i přehrávání. Počty kanálů se mohou lišit, ale musí být platné, tj. minimálně 1 i když vstup/výstup není použit. sb_open() skousne i NULL parametry místo GUID. V tom případě prostě vstupní/výstupní zařízení nebude použito. Pokud je zadáno prázdné GUID (samé nuly), otevře se defaultní vstupní/výstupní zařízení. Upozorňuji, že to není standardní chování DirectSound! To jsem tam přidal dodatečně, protože defaultní zařízení ve výčtu z nějakého důvodu nemá přiřazeno GUID.
   Pokud bylo zadáno platné GUID vstupního zařízení, začne záznam okamžitě (viz. časové značky výše), takže je zařízení okamžitě používáno!

  // create a wave format descriptor
  TSBfmt fmt = {sampling_rate,output_channels,input_channels,wave_format_id};
  
  // make&clean DirectSound structure
  TSBhndl ds; 
  ZeroMemory((void*)&ds,sizeof(TSBhndl));
  
  // create DirectSound object (both input/output devices)
  sb_open(&ds,&inpguid,&outguid,&fmt);

   Vstupní i výstupní zařízení lze zavřít voláním sb_close(). To zároveň ukončí případný záznam nebo přehrávání.

  // close DirectSound devices
  sb_close(&ds);

4.1.2.3 - Přehrávání záznamu

   Pokud je výstupní zařízení otevřeno, lze zahájit přehrávání pomocí sb_output_write(). Pole "data" je dvourozměrné pole s prvky podle formátu ve "TSBfmt" struktuře použité při sb_open(). Kanály jsou proloženy, tj. 1. levý, 1. pravý, 2. levý, 2. pravý,... a používá se pochopitelně little-endian. Přehrávání lze spustit buďto rovnou pomocí sb_output_write(...,1/2) nebo zpožděně pomocí sb_output_play(). To lze použít i opakovaně, pokud výstupní buffer stále existuje. Obě funkce jsou neblokující, tj. vrací ihned.

  // writing data and starting playback immediately (loop_mode > 1)
  sb_output_write(&ds,(void*)data,samples_count,loop_mode);
  
  
  // writing data but not starting playback yet
  sb_output_write(&ds,(void*)data,samples_count,0);
  
  // doing some stuff ... for instance setting the volume and panning
  sb_output_set_volume(&ds,0,0);
  
  // starting the playback (only if the data were written before)
  sb_output_play(&ds,loop_mode); 

   Přehrávání lze ukončit voláním sb_output_stop(). Stav přehrávání, resp. výstupního zařízení obecně, lze vyčíst pomocí sb_output_status(). Příklad použití při čekání na dokončení přehrávání je v následujícím kódu.

  while(1){    
    // get playback status
    DWORD count;
    int stat;
    int err = sb_output_status(&ds,&stat,NULL,&count,NULL,NULL);
    
    // leave if playback is done or no output data or something f.cked up
    if(err || !count || !stat)
      break;
    else
      Sleep(50);
  }       

4.1.2.4 - Záznam

   Pokud bylo otevřeno vstupní zařízení, může být zahájen záznam daného počtu vzorků pomocí sb_capture_wave(). Tato funkce je na rozdíl od sb_output_write() synchronní, tj. nevrátí, dokud není záznam dokončen. Příklad záznamu s formátem "float" (DSDLLF_IEEE32) je ukázán v následujícím kódu.

  // allocate buffer for the captured data
  float *buf = (float*)malloc(sample_count*input_channels*sizeof(float));
  
  // capture the waveform, return actual samples count 'read' and the 'time_stamp' 
  double time_stamp;
  DWORD read;
  sb_capture_wave(&ds,(void*)buf,sample_count,&read,&time_stamp);

   Alternativně může být potřebná velikost bufferu získána pomocí sb_capture_get_size(). I když je funkce sb_capture_wave() synchronní, může být přerušena voláním sb_capture_wave_abort() a lze také vyčítat stav záznamu sb_capture_wave_get_status(). Tyto funkce je ale pochopitelně třeba volat z jiného vlákna, než sb_capture_wave(). Je to trochu netradiční řešení, obvykle to bývá opačně, ale vzhledem k tomu, že tyto funkce používám výhradně v GUI, které stejně vždy mám v jiném vlákně, tak to nepředstavuje problém.


4.1.2.5 - Ostatní

   DSDLL má ještě řadu dalších funkcí pro konfiguraci, výpis chyb apod. Tyto funkce jsou popsány ve zdrojáku, takže není třeba je zde rozebírat. Jedinou podstatnou věcí je možnost logování pro účely ladění. Pokud je ve složce s "dsdll.dll" nalezen soubor "dsdll.ini" a je v něm následující:

  [DEBUG]
  ;enable logging (0-none, 1-basic function, 2-include status checking functions)
  log = 1

pak bude po přilinkování vytvořen soubor "debug.log" ve stejné složce. Do toho by se měly ukládat všechny volání jednotlivých funkcí knihovny.
Tento INI soubor navíc může obsahovat následující položky:

  [CAPTURE]
  ;default capture thread priority (see SetThreadPriority() API for values, default: +1 - above normal)
  thread_priority = +1
  ;default capture thread idle time [ms] (typically 10 ms, max 500 ms)
  thread_idle_time = 10

Ty ovlivňují časování záznamového vlákna. Zvyšování priority vlákna by teoreticky mohlo mít účinek při těžce vytíženém CPU nebo u jednojádrových CPU. Prodlužování idle periody vlákna, když zrovna neprobíhá záznam by mohlo snížit zatížení CPU u nějaké starší šunky, ale nelze ho zvýšit nad 500 ms, protože velikost záznamového bufferu je jen cca 1 s.


4.2 - C/C++ příklady

   Pro snažší pochopení jsem připravil pár příkladů v C/C++. Zdrojáky i binárky jsou součástí projektu knihovny.

4.2.1 - Základní přehrávání

   Toto demo provede výčet DS zařízení, otevře defaultní výstup a spustí přehrávání sinusovky.

Download (verze 1.0, 2016-08-20): dsdll_demo_basic.zip.

Základní přehrávání.
Obr. 4.2 - Základní přehrávání.

4.2.2 - Replay demo

   Toto demo provede výčet DS zařízení. Otevře defaultní vstup a výstup, zaznamená pár vteřin a pak tento záznam opakuje ve smyčce až do stisku klávesy.

Download (verze 1.0, 2016-08-20): dsdll_demo_basic.zip.

Replay demo.
Obr. 4.3 - Replay demo.

4.2.3 - Časové značky

   Toto demo je jednoduchý příklad použití časových značek. Line-out zvukovky je zapojen do line-in. Výstupní zařízení generuje kontinuálně sinusovku. Frekvence signálu a počet vzorků smyčky je nastaven tak, aby signál byl koherentní. Vstupní zařízení opakovaně zaznamenává signál z line-in. Spouštění vzorkování je záměrně pseudonáhodné. Počet zaznamenávaných vzorků je opět nastaven tak, aby zaznamenaný průběh (délka okna) byl koherentní s generovaným signálem. Díky tomu lze amplitudu a fázi zaznamenaného průběhu snadno spočítat pomocí DFT. Vypočtená fáze Φx takto zaznamenaného signálu je pochopitelně náhodná (viz zde), takže je korigována právě pomocí časových značek:

\phi'=\phi_\text{X}-\omega \cdot t_\text{S} = \phi_X - 2\pi \cdot f_\text{X} \cdot t_\text{S}

kde fX je frekvence signálu v Hz a tS je časová značka vrácená sb_capture_wave(). Pokud všechno v pořádku, měla by vypočtená fáze být téměř neměnná. Bude samozřejmě vykazovat pomalý drift daný teplotní závislostí analogové části karty a nějaký ten šum. Případné rychlé změny nebo skoky svědčí o tom, že karta buďto nevzorkuje synchronně (zmíněné adaptivní vzorkování) nebo někde v DSDLL přetékají/podtékají buffery, což by mohlo být způsobené nevhodným nastavením časování záznamového vlákna.

Download (verze 1.0, 2016-08-20): dsdll_demo_timestamp.zip.

Demo s časovými značkami.
Obr. 4.4 - Demo s časovými značkami.

   Následující grafy ukazují příklad měření s tímto prográmkem s Creative X-FI HD USB. Po nějakých 80ti minutách se fáze změnila o méně, než 1.5 µrad a amplituda pod 4 ppm, což není špatný výsledek na zvukovku za 1800 KČ. V laborce s klimatizací by to nejspíše bylo ještě lepší.

Měření stability fáze a amplitudy Creative X-FI HD USB při zapojení line-out do line-in, amplituda 0.5, 1 kHz, 48 kSa/s, 24bit.
Obr. 4.5 - Měření stability fáze a amplitudy Creative X-FI HD USB při zapojení line-out do line-in, amplituda 0.5, 1 kHz, 48 kSa/s, 24bit.


4.3 - Příklady pro GNU Octave/Matlab

   Osobně celkem často používám pro zpracování signálu a dat prostředí GNU Octave. Je to téměř 100 % kompatibilní s Matlabem a po letech vývoje se to dokonce už i dá rozumně použít ve Windoze. Problém je, že volat z GNU Octave přímo DLL je trochu oříšek. Z toho důvodu jsem vyrobil jednoduchou utilitku "dsrec.exe".
   Tento prográmek umožňuje provádět výčet DS zařízení do souboru nebo na stdout. Dále umožňuje generovat signál buďto ve smyčce nebo jednorázově. Souběžně s tím umožňuje záznam daného počtu vzorků ze vstupního zařízení. Vstupní i výstupní data jsou přenášeny přes soubory buďto ve formátu CSV nebo binárně v MAT-v4 (starý formát Matlab). Díky přenosu dat přes soubory se dá snadno použít v kombinaci prakticky s čímkoliv, včetně Octave. Detaily použití jsou snad zřejmé z obrázku. Zdroják utilitky je součástí zdrojáků celého projektu.

Download (verze 1.0, 2016-08-20): dsdll_dsrec.zip.

dsrec.exe utilitka pro jednoduchá měření.
Obr. 4.6 - "dsrec.exe" utilitka pro jednoduchá měření.

4.3.1 - Synchronní vzorkovací fázoměr

   Toto jednoduché demo je základní implementace vzorkovacího fázoměru pro koherentní signály. Na výstupu generuje sinusovky s daným fázovým posuvem o zadaném kmitočtu a amplitudě, které jsou spočítány tak, aby bylo možně generovat signál ve smyčce a byl koherentní. Průběhy zaznamenané vstupním zařízením jsou analyzovány pomocí FFT a z rozdílu fází je vypočten fázový posuv. Vzhledem k tomu, že se používá primitivní FFT, tak tento fázoměr lze použít výhradně na měření fáze generovaných signálů, resp. je lze prohnat nějakým analogovým obvodem, ale nelze měřit fáze externích signálů, protože nebudou koherentní.
   Demo neprovádí žádné korekce na mezikanálové chyby nebo přeslech mezi kanály, takže přesnost je omezená. Ale i tak to na 1 kHz s kartou Creative X-FI HD USB ukazuje chybu pod 0.0025°. Demo bylo testováno jen v Octave 4.0.0 a nemělo by vyžadovat žádné nestandardní balíky. S pár úpravami syntaxe by to mělo jet i v Matlabu, ale to nemám jak vyzkoušet.

Download (verze 1.0, 2016-08-20): dsdll_octave_synphase.zip.

Příklad měření fázového posuvu použitím utilitky 'dsrec.exe' a GNU Octave. f = 1 kHz.
Obr. 4.7 - Příklad měření fázového posuvu s použitím utilitky "dsrec.exe" a GNU Octave. f = 1 kHz.

Příklad měření fázového posuvu použitím utilitky 'dsrec.exe' a GNU Octave. f = 1 kHz.
Obr. 4.8 - Příklad měření fázového posuvu s použitím utilitky "dsrec.exe" a GNU Octave. f = 1 kHz.

4.3.2 - Jednoduchý měřič vstupní impedance zvukové karty

   Toto jednoduché demo umožňuje měřit vstupní impedanci kanálu zvukovky s použitím známé externí impedance. Výstup zvukovky generuje sinus na zvolených kmitočtech. Jeden vstupní kanál je zapojen přímo na výstup a slouží k měření referenčního napětí U1. Druhý vstup je zapojen na výstup přes referenční impedanci Zref a je na něm měřeno napětí U2. Zref tvoří spolu se vstupní impedancí Zi napěťový dělič, takže lze ze známých vstupních a výstupních napětí U1 a U2 a referenční impedance Zref vypočítat vstupní impedanci:

Z_i = Z_{ref} \frac{U_2}{U_1 - U_2}.

Zapojení zvukové karty pro měření vstupní impedance.
Obr. 4.9 - Zapojení zvukové karty pro měření vstupní impedance.

   Je to vlastně jednoduchý impedanční můstek, jen měřená impedance je přímo vstup zvukovky. Aby měření mělo nějaký smysl, přidal jsem do skriptu i funkci na korekci mezikanálových chyb karty, bez kterých zejména vedlejší složka impedance, v tomto případě ekvivalentní paralelní kapacita, bude mít velmi mizernou přesnost. Skript tedy uživatele vyzve k zapojení obou vstupních kanálů přímo do výstupu karty. Provede se série měření mezikanálových chyb fáze a amplitudy, výsledek se uloží do souboru a ten je pak při samotném měření použit jako gain/phase korekce.
   Pro optimální funkci je třeba jako referenční impedanci zvolit rezistor s hodnotou co nejbližší měřené impedanci a měl by mít co nejnižší parazitní kapacitu. V mém případě vyhověl etalon odporu 10 kΩ, ale bez problému to bude fungovat s obyčejným odporem v pouzdře 0207 od chobota. Typická paralelní parazitní kapacita takového odporu je cca 0.5 pF, ale její vliv na výsledek bude vzhledem k neznámé kapacitě kabelu minimální. Nicméně kapacitu kabelu lze snadno zjistit připojením dalšího identického kabelu paralelně ke vstupu karty a hodnotu kapacity pak lze snadno určit z rozdílu změřené kapacity.
   Tento skript nevyžaduje žádné speciální balíky a měl by fungovat snad ve všech verzích Octave, ale testován byl jen od verze 3.6.4. S pár drobnými úpravami syntaxe by měl chodit i v Matlabu.
   Příklad měření je ukázán na následujících obrázcích. Vstupní paralelní ekvivalentní odpor je celkem konstantní. Ekvivalentní paralelní kapacita vypadá sice dost podezřele, ale změřil jsem totéž s referenčním odporem 100 kΩ i regulérním impedančním můstkem, takže to asi budou reálná čísla.

Download (verze 1.0, 2016-08-20): dsdll_octave_inpz.zip.

Příklad měření vstupní impedance zvukové karty Creative X-FI HD USB pomocí utilitky 'dsrec.exe' a GNU Octave.
Obr. 4.10 - Příklad měření vstupní impedance zvukové karty Creative X-FI HD USB pomocí utilitky "dsrec.exe" a GNU Octave.

říklad měření vstupní impedance zvukové karty Creative X-FI HD USB pomocí utilitky 'dsrec.exe' a GNU Octave.
Obr. 4.11 - Příklad měření vstupní impedance zvukové karty Creative X-FI HD USB pomocí utilitky "dsrec.exe" a GNU Octave.

4.3.3 - Měření přeslechu zvukové karty

   Update 2.9.2016: V několika aplikacích jsem se pokoušel použít zvukovou kartu Creative X-FI HD USB jako ADC a DAC. Vzhledem k tomu, že běžně měřím s nejistotami v řádu ppm (0.0001 %) a jednotek µrad, je nezbytné korigovat všechny možné rušivé vlivy. Jeden z těch základních zdrojů chyb je samozřejmě přeslech mezi vstupními kanály a také přeslech z výstupu na vstup. Zní to ovšem jednodušeji, než jaké to ve skutečnosti je. Problém parazitních vazeb v rámci dvoukanálové zvukovky je ilustrován na následujícím obrázku.

Problém přeslechu mezi kanály zvukové karty.
Obr. 4.12 - Problém přeslechu mezi kanály zvukové karty.

   Je zřejmé, že možných vazeb je uvnitř zvukové karty minimálně osm. Na to, aby je bylo možné nějak rozumně korigovat je potřeba je změřit a to jednotlivě. Pokud je použit jen jeden výstupní kanál karty, což v mých aplikací je zatím vždy, tak se situace trochu zjednoduší, zůstanou už jen čtyři přenosy. Ty ale musí vzájemně být odděleny. Např. pro měření přenosu C34 nelze jednoduše generovat sinus na "line-out R", připojit ho do "line-in L" a měřit parazitní napětí U1R na "line-in R", protože na něm bude superponováno také napětí přes vazbu C24 přímo z výstupu "line-out R". Jediná cesta tedy zřejmě je buďto použít jiný zdroj napětí, který ale nebude koherentní, což značně zkomplikuje zpracování signálu, nebo nejdřív nechat oba vstupy nezapojené, změřit zbytkové napětí U0R na "line-in R" indukované přes vazbu C24 a pak ho odečíst při vlastním měření vazby C34. To má ovšem drobný háček. Všechny napětí a přenosy musí být měřené jako komplexní čísla, tj. nejen amplituda, ale i fáze. A pokud při měření U0R neznám fázi referenčního signálu, jakože ji neznám, protože ho kvůli přeslechům nemohu připojit na žádný vstupní kanál, tak lze změřit jen amplitudu. Jediné řešení tedy zřejmě je použití časového multiplexu a externího multiplexeru (odpojovače) mezi výstupem a analyzovaným vstupem. K tomuto účelu jsem spíchl přípravek podle nesledujícího schématu.

Multiplexer pro měření komplexního přeslechu zvukové karty.
Obr. 4.13 - Multiplexer pro měření komplexního přeslechu zvukové karty.

   Aby to k něčemu bylo, musí odpojovač zajistit izolaci lepší, než nějakých -140 dB. Proto jsem použil dvě vhodně zapojené relé za sebou. Jsou asi 2cm daleko a druhé relé je odděleno kovovou přepážkou, protože jinak to přes plastové pouzdro chytalo kapacitní vazbu. Změřený parazitní přenos přes rozpojený multiplexer je menší, než -150 dB na 20 kHz. Relé jsou navíc zapojeny tak, aby při rozpojené cestě byl výstup zvukovky zatížen zhruba stejnou impedancí, jako v průchozím stavu. K tomu slouží substituční impedance RL a CL. Ovládací cívky jsou jedním koncem zapojeny proti signálové zemi a jsou blokovány kondíky a vzájemně ještě odděleny RCR členem, jinak to má tendenci chytat vazbu přes cívky. Konkrétně jsem použil bistabilní relé ZETTLER AZ850P1-5, které se i přes nízkou cenu celkem osvědčily. Abych nemusel tahat nějaké ovládací signály z COM portu nebo USB a psát kvůli tomu ještě další program, je multiplexer ovládán pomocí nevyužitého kanálu zvukovky. Ten generuje kladné a záporné pulzy. Libovolný J-FET operák s rail-to-rail výstupem funguje jako schmittův KO, tranzistory ho proudově posílí a kondík C3 už generuje samotné ovládací pulzy pro cívky relé. Hodnoty součástek jsou nastavené tak, že to s 1 ms pulzy spolehlivě spíná při line-out výstupu Creative X-FI HD USB ještě s poloviční amplitudou. S uvedeným OZ je klidová spotřeba cca 40 µA, ale dalo by se to poladit i na pár µA. To je velká výhoda bistabilních relé. Jeden takový multiplexer mám postavený a klidová spotřeba je jen cca 0.5µA. To už pak není ani potřeba odpojovat baterku a s nějakou lithiovou 9V to vydrží klidně 5 let provozu. :-)
   Měření tedy probíhá následovně: V první fázi měření jsou relé RE1 a RE2 průchozí, tj. "line-out R" je připojen na "line-in L". Změří se vektory napětí na obou vstupních kanálech U1L a U1R. V další fázi je vstup "line-in L" izolován, zatímco výstup stále generuje signál. Vstup "line-in L" je v této fázi zakončen vhodnou impedancí RZ1, v mém případě to byl odpor 1 Ω, což zhruba odpovídá výstupní impedancí nějakého sledovače s OZ včetně kabelu. V tomto stavu jsou opět změřeny vektory napětí obou kanálů U0L a U0R. Kanál "line-in R" je v obou fázích zakončen odporem RZ2 = 1 Ω. Vektory napětí U1X a U0X jsou vzájemně synchronizovány pomocí časových značek. Následně již lze vypočítat jednotlivé koeficienty vazeb (přeslechů):

\\\hat{C}_{23} = \frac{\hat{U}_\text{0L}}{\hat{U}_\text{1L}},\\\hat{C}_{24} = \frac{\hat{U}_\text{0R}}{\hat{U}_\text{1L}},\\\hat{C}_{34} = \frac{\hat{U}_\text{1R}}{\hat{U}_\text{1L}} - \hat{C}_{24}.

   Pro určení zbylého přenosu C43 je třeba prohodit vstupní kanály a měření zopakovat. Obdobně pro určení vazeb z levého výstupu, pokud je třeba.
   Samotnou měřící proceduru jsem napsal z lenosti opět v Octave, protože pro toto měření není třeba žádné GUI. Konzole bohatě stačí. Skripty jsem otestoval ve verzi 3.6.4 a 4.0.0. Použití je popsáno přímo ve skriptu.

Download (verze 1.0, 2016-08-31): dsdll_octave_crosstalk.zip.

   Následující dva grafy ukazují měření jednotlivých koeficientů v závislosti na kmitočtu. Jak je patrné, měření s referenčním signálem na obou line-in kanálech ukazuje zhruba identické přeslechy. Je celkem zvláštní, že přenos výstup-vstup má optimum okolo 1 kHz a na obě strany přeslech roste. Přeslech mezi vstupními kanály je asi až do 3 kHz nějakých -94 dB, pak roste zřejmě vlivem kapacitní vazby. To je celkem dost vysoký přeslech a je tedy zřejmé, že měřit cokoliv seriózně oběma kanály současně bez korekcí v podstatě nelze a celkem to i vysvětluje, chyby pokusného fázoměru.

Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 13 kΩ. Referenční kanál line-in L.
Obr. 4.14 - Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 13 kΩ. Referenční kanál line-in L.

Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 13 kΩ. Referenční kanál line-in R.
Obr. 4.15 - Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 13 kΩ. Referenční kanál line-in R.

   Protože mi tvar přeslechu výstup-vstup přišel poněkud podezřelý, zkusil jsem měření zopakovat při zvýšené zátěži výstupu. Použil jsem k tomu odpory 1 kΩ a 100 Ω. Následující dva grafy ukazují, jak se přeslechy změnily.

Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 1 kΩ. Referenční kanál line-in L.
Obr. 4.16 - Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 1 kΩ. Referenční kanál line-in L.

Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 100 Ω. Referenční kanál line-in L.
Obr. 4.17 - Přeslechy line-in/line-out zvukové karty Creative X-FI HD USB při zátěži výstupu 100 Ω. Referenční kanál line-in L.

   Přeslech na nízkých kmitočtech se celkem brutálně zvýšil a na vysokých se dost podezřele zdeformoval. Nemám náladu provádět reverzní inženýrství karty, abych zjistil příčinu, ale plyne z toho závěr, že není rozumné výstupy příliš zatěžovat. Je zřejmě vhodné oddělit výstup nějakým sledovačem s J-FET operákem. Kapacitní zátěž kabelem sice zůstane, ale činná složka tak vzroste minimálně do řádu MΩ.



4.4 - LabVIEW knihovna

Aktualizace 22.4.2017: Konečně jsem měl náladu trochu poladit a otestovat svoji DSDLL knihovnu pro LV. Jedná se v zásadě jen o wrapper DLL knihovny napsaný tak, aby se tím daly snadno nahradit ovladače libovolného profesionálního digitizéru. Knihovna obsahuje také pár demo aplikací napsaných tak, aby nevyžadovaly žádné knihovny, což je trochu problém, protože základní LV neobsahuje ani FFT.

VI Tree of the LabVIEW DSDLL library.
Obr. 4.18 - DSDLL knihovna pro LabVIEW.

   Knihovna je napsané v LV 2013 a je dostupná pod licencí GNU Lesser General Public License. Je rozdělená do několika skupin funkcí: Inicializace, řízení, přehrávání a záznam. Jedinou změnou proti volání z C/C++ je, že se musí inicializovat handle DSDLL. To slouží k nalinkování DLL knihovny. Standardně je očekávaná ve složce s binárkou, ale lze zadat konkrétní cestu. Navíc byly přidány vstupně/výstupní funkce pracující s formátem 'double', které si samy převedou data na potřebný formát. Zbytek je identický přímému volání DLL.

Zdroják, binárka a dema ke stažení (verze 1.0, 2016-11-11): dsdll_lv2013.zip.

4.4.1 - Základní demo

   Je to nejjednodušší použití knihovny. Umožňuje nahrát vzorek a ten následně přehrávat ve smyčce. Mimo to asynchronně zjišťuje pozice pointerů v bufferech.

Základní DSDLL demo pro LV.
Obr. 4.19 - Základní DSDLL demo pro LV.

4.4.2 - Měření frekvenčního přenosu

   Toto demo je určeno k měření frekvenčního přenosu dvoubranu pomocí bílého šumu. Na line-out je generován bílý šum. Ten je přiveden na vstup dvoubranu. Na levém a pravém kanálu line-in je nahráváno vstupní a výstupní napětí uL,R(t). Z těch jsou následně vypočteny spektra UL,R(f). Komplexní přenos je vypočten z jejich poměru: G(f) = UR(f)/UL(f). Výsledek je filtrován a zobrazen jako Bode plot a v Nyquistově rovině. Na screenshotu na obr. 4.20 je měřena pásmová zádrž (TT-filtr).

Příklad měření frekvenčního přenosu s DSDLL knihonvou pro LV.
Obr. 4.20 - Příklad měření frekvenčního přenosu s DSDLL knihovnou pro LV.

4.4.3 - Měření stability

   Poslední demo je ekvivalentní C/C++ demu pro měření stability. Na line-out je generován sinus, na line-in je nahráván. Pomocí FFT je vypočtena amplituda a fáze, pomocí časových značek je korigována fáze, takže když to funguje, měla by být fáze i amplituda konstantní jen s pomalými fluktuacemi díky teplotní nestabilitě analogové části zvukové karty.

Příklad měření stability s DSDLL knihonvou pro LV.
Obr. 4.21 - Příklad měření stability s DSDLL knihovnou pro LV.

5 - Nějaké ty měření

   Zde je/bude pár příkladů měření s Creative X-FI HD USB.


5.1 - Nekoherentní vzorkovací fázoměr

   Jen tak ze zvědavosti jsem zkusil napsat jednoduchý vzorkovací fázoměr pro sinusové signály. Vzhledem tomu, že generátor Clarke-Hess 5000, který jsem použil ke generování referenčního fázového posuvu nelze nijak synchronizovat se vzorkovací kartou, jsou zaznamenané průběhy pochopitelně nekoherentní a nelze tudíž použít primitivní DFT. Pro tyto účely existuje celá řada algoritmů, ale asi nejjednodušší je prosté proložení navzorkovaného průběhu víceparametrovým modelem harmonické funkce. Může to znít trochu komplikovaně, ale naštěstí v GNU Octave je pro účely prokládání nelineárních funkcí funkce leasqr() z balíku "optim". Tato funkce dokáže proložit snad cokoliv. Konkrétně je výpočet fáze zaznamenaného průběhu proveden následovně:

  % four parameter model of the harmonic function (amplitude, phase, frequency and dc offset)
  F = inline(' th(1).*sin(2*pi*th(2)*t + th(3)) + th(4) ','t','th');
  
  % initial guess of the captured waveform parameters
  amplitude = 0.5*(max(waveform) - min(waveform));
  offset = 0.5*(max(waveform) + min(waveform));
  phase = 0;
  frequency = must_be_known_at_least_approximately;
  
  % vector of initial parameters
  th = [amplitude frequency phase offset];
  
  % fit the 'waveform' which is function of 'time' by the function 'F'
  [..., par] = leasqr(time, waveform, F, th, ...)
  
  % calculate complex signal amplitude
  U = par(1)*exp(j*par(3));
  
  % extract the phase
  measured_phase = arg(U);

   Tento primitivní skript je použit pro výpočet fází obou signálů a ty jsou pak odečteny. Vypadá to sice dost jednoduše, ale při dobrém počátečním odhadu parametrů to snad vždy konverguje. V příkladu nijak neřeším počáteční odhad fáze, což často vede k inverzi amplitudy, ale s tím si poradí předposlední řádek. Pro prověření funkce jsem vstupy karty zapojil přímo na generátor CH5000. Měření bylo provedeno jen pro několik kmitočtů a napětí 1 Vrms na obou kanálech. Pochopitelně před samotným měřením jsem spojil oba kanály na jeden výstup generátoru a provedl odečet zbytkového fázového posuvu mezi kanály karty. Tím pak korigují vlastní měření. Výsledek tohoto experimentu je uveden v tabulce 4.1.

Měření fázového posuvu s kartou Creative X-FI HD USB pomocí víceparametrového fitování harmonickou funkcí v GNU Octave. Nejistota měření je rozšířena pro p = 95 %.
Tab. 4.1 - Měření fázového posuvu s kartou Creative X-FI HD USB pomocí víceparametrového fitování harmonickou funkcí v GNU Octave. Nejistota měření je rozšířena pro p = 95 %.

   Dle očekávání jsou výsledky celkem uspokojivé. Odchylky fáze jsou max v řádu m°. Nejistota měření je dána především specifikacemi CH5000. Jeho kalibrační data mají sice nejistotu podstatně nižší, ale už jsou přes rok stará, takže nelze odhadnout, jestli odchylky způsobuje CH5000 nebo fázoměr. Každopádně to není špatný výsledek a bez problému předčí komerční přístroje v řádu desítek kKČ. Kdyby se k tomu dodělaly korekce na přeslech mezi kanály, tak by to mohlo mít přesnost ještě vyšší.

   Update 5.9.2016: Nějak jsem neodolal a zopakoval jsem totéž měření s korekcemi na mezikanálové přeslechy. Celá korekce spočívá v odečtení parazitních napětí na obou kanálech indukovaných přes přenosy C34 z levého do pravého line-in vstupu a C43 z pravého do levého vstupu:

  % U1 and U2 are the fitted complex voltages U from the fitting script
  
  % fix the voltages by subtracting the crosstalk voltages
  U1c = U1 - U2*C43;
  U2c = U2 - U1*C34;
  
  % calculate the phase difference
  phase_shift = arg(U1c/U2c);

   Podle očekávání se chyby dost snížily především na vysokých kmitočtech, kde byly přeslechy nejhorší. Nové výsledky měření jsou uvedeny v tab. 4.2. Efekt korekcí by byl ještě vyšší, kdyby byl měřen jiný poměr napětí, než 1:1.

Měření fázového posuvu s kartou Creative X-FI HD USB pomocí víceparametrového fitování harmonickou funkcí v GNU Octave s korekcemi na přeslechy. Nejistota měření je rozšířena pro p = 95 %.
Table 4.2 - Měření fázového posuvu s kartou Creative X-FI HD USB pomocí víceparametrového fitování harmonickou funkcí v GNU Octave s korekcemi na přeslechy. Nejistota měření je rozšířena pro p = 95 %.









(c) 2017, Stanislav Mašláň - All rights reserved.

Poslední aktualizace: 22.4.2017 Up