Struktura zdrojáku analogového záznamu v jazyce C

11. 4. 2024
Doba čtení: 10 minut

Sdílet

 Autor: Depositphotos
Dnes popíšeme formát datového pole s ASCII-artem nesoucím data a pomocných proměnných, které spolu s přibaleným zdrojákem přehrávače tvoří analogový záznam v ASCII-artu v jazyce C, a způsob jejich čtení.

Při záznamu program c_recVACu vytvoří soubor recording.c, což je výsledný záznam, který se skládá z:

  • recording.h, což je samotný výstup z programu c_rec a obsahuje především obrovský řetězec unsigned char tape_data[] a parametry záznamu:
    • preemphasis_usec
    • sample_rate_Hz
    • mono
    • seed – startovací pozice pseudonáhodného generátoru
    • analogizen – počet digitálních symbolů
    • analogizen_half – symbol na půl cesty – půlka počtu digitálních symbolů
    • char_swap_rd – zpětná překladová tabulka pro 1-znakové přehrávání, aby data mohla být v ASCII artu, kde tichu odpovídají mezery atd.
    • label0, label1, label2, label3 – jednotlivé řádky popisky kazety
    • playback_gain_lin – je známý až po záznamu, tak je na konci. Zesílení při přehrávání.
  • c_play1.c, první část přehrávače – proměnné
  • pair1.h, definice konstant společných pro záznam o přehrávač: STRBASE, STR2BASE, STRBASE_HALF, STR2BASE_HALF
  • ink_pixels.h: tabulka kolik inkoustových pixelů má který znak. Generováno programem count2
  • pair2.h: kód překladových tabulek 1– a 2-znakového záznamu který musí být identický při záznamu i přehrávání, aby dával stejné výsledky, proto je v separátním souboru .h, který se vloží pomocí #include
  • c_play2.c – většina kódu přehrávače

Čtení z kazety

V souboru c_play2.c read_byte_pretty_printed pouze přečte bajt z řetězce tape_data. Funkce compress_char vyhodí z řady hodnot 3 znaky které se v C řetězcích nedají vždy napsat jako 1 znak: uvozovky, otazník a obrácené lomítko. Návratová hodnota 0 znamená že se další bajt z pásky nepodařilo přečíst a je tedy konec přehrávání.

static inline unsigned read_byte_clean(unsigned char *clean_byte)
{
        unsigned retval;
        unsigned char expanded_char, compressed_char;

        retval=read_byte_pretty_printed(&expanded_char);
        if (!retval) return 0;
        compressed_char=compress_char(expanded_char);
        *clean_byte=char_swap_rd[((unsigned)compressed_char)&0xFFU]; /* int cannot be used because bitwise ops don't have guaranteed meaning in terms
                                                                    of value */
        return 1;
}

Přeskakování 3 problematických znaků

Proč se roztahování číselné řady na přeskočení uvozovky, otazníku a obráceného lomítka nedělal při záznamu? Záhadu objasní tyto komentáře v c_rec.c: pro zápis je tato operace integrována v překladové tabulce, pro čtení nikoliv.

static unsigned char char_swap_wr[STRBASE]; /* For recording. Contains directly expanded chars. */
static unsigned char char_swap_rd[STRBASE]; /* For playback. Indexed by compressed char. */

Máme-li odstup od šumu větší jak 41 dB, používají se místo toho dva znaky na symbol a tato rutina s překladovou tabulkou pair_rd, kterou si přehrávač vytoří identicky se záznamovým programem.

static inline unsigned read_pair(unsigned *zeroed_on_STR2BASE_HALF)
{
        unsigned rv0, rv1;
        unsigned char ec0, ec1, cc0, cc1;

        rv0=read_byte_pretty_printed(&ec0);
        rv1=read_byte_pretty_printed(&ec1);

        if (!(rv0&&rv1)) return 0;

        cc0=compress_char(ec0);
        cc1=compress_char(ec1);

        *zeroed_on_STR2BASE_HALF=pair_rd[cc0][cc1];
        return 1;
}

Čtení diferenciální číslice

Obě funkce se pak sjednotí ve funkci čtení diferenciální číslice (symbolu) – tedy ne skutečného symbolu ale rozdílu od předchozího. STRBASE_HALF je symbol zhruba v půlce rozsahu pro 1-znakové nahrávání (zvládne max. 92 úrovní), STR2BASE_HALF je pro 2-znakové.

Zde je opět třeba zajistit, že před počítáním zbytku %n_levels neprotočíme čísla pod nulu. Hodnota proměnné zeroed_on_STR(2)BASE_HALF je v rozsahu  >=STR(2)BASE_HALF-analogize_n_half...<STR(2)BASE_HALF-analogize_n_half+n_levels.

Nulová změna sobě jdoucích symbolů odpovídá STR(2)BASE_HALF v případě proměnné zeroed_on_STR(2)BASE_HALF a ve výstupu toto bude odpovídat analogize_n_half, což je zhruba půlka n_levels. Nejmenší hodnota která může být v zeroed_on_STR(2)BASE_HALF  je tedy STR(2)BASE_HALF-analogize_n_half. K tomu se přičte analogize_n_half-STR(2)BASE_HALF, takže dostaneme STR(2)BASE_HALF-analogize_n_half+analogize_n_half-STR(2)BASE_HALF, a toto je očividně 0.

Pokud je obsah pásky porušen, podmínka splněna být nemusí, ale tam je pak jedno, co za hodnotu se z pásky přečte.

static inline unsigned read_diff_digit(unsigned long *diff_digit, unsigned long n_levels)
{
        if (n_levels>STRBASE){
                unsigned zeroed_on_STR2BASE_HALF;
                if (!read_pair(&zeroed_on_STR2BASE_HALF)) return 0;
                /* Now zeroed_on_STR2BASE_HALF is zeroed on (=corresponds to no change) STR2BASE_HALF.
                 * >=STR2BASE_HALF-analogize_n_half...<STR2BASE_HALF-analogize_n_half+n_levels */
                *diff_digit=((unsigned long)zeroed_on_STR2BASE_HALF+(unsigned long)analogize_n_half
                        -(unsigned long)STR2BASE_HALF)%n_levels;
                return 1;
        }else{
                unsigned char zeroed_on_STRBASE_HALF;
                if (!read_byte_clean(&zeroed_on_STRBASE_HALF)) return 0;
                /* Now zeroed_on_STRBASE_HALF is zeroed on (=corresponds to no change) STRBASE_HALF.
                 * >=STRBASE_HALF-analogize_n_half...<STRBASE_HALF-analogize_n_half+n_levels */
                *diff_digit=((unsigned)zeroed_on_STRBASE_HALF+(unsigned)analogize_n_half
                        -(unsigned)STRBASE_HALF)%n_levels;
                return 1;
        }
}

Z read_diff_digit  nám padají diferenciální symboly (číslice) tedy rozdíly vůči symbolu předchozímu. Skutečné symboly z nich udělá funkce read_smooth_11 a vytvoří z digitálních číslic analogové hodnoty voláním deanalogize_11. Používá se proměnná previous_real_digit, v které si pamatujeme hodnotu předchozí skutečné (nediferenciální) číslice a musí být v rozsahu >=0…<analogizen. Výraz (unsigned long)diff_digit+n_levels-analogize_n_half+*previous_real_digit se nám neprotočí pod nulu, protože jediná odčítaná hodnota analogize_n_half je menší přičítaná hodnota. n_levels=analogize_n.

int read_smooth_11(unsigned long *previous_real_digit, double *smooth, unsigned long n_levels)
{
        unsigned long diff_digit;
        unsigned long real_digit;

        if (!read_diff_digit(&diff_digit, n_levels)) return 0;
        real_digit=((unsigned long)diff_digit+n_levels-analogize_n_half+*previous_real_digit)%n_levels;
        *previous_real_digit=real_digit;
        *smooth=deanalogize_11(real_digit, n_levels);
        return 1;
}

Je nutné aby proměnná previous_real_digit byla inicializovaná na analogizen_half identicky jako při záznamu. Toto se dělá ve funkci  read_tape_header:

        prev_real_dig_0=analogize_n_half;
        prev_real_dig_1=analogize_n_half;

Jak napovídala přípona _11 názvů read_smooth_11 a deanalogize_11, pracovalo se v rozsahu reálných čísel >=-1…<=+1. To se triviálně mění ve funkci deanalogize_11 na rozsah >=0…<=+1: pro funkci  deanalogize_01.

Rádiový program: Hacker Public Radio 3811: pojmenované roury příkazem mkfifo (anglicky)

Myslím si, že k hackerskému rádiu dobře sedne, když je šířeno ve formátu programovacího jazyka C, a s vysílacím kompresorem zní lépe, skoro jako profesionální rádio. Tentokrát si prosvištíme slovíčka v angličtině.

 _______________________________________________________
(+)  _______________________________________________  (+)
|   / Hacker Public Radio ep. 3811: mkfifo and      \   |
|  | named pipes, by Klaatu, CC-BY-SA 4.0            |  |
|  | Have you ever named a pipe?                     |  |
|  | https://hackerpublicradio.org/eps.php?id=3811   |  |
|  |        _________________________________        |  |
|  |       /            _________            \       |  |
|  |      |  A " A     |         |     A " A  |      |  |
|  |      | (     )    |   | |   |    (     ) |      |  |
|  |      |  v , v     |_________|     v , v  |      |  |
|  | 75 μs \_________________________________/       |  |
|  |                                                 |  |
|  | VIRTUAL ANALOG CASSETTE        BY KAREL KULHAVÝ |  |
|  |_________________________________________________|  |
|            /             (+)             \            |
|           /        _             _        \           |
|          /   _    (_)           (_)    _   \          |
(+)_______/___(_)_______________________(_)___\_______(+)
logo

(logo public domain)

Kompilační příkaz:

bzip2 -dc hpr_3811_256_fm.c.bz2 | cc -O3 -x c - -lm -o recording && ./recording 0 | mpv -
Zakomprimovaný zdroják ke stažení Délka souboru zakomprimovaná Odstup analogového signálu od šumu analogové nahrávky analogizované na digitální nosič Preemfáze Modulační procesor Počet symbolů digitálního nosiče analogové nahrávky Bitová rychlost ve zkomprimovaném stavu Bitová rychlost zdroje Délka min:sec vzorkovací frekvence Počet kanálů Kvalita zdroje SHA256 .c.bz2 souboru
hpr_3811_256_fm.c.bz2 14 586 150 B 50 dB 50 μs (evropské FM rádio) SoX FM 256 172 kbps 1,15 Mbps 11:19 44 100 Hz mono 24-bit WAV 48 kHz 9C21299D0508B2208FACB46C90E44325 0B11CB0FE14191D97B560D63109FD10F
hpr_3811_256_wam.c.bz2 9 803 039 B 50 dB 75 μs (AM rádio) SoX AM 256 116 kbps 1,15 Mbps 11:19 22 050 Hz mono 24-bit WAV 48 kHz 409E2E9269164DF2F355C6C73­0761D92 44160D1315132E4DE481DC9FDE427978
hpr_3811_256_am.c.bz2 5 278 091 B 50 dB 75 μs (AM rádio) SoX AM 256 62 kbps 1,15 Mbps 11:19 11 025 Hz mono 24-bit WAV 48 kHz 40917373A9E7A138BEEAF8C15975222C FC01FA1AF5ED83E8E4282FE2465F5696

Analogový rádiový poslech přímo z exotických krajin přinášený na vlnách jazyka C

Thajsko AM

Thajské či kambodžské analogové rádio si zde můžete v ukázce poslechnout zcela autenticky, aniž byste se tam museli osobně trmácet. Znít bude přesně stejně, jako byste se tam osobně dostavili (za předpokladu akustického šumu okolí podobného tomu na pásce) – po cestě žádná kvantizace ani psychoakustická komprese nebude. 16-bitovou kvantizaci KiwiSDR trapně zamlčím, škoda že KiwiSDR neumí více bitů. FM přijímač nahrávaný na 24-bitové zvukovce tím naštěstí netrpí (doufám že není ve smartphonu FM rádio implementováno digitálně).

Zda je AM preemfáze 75 μs v Thajsku povinná nevím, podle poslechu tipuju, že je přítomna, tak jsem příkazem SoX provedl deemfázi (2122 Hz=1/(2π*75μs)):

sox vstupni.wav vystupni.wav lowpass -1 2122

Do C jsem analogově nahrál Sabai Sabai z roku 1987 na KiwiSDR v Cha-Am, Thajsku na 675 kHz pravděpodobně 126 km přes hladinu moře z 5kW vysílače v Bangkoku. SDR v Cha-Am má trvalé prskavé rušení, slyšitelné v pozadí.

Thajská modlitba není v thajštině, ale v páli, jazyku původem z Indie, stejně jako v Kambodži. páli, příp. sanskrt, je pro tyto země totéž co latina pro Česko.

Kdybyste se divili, co má Čeština společného s Indií, když se říká, že je indoevropský jazyk, myslím, že tato tabulka hovoří sama za sebe:

čeština páli nebo sanskrt
dvě dvi
dvě stě dvsatau
tři ti
třista tisata
čtyři čatu
čtyři sta čatsata
deset dasa
smrt mrť
život čivita
věda véda

Pořad o domácích thajských receptech v angličtině byl vysílán svazkem na Evropu z Ban Dungu u Udon Thani v Thajsku na 9,920 MHz mezi 19:00Z a 20:00Z a odrazy mezi zemí a ionosférou oběhly 80 stupňů zemského povrchu. Překvapilo mě, v jak dobré kvalitě jsem ho chytil v Nizozemí na vzdálenost 8 895 km, ač vysílač vysílá pouze na 50 % výkonu, 250 kW místo 500 kW.

Softwarově definované rádio (SDR) v Cha-Am

Autor: Karel Kulhavý

Cha-Am, Phetchaburi, Thajsko

Autor: public domain

Cha-Am

Autor: แอนเดอร์สัน, CC-by-sa 4.0

Cha-Am

Autor: public domain

Krátkovlnný 250 kW (max. 500 kW) vysílač Ban Dung, Udon Thani, Thajsko, dlouhý 1,8 km, který jsem v dobré kvalitě nahrál v Nizozemí na vzdálenost 8 895 km. Anténa je typu curtain array.

Autor: public domain

Detail vysílače Ban Dung, modré značky zleva doprava: 1. anténní pole směrem na Evropu, 2. vysokofrekvenční napájecí vedení k jednotlivým anténám, 3. budova vysílače

Autor: USGS Earth Explorer

Celý areál vysílače Ban Dung

Autor: USGS Earth Explorer

Nedaleko vysílače Ban Dung jsem byl v džunglově ostrovním klášteře Kham Chanot s jezerem, mostem, domečky pro duchy, tajemnou studánkou a gongem, co se dá rozeznít třením rukou.

bitcoin_skoleni

 _______________________________________________________
(+)  _______________________________________________  (+)
|   / Thongchai McIntyre: Sabai Sabai (GMM Grammy)  \   |
|  | Cha-Am SDR 28.4.2023 09:13:10Z 675 kHz          |  |
|  | https://www.youtube.com/watch?v=03xOU4xyYMo     |  |
|  | 30 s ukázka                                     |  |
|  |        _________________________________        |  |
|  |       /            _________            \       |  |
|  |      |  A " A     |         |     A " A  |      |  |
|  |      | (     )    |   | |   |    (     ) |      |  |
|  |      |  v , v     |_________|     v , v  |      |  |
|  | 75 μs \_________________________________/       |  |
|  |                                                 |  |
|  | VIRTUAL ANALOG CASSETTE        BY KAREL KULHAVÝ |  |
|  |_________________________________________________|  |
|            /             (+)             \            |
|           /        _             _        \           |
|          /   _    (_)           (_)    _   \          |
(+)_______/___(_)_______________________(_)___\_______(+)

 _______________________________________________________
(+)  _______________________________________________  (+)
|   / Thajská modlitba v jazyce Páli, Cha-Am SDR    \   |
|  | 28.4.2023 9:18:27Z 819 kHz                      |  |
|  | 30 s ukázka                                     |  |
|  |                                                 |  |
|  |        _________________________________        |  |
|  |       /            _________            \       |  |
|  |      |  A " A     |         |     A " A  |      |  |
|  |      | (     )    |   | |   |    (     ) |      |  |
|  |      |  v , v     |_________|     v , v  |      |  |
|  | 75 μs \_________________________________/       |  |
|  |                                                 |  |
|  | VIRTUAL ANALOG CASSETTE        BY KAREL KULHAVÝ |  |
|  |_________________________________________________|  |
|            /             (+)             \            |
|           /        _             _        \           |
|          /   _    (_)           (_)    _   \          |
(+)_______/___(_)_______________________(_)___\_______(+)

 _______________________________________________________
(+)  _______________________________________________  (+)
|   / Radio Thailand World Service 9,920 MHz        \   |
|  | 27.4.2023 19:03:42Z chyceno v Nizozemí UTwente  |  |
|  | WebSDR z 250 kW Ban Dung, Udon Thani, Thajsko   |  |
|  | Anglicky recepty thajská jídla      30 s ukázka |  |
|  |        _________________________________        |  |
|  |       /            _________            \       |  |
|  |      |  A " A     |         |     A " A  |      |  |
|  |      | (     )    |   | |   |    (     ) |      |  |
|  |      |  v , v     |_________|     v , v  |      |  |
|  | 75 μs \_________________________________/       |  |
|  |                                                 |  |
|  | VIRTUAL ANALOG CASSETTE        BY KAREL KULHAVÝ |  |
|  |_________________________________________________|  |
|            /             (+)             \            |
|           /        _             _        \           |
|          /   _    (_)           (_)    _   \          |
(+)_______/___(_)_______________________(_)___\_______(+)

Kompilační příkaz:

bzip2 -dc sabai_92.c.bz2 | cc -O3 -x c - -lm -o recording && ./recording 0 | mpv -
Zakomprimovaný zdroják ke stažení Délka souboru zakomprimovaná Odstup analogového signálu od šumu analogové nahrávky analogizované na digitální nosič Preemfáze Modulační procesor Počet symbolů digitálního nosiče analogové nahrávky Bitová rychlost ve zkomprimovaném stavu Bitová rychlost zdroje Délka min:sec vzorkovací frekvence Počet kanálů Kvalita zdroje SHA256 .c.bz2 souboru
sabai92.c.bz2 251 118 B 41 dB 75 μs (AM rádio) AM rádio 92 67 kbps 192 kbps 0:30 12 kHz mono 12 kHz WAV 16-bit 803611682B73096AA33FE46F240BF6B4 7A42F46934360B945751D8EDCA4C2184
sabai32.c.bz2 189 845 B 32 dB 75 μs (AM rádio) AM rádio 32 51 kbps 192 kbps 0:30 12 kHz mono 12 kHz WAV 16-bit F80FDFA3E80A731676EBA04BA79A3C57 2784E6C23F­37E4C5F3851968AEABE78D
modlitba92.c.bz2 240 670 B 41 dB 75 μs (AM rádio) AM rádio 92 64 kbps 192 kbps 0:30 12 kHz mono 12 kHz WAV 16-bit 51107B491E9593A675C8E6B0E3235DE2 BE994C2BCAEE81379F372FCFD533C01F
modlitba32.c.bz2 176 747 B 32 dB 75 μs (AM rádio) AM rádio 32 47 kbps 192 kbps 0:30 12 kHz mono 12 kHz WAV 16-bit EC24CCF80ACDC719C1DFF76849FE6BF1 F6D9CC71AB7759A05AEAB2EF0087FFB3
thajsko_v_nizozemi512.c.bz2 386 453 B 56 dB 75 μs (AM rádio) AM rádio 512 103 kbps 228 kbps 0:30 14 238 Hz mono 14 238 Hz WAV 16-bit 845AD14B596729944AA10EB2A4051007 40B6891AC0BA7F7139A0ADAAEB18CD97
thajsko_v_nizozemi92.c.bz2 211 451 B 41 dB 75 μs (AM rádio) AM rádio 92 56 kbps 228 kbps 0:30 14 238 Hz mono 14 238 Hz WAV 16-bit D918FF41BE9A2CC9BEB0256694DA7164 DACF6D8A226E82404A5A08D89124B0CE

Příště budeme pokračovat v dekódování, až se nám analogový signál podaří dostat úplně ven.

Autor článku

Karel Kulhavý vystudoval operační systémy, sítě a překladače na MFF UK a je autorem optického pojítka Twibright Ronja a spoluautorem textového a grafického webového prohlížeče Twibright Links.