Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

prasejarah

Sebagai pencinta perkakasan retro, saya pernah membeli ZX Spectrum+ daripada penjual di UK. Termasuk dengan komputer itu sendiri, saya menerima beberapa kaset audio dengan permainan (dalam pembungkusan asal dengan arahan), serta program yang dirakam pada kaset tanpa tanda khas. Yang menghairankan, data daripada kaset berusia 40 tahun boleh dibaca dengan baik dan saya dapat memuat turun hampir semua permainan dan program daripadanya.

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Walau bagaimanapun, pada beberapa kaset saya dapati rakaman yang jelas tidak dibuat oleh komputer ZX Spectrum. Mereka berbunyi berbeza dan, tidak seperti rakaman dari komputer yang disebutkan, mereka tidak bermula dengan pemuat but ASAS yang pendek, yang biasanya terdapat dalam rakaman semua program dan permainan.

Untuk beberapa waktu ini menghantui saya - saya benar-benar ingin mengetahui apa yang tersembunyi di dalamnya. Jika anda boleh membaca isyarat audio sebagai jujukan bait, anda boleh mencari aksara atau apa sahaja yang menunjukkan asal usul isyarat. Sejenis retro-arkeologi.

Sekarang saya telah pergi jauh dan melihat label kaset itu sendiri, saya tersenyum kerana

jawapan itu ada di depan mata saya selama ini
Pada label kaset kiri adalah nama komputer TRS-80, dan betul-betul di bawah nama pengilang: "Dikilangkan oleh Radio Shack di Amerika Syarikat"

(Jika anda ingin mengekalkan tipu daya sehingga akhir, jangan pergi di bawah spoiler)

Perbandingan isyarat audio

Pertama sekali, mari kita mendigitalkan rakaman audio. Anda boleh mendengar bunyinya:


Dan seperti biasa rakaman dari komputer ZX Spectrum berbunyi:


Dalam kedua-dua kes, pada permulaan rakaman terdapat apa yang dipanggil nada perintis - bunyi dengan frekuensi yang sama (dalam rakaman pertama ia sangat pendek <1 saat, tetapi boleh dibezakan). Nada perintis memberi isyarat kepada komputer untuk bersedia untuk menerima data. Sebagai peraturan, setiap komputer hanya mengenali nada pandu "sendiri" dengan bentuk isyarat dan kekerapannya.

Ia adalah perlu untuk mengatakan sesuatu tentang bentuk isyarat itu sendiri. Sebagai contoh, pada ZX Spectrum bentuknya adalah segi empat tepat:

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Apabila nada perintis dikesan, ZX Spectrum memaparkan bar merah dan biru berselang-seli pada sempadan skrin untuk menunjukkan bahawa isyarat telah dikenali. Nada juruterbang tamat nadi segerak, yang memberi isyarat kepada komputer untuk mula menerima data. Ia dicirikan oleh tempoh yang lebih pendek (berbanding dengan nada perintis dan data seterusnya) (lihat rajah)

Selepas nadi penyegerakan diterima, komputer merekodkan setiap kenaikan/kejatuhan isyarat, mengukur tempohnya. Jika tempohnya kurang daripada had tertentu, bit 1 ditulis pada ingatan, jika tidak 0. Bit dikumpulkan ke dalam bait dan proses diulang sehingga N bait diterima. Nombor N biasanya diambil daripada pengepala fail yang dimuat turun. Urutan pemuatan adalah seperti berikut:

  1. nada perintis
  2. pengepala (panjang tetap), mengandungi saiz data yang dimuat turun (N), nama fail dan jenis
  3. nada perintis
  4. data itu sendiri

Untuk memastikan bahawa data dimuatkan dengan betul, ZX Spectrum membaca apa yang dipanggil bait pariti (bait pariti), yang dikira semasa menyimpan fail dengan XORing semua bait data bertulis. Apabila membaca fail, komputer mengira bait pariti daripada data yang diterima dan, jika hasilnya berbeza daripada yang disimpan, memaparkan mesej ralat "Ralat memuatkan Pita R". Tegasnya, komputer boleh mengeluarkan mesej ini lebih awal jika, semasa membaca, ia tidak dapat mengecam nadi (terlepas atau tempohnya tidak sepadan dengan had tertentu)

Jadi, mari kita lihat rupa isyarat yang tidak diketahui:

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Ini adalah nada perintis. Bentuk isyarat adalah berbeza dengan ketara, tetapi jelas bahawa isyarat terdiri daripada denyutan pendek berulang pada frekuensi tertentu. Pada frekuensi pensampelan 44100 Hz, jarak antara "puncak" adalah lebih kurang 48 sampel (yang sepadan dengan frekuensi ~918 Hz). Mari kita ingat angka ini.

Sekarang mari kita lihat serpihan data:

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Jika kita mengukur jarak antara denyutan individu, ternyata jarak antara denyutan "panjang" masih ~48 sampel, dan antara yang pendek - ~24. Melihat ke hadapan sedikit, saya akan mengatakan bahawa pada akhirnya ternyata denyutan "rujukan" dengan frekuensi 918 Hz mengikuti secara berterusan, dari awal hingga akhir fail. Ia boleh diandaikan bahawa apabila menghantar data, jika nadi tambahan ditemui di antara denyut rujukan, kami menganggapnya sebagai bit 1, sebaliknya 0.

Bagaimana pula dengan nadi penyegerakan? Mari kita lihat pada permulaan data:

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Nada perintis tamat dan data bermula serta-merta. Tidak lama kemudian, selepas menganalisis beberapa rakaman audio yang berbeza, kami dapat menemui bahawa bait pertama data sentiasa sama (10100101b, A5h). Komputer mungkin mula membaca data selepas ia menerimanya.

Anda juga boleh memberi perhatian kepada anjakan nadi rujukan pertama sejurus selepas 1 terakhir dalam bait penyegerakan. Ia ditemui lebih lama kemudian dalam proses membangunkan program pengecaman data, apabila data pada permulaan fail tidak dapat dibaca dengan stabil.

Sekarang mari kita cuba menerangkan algoritma yang akan memproses fail audio dan memuatkan data.

Memuatkan Data

Mula-mula, mari kita lihat beberapa andaian untuk memastikan algoritma mudah:

  1. Kami hanya akan mempertimbangkan fail dalam format WAV;
  2. Fail audio mesti bermula dengan nada perintis dan tidak boleh mengandungi senyap pada mulanya
  3. Fail sumber mesti mempunyai kadar pensampelan 44100 Hz. Dalam kes ini, jarak antara denyut rujukan 48 sampel sudah ditentukan dan kita tidak perlu mengiranya secara pemrograman;
  4. Format sampel boleh menjadi mana-mana (8/16 bit/titik terapung) - kerana apabila membaca kita boleh menukarnya kepada yang dikehendaki;
  5. Kami menganggap bahawa fail sumber dinormalkan oleh amplitud, yang sepatutnya menstabilkan hasilnya;

Algoritma bacaan adalah seperti berikut:

  1. Kami membaca fail ke dalam ingatan, pada masa yang sama menukar format sampel kepada 8 bit;
  2. Tentukan kedudukan nadi pertama dalam data audio. Untuk melakukan ini, anda perlu mengira bilangan sampel dengan amplitud maksimum. Untuk memudahkan, kami akan mengiranya sekali secara manual. Mari kita simpan ke pembolehubah prev_pos;
  3. Tambahkan 48 pada kedudukan nadi terakhir (pos := prev_pos + 48)
  4. Memandangkan meningkatkan kedudukan sebanyak 48 tidak menjamin bahawa kita akan sampai ke kedudukan nadi rujukan seterusnya (kecacatan pita, operasi mekanisme pemacu pita yang tidak stabil, dsb.), kita perlu melaraskan kedudukan nadi pos. Untuk melakukan ini, ambil sekeping kecil data (pos-8;pos+8) dan cari nilai amplitud maksimum padanya. Kedudukan yang sepadan dengan maksimum akan disimpan dalam pos. Di sini 8 = 48/6 ialah pemalar yang diperoleh secara eksperimen, yang menjamin bahawa kami akan menentukan maksimum yang betul dan tidak akan menjejaskan impuls lain yang mungkin berdekatan. Dalam kes yang sangat teruk, apabila jarak antara denyutan adalah lebih kurang daripada atau lebih besar daripada 48, anda boleh melaksanakan pencarian paksa untuk nadi, tetapi dalam skop artikel saya tidak akan menerangkan ini dalam algoritma;
  5. Pada langkah sebelumnya, ia juga perlu untuk memeriksa sama ada nadi rujukan ditemui sama sekali. Iaitu, jika anda hanya mencari maksimum, ini tidak menjamin bahawa impuls hadir dalam segmen ini. Dalam pelaksanaan terbaru program bacaan saya, saya menyemak perbezaan antara nilai amplitud maksimum dan minimum pada segmen, dan jika ia melebihi had tertentu, saya mengira kehadiran impuls. Persoalannya juga apa yang perlu dilakukan jika nadi rujukan tidak dijumpai. Terdapat 2 pilihan: sama ada data telah tamat dan senyap menyusul, atau ini harus dianggap sebagai ralat bacaan. Walau bagaimanapun, kami akan meninggalkan ini untuk memudahkan algoritma;
  6. Pada langkah seterusnya, kita perlu menentukan kehadiran nadi data (bit 0 atau 1), untuk ini kita mengambil bahagian tengah segmen (prev_pos;pos) middle_pos sama dengan middle_pos := (prev_pos+pos)/2 dan dalam beberapa kejiranan middle_pos pada segmen (middle_pos-8;middle_pos +8) mari kita mengira amplitud maksimum dan minimum. Jika perbezaan di antara mereka adalah lebih daripada 10, kami menulis bit 1 ke dalam hasilnya, jika tidak 0. 10 ialah pemalar yang diperoleh secara eksperimen;
  7. Simpan kedudukan semasa dalam prev_pos (prev_pos := pos)
  8. Ulang bermula dari langkah 3 sehingga kita membaca keseluruhan fail;
  9. Tatasusunan bit yang terhasil mesti disimpan sebagai satu set bait. Oleh kerana kami tidak mengambil kira bait penyegerakan semasa membaca, bilangan bit mungkin bukan gandaan 8, dan mengimbangi bit yang diperlukan juga tidak diketahui. Dalam pelaksanaan pertama algoritma, saya tidak tahu tentang kewujudan bait penyegerakan dan oleh itu hanya menyimpan 8 fail dengan bilangan bit offset yang berbeza. Salah satu daripadanya mengandungi data yang betul. Dalam algoritma akhir, saya hanya mengeluarkan semua bit sehingga A5h, yang membolehkan saya segera mendapatkan fail output yang betul

Algoritma dalam Ruby, untuk mereka yang berminat
Saya memilih Ruby sebagai bahasa untuk menulis program, kerana... Saya memprogramkannya pada kebanyakan masa. Pilihannya tidak berprestasi tinggi, tetapi tugas untuk membuat kelajuan membaca secepat mungkin tidak berbaloi.

# Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ 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*")

Keputusan

Setelah mencuba beberapa varian algoritma dan pemalar, saya bernasib baik kerana mendapat sesuatu yang sangat menarik:

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Jadi, berdasarkan rentetan aksara, kami mempunyai program untuk memplot graf. Walau bagaimanapun, tiada kata kunci dalam teks program. Semua kata kunci dikodkan sebagai bait (setiap nilai > 80j). Sekarang kita perlu mengetahui komputer dari tahun 80-an yang boleh menyimpan program dalam format ini.

Malah, ia hampir sama dengan program BASIC. Komputer ZX Spectrum menyimpan program dalam format yang lebih kurang sama dalam memori dan menyimpan program ke pita. Untuk berjaga-jaga, saya menyemak kata kunci terhadapnya meja. Walau bagaimanapun, hasilnya jelas negatif.

Saya juga menyemak kata kunci ASAS Atari yang popular, Commodore 64 dan beberapa komputer lain pada masa itu, yang mana saya dapat mencari dokumentasi, tetapi tidak berjaya - pengetahuan saya tentang jenis komputer retro ternyata tidak begitu luas.

Kemudian saya memutuskan untuk pergi senarai, dan kemudian pandangan saya jatuh pada nama pengeluar Radio Shack dan komputer TRS-80. Ini adalah nama-nama yang ditulis pada label kaset yang terletak di atas meja saya! Saya tidak tahu nama-nama ini sebelum ini dan tidak biasa dengan komputer TRS-80, jadi saya rasa Radio Shack ialah pengeluar kaset audio seperti BASF, Sony atau TDK, dan TRS-80 adalah masa main balik. Kenapa tidak?

Komputer Tandy/Radio Shack TRS-80

Kemungkinan besar rakaman audio yang dimaksudkan, yang saya berikan sebagai contoh pada permulaan artikel, dibuat pada komputer seperti ini:

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Ternyata komputer ini dan jenisnya (Model I/Model III/Model IV, dsb.) sangat popular pada satu masa (sudah tentu, bukan di Rusia). Perlu diperhatikan bahawa pemproses yang mereka gunakan juga adalah Z80. Untuk komputer ini anda boleh mencari di Internet banyak maklumat. Pada tahun 80-an, maklumat komputer telah diedarkan di majalah. Pada masa ini terdapat beberapa emulator komputer untuk platform yang berbeza.

Saya memuat turun emulator trs80gp dan buat pertama kalinya saya dapat melihat bagaimana komputer ini berfungsi. Sudah tentu, komputer tidak menyokong output warna; resolusi skrin hanya 128x48 piksel, tetapi terdapat banyak sambungan dan pengubahsuaian yang boleh meningkatkan resolusi skrin. Terdapat juga banyak pilihan untuk sistem pengendalian untuk komputer ini dan pilihan untuk melaksanakan bahasa BASIC (yang, tidak seperti Spektrum ZX, dalam sesetengah model tidak "berkelip" ke dalam ROM dan sebarang pilihan boleh dimuatkan daripada cakera liut, sama seperti OS itu sendiri)

saya pun jumpa utiliti untuk menukar rakaman audio ke dalam format CAS, yang disokong oleh emulator, tetapi atas sebab tertentu tidak dapat membaca rakaman daripada kaset saya menggunakannya.

Setelah mengetahui format fail CAS (yang ternyata hanya salinan sedikit demi sedikit data daripada pita yang saya sedia ada, kecuali pengepala dengan kehadiran bait penyegerakan), saya membuat beberapa perubahan pada program saya dan dapat mengeluarkan fail CAS yang berfungsi yang berfungsi dalam emulator (TRS-80 Model III):

Bagaimana saya memulihkan data dalam format yang tidak diketahui daripada pita magnetik

Saya mereka bentuk versi terkini utiliti penukaran dengan penentuan automatik denyutan pertama dan jarak antara denyut rujukan sebagai pakej PERMATA, kod sumber tersedia di Github.

Kesimpulan

Laluan yang telah kami lalui ternyata menjadi perjalanan yang menarik ke masa lalu, dan saya gembira kerana akhirnya saya menemui jawapannya. Antara lain, saya:

  • Saya mengetahui format untuk menyimpan data dalam Spektrum ZX dan mengkaji rutin ROM terbina dalam untuk menyimpan/membaca data daripada kaset audio
  • Saya berkenalan dengan komputer TRS-80 dan jenisnya, mengkaji sistem pengendalian, melihat program sampel dan juga berpeluang melakukan penyahpepijatan dalam kod mesin (lagipun, semua mnemonik Z80 sudah biasa bagi saya)
  • Menulis utiliti lengkap untuk menukar rakaman audio kepada format CAS, yang boleh membaca data yang tidak dikenali oleh utiliti "rasmi"

Sumber: www.habr.com

Tambah komen