Jak jsem obnovil data v neznámém formátu z magnetické pásky

pravěk

Jako milovník retro hardwaru jsem si jednou koupil ZX Spectrum+ od prodejce ve Velké Británii. Součástí samotného počítače jsem dostal několik audiokazet s hrami (v originálním balení s návodem) a také programy nahrané na kazetách bez speciálního označení. Kupodivu byla data ze 40 let starých kazet dobře čitelná a mohl jsem si z nich stáhnout téměř všechny hry a programy.

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Na některých kazetách jsem však našel nahrávky, které zjevně nebyly pořízeny počítačem ZX Spectrum. Zněly úplně jinak a na rozdíl od nahrávek ze zmíněného počítače nezačínaly krátkým BASIC bootloaderem, který bývá přítomen v nahrávkách všech programů a her.

Nějakou dobu mě to pronásledovalo - opravdu jsem chtěl zjistit, co se v nich skrývá. Pokud byste mohli číst zvukový signál jako sekvenci bajtů, mohli byste hledat znaky nebo cokoli, co naznačuje původ signálu. Jakási retro-archeologie.

Teď, když jsem prošel celou cestu a podíval se na štítky samotných kazet, usmívám se, protože

odpověď jsem měl celou dobu přímo před očima
Na štítku levé kazety je název počítače TRS-80 a hned pod názvem výrobce: „Manufactured by Radio Shack in USA“

(Pokud si chcete intriku nechat až na konec, nechoďte pod spoiler)

Porovnání zvukových signálů

Nejprve si zdigitalizujme zvukové záznamy. Můžete si poslechnout, jak to zní:


A jako obvykle zní záznam z počítače ZX Spectrum:


V obou případech je na začátku záznamu tzv pilotní tón - zvuk stejné frekvence (v prvním záznamu je velmi krátký <1 sekunda, ale je rozlišitelný). Pilotní tón signalizuje počítači, aby se připravil na příjem dat. Každý počítač zpravidla rozpoznává pouze svůj „vlastní“ pilotní tón podle tvaru signálu a jeho frekvence.

K samotnému tvaru signálu je potřeba něco říci. Například na ZX Spectrum je jeho tvar obdélníkový:

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Když je detekován pilotní tón, ZX Spectrum zobrazuje na okraji obrazovky střídavé červené a modré pruhy, které indikují, že signál byl rozpoznán. Pilotní tón končí synchronní puls, která signalizuje počítači, aby začal přijímat data. Vyznačuje se kratší dobou trvání (ve srovnání s pilotním tónem a následnými daty) (viz obrázek)

Po přijetí synchronizačního impulsu počítač zaznamená každý vzestup/pokles signálu a změří jeho trvání. Pokud je doba trvání kratší než určitý limit, bit 1 se zapíše do paměti, jinak 0. Bity se shromažďují do bajtů a proces se opakuje, dokud není přijato N bajtů. Číslo N je obvykle převzato z hlavičky stahovaného souboru. Pořadí načítání je následující:

  1. pilotní tón
  2. hlavička (pevná délka), obsahuje velikost stahovaných dat (N), název a typ souboru
  3. pilotní tón
  4. samotná data

Aby bylo zajištěno správné načtení dat, načte ZX Spectrum tzv paritní bajt (paritní byte), který se vypočítá při ukládání souboru XORingem všech bajtů zapsaných dat. Při čtení souboru počítač vypočítá paritní bajt z přijatých dat a pokud se výsledek liší od uloženého, ​​zobrazí chybovou zprávu „R Tape loading error“. Přesně řečeno, počítač může tuto zprávu vydat dříve, pokud při čtení nedokáže rozpoznat puls (zmeškaný nebo jeho trvání neodpovídá určitým limitům)

Pojďme se tedy nyní podívat, jak neznámý signál vypadá:

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Toto je pilotní tón. Tvar signálu je výrazně odlišný, ale je zřejmé, že signál se skládá z opakujících se krátkých pulzů o určité frekvenci. Při vzorkovací frekvenci 44100 Hz je vzdálenost mezi „vrchly“ přibližně 48 vzorků (což odpovídá frekvenci ~918 Hz). Tento údaj si zapamatujte.

Podívejme se nyní na datový fragment:

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Pokud změříme vzdálenost mezi jednotlivými pulzy, ukáže se, že vzdálenost mezi „dlouhými“ pulzy je stále ~48 vzorků a mezi krátkými - ~24. Když se podívám trochu dopředu, řeknu, že se nakonec ukázalo, že „referenční“ pulsy s frekvencí 918 Hz následují nepřetržitě, od začátku do konce souboru. Lze předpokládat, že při přenosu dat, pokud se mezi referenčními impulsy objeví další impuls, považujeme jej za bit 1, jinak 0.

A co synchronizační puls? Podívejme se na začátek dat:

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Pilotní tón skončí a okamžitě začnou data. O něco později, po analýze několika různých zvukových nahrávek, jsme byli schopni zjistit, že první bajt dat je vždy stejný (10100101b, A5h). Počítač může začít číst data poté, co je přijme.

Můžete také věnovat pozornost posunu prvního referenčního impulsu bezprostředně po posledním 1. v synchronizačním byte. To bylo objeveno mnohem později v procesu vývoje programu pro rozpoznávání dat, kdy data na začátku souboru nebylo možné číst stabilně.

Nyní se pokusíme popsat algoritmus, který zpracuje zvukový soubor a načte data.

Načítání dat

Nejprve se podívejme na několik předpokladů, aby byl algoritmus jednoduchý:

  1. Budeme uvažovat pouze soubory ve formátu WAV;
  2. Zvukový soubor musí začínat pilotním tónem a na začátku nesmí obsahovat ticho
  3. Zdrojový soubor musí mít vzorkovací frekvenci 44100 Hz. V tomto případě je vzdálenost mezi referenčními impulsy 48 vzorků již určena a nemusíme ji programově počítat;
  4. Formát vzorku může být libovolný (8/16 bitů/plovoucí desetinná čárka) - protože při čtení jej můžeme převést na požadovaný;
  5. Předpokládáme, že zdrojový soubor je normalizován amplitudou, což by mělo stabilizovat výsledek;

Algoritmus čtení bude následující:

  1. Načteme soubor do paměti a zároveň převedeme formát vzorku na 8 bitů;
  2. Určete polohu prvního impulsu v audio datech. Chcete-li to provést, musíte vypočítat počet vzorků s maximální amplitudou. Pro jednoduchost to spočítáme jednou ručně. Uložme to do proměnné prev_pos;
  3. Přidejte 48 k pozici posledního pulzu (pos := prev_pos + 48)
  4. Vzhledem k tomu, že zvýšení polohy o 48 nezaručuje, že se dostaneme do polohy dalšího referenčního impulzu (vady pásky, nestabilní chod mechanismu pohonu pásky apod.), musíme polohu impulzu pozice upravit. Chcete-li to provést, vezměte malý kus dat (pos-8;pos+8) a najděte na něm maximální hodnotu amplitudy. Pozice odpovídající maximu bude uložena v poz. Zde 8 = 48/6 je experimentálně získaná konstanta, která zaručuje, že určíme správné maximum a neovlivníme další impulsy, které mohou být poblíž. Ve velmi špatných případech, kdy je vzdálenost mezi pulzy mnohem menší nebo větší než 48, můžete implementovat vynucené hledání pulzu, ale v rámci článku to nebudu v algoritmu popisovat;
  5. V předchozím kroku by bylo také nutné zkontrolovat, zda byl referenční impuls vůbec nalezen. To znamená, že pokud jednoduše hledáte maximum, nezaručuje to, že impuls je v tomto segmentu přítomen. V mé nejnovější implementaci čtecího programu zkontroluji rozdíl mezi maximální a minimální hodnotou amplitudy na segmentu, a pokud překročí určitou mez, započítám přítomnost impulsu. Otázkou také je, co dělat, když se referenční impuls nenajde. Existují 2 možnosti: buď data skončila a následuje ticho, nebo by to mělo být považováno za chybu čtení. Pro zjednodušení algoritmu to však vynecháme;
  6. V dalším kroku potřebujeme určit přítomnost datového impulsu (bit 0 nebo 1), k tomu vezmeme střed segmentu (prev_pos;pos) middle_pos rovný middle_pos := (prev_pos+pos)/2 a v nějakém sousedství middle_pos na segmentu (middle_pos-8;middle_pos +8) vypočítejme maximální a minimální amplitudu. Pokud je rozdíl mezi nimi větší než 10, zapíšeme do výsledku bit 1, jinak 0. 10 je konstanta získaná experimentálně;
  7. Uložit aktuální pozici do prev_pos (prev_pos := pos)
  8. Opakujte od kroku 3, dokud nepřečteme celý soubor;
  9. Výsledné bitové pole musí být uloženo jako sada bajtů. Vzhledem k tomu, že jsme při čtení nebrali v úvahu synchronizační bajt, nemusí být počet bitů násobkem 8 a není znám ani požadovaný bitový offset. Při první implementaci algoritmu jsem o existenci synchronizačního bytu nevěděl, a proto jsem jednoduše uložil 8 souborů s různým počtem offsetových bitů. Jeden z nich obsahoval správné údaje. V konečném algoritmu jednoduše odstraním všechny bity až do A5h, což mi umožňuje okamžitě získat správný výstupní soubor

Algoritmus v Ruby, pro zájemce
Jako jazyk pro psaní programu jsem zvolil Ruby, protože... Většinu času na něm programuji. Možnost není vysoce výkonná, ale úkol dosáhnout co nejrychlejší rychlosti čtení za to nestojí.

# Используем gem 'wavefile'
require 'wavefile'

reader = WaveFile::Reader.new('input.wav')
samples = []
format = WaveFile::Format.new(:mono, :pcm_8, 44100)

# Читаем WAV файл, конвертируем в формат Mono, 8 bit 
# Массив samples будет состоять из байт со значениями 0-255
reader.each_buffer(10000) do |buffer|
  samples += buffer.convert(format).samples
end

# Позиция первого импульса (вместо 0)
prev_pos = 0
# Расстояние между импульсами
distance = 48
# Значение расстояния для окрестности поиска локального максимума
delta = (distance / 6).floor
# Биты будем сохранять в виде строки из "0" и "1"
bits = ""

loop do
  # Рассчитываем позицию следующего импульса
  pos = prev_pos + distance
  
  # Выходим из цикла если данные закончились 
  break if pos + delta >= samples.size

  # Корректируем позицию pos обнаружением максимума на отрезке [pos - delta;pos + delta]
  (pos - delta..pos + delta).each { |p| pos = p if samples[p] > samples[pos] }

  # Находим середину отрезка [prev_pos;pos]
  middle_pos = ((prev_pos + pos) / 2).floor

  # Берем окрестность в середине 
  sample = samples[middle_pos - delta..middle_pos + delta]

  # Определяем бит как "1" если разница между максимальным и минимальным значением на отрезке превышает 10
  bit = sample.max - sample.min > 10
  bits += bit ? "1" : "0"
end

# Определяем синхро-байт и заменяем все предшествующие биты на 256 бит нулей (согласно спецификации формата) 
bits.gsub! /^[01]*?10100101/, ("0" * 256) + "10100101"

# Сохраняем выходной файл, упаковывая биты в байты
File.write "output.cas", [bits].pack("B*")

Výsledek

Když jsem vyzkoušel několik variant algoritmu a konstant, měl jsem štěstí, že jsem dostal něco extrémně zajímavého:

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Takže, soudě podle řetězců znaků, máme program pro vykreslování grafů. V textu programu však nejsou žádná klíčová slova. Všechna klíčová slova jsou zakódována jako bajty (každá hodnota > 80h). Nyní musíme zjistit, který počítač z 80. let uměl ukládat programy v tomto formátu.

Ve skutečnosti je velmi podobný programu BASIC. Počítač ZX Spectrum ukládá programy v přibližně stejném formátu do paměti a ukládá programy na pásku. Pro jistotu jsem klíčová slova porovnal stůl. Výsledek byl však zjevně negativní.

Zkontroloval jsem také klíčová slova BASIC populárních počítačů Atari, Commodore 64 a několika dalších tehdejších počítačů, ke kterým se mi podařilo najít dokumentaci, ale bez úspěchu - moje znalosti o typech retro počítačů se ukázaly jako ne tak široké.

Pak jsem se rozhodl jít seznam, a pak můj pohled padl na jméno výrobce Radio Shack a počítač TRS-80. To jsou jména, která byla napsána na etiketách kazet, které mi ležely na stole! Dříve jsem tato jména neznal a nebyl jsem obeznámen s počítačem TRS-80, takže se mi zdálo, že Radio Shack byl výrobcem audiokazet jako BASF, Sony nebo TDK a TRS-80 byla doba přehrávání. Proč ne?

Počítač Tandy/Radio Shack TRS-80

Je velmi pravděpodobné, že dotyčný zvukový záznam, který jsem uvedl jako příklad na začátku článku, byl pořízen na počítači, jako je tento:

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Ukázalo se, že tento počítač a jeho odrůdy (Model I/Model III/Model IV atd.) byly svého času velmi populární (samozřejmě ne v Rusku). Je pozoruhodné, že procesor, který použili, byl také Z80. Pro tento počítač můžete najít na internetu mnoho informací. V 80. letech byly počítačové informace distribuovány v časopisy. V současné době je jich několik emulátory počítače pro různé platformy.

Stáhl jsem si emulátor trs80gp a poprvé jsem mohl vidět, jak tento počítač funguje. Počítač samozřejmě nepodporoval barevný výstup, rozlišení obrazovky bylo pouze 128x48 bodů, ale došlo k mnoha rozšířením a úpravám, které mohly rozlišení obrazovky zvýšit. Pro tento počítač bylo také mnoho možností operačních systémů a možností implementace jazyka BASIC (který se na rozdíl od ZX Spectra u některých modelů ani „neflashoval“ do ROM a libovolnou volbu bylo možné načíst z diskety, stejně jako např. samotný OS)

Také jsem našel nástroj převést zvukové nahrávky do formátu CAS, který podporují emulátory, ale z nějakého důvodu pomocí nich nebylo možné číst nahrávky z mých kazet.

Když jsem přišel na formát souboru CAS (což se ukázalo být jen bitovou kopií dat z pásky, kterou jsem již měl po ruce, kromě hlavičky s přítomností synchronizačního bajtu), vytvořil jsem několik změn v mém programu a byl jsem schopen vytisknout pracovní soubor CAS, který fungoval v emulátoru (TRS-80 Model III):

Jak jsem obnovil data v neznámém formátu z magnetické pásky

Poslední verzi konverzní utility s automatickým určením prvního pulsu a vzdálenosti mezi referenčními pulsy jsem navrhl jako balíček GEM, zdrojový kód je k dispozici na GitHub.

Závěr

Cesta, kterou jsme prošli, se ukázala jako fascinující cesta do minulosti a jsem rád, že jsem nakonec našel odpověď. Mimo jiné jsem:

  • Přišel jsem na formát pro ukládání dat v ZX Spectrum a studoval jsem vestavěné ROM rutiny pro ukládání/čtení dat z audiokazet
  • Seznámil jsem se s počítačem TRS-80 a jeho odrůdami, studoval operační systém, podíval se na ukázkové programy a dokonce jsem měl možnost ladit ve strojových kódech (ostatně všechny mnemotechnické pomůcky Z80 jsou mi známé)
  • Napsal plnohodnotný nástroj pro převod zvukových nahrávek do formátu CAS, který dokáže číst data, která „oficiální“ nástroj nerozpozná

Zdroj: www.habr.com

Přidat komentář