Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

esihistoria

Koska olen retrolaitteiston ystävä, ostin kerran ZX Spectrum+:n myyjältä Isossa-Britanniassa. Itse tietokoneen mukana sain useita äänikasetteja peleineen (alkuperäisessä pakkauksessa ohjeineen) sekä kasetteille tallennettuja ohjelmia ilman erityisiä merkintöjä. Yllättäen data 40 vuotta vanhoilta kasetteilta oli hyvin luettavissa ja niistä pystyin lataamaan lähes kaikki pelit ja ohjelmat.

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Joiltakin kasetteilta löysin kuitenkin tallenteita, jotka eivät selvästikään olleet ZX Spectrum -tietokoneen tekemiä. Ne kuulostivat täysin erilaisilta ja toisin kuin mainitun tietokoneen tallenteet, ne eivät alkaneet lyhyellä BASIC-käynnistyslataimella, joka on yleensä läsnä kaikkien ohjelmien ja pelien tallennuksissa.

Tämä kummitteli minua jonkin aikaa - halusin todella saada selville, mitä niissä piilee. Jos voisit lukea äänisignaalin tavujonona, voit etsiä niistä merkkejä tai mitä tahansa, joka ilmaisee signaalin alkuperän. Eräänlaista retro-arkeologiaa.

Nyt kun olen mennyt loppuun ja katsonut itse kasettien etikettejä, hymyilen, koska

vastaus oli silmieni edessä koko ajan
Vasemman kasetin etiketissä on TRS-80-tietokoneen nimi ja heti valmistajan nimen alapuolella: "Valmistaja Radio Shack in USA"

(Jos haluat säilyttää juonittelun loppuun asti, älä mene spoilerin alle)

Äänisignaalien vertailu

Ensinnäkin digitoidaan äänitallenteet. Voit kuunnella miltä se kuulostaa:


Ja kuten tavallista, ZX Spectrum -tietokoneen äänitys kuulostaa:


Molemmissa tapauksissa tallennuksen alussa on ns pilottiääni - samantaajuinen ääni (ensimmäisessä tallenteessa se on hyvin lyhyt <1 sekunti, mutta erottuu). Pilottiääni ilmoittaa tietokoneelle valmistautumaan vastaanottamaan tietoja. Yleensä jokainen tietokone tunnistaa vain "oman" pilottiäänensä signaalin muodon ja sen taajuuden perusteella.

On tarpeen sanoa jotain itse signaalin muodosta. Esimerkiksi ZX Spectrumissa sen muoto on suorakaiteen muotoinen:

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Kun pilottiääni havaitaan, ZX Spectrum näyttää vuorotellen punaisia ​​ja sinisiä palkkeja näytön reunassa osoittaen, että signaali on tunnistettu. Pilottiääni päättyy tahdistettu pulssi, joka ilmoittaa, että tietokone alkaa vastaanottaa tietoja. Sille on ominaista lyhyempi kesto (verrattuna pilottiääneen ja sitä seuraaviin tietoihin) (katso kuva)

Kun synkronointipulssi on vastaanotettu, tietokone tallentaa signaalin jokaisen nousun/laskemisen ja mittaa sen keston. Jos kesto on alle tietyn rajan, muistiin kirjoitetaan bitti 1, muuten 0. Bitit kerätään tavuiksi ja prosessia toistetaan, kunnes vastaanotetaan N tavua. Numero N on yleensä otettu ladatun tiedoston otsikosta. Latausjärjestys on seuraava:

  1. pilottiääni
  2. otsikko (kiinteä pituus), sisältää ladattujen tietojen koon (N), tiedostonimen ja tyypin
  3. pilottiääni
  4. itse tiedot

Varmistaakseen, että tiedot ladataan oikein, ZX Spectrum lukee ns pariteettitavu (pariteettitavu), joka lasketaan tallennettaessa tiedostoa XOR-korjaamalla kaikki kirjoitetun tiedon tavut. Lukeessaan tiedostoa tietokone laskee vastaanotetuista tiedoista pariteettitavun ja jos tulos poikkeaa tallennetusta, näyttää virheilmoituksen "R Tape loading error". Tarkkaan ottaen tietokone voi antaa tämän viestin aikaisemmin, jos se ei lukiessaan tunnista pulssia (jäänyt väliin tai sen kesto ei vastaa tiettyjä rajoja)

Katsotaanpa nyt, miltä tuntematon signaali näyttää:

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Tämä on pilottiääni. Signaalin muoto on merkittävästi erilainen, mutta on selvää, että signaali koostuu tietyn taajuuden toistuvista lyhyistä pulsseista. Näytteenottotaajuudella 44100 Hz "huippujen" välinen etäisyys on noin 48 näytettä (joka vastaa taajuutta ~918 Hz). Muista tämä kuva.

Katsotaanpa nyt datafragmenttia:

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Jos mittaamme yksittäisten pulssien välisen etäisyyden, selviää, että "pitkien" pulssien välinen etäisyys on edelleen ~48 näytettä ja lyhyiden välillä ~24. Hieman eteenpäin katsoen sanon, että lopulta kävi ilmi, että "referenssi" pulssit taajuudella 918 Hz seuraavat jatkuvasti, tiedoston alusta loppuun. Voidaan olettaa, että dataa siirrettäessä, jos referenssipulssien väliin tulee lisäpulssi, katsotaan se bitiksi 1, muuten 0.

Entä synkronointipulssi? Katsotaanpa datan alkua:

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Pilottiääni päättyy ja data alkaa välittömästi. Hieman myöhemmin useiden eri äänitallenteiden analysoinnin jälkeen pystyimme havaitsemaan, että datan ensimmäinen tavu on aina sama (10100101b, A5h). Tietokone voi alkaa lukea tietoja vastaanotettuaan ne.

Voit myös kiinnittää huomiota ensimmäisen referenssipulssin siirtoon välittömästi synkronointitavun viimeisen ensimmäisen jälkeen. Se havaittiin paljon myöhemmin tiedontunnistusohjelmaa kehitettäessä, kun tiedoston alussa olevia tietoja ei voitu lukea vakaasti.

Yritetään nyt kuvata algoritmi, joka käsittelee äänitiedoston ja lataa tiedot.

Ladataan tietoja

Katsotaanpa ensin muutamia oletuksia algoritmin pitämiseksi yksinkertaisena:

  1. Käsittelemme vain WAV-muodossa olevia tiedostoja;
  2. Äänitiedoston tulee alkaa pilottiäänellä, eikä sen alussa saa olla hiljaisuutta
  3. Lähdetiedoston näytteenottotaajuuden on oltava 44100 Hz. Tässä tapauksessa 48 näytteen referenssipulssien välinen etäisyys on jo määritetty, eikä sitä tarvitse laskea ohjelmallisesti;
  4. Esimerkkimuoto voi olla mikä tahansa (8/16 bittiä/liukuluku) - koska luettaessa voimme muuntaa sen halutuksi;
  5. Oletetaan, että lähdetiedosto on normalisoitu amplitudilla, minkä pitäisi vakauttaa tulos;

Lukualgoritmi on seuraava:

  1. Luimme tiedoston muistiin, samalla muunnamme näytemuodon 8 bitiksi;
  2. Määritä ensimmäisen pulssin paikka äänidatassa. Tätä varten sinun on laskettava näytteen lukumäärä suurimmalla amplitudilla. Yksinkertaisuuden vuoksi laskemme sen kerran manuaalisesti. Tallennetaan se muuttujaan prev_pos;
  3. Lisää 48 viimeisen pulssin kohtaan (pos := prev_pos + 48)
  4. Koska paikan lisääminen 48:lla ei takaa, että pääsemme seuraavan referenssipulssin asentoon (nauhaviat, nauha-aseman epävakaa toiminta jne.), meidän on säädettävä pulssin asentoa. Tätä varten ota pieni osa dataa (pos-8;pos+8) ja etsi siitä suurin amplitudiarvo. Maksimiarvoa vastaava sijainti tallennetaan asentoon. Tässä 8 = 48/6 on kokeellisesti saatu vakio, joka takaa, että määritämme oikean maksimin eikä vaikuta muihin lähellä mahdollisesti oleviin impulsseihin. Erittäin huonoissa tapauksissa, kun pulssien välinen etäisyys on paljon pienempi tai suurempi kuin 48, voit suorittaa pakotetun pulssin haun, mutta artikkelin puitteissa en kuvaa tätä algoritmissa;
  5. Edellisessä vaiheessa olisi myös tarpeen tarkistaa, että vertailupulssi löytyi ollenkaan. Eli jos etsit vain maksimia, tämä ei takaa, että impulssi on läsnä tässä segmentissä. Viimeisimmässä lukuohjelman toteutuksessani tarkistan segmentin maksimi- ja minimiamplitudiarvojen eron, ja jos se ylittää tietyn rajan, lasken impulssin olemassaolon. Kysymys kuuluu myös, mitä tehdä, jos referenssipulssia ei löydy. Vaihtoehtoja on 2: joko data on päättynyt ja sitä seuraa hiljaisuus tai tämä tulee katsoa lukuvirheeksi. Jätämme tämän kuitenkin pois algoritmin yksinkertaistamiseksi;
  6. Seuraavassa vaiheessa meidän on määritettävä datapulssin olemassaolo (bitti 0 tai 1), tätä varten otamme segmentin keskikohdan (prev_pos;pos) middle_pos yhtä kuin middle_pos := (prev_pos+pos)/2 ja jossain segmentin Middle_pos naapurustossa (middle_pos-8;middle_pos +8) lasketaan maksimi- ja minimiamplitudi. Jos niiden välinen ero on suurempi kuin 10, tulokseen kirjoitetaan bitti 1, muuten 0. 10 on kokeellisesti saatu vakio;
  7. Tallenna nykyinen sijainti kohtaan prev_pos (prev_pos := pos)
  8. Toista alkaen vaiheesta 3, kunnes luemme koko tiedoston;
  9. Tuloksena oleva bittijoukko on tallennettava tavujoukona. Koska emme huomioineet synkronointitavua lukemisessa, bittien määrä ei välttämättä ole 8:n kerrannainen, ja vaadittu bittipoikkeama on myös tuntematon. Algoritmin ensimmäisessä toteutuksessa en tiennyt synkronointitavun olemassaolosta ja siksi yksinkertaisesti tallensin 8 tiedostoa eri määrällä offsetbittejä. Yksi niistä sisälsi oikeat tiedot. Lopullisessa algoritmissa poistan yksinkertaisesti kaikki bitit A5h asti, jolloin saan heti oikean tulostustiedoston

Ruby-algoritmi kiinnostuneille
Valitsin Rubyn ohjelman kirjoituskieleksi, koska... Ohjelmoin siihen suurimman osan ajasta. Vaihtoehto ei ole korkean suorituskyvyn, mutta tehtävä lukunopeuden nopeuttamiseksi mahdollisimman nopeasti ei ole sen arvoista.

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

Tulos

Kokeittuani useita algoritmin ja vakioiden muunnelmia, minulla oli onni saada jotain erittäin mielenkiintoista:

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Joten merkkijonoista päätellen meillä on ohjelma kaavioiden piirtämiseen. Ohjelmatekstissä ei kuitenkaan ole avainsanoja. Kaikki avainsanat on koodattu tavuina (kukin arvo > 80h). Nyt meidän on selvitettävä, mikä 80-luvun tietokone voisi tallentaa ohjelmia tässä muodossa.

Itse asiassa se on hyvin samanlainen kuin BASIC-ohjelma. ZX Spectrum -tietokone tallentaa ohjelmat suunnilleen samassa muodossa muistiin ja tallentaa ohjelmat nauhalle. Varmuuden vuoksi tarkistin avainsanoja vastaan pöytä. Tulos oli kuitenkin selvästi negatiivinen.

Tarkistin myös suositun Atarin, Commodore 64:n ja useiden muiden tuon ajan tietokoneiden BASIC-avainsanat, joista onnistuin löytämään dokumentaatiota, mutta tuloksetta - tietämykseni retrotietokoneiden tyypeistä ei osoittautunut niin laajaksi.

Sitten päätin mennä lista, ja sitten katseeni osui valmistajan Radio Shackin ja TRS-80-tietokoneen nimeen. Nämä ovat nimet, jotka kirjoitettiin pöydälläni makaavien kasettien etiketteihin! En tiennyt näitä nimiä aiemmin enkä tuntenut TRS-80-tietokonetta, joten minusta tuntui, että Radio Shack oli äänikasettien valmistaja, kuten BASF, Sony tai TDK, ja TRS-80 oli toistoaika. Miksi ei?

Tietokone Tandy/Radio Shack TRS-80

On hyvin todennäköistä, että kyseinen äänitallenne, jonka annoin esimerkkinä artikkelin alussa, on tehty tällaisella tietokoneella:

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Kävi ilmi, että tämä tietokone ja sen lajikkeet (malli I / malli III / malli IV jne.) olivat erittäin suosittuja kerralla (ei tietenkään Venäjällä). On huomionarvoista, että heidän käyttämä prosessori oli myös Z80. Tämän tietokoneen löydät Internetistä paljon tietoa. 80-luvulla tietokonetietoa jaettiin aikakauslehtiä. Tällä hetkellä niitä on useita emulaattorit tietokoneita eri alustoille.

Latasin emulaattorin trs80gp ja ensimmäistä kertaa pystyin näkemään kuinka tämä tietokone toimii. Tietokone ei tietenkään tukenut väritulostusta, näytön resoluutio oli vain 128x48 pikseliä, mutta siellä oli monia laajennuksia ja muutoksia, jotka saattoivat lisätä näytön resoluutiota. Tämän tietokoneen käyttöjärjestelmille oli myös monia vaihtoehtoja ja vaihtoehtoja BASIC-kielen toteuttamiseen (jota, toisin kuin ZX Spectrumissa, joissain malleissa ei edes "viljattu" ROM:iin ja minkä tahansa vaihtoehdon voitiin ladata levykkeeltä, aivan kuten itse käyttöjärjestelmä)

minäkin löysin hyödyllisyys muuntaa äänitallenteita CAS-muotoon, jota emulaattorit tukevat, mutta jostain syystä ei ollut mahdollista lukea äänitteitä kaseteistani niillä.

Selvitettyään CAS-tiedostomuodon (joka osoittautui vain bittikohtaiseksi kopioksi tiedoista nauhalta, joka minulla jo oli käsilläni, lukuun ottamatta otsikkoa, jossa oli synkronointitavu), tein muutamia muutoksia ohjelmaani ja pystyin tulostamaan toimivan CAS-tiedoston, joka toimi emulaattorissa (TRS-80 Model III):

Kuinka palautin tietoja tuntemattomassa muodossa magneettinauhalta

Suunnittelin muunnosapuohjelman uusimman version, jossa ensimmäinen pulssi ja vertailupulssien välinen etäisyys määritetään automaattisesti GEM-paketiksi, lähdekoodi on saatavilla osoitteessa Github.

Johtopäätös

Kuljemamme polku osoittautui kiehtovaksi matkaksi menneisyyteen, ja olen iloinen, että lopulta löysin vastauksen. Muun muassa minä:

  • Selvitin tietojen tallennusmuodon ZX Spectrumissa ja tutkin sisäänrakennettuja ROM-rutiineja äänikasettien datan tallentamiseen/lukemiseen.
  • Tutustuin TRS-80-tietokoneeseen ja sen lajikkeisiin, tutkin käyttöjärjestelmää, katselin esimerkkiohjelmia ja sain jopa mahdollisuuden tehdä virheenkorjausta konekoodeissa (kaikki Z80-muistomerkit ovat minulle tuttuja)
  • Kirjoitti täysimittaisen apuohjelman äänitallenteiden muuntamiseen CAS-muotoon, joka voi lukea tietoja, joita "virallinen" apuohjelma ei tunnista

Lähde: will.com

Lisää kommentti