Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Pristoria

Essendu un amante di hardware retro, una volta aghju compru un ZX Spectrum + da un venditore in u Regnu Unitu. Inclusu cù l'urdinatore stessu, aghju ricivutu parechje cassette audio cù ghjochi (in l'imballu originale cù struzzioni), è ancu prugrammi arregistrati nantu à cassette senza marcatura speciale. Sorprendentemente, i dati da cassette di 40 anni sò leghjite bè è aghju pussutu scaricà quasi tutti i ghjochi è i prugrammi da elli.

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

In ogni casu, in certi cassette aghju trovu registrazioni chjaramente micca fattu da l'urdinatore ZX Spectrum. Sonavanu cumplettamente sfarente è, à u cuntrariu di e gravazioni da l'urdinatore mintuatu, ùn anu micca principiatu cù un cortu bootloader BASIC, chì hè di solitu presente in i registrazioni di tutti i prugrammi è di i ghjochi.

Per qualchì tempu questu m'hà perseguitatu - vulia veramente sapè ciò chì era ammucciatu in elli. Se pudete leghje u signale audio cum'è una sequenza di bytes, pudete cercà caratteri o qualcosa chì indica l'origine di u signale. Una spezia di retro-archeologia.

Avà ch'e aghju andatu in tuttu è fighjulà l'etichette di e cassette stesse, surrisu perchè

a risposta era ghjustu davanti à i mo ochji per tuttu
In l'etichetta di a cassetta sinistra hè u nome di l'urdinatore TRS-80, è ghjustu sottu u nome di u fabricatore: "Fabricatu da Radio Shack in USA"

(Se vulete mantene l'intriga finu à a fine, ùn andate micca sottu u spoiler)

Comparazione di signali audio

Prima di tuttu, digitalizemu e registrazioni audio. Pudete sente ciò chì sona cum'è:


E cum'è di solitu, a registrazione da l'urdinatore ZX Spectrum sona:


In i dui casi, à u principiu di l'arregistramentu ci hè un cusì chjamatu tonu di pilotu - un sonu di a listessa freccia (in a prima registrazione hè assai corta <1 secunna, ma hè distinguibile). U tonu di pilotu signala l'urdinatore per preparà per riceve dati. Comu regula, ogni computer ricunnosce solu u so "propiu" tonu pilotu da a forma di u signale è a so freccia.

Hè necessariu di dì qualcosa nantu à a forma di u signale stessu. Per esempiu, nantu à u ZX Spectrum, a so forma hè rettangulare:

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Quandu un tonu pilotu hè rilevatu, u ZX Spectrum mostra alternate barre rosse è blu à u cunfini di u screnu per indicà chì u signale hè statu ricunnisciutu. U tonu di pilotu finisce impulsu di sincronia, chì signala l'urdinatore per cumincià à riceve dati. Hè carattarizatu da una durata più corta (paragunatu à u tonu pilotu è i dati sussegwenti) (vede a figura)

Dopu chì u pulsu di sincronia hè ricevutu, l'urdinatore registra ogni crescita / caduta di u segnu, misurandu a so durata. Se a durata hè menu di un certu limitu, u bit 1 hè scrittu in memoria, altrimente 0. I bits sò cullati in byte è u prucessu hè ripetutu finu à chì N byte sò ricevuti. U numeru N hè generalmente pigliatu da l'intestazione di u schedariu telecaricatu. A sequenza di carica hè a siguenti:

  1. tonu di pilotu
  2. header (lunghezza fissa), cuntene a dimensione di i dati scaricati (N), u nome di u schedariu è u tipu
  3. tonu di pilotu
  4. i dati stessi

Per assicurà chì i dati sò caricati currettamente, u ZX Spectrum leghje u cusì chjamatu byte di parità (byte di parità), chì hè calculatu quandu salvà un schedariu XORing tutti i bytes di e dati scritti. Quandu leghje un schedariu, l'urdinatore calcula u byte di parità da i dati ricevuti è, se u risultatu hè diversu da quellu salvatu, mostra u missaghju d'errore "R Tape loading error". In modu strettu, l'urdinatore pò emette stu missaghju prima se, durante a lettura, ùn pò micca ricunnosce un impulsu (miccatu o a so durata ùn currisponde à certi limiti)

Allora, vedemu avà ciò chì pare un signalu scunnisciutu:

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Questu hè u tonu pilotu. A forma di u signale hè significativamente sfarente, ma hè chjaru chì u signale hè custituitu di ripetiri impulsi brevi di una certa frequenza. À una frequenza di campionamentu di 44100 Hz, a distanza trà i "picchi" hè di circa 48 campioni (chì currisponde à una freccia di ~ 918 Hz). Ricordemu sta figura.

Fighjemu avà u frammentu di dati:

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Se misuramu a distanza trà i pulsati individuali, ci hè chì a distanza trà i pulsati "longu" hè sempre ~ 48 campioni, è trà i brevi - ~ 24. Fighjendu un pocu avanti, diceraghju chì à a fine hè stata chì i pulsazioni "di riferimentu" cù una freccia di 918 Hz seguitanu continuamente, da u principiu à a fine di u schedariu. Pò esse presumitu chì quandu trasmettenu dati, se un impulsu supplementu hè scontru trà l'impulsi di riferimentu, u cunsideremu cum'è bit 1, altrimenti 0.

Chì ci hè di u pulse di sincronia? Fighjemu u principiu di i dati:

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

U tonu pilotu finisce è i dati cumincianu immediatamente. Un pocu dopu, dopu l'analisi di parechje registrazioni audio diffirenti, pudemu scopre chì u primu byte di dati hè sempre u stessu (10100101b, A5h). L'urdinatore pò principià à leghje i dati dopu avè ricevutu.

Pudete ancu attentu à u cambiamentu di u primu impulsu di riferimentu immediatamente dopu à l'ultimu 1u in u byte di sincronia. Hè statu scupertu assai più tardi in u prucessu di sviluppà un prugramma di ricunniscenza di dati, quandu i dati à l'iniziu di u schedariu ùn puderanu micca leghje stabilmente.

Avà pruvemu di discrìviri un algoritmu chì processerà un schedariu audio è carica dati.

Caricà Dati

Prima, fighjemu uni pochi supposizioni per mantene l'algoritmu simplice:

  1. Avemu da cunsiderà solu i schedari in formatu WAV;
  2. U schedariu audio deve principià cù un tonu pilotu è ùn deve micca cuntene u silenziu à u principiu
  3. U schedariu fonte deve avè una freccia di campionamentu di 44100 Hz. In questu casu, a distanza trà i pulsati di riferimentu di 48 campioni hè digià determinata è ùn avemu micca bisognu di calculà in modu programmaticu;
  4. U formatu di mostra pò esse qualsiasi (8/16 bits / flottante) - postu chì quandu leghje pudemu cunvertisce à quellu desideratu;
  5. Assumimu chì u schedariu fonte hè nurmalizatu da amplitude, chì deve stabilizzà u risultatu;

L'algoritmu di lettura serà cusì:

  1. Avemu leghje u schedariu in memoria, à u listessu tempu cunvertisce u furmatu di mostra à 8 bits;
  2. Determina a pusizione di u primu impulsu in i dati audio. Per fà questu, avete bisognu di calculà u numeru di mostra cù l'amplitude massima. Per simplicità, calculeremu una volta manualmente. Salvemu à a variabile prev_pos;
  3. Aghjunghjite 48 à a pusizione di l'ultimu impulsu (pos := prev_pos + 48)
  4. Siccomu l'aumentu di a pusizione da 48 ùn guarantisci micca chì avemu da ghjunghje à a pusizione di u prossimu impulsu di riferimentu (difetti di cinta, funziunamentu inestabile di u mecanismu di l'unità di cinta, etc.), avemu bisognu di aghjustà a pusizione di u pulsu pos. Per fà questu, pigliate un picculu pezzu di dati (pos-8; pos + 8) è truvate u valore di l'amplitude massima nantu à questu. A pusizioni currispundenti à u massimu serà guardatu in pos. Quì 8 = 48/6 hè una constante ottenuta spirimintali, chì guarantisci chì determinà u massimu currettu è ùn affetterà micca altri impulsi chì ponu esse vicinu. In casi assai cattivi, quandu a distanza trà i pulsati hè assai menu o più grande di 48, pudete implementà una ricerca furzata per un pulse, ma in u scopu di l'articulu ùn aghju micca discrittu questu in l'algoritmu;
  5. À u passu precedente, saria ancu necessariu di verificà chì u pulsu di riferimentu hè statu trovu in tuttu. Vale à dì, sè solu cercà u massimu, questu ùn guarantisci micca chì l'impulsu hè presente in questu segmentu. In a mo ultima implementazione di u prugramma di lettura, aghju verificatu a diffarenza trà i valori di ampiezza massima è minima nantu à un segmentu, è s'ellu supera un certu limitu, cuntu a presenza di un impulsu. A quistione hè ancu ciò chì fà s'ellu ùn hè micca truvatu u pulsu di riferimentu. Ci sò 2 opzioni: o i dati sò finiti è u silenziu seguita, o questu deve esse cunsideratu un errore di lettura. In ogni casu, omettemu questu per simplificà l'algoritmu;
  6. À u prossimu passu, avemu bisognu di determinà a prisenza di un impulsu di dati (bit 0 o 1), per questu pigliemu a mità di u segmentu (prev_pos;pos) middle_pos uguale à middle_pos := (prev_pos+pos)/2 è in qualchì vicinatu di middle_pos nantu à u segmentu (middle_pos-8; middle_pos +8) calculemu l'amplitude massima è minima. Se a diffarenza trà elli hè più di 10, scrivimu u bit 1 in u risultatu, altrimente 0. 10 hè una constante ottenuta sperimentalmente;
  7. Salvà a pusizione attuale in prev_pos (prev_pos := pos)
  8. Repetite da u passu 3 finu à leghje u schedariu sanu;
  9. L'array di bit resultante deve esse salvatu cum'è un set di byte. Siccomu ùn avemu micca cunsideratu u byte di sincronia durante a lettura, u numeru di bit pò esse micca un multiplu di 8, è l'offset di bit necessariu hè ancu scunnisciutu. In a prima implementazione di l'algoritmu, ùn sapia micca di l'esistenza di u byte di sincronia è per quessa hà salvatu simpliciamente 8 schedarii cù parechji numeri di offset bits. Unu di elli cuntene dati curretti. In l'algoritmu finali, solu sguassate tutti i bits finu à A5h, chì mi permette di ottene immediatamente u schedariu di output currettu.

Algoritmu in Ruby, per quelli interessati
Aghju sceltu Ruby cum'è lingua per scrive u prugramma, perchè ... U prugramma nantu à a maiò parte di u tempu. L'opzione ùn hè micca d'alta prestazione, ma u compitu di fà a velocità di lettura u più veloce pussibule ùn vale a pena.

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

risultatu

Dopu avè pruvatu parechje varianti di l'algoritmu è custanti, aghju avutu a furtuna di ottene qualcosa estremamente interessante:

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Allora, à ghjudicà da e stringhe di caratteri, avemu un prugramma per tracciate gràfiche. Tuttavia, ùn ci sò micca parole chjave in u testu di u prugramma. Tutte e parolle chjave sò codificate cum'è byte (ogni valore> 80h). Avà avemu bisognu di sapè quale computer da l'anni 80 puderia salvà i prugrammi in stu formatu.

In fatti, hè assai simile à un prugramma BASIC. L'urdinatore ZX Spectrum guarda i prugrammi in circa u listessu formatu in memoria è salva i prugrammi in cinta. In casu, aghju verificatu e parolle chjave contru tavula. Tuttavia, u risultatu era ovviamente negativu.

Aghju verificatu ancu i chjavi BASIC di u populari Atari, Commodore 64 è parechji altri computer di quellu tempu, per quale aghju pussutu truvà documentazione, ma senza successu - a mo cunniscenza di i tipi di computer retro ùn sò micca cusì largu.

Allora decisu di andà a lista, è dopu u mo sguardu hè cascatu nantu à u nome di u fabricatore Radio Shack è l'urdinatore TRS-80. Quessi sò i nomi chì eranu scritti nantu à l'etichette di e cassette chì stavanu nantu à a mo tavula ! Ùn cunnosci micca questi nomi prima è ùn era micca familiarizatu cù l'urdinatore TRS-80, cusì mi pareva chì Radio Shack era un fabricatore di cassette audio cum'è BASF, Sony o TDK, è u TRS-80 era u tempu di riproduzione. Perchè nò?

Computer Tandy/Radio Shack TRS-80

Hè assai prubabile chì l'arregistramentu audio in quistione, chì aghju datu cum'è un esempiu à u principiu di l'articulu, hè stata fatta in un computer cum'è questu:

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Hè risultatu chì questu computer è e so varietà (Model I / Model III / Model IV, etc.) eranu assai populari in un tempu (di sicuru, micca in Russia). Hè nutate chì u processatore chì anu utilizatu era ancu Z80. Per questu computer pudete truvà nantu à Internet assai infurmazione. In l'anni 80, l'infurmazione di l'informatica hè stata distribuita riviste. À u mumentu ci sò parechji emulatori computer per diverse piattaforme.

Aghju scaricatu l'emulatore trés 80 gp è per a prima volta aghju pussutu vede cumu si travaglia stu computer. Di sicuru, l'urdinatore ùn sustene micca a pruduzzioni di culore; a risoluzione di u screnu era solu 128x48 pixel, ma ci sò parechje estensioni è mudificazioni chì puderanu aumentà a risoluzione di u screnu. Ci era ancu parechje opzioni per i sistemi operativi per questu urdinatore è l'opzioni per implementà a lingua BASIC (chì, à u cuntrariu di u ZX Spectrum, in certi mudelli ùn era ancu "flash" in ROM è qualsiasi opzione puderia esse caricata da un dischettu, cum'è u OS stessu)

Aghju trovu ancu utilità per cunvertisce l'arregistramentu audio in u formatu CAS, chì hè supportatu da emulatori, ma per qualchì mutivu ùn era micca pussibule di leghje e registrazioni da i mo cassette cù elli.

Dopu avè scupertu u furmatu di u schedariu CAS (chì hè diventatu solu una copia di e dati da a cinta chì aghju digià in manu, fora di l'intestazione cù a presenza di un byte di sincronia), aghju fattu un pochi cambiamenti à u mo prugramma è hà sappiutu pruduce un schedariu CAS di travagliu chì hà travagliatu in l'emulatore (TRS-80 Model III):

Cumu aghju recuperatu dati in un formatu scunnisciutu da a cinta magnetica

Aghju cuncepitu l'ultima versione di l'utilità di cunversione cù a determinazione automatica di u primu impulsu è a distanza trà i pulsati di riferimentu cum'è un pacchettu GEM, u codice fonte hè dispunibule à Github.

cunchiusioni

U caminu chì avemu viaghjatu hè diventatu un viaghju fascinante in u passatu, è sò cuntentu chì à a fine aghju trovu a risposta. Frà altre cose, aghju:

  • Aghju capitu u formatu per salvà dati in u ZX Spectrum è studiatu e rutine ROM integrate per salvà / leghje dati da cassette audio
  • Aghju cunnisciutu l'urdinatore TRS-80 è e so variità, hà studiatu u sistema operatore, hà guardatu i prugrammi di mostra è ancu avutu l'uppurtunità di fà debugging in i codici di a macchina (dopu à tuttu, tutti i mnemotichi Z80 sò familiari per mè)
  • Scrive una utilità cumpleta per cunvertisce registrazioni audio in u formatu CAS, chì pò leghje dati chì ùn sò micca ricunnisciuti da l'utilità "ufficiale".

Source: www.habr.com

Add a comment