Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Pas historia

Duke një adhurues i hardware-it retro, bleva një ZX Spectrum+ nga një shitës në Britaninë e Madhe. Me kompjuterin e vet, mora disa kaseta audio me lojëra (në paketim origjinal me udhëzime), si dhe programe të regjistruara në kaseta pa asnjë shënim të veçantë. Për habinë time, të dhënat nga kasetat e 40 vjetëve më parë lexoheshin mirë dhe arrita të ngarkoja pothuajse të gjitha lojërat dhe programet prej tyre.

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Megjithatë, në disa kaseta zbulova regjistrime që ishin qartë jo nga kompjuteri ZX Spectrum. Ato dëgjoheshin krejt ndryshe dhe, përkundër regjistrimeve nga kompjuteri i përmendur, nuk fillonin me një ngarkues të shkurtër BASIC, i cili zakonisht është i pranishëm në regjistrimet e të gjitha programeve dhe lojërave.

Për një kohë të gjatë, kjo gjë më shqetësonte — shumë doja të dija se çfarë fshihej në to. Sikur të mund të lexoja sinjalin audio si një sekuencë bytesh, do të mundja të kërkoja në to simbole ose diçka që tregon origjinën e sinjalit. Njëfarë retro-arkeologjie.

Sot, kur kam kaluar të gjithë këtë rrugë dhe shikoj etiketat e kasetave, qesh, sepse

përgjigjja ka qenë përpara syve të mi gjatë gjithë kësaj kohe.
Në etiketën e kasetës së majtë — titulli i kompjuterit TRS-80, dhe pak më poshtë emri i prodhuesit: «Manufactured by Radio Shack in USA»

(Nëse dëshironi të ruani intriguese deri në fund, mos e lexoni nën spojler)

Krahasimi i sinjaleve audio

Së pari, do ta digitalizojmë regjistrimin audio. Mund ta dëgjoni se si dëgjohet:


Dhe si regjistrohet si zakonisht nga kompjuteri ZX Spectrum:


Në të dyja rastet në fillim të regjistrimit është i pranishëm ashtuquajtur ton pilot — një tingull i një frekuence (në regjistrimin e parë është shumë i shkurtër <1 sek, megjithatë dukshëm i dallueshëm). Tonit pilot i shërben si një sinjal kompjuterit që duhet të përgatitet për të marrë të dhëna. Në përgjithësi, çdo kompjuter njeh vetëm 'tonin' e tij pilot sipas formës së sinjalit dhe frekuencës së tij.

Duhet të flas për vetë formën e sinjalit. Për shembull, në ZX Spectrum forma e tij është katrore:

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Kur zbulohet ton pilot, ZX Spectrum tregon grepa me ngjyrë të kuqe dhe blu në pjesën kufitare të ekranit, duke treguar se sinjali është njohur. Tonit pilot i jepet fund me njëimpuls sinkron

, i cili e njofton kompjuterin që duhet të fillojë të pranojë të dhëna. Ai karakterizohet nga një kohëzgjatje më e shkurtër (në krahasim me tonin pilot dhe të dhënat që pasojnë) (shiko figurën)

  1. ton pilot
  2. Pas marrjes së impulsit sinkron, kompjuteri regjistron çdo rritje/rënie të sinjalit, duke matur kohëzgjatjen e saj. Nëse kohëzgjatja është më e vogël se një kufi të caktuar, një bit 1 regjistrohet në memorie, përndryshe 0. Bitët grumbullohen në byte dhe procesi përsëritet deri sa të merren N byte. Numri N, zakonisht, merren nga header-i i skedarit që ngarkohet. Rrjedha e ngarkimit është si më poshtë:
  3. ton pilot
  4. header (me gjatësi fikse), përmban madhësinë e të dhënave të ngarkuara (N), emrin dhe tipin e skedarit

të dhënat vetë Për të siguruar që të dhënat janë ngarkuar saktë, ZX Spectrum lexon byte-n e quajtur byte pariteti

(parity byte), i cili llogaritet gjatë ruajtjes së skedarit me operacionin XOR mbi të gjitha byte-t e të dhënave të regjistruara. Gjatë leximit të skedarit, kompjuteri llogarit byte-n e paritetit nga të dhënat e marra dhe, nëse rezultati ndryshon nga ai i ruajtur, shfaq mesazhin e gabimit 'R Tape loading error'. Rrugës strikte, kompjuteri mund të japë këtë mesazh më herët, nëse gjatë leximit nuk mund të njohë impulsin (ka humbur ose koha e saj nuk përputhet me kufijtë e caktuar)

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Pra, le të shohim tani se si duket sinjali i panjohur:

Ky është ton pilot. Forma e sinjalit është ndjeshëm e ndryshme, por duket se sinjali përbëhet nga impuls të shkurtra të përsëritura me një frekuencë të caktuar. Me një frekuencë të mostrimit 44100 Hz, distanca midis 'majave' është rreth 48 mostra (që korrespondon me një frekuencë ~918 Hz). Le të e mbajmë mend këtë numër.

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Le të shohim tani një fragment me të dhënat:

Nëse matim distancën midis impulsit të veçantë, do të rezultojë se midis impulsve 'të gjatë' distanca është përsëri rreth 48 mostra, ndërsa midis 'të shkurtrave' është ~24. Duke shkuar pak përpara, do të them se në fund u zbulua, impulset 'mbështetëse' me frekuencë 918 Hz ndjekin vazhdimisht, nga fillimi deri në fund të skedarit. Mund të supozojmë se gjatë transferimit të të dhënave, nëse midis impulsave mbështetës shfaqet një impuls shtesë, ne e konsiderojmë atë si bit 1, ndryshe 0.

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Çfarë ndodhi me impulsin sinkron? Le të shohim fillimin e të dhënave:

Ton pilot i jep fund dhe menjëherë fillojnë të dhënat. Pak më vonë, duke analizuar disa regjistrime të ndryshme audio, arrita të zbuloj se byte-i i parë i të dhënave gjithmonë është i njëjtë (10100101b, A5h). Ndoshta, kompjuteri fillon të lexojë të dhënat, pasi të marrë atë.

Tani do të përpiqemi të përshkruajmë algoritmin që do të përpunojë skedarin audio dhe do të ngarkojë të dhënat.

Ngarkimi i të dhënave

Fillimisht do të shqyrtojmë disa supozime, për të mos e komplikuar algoritmin:

  1. Do të shqyrtojmë skedarët vetëm në format WAV;
  2. Skedari audio duhet të fillojë me një ton pilot dhe nuk duhet të ketë heshtje në fillim.
  3. Skedari origjinal duhet të ketë një frekuencë marrëdhënjeje prej 44100 Hz. Në këtë rast, distanca midis impulseve referuese në 48 mostra është tashmë e përcaktuar dhe nuk është e nevojshme ta llogarisim programarisht;
  4. Formati i mostrave mund të jetë çfarëdo (8/16 bit/floating point) — pasi gjatë leximit mund ta konvertojmë në formatin e nevojshëm;
  5. Supozojmë se skedari origjinal është normalizuar në amplitudë, gjë që duhet të stabilizojë rezultatin;

Algoritmi i leximit do të jetë si në vijim:

  1. Lexojmë skedarin në memorie, duke konvertuar në të njëjtën kohë formatin e mostrave në 8 bit;
  2. Përcaktojmë pozitat e impulsit të parë në të dhënat audio. Për këtë, duhet të llogaritim numrin e mostrës me amplitudën maksimale. Për thjeshtësi, le ta llogarisim një herë manualisht. Ta ruajmë në variablën prev_pos;
  3. Shtojmë 48 në pozitat e impulsit të fundit (pos := prev_pos + 48)
  4. Për shkak se rritja e pozites me 48 nuk garanton që do të arrijmë në pozitat e impulsit të ardhshëm referues (defektet e kasetave, funksionimi i paqëndrueshëm i mekanizmit të transportit dhe të tjera), duhet të korrigjojmë pozitat. Për këtë, do të marrim një segment të vogël të të dhënave (pos-8;pos+8) dhe do të gjejmë maksimumin e amplitudës. Pozitën që korrespondon me maksimumin, ta ruajmë në pos. Këtu 8 = 48/6 — është një konstantë e fituar eksperimentalisht, e cila garanton se ne do të përcaktojmë maksimumin e saktë dhe nuk do të prekim impulse të tjera që mund të jenë afër. Në raste shumë të këqija, ku distanca midis impulseve është shumë më e vogël ose më e madhe se 48, mund të realizohet një kërkim i detyrueshëm për impulsin, por në kuadër të këtij artikulli nuk do ta përshkruaj këtë në algoritëm;
  5. Në hapin e mëparshëm, gjithashtu duhet të kontrollojmë që impulsi referues të jetë gjetur. Domethënë, nëse thjesht kërkosh maksimumin, kjo nuk garanton që impulsi është i pranishëm në këtë segment. Në implementimin tim të fundit të programit të leximit, kontrolloj diferencën midis vlerave maksimale dhe minimale të amplitudës në segment, dhe nëse ajo kalon një kufi të caktuar, e pranoj praninë e impulsit. Problemi është gjithashtu se çfarë të bëjmë nëse impulsi referues nuk është gjetur. Ka dy mundësi: ose të dhënat kanë mbaruar dhe pasojnë heshtje, ose duhet ta konsiderojmë si një gabim në lexim. Megjithatë, do ta anashkalojmë këtë për të thjeshtuar algoritmin;
  6. Në hapin e ardhshëm duhet të përcaktojmë praninë e impulsit të të dhënave (bit 0 ose 1), për këtë do të marrim mesin e segmentit (prev_pos;pos) middle_pos e cila është e barabartë me middle_pos := (prev_pos+pos)/2 dhe në një rrethinë të middle_pos në segmentin (middle_pos-8;middle_pos+8) do të llogarisim maksimumin dhe minimumin e amplitudës. Nëse diferenca midis tyre është më e madhe se 10, regjistrojmë bitin 1, përndryshe 0. 10 — është një konstantë e fituar përmes eksperimentit;
  7. Ruajmë pozitat aktuale në prev_pos (prev_pos := pos)
  8. Përsërisim duke filluar nga hapi 3, derisa të lexojmë të gjithë skedarin;
  9. Mundësinë e marrë përbit duhet ta ruajmë si një set bajtësh. Duke qenë se ne nuk e morëm parasysh sinkro-bajt gjatë leximit, numri i bitëve mund të mos jetë i shumëfishueshëm me 8, dhe gjithashtu nuk dihet largësia e nevojshme në bitë. Në implementimin e parë të algoritmit nuk dija për ekzistencën e sinkro-bajt dhe prandaj thjesht ruaja 8 skedarë me pjesshëm të ndryshme në numrin e bitëve të shkëputur. Një prej tyre përmbante të dhëna të sakta. Në algoritmin përfundimtar, thjesht fshij të gjithë bitët deri në A5h, gjë që lejon që të marrim menjëherë një skedar të saktë në dollo;

Algoritmi në Ruby, për ata që janë të interesuar
Si gjuhë për të shkruar programin zgjodha Ruby, pasi kaloj pjesën më të madhe të kohës duke programuar me të. Zgjidhja nuk është me performancë të lartë, megjithatë nuk është qëllim të bëhet shpejtësia e leximit sa më e shpejtë.

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

Rezultati

Pas provimit të disa varianteve të algoritmit dhe konstantave, pata fatin të marr diçka të jashtëzakonshme interesante:

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Pra, gjykuar nga vargjet simbolike, kemi një program për ndërtimin e grafikëve. Megjithatë, në tekstin e programit mungojnë fjalët kyçe. Të gjitha fjalët kyçe janë koduar në formë bajtesh (vlera e çdo njërit > 80h). Tani duhet të zbulojmë se cili kompjuter nga vitet '80 mund të ruante programe në këtë format.

Në të vërtetë, kjo është shumë e ngjashme me një program në gjuhën BASIC. Në një format të ngjashëm, kompjuteri ZX Spectrum ruan në memorje dhe ruan programet në kasetë. Për çdo rast, kontrollova fjalët kyçe për përputhshmërinë me tabelën. Megjithatë, rezultati doli qartë negativ.

Gjithashtu, kontrollova fjalët kyçe të BASIC nga kompjuterët popullorë të asaj kohe, si Atari, Commodore 64 dhe disa të tjerë për të cilët kam gjetur dokumentacion, por me dështim — njohuritë e mia për llojet e kompjuterëve retro nuk ishin aq të gjera.

Atëherë vendosa të ndiqja një rrugë tjetër, listës, dhe aty syri im u ndal në emrin e prodhuesit Radio Shack dhe kompjuterit TRS-80. Pikërisht këto emra ishin shkruar në etiketat e kasetave që ishin në tavolinën time! Nuk e dija më parë këto emra dhe nuk isha i njohur me kompjuterin TRS-80, prandaj më dukej se Radio Shack është prodhues kasetash audiovizive, si BASF, Sony apo TDK, dhe TRS-80 — kohëzgjatja e luajtjes. Pse jo?

Kompjuteri Tandy/Radio Shack TRS-80

Është shumë e mundshme që regjistrimi audio në të cilin u referova si shembull në fillim të artikullit, ishte bërë në një kompjuter të tillë:

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Doli se ky kompjuter dhe llojet e tij (Model I/Model III/Model IV etj.) ishin shumë të njohur në kohën e tyre (sigurisht, jo në Rusi). E veçanta është se procesori i përdorur në to ishte gjithashtu Z80. Informacione mbi këtë kompjuter mund të gjesh një shumë informacion. Në vitet '80, informatat për kompjuterët shpërndaheshin në revista. Aktualisht ekzistojnë disa emulatorë kompjuteri për platforma të ndryshme.

Shkarkova emulatorin trs80gp dhe për herë të parë arrita të shoh se si punonte ky kompjuter. Sigurisht, kompjuteri nuk mbështeste daljen me ngjyrë, rezolucioni i ekranit ishte vetëm 128x48 pikselë, por kishte shumë zgjerime dhe modifikime që mund të rrisnin rezolucionin e ekranit. Po ashtu, kishte shumë variante sistemesh operativ për këtë kompjuter dhe realizime të ndryshme të gjuhës BASIC (e cila, ndryshe nga ZX Spectrum, në disa modele as nuk ishte “e shkruar” në ROM dhe çdo variant mund të ngarkohej nga disku, si edhe vetë OS)

Gjithashtu, gjeta utility për konvertimin e regjistrimeve audio në formatin CAS, që mbështeten nga emulatorët, por për një arsye që nuk e dinte, nuk mund të lexoja regjistrimet nga kasetat e mia.

Pas zgjidhjes së formatit të skedarit CAS (i cili u doli se ishte thjesht një kopje bit për bit e të dhënave nga kasetat që kam pasur në duar, përveç kryetitullit me sinjalin e sinkronizimit), bëra disa ndryshime në programin tim dhe arrita të prodhoj një file CAS funksional, i cili punoi në emulatorin (TRS-80 Model III):

Si unë rikuperova të dhënat në një format të panjohur nga kaseta magnetike

Versioni i fundit i utilitarit për konvertim me përcaktimin automatik të impulsit të parë dhe distancës midis impulsave referues e kam paraqitur si një paketë GEM, kodin burimor e gjen në Github.

Përfundimi

Rruga e kaluar u dha një udhëtim fascinues në të kaluarën dhe jam i lumtur që në të fund e gjeta zgjidhjen. Përveç kësaj, unë:

  • Kuptova formatin e ruajtjes së të dhënave në ZX Spectrum dhe studiuam nënprogramet e integruara në ROM për ruajtjen/leximin e të dhënave nga kasetat audio
  • Njoha kompjuterin TRS-80 dhe llojet e tij, studiuam sistemin operativ, pashë shembuj programesh dhe madje pata mundësinë të merrem me debugimin në kodin makinerik (edhe sepse të gjitha mnemoniket Z80 më janë shumë të njohura)
  • Shkruajta një utilitar të kompletuar për konvertimin e regjistrimeve audio në formatin CAS, i cili mund të lexojë të dhëna që “utilitari zyrtar” nuk i njeh

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster