Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

prehistoria

Będąc miłośnikiem sprzętu retro, kupiłem kiedyś ZX Spectrum+ od sprzedawcy w Wielkiej Brytanii. W zestawie z samym komputerem otrzymałem kilka kaset audio z grami (w oryginalnym opakowaniu z instrukcją), a także programy nagrane na kasetach bez specjalnych oznaczeń. O dziwo, dane z 40-letnich kaset były dobrze czytelne i udało mi się z nich pobrać prawie wszystkie gry i programy.

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Na niektórych kasetach znalazłem jednak nagrania, które ewidentnie nie zostały wykonane komputerem ZX Spectrum. Zabrzmiały zupełnie inaczej i w odróżnieniu od nagrań ze wspomnianego komputera nie zaczynały się od krótkiego bootloadera BASIC-a, który zazwyczaj jest obecny na nagraniach wszelkich programów i gier.

Od jakiegoś czasu nie dawało mi to spokoju – bardzo chciałam dowiedzieć się, co się w nich kryje. Jeśli potrafisz odczytać sygnał audio jako sekwencję bajtów, możesz poszukać znaków lub czegokolwiek, co wskazuje na pochodzenie sygnału. Rodzaj retroarcheologii.

Teraz, kiedy już przeszedłem całą drogę i spojrzałem na etykiety samych kaset, uśmiecham się, bo

odpowiedź przez cały czas miałem przed oczami
Na naklejce lewej kasety widnieje nazwa komputera TRS-80, a tuż pod nazwą producenta: „Manufactured by Radio Shack in USA”

(Jeśli chcesz zachować intrygę do końca, nie wchodź pod spoiler)

Porównanie sygnałów audio

Przede wszystkim zdigitalizujmy nagrania audio. Możesz posłuchać jak to brzmi:


I jak zwykle brzmi nagranie z komputera ZX Spectrum:


W obu przypadkach na początku nagrania znajduje się tzw ton pilota - dźwięk o tej samej częstotliwości (w pierwszym nagraniu jest bardzo krótki <1 sekundy, ale jest rozpoznawalny). Sygnał pilota sygnalizuje komputerowi konieczność przygotowania się do odbioru danych. Z reguły każdy komputer rozpoznaje tylko „własny” ton pilota na podstawie kształtu sygnału i jego częstotliwości.

Trzeba powiedzieć coś o samym kształcie sygnału. Na przykład w ZX Spectrum jego kształt jest prostokątny:

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Po wykryciu sygnału pilota ZX Spectrum wyświetla na przemian czerwone i niebieskie paski na krawędzi ekranu, wskazując, że sygnał został rozpoznany. Sygnał pilota zakończy się impuls synchroniczny, co sygnalizuje komputerowi rozpoczęcie odbierania danych. Charakteryzuje się krótszym czasem trwania (w porównaniu z sygnałem pilota i kolejnymi danymi) (patrz rysunek)

Po odebraniu impulsu synchronizującego komputer rejestruje każdy wzrost/spadek sygnału, mierząc jego czas trwania. Jeśli czas trwania jest krótszy niż określony limit, do pamięci zapisywany jest bit 1, w przeciwnym razie 0. Bity są zbierane w bajty i proces jest powtarzany aż do odebrania N bajtów. Liczba N jest zwykle pobierana z nagłówka pobranego pliku. Kolejność ładowania jest następująca:

  1. ton pilota
  2. nagłówek (stała długość), zawiera rozmiar pobieranych danych (N), nazwę i typ pliku
  3. ton pilota
  4. same dane

Aby mieć pewność, że dane zostaną poprawnie załadowane, ZX Spectrum odczytuje tzw bajt parzystości (bajt parzystości), który jest obliczany podczas zapisywania pliku poprzez XORing wszystkich bajtów zapisanych danych. Podczas odczytu pliku komputer oblicza bajt parzystości z odebranych danych i jeśli wynik różni się od zapisanego, wyświetla komunikat o błędzie „Błąd ładowania taśmy R”. Ściśle mówiąc, komputer może wygenerować ten komunikat wcześniej, jeśli podczas odczytu nie może rozpoznać pulsu (brak lub czas jego trwania nie mieści się w określonych granicach)

Zobaczmy teraz, jak wygląda nieznany sygnał:

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

To jest ton pilota. Kształt sygnału jest znacząco inny, ale jasne jest, że sygnał składa się z powtarzających się krótkich impulsów o określonej częstotliwości. Przy częstotliwości próbkowania 44100 Hz odległość pomiędzy „szczytami” wynosi około 48 próbek (co odpowiada częstotliwości ~918 Hz).Zapamiętajmy tę liczbę.

Przyjrzyjmy się teraz fragmentowi danych:

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Jeśli zmierzymy odległość pomiędzy pojedynczymi impulsami, okaże się, że odległość pomiędzy „długimi” impulsami wynosi w dalszym ciągu ~48 próbek, a pomiędzy krótkimi – ~24. Patrząc trochę w przyszłość powiem, że ostatecznie okazało się, że impulsy „odniesienia” o częstotliwości 918 Hz następują w sposób ciągły, od początku do końca pliku. Można założyć, że podczas transmisji danych, jeśli pomiędzy impulsami odniesienia pojawi się dodatkowy impuls, traktujemy go jako bit 1, w przeciwnym razie 0.

A co z impulsem synchronizacji? Spójrzmy na początek danych:

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Sygnał pilota zakończy się i natychmiast rozpocznie się przesyłanie danych. Nieco później, po przeanalizowaniu kilku różnych nagrań audio, udało nam się odkryć, że pierwszy bajt danych jest zawsze taki sam (10100101b, A5h). Komputer może rozpocząć odczytywanie danych po ich otrzymaniu.

Można także zwrócić uwagę na przesunięcie pierwszego impulsu odniesienia bezpośrednio po ostatnim pierwszym w bajcie synchronizacji. Zostało to odkryte znacznie później w procesie opracowywania programu do rozpoznawania danych, gdy dane na początku pliku nie mogły zostać stabilnie odczytane.

Spróbujmy teraz opisać algorytm, który przetworzy plik audio i załaduje dane.

Ładowanie danych

Najpierw przyjrzyjmy się kilku założeniom, aby algorytm był prosty:

  1. Rozważymy tylko pliki w formacie WAV;
  2. Plik audio musi rozpoczynać się dźwiękiem pilota i nie może zawierać na początku ciszy
  3. Plik źródłowy musi mieć częstotliwość próbkowania 44100 Hz. W tym przypadku odległość pomiędzy impulsami referencyjnymi 48 próbek jest już określona i nie musimy jej programowo obliczać;
  4. Format próbki może być dowolny (8/16 bitów/zmiennoprzecinkowy) - gdyż podczas odczytu możemy go przekonwertować na żądany;
  5. Zakładamy, że plik źródłowy jest znormalizowany amplitudowo, co powinno ustabilizować wynik;

Algorytm odczytu będzie następujący:

  1. Wczytujemy plik do pamięci, jednocześnie konwertując format próbki do 8 bitów;
  2. Określ położenie pierwszego impulsu w danych audio. Aby to zrobić, musisz obliczyć liczbę próbek o maksymalnej amplitudzie. Dla uproszczenia obliczymy to raz ręcznie. Zapiszmy to w zmiennej prev_pos;
  3. Dodaj 48 do pozycji ostatniego impulsu (poz := prev_pos + 48)
  4. Ponieważ zwiększenie pozycji o 48 nie gwarantuje, że dotrzemy do pozycji kolejnego impulsu odniesienia (wady taśmy, niestabilna praca mechanizmu napędu taśmy itp.), należy skorygować położenie poz. impulsu. Aby to zrobić, weź mały fragment danych (poz-8;poz+8) i znajdź na nim maksymalną wartość amplitudy. Pozycja odpowiadająca maksimum zostanie zapisana w poz. Tutaj 8 = 48/6 jest stałą uzyskaną eksperymentalnie, która gwarantuje, że wyznaczymy prawidłowe maksimum i nie wpłyniemy na inne impulsy, które mogą znajdować się w pobliżu. W bardzo złych przypadkach, gdy odległość między impulsami jest znacznie mniejsza lub większa niż 48, można zastosować wymuszone wyszukiwanie impulsu, ale w ramach artykułu nie będę tego opisywał w algorytmie;
  5. Na poprzednim etapie należałoby także sprawdzić, czy w ogóle został znaleziony impuls referencyjny. Oznacza to, że jeśli po prostu szukasz maksimum, nie gwarantuje to obecności impulsu w tym segmencie. W mojej najnowszej realizacji programu odczytującego sprawdzam różnicę pomiędzy maksymalną i minimalną wartością amplitudy na segmencie, a jeśli przekroczy ona pewną granicę, liczę obecność impulsu. Pytaniem jest też co zrobić w przypadku nie znalezienia impulsu referencyjnego. Istnieją 2 możliwości: albo dane się skończyły i następuje cisza, albo należy to uznać za błąd odczytu. Pominiemy to jednak, aby uprościć algorytm;
  6. W kolejnym kroku musimy określić obecność impulsu danych (bit 0 lub 1), w tym celu bierzemy środek segmentu (prev_pos;pos) środkowy_pos równy środkowy_pos := (prev_pos+pos)/2 i w pewnym sąsiedztwie Middle_pos w segmencie (middle_pos-8;middle_pos +8) obliczmy maksymalną i minimalną amplitudę. Jeżeli różnica między nimi jest większa niż 10, w wyniku zapisujemy bit 1, w przeciwnym razie 0, 10 jest stałą otrzymaną eksperymentalnie;
  7. Zapisz bieżącą pozycję w prev_pos (prev_pos := poz)
  8. Powtarzaj zaczynając od kroku 3, aż przeczytamy cały plik;
  9. Wynikową tablicę bitów należy zapisać jako zestaw bajtów. Ponieważ podczas odczytu nie uwzględniliśmy bajtu synchronizacji, liczba bitów może nie być wielokrotnością 8, a wymagane przesunięcie bitowe również nie jest znane. W pierwszej implementacji algorytmu nie wiedziałem o istnieniu bajtu synchronizacji i dlatego po prostu zapisałem 8 plików z różną liczbą bitów offsetu. Jeden z nich zawierał prawidłowe dane. W ostatecznym algorytmie po prostu usuwam wszystkie bity aż do A5h, co pozwala mi od razu uzyskać poprawny plik wyjściowy

Algorytm w Ruby, dla zainteresowanych
Jako język do pisania programu wybrałem Ruby, ponieważ... Programuję na nim najczęściej. Opcja nie jest wysoka, ale zadanie zapewnienia jak największej prędkości odczytu nie jest tego warte.

# Используем 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*")

Doświadcz mocnych i skutecznych rezultatów

Po wypróbowaniu kilku wariantów algorytmu i stałych udało mi się uzyskać coś niezwykle interesującego:

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Sądząc po ciągach znaków, mamy program do rysowania wykresów. W tekście programu nie ma jednak słów kluczowych. Wszystkie słowa kluczowe są kodowane w bajtach (każda wartość > 80h). Teraz musimy dowiedzieć się, który komputer z lat 80. mógł zapisywać programy w tym formacie.

W rzeczywistości jest bardzo podobny do programu BASIC. Komputer ZX Spectrum przechowuje programy w mniej więcej tym samym formacie w pamięci i zapisuje je na taśmie. Na wszelki wypadek sprawdziłem słowa kluczowe stół. Wynik był jednak oczywiście negatywny.

Sprawdziłem także słowa kluczowe BASIC popularnych Atari, Commodore 64 i kilku innych ówczesnych komputerów, dla których udało mi się znaleźć dokumentację, ale bez powodzenia - moja wiedza na temat typów komputerów retro okazała się niezbyt szeroka.

Wtedy zdecydowałem się pojechać Lista, a potem mój wzrok padł na nazwę producenta Radio Shack i komputer TRS-80. Oto nazwy, które widniały na etykietach kaset, które leżały na moim stole! Nie znałem wcześniej tych nazw i nie miałem styczności z komputerem TRS-80, więc wydawało mi się, że Radio Shack to producent kaset audio, taki jak BASF, Sony czy TDK, a TRS-80 to czas odtwarzania. Dlaczego nie?

Komputer Tandy/Radio Shack TRS-80

Jest bardzo prawdopodobne, że omawiane nagranie audio, które podałem jako przykład na początku artykułu, zostało wykonane na takim komputerze:

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Okazało się, że ten komputer i jego odmiany (Model I/Model III/Model IV itp.) były kiedyś bardzo popularne (oczywiście nie w Rosji). Warto zauważyć, że procesor, z którego korzystali, również był Z80. Do tego komputera można znaleźć w Internecie dużo informacji. W latach 80. informacja komputerowa była rozpowszechniana w czasopisma. W tej chwili jest ich kilka emulatory komputery na różne platformy.

Pobrałem emulator trs80gp i po raz pierwszy mogłem zobaczyć jak działa ten komputer. Oczywiście komputer nie obsługiwał wyjścia kolorowego, rozdzielczość ekranu wynosiła tylko 128x48 pikseli, ale istniało wiele rozszerzeń i modyfikacji, które mogły zwiększyć rozdzielczość ekranu. Nie zabrakło także wielu opcji systemów operacyjnych dla tego komputera oraz opcji implementacji języka BASIC (który w przeciwieństwie do ZX Spectrum w niektórych modelach nie był nawet „wgrywany” do ROM-u i każdą opcję można było wczytać z dyskietki, tak jak sam system operacyjny)

Znalazłem również narzędzie do konwersji nagrań audio do formatu CAS, który jest obsługiwany przez emulatory, ale z jakiegoś powodu nie można było za ich pomocą odczytać nagrań z moich kaset.

Po ustaleniu formatu pliku CAS (który okazał się po prostu kopią bit po bicie danych z taśmy, którą już miałem pod ręką, z wyjątkiem nagłówka z obecnością bajtu synchronizacji), zrobiłem kilka zmian w moim programie i udało mi się wygenerować działający plik CAS, który działał w emulatorze (TRS-80 Model III):

Jak odzyskać dane w nieznanym formacie z taśmy magnetycznej

Najnowszą wersję narzędzia do konwersji z automatycznym wyznaczaniem pierwszego impulsu i odległości pomiędzy impulsami odniesienia zaprojektowałem jako pakiet GEM, kod źródłowy dostępny jest pod adresem Github.

wniosek

Droga, którą przebyliśmy okazała się fascynującą podróżą w przeszłość i cieszę się, że w końcu znalazłam odpowiedź. Między innymi ja:

  • Rozpracowałem format zapisywania danych w ZX Spectrum i przestudiowałem wbudowane procedury ROM do zapisywania/odczytu danych z kaset audio
  • Zapoznałem się z komputerem TRS-80 i jego odmianami, przestudiowałem system operacyjny, przyjrzałem się przykładowym programom, a nawet miałem okazję debugować w kodach maszynowych (w końcu wszystkie mnemoniki Z80 są mi znane)
  • Napisano pełnoprawne narzędzie do konwersji nagrań audio do formatu CAS, które może odczytać dane nierozpoznawane przez „oficjalne” narzędzie

Źródło: www.habr.com

Dodaj komentarz