چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

ماقبل تاریخ

از آنجایی که عاشق سخت افزار یکپارچهسازی با سیستمعامل بودم، یک بار یک ZX Spectrum+ از فروشنده ای در بریتانیا خریدم. همراه با خود رایانه، چندین نوار کاست صوتی با بازی (در بسته بندی اصلی با دستورالعمل) و همچنین برنامه های ضبط شده روی نوارهای بدون علامت خاص دریافت کردم. در کمال تعجب، داده های کاست های 40 ساله به خوبی قابل خواندن بود و من توانستم تقریباً همه بازی ها و برنامه ها را از آنها دانلود کنم.

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

با این حال، در برخی کاست‌ها، ضبط‌هایی پیدا کردم که به وضوح توسط کامپیوتر ZX Spectrum ساخته نشده بودند. صدای آنها کاملاً متفاوت بود و برخلاف ضبط های رایانه ذکر شده، با یک بوت لودر BASIC کوتاه که معمولاً در ضبط همه برنامه ها و بازی ها وجود دارد، شروع نمی کردند.

برای مدتی این من را آزار می داد - من واقعاً می خواستم بفهمم چه چیزی در آنها پنهان شده است. اگر می‌توانید سیگنال صوتی را به صورت دنباله‌ای از بایت بخوانید، می‌توانید به دنبال کاراکترها یا هر چیزی که منشأ سیگنال را نشان می‌دهد بگردید. نوعی رترو باستان شناسی.

حالا که تمام راه را رفته‌ام و به برچسب‌های کاست‌ها نگاه می‌کنم، لبخند می‌زنم چون

جواب در تمام مدت جلوی چشمانم بود
روی برچسب نوار کاست سمت چپ، نام کامپیوتر TRS-80 و دقیقاً زیر نام سازنده قرار دارد: "ساخت شده توسط Radio Shack در ایالات متحده آمریکا"

(اگر می خواهید فتنه را تا آخر نگه دارید زیر اسپویلر نروید)

مقایسه سیگنال های صوتی

اول از همه، بیایید صداهای ضبط شده را دیجیتالی کنیم. می توانید به صدای آن گوش دهید:


و طبق معمول صدای ضبط شده از رایانه ZX Spectrum به گوش می رسد:


در هر دو مورد، در ابتدای ضبط به اصطلاح وجود دارد لحن خلبان - صدایی با همان فرکانس (در ضبط اول بسیار کوتاه <1 ثانیه است، اما قابل تشخیص است). صدای خلبان به کامپیوتر سیگنال می دهد که برای دریافت داده ها آماده شود. به عنوان یک قاعده، هر کامپیوتر تنها صدای خلبان "خود" خود را با شکل سیگنال و فرکانس آن تشخیص می دهد.

لازم است در مورد خود شکل سیگنال چیزی بگوییم. به عنوان مثال، در ZX Spectrum شکل آن مستطیل است:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

هنگامی که یک آهنگ پایلوت تشخیص داده می شود، طیف ZX نوارهای قرمز و آبی متناوب را در حاشیه صفحه نمایش می دهد تا نشان دهد که سیگنال شناسایی شده است. لحن خلبان به پایان می رسد پالس همگام، که به رایانه سیگنال می دهد تا شروع به دریافت داده کند. با مدت زمان کوتاه تر (در مقایسه با صدای خلبان و داده های بعدی) مشخص می شود (شکل را ببینید)

پس از دریافت پالس همگام سازی، کامپیوتر هر افزایش/افت سیگنال را ثبت می کند و مدت زمان آن را اندازه گیری می کند. اگر مدت زمان کمتر از حد معینی باشد، بیت 1 در حافظه نوشته می شود، در غیر این صورت 0. بیت ها به بایت جمع آوری می شوند و این فرآیند تا زمانی که N بایت دریافت شود تکرار می شود. عدد N معمولا از سربرگ فایل دانلود شده گرفته می شود. ترتیب بارگذاری به شرح زیر است:

  1. لحن خلبان
  2. هدر (طول ثابت)، شامل اندازه داده های دانلود شده (N)، نام و نوع فایل است
  3. لحن خلبان
  4. خود داده ها

برای اطمینان از بارگذاری صحیح داده ها، ZX Spectrum به اصطلاح می خواند بایت برابری (بایت برابری)، که هنگام ذخیره یک فایل با XOR کردن تمام بایت های داده های نوشته شده محاسبه می شود. هنگام خواندن یک فایل، کامپیوتر بایت برابری را از داده های دریافتی محاسبه می کند و اگر نتیجه با ذخیره شده متفاوت باشد، پیغام خطا "R Tape loading error" را نمایش می دهد. به بیان دقیق، اگر رایانه هنگام خواندن نتواند پالس را تشخیص دهد (از دست رفته یا مدت زمان آن با محدودیت های خاصی مطابقت ندارد) می تواند زودتر این پیام را صادر کند.

بنابراین، اکنون ببینیم یک سیگنال ناشناخته چگونه به نظر می رسد:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

این لحن خلبان است. شکل سیگنال به طور قابل توجهی متفاوت است، اما واضح است که سیگنال شامل تکرار پالس های کوتاه با یک فرکانس خاص است. در فرکانس نمونه برداری 44100 هرتز، فاصله بین "پیک ها" تقریبا 48 نمونه است (که با فرکانس ~918 هرتز مطابقت دارد). بیایید این شکل را به خاطر بسپاریم.

بیایید اکنون به قطعه داده نگاه کنیم:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

اگر فاصله بین پالس های فردی را اندازه گیری کنیم، معلوم می شود که فاصله بین پالس های "بلند" هنوز 48 نمونه و بین پالس های کوتاه - 24 است. با کمی نگاه کردن به آینده، می گویم که در پایان معلوم شد که پالس های "مرجع" با فرکانس 918 هرتز به طور مداوم از ابتدا تا انتهای فایل دنبال می شوند. می توان فرض کرد که هنگام انتقال داده، اگر یک پالس اضافی بین پالس های مرجع مواجه شد، آن را بیت 1 و در غیر این صورت 0 در نظر می گیریم.

در مورد پالس همگام سازی چطور؟ بیایید به ابتدای داده ها نگاه کنیم:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

صدای خلبان به پایان می رسد و داده ها بلافاصله شروع می شوند. کمی بعد، پس از تجزیه و تحلیل چندین ضبط صوتی مختلف، ما توانستیم کشف کنیم که اولین بایت داده همیشه یکسان است (10100101b، A5h). ممکن است کامپیوتر بعد از دریافت داده ها شروع به خواندن کند.

همچنین می توانید به جابجایی اولین پالس مرجع بلافاصله پس از آخرین 1 در بایت همگام سازی توجه کنید. خیلی دیرتر در فرآیند توسعه یک برنامه تشخیص داده ها کشف شد، زمانی که داده های ابتدای فایل به طور پایدار قابل خواندن نبود.

اکنون بیایید سعی کنیم الگوریتمی را توصیف کنیم که یک فایل صوتی را پردازش کرده و داده ها را بارگذاری می کند.

در حال بارگیری داده ها

ابتدا، اجازه دهید به چند فرض برای ساده نگه داشتن الگوریتم نگاه کنیم:

  1. ما فقط فایل ها را با فرمت WAV در نظر خواهیم گرفت.
  2. فایل صوتی باید با لحن پایلوت شروع شود و در ابتدا حاوی سکوت نباشد
  3. فایل منبع باید دارای نرخ نمونه برداری 44100 هرتز باشد. در این حالت، فاصله بین پالس های مرجع 48 نمونه از قبل تعیین شده است و نیازی به محاسبه آن به صورت برنامه ای نداریم.
  4. قالب نمونه می تواند هر (8/16 بیت / نقطه شناور) باشد - زیرا هنگام خواندن می توانیم آن را به مورد دلخواه تبدیل کنیم.
  5. ما فرض می کنیم که فایل منبع با دامنه نرمال شده است، که باید نتیجه را تثبیت کند.

الگوریتم خواندن به صورت زیر خواهد بود:

  1. ما فایل را به حافظه می خوانیم، در همان زمان فرمت نمونه را به 8 بیت تبدیل می کنیم.
  2. موقعیت اولین پالس را در داده های صوتی تعیین کنید. برای این کار باید تعداد نمونه را با حداکثر دامنه محاسبه کنید. برای سادگی، یک بار آن را به صورت دستی محاسبه می کنیم. بیایید آن را در متغیر prev_pos ذخیره کنیم.
  3. 48 را به موقعیت آخرین پالس اضافه کنید (pos := prev_pos + 48)
  4. از آنجایی که افزایش موقعیت به میزان 48 تضمین نمی کند که به موقعیت پالس مرجع بعدی برسیم (نقایص نوار، عملکرد ناپایدار مکانیزم درایو نوار و غیره)، باید موقعیت پالس pos را تنظیم کنیم. برای انجام این کار، یک داده کوچک (pos-8;pos+8) بردارید و حداکثر مقدار دامنه را روی آن بیابید. موقعیت مربوط به حداکثر در pos ذخیره می شود. در اینجا 8 = 48/6 یک ثابت تجربی به دست آمده است، که تضمین می کند که ما حداکثر صحیح را تعیین می کنیم و بر سایر تکانه هایی که ممکن است نزدیک باشند تأثیر نمی گذارد. در موارد بسیار بد، زمانی که فاصله بین پالس ها بسیار کمتر یا بیشتر از 48 باشد، می توانید جستجوی اجباری برای یک پالس را اجرا کنید، اما در محدوده مقاله من این را در الگوریتم شرح نمی دهم.
  5. در مرحله قبل، بررسی اینکه آیا پالس مرجع اصلاً پیدا شده است نیز ضروری است. یعنی اگر به سادگی به دنبال حداکثر باشید، این تضمین نمی کند که تکانه در این بخش وجود داشته باشد. در آخرین اجرای برنامه خواندن، تفاوت بین مقادیر حداکثر و حداقل دامنه را در یک قطعه بررسی می کنم و اگر از حد معینی فراتر رفت، وجود یک ضربه را حساب می کنم. همچنین سوال این است که اگر پالس مرجع پیدا نشد چه باید کرد. 2 گزینه وجود دارد: یا داده ها به پایان رسیده است و سکوت به دنبال دارد، یا این باید به عنوان یک خطای خواندن در نظر گرفته شود. با این حال، برای ساده کردن الگوریتم، این مورد را حذف می کنیم.
  6. در مرحله بعد، باید وجود یک پالس داده (بیت 0 یا 1) را تعیین کنیم، برای این کار وسط قطعه (prev_pos;pos) middle_pos را برابر Middle_pos := (prev_pos+pos)/2 می گیریم و در برخی از همسایگی های middle_pos در قطعه (middle_pos-8;middle_pos +8) بیایید دامنه حداکثر و حداقل را محاسبه کنیم. اگر اختلاف بین آنها بیش از 10 باشد، بیت 1 را در نتیجه می نویسیم، در غیر این صورت 0. 10 ثابتی است که به طور تجربی به دست می آید.
  7. موقعیت فعلی را در prev_pos ذخیره کنید (prev_pos := pos)
  8. از مرحله 3 شروع کنید تا کل فایل را بخوانیم.
  9. آرایه بیت حاصل باید به صورت مجموعه ای از بایت ها ذخیره شود. از آنجایی که هنگام خواندن بایت همگام‌سازی را در نظر نگرفتیم، ممکن است تعداد بیت‌ها مضرب 8 نباشد و مقدار بیت مورد نیاز نیز ناشناخته است. در اولین اجرای الگوریتم، من از وجود بایت همگام سازی اطلاعی نداشتم و بنابراین به سادگی 8 فایل را با تعداد بیت های افست مختلف ذخیره کردم. یکی از آنها حاوی داده های صحیح بود. در الگوریتم نهایی، من به سادگی تمام بیت ها را تا A5h حذف می کنم، که به من امکان می دهد بلافاصله فایل خروجی صحیح را دریافت کنم.

الگوریتم در روبی، برای علاقه مندان
من Ruby را به عنوان زبان نوشتن برنامه انتخاب کردم، زیرا ... من بیشتر اوقات روی آن برنامه ریزی می کنم. این گزینه کارایی بالایی ندارد، اما وظیفه افزایش سرعت خواندن تا حد امکان ارزش آن را ندارد.

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

نتیجه

با امتحان چندین نوع الگوریتم و ثابت، خوش شانس بودم که چیز بسیار جالبی به دست آوردم:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

بنابراین، با قضاوت بر اساس رشته کاراکترها، ما برنامه ای برای رسم نمودارها داریم. با این حال، هیچ کلمه کلیدی در متن برنامه وجود ندارد. همه کلمات کلیدی به صورت بایت کدگذاری می شوند (هر مقدار > 80 ساعت). اکنون باید دریابیم که کدام رایانه از دهه 80 می تواند برنامه ها را در این قالب ذخیره کند.

در واقع شباهت زیادی به برنامه بیسیک دارد. کامپیوتر ZX Spectrum برنامه ها را تقریباً با همان فرمت در حافظه ذخیره می کند و برنامه ها را روی نوار ذخیره می کند. فقط در مورد، من کلمات کلیدی را بررسی کردم جدول. با این حال، نتیجه آشکارا منفی بود.

من همچنین کلمات کلیدی BASIC Atari ، Commodore 64 و چندین رایانه دیگر را در آن زمان بررسی کردم ، که توانستم اسنادی را برای آنها پیدا کنم ، اما بدون موفقیت - معلوم شد که دانش من در مورد انواع رایانه های قدیمی چندان گسترده نیست.

بعد تصمیم گرفتم برم لیستو سپس نگاهم به نام سازنده Radio Shack و کامپیوتر TRS-80 افتاد. اینها اسم هایی هستند که روی برچسب کاست هایی که روی میز من بود نوشته شده بود! من قبلاً این نام ها را نمی دانستم و با رایانه TRS-80 آشنا نبودم، بنابراین به نظرم رسید که Radio Shack یک سازنده کاست صوتی مانند BASF، Sony یا TDK است و TRS-80 زمان پخش است. چرا که نه؟

کامپیوتر Tandy/Radio Shack TRS-80

به احتمال بسیار زیاد صدای ضبط شده مورد نظر که در ابتدای مقاله به عنوان مثال آورده بودم، بر روی رایانه ای مانند این ساخته شده است:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

معلوم شد که این کامپیوتر و انواع آن (Model I/Model III/Model IV و غیره) زمانی بسیار محبوب بودند (البته نه در روسیه). قابل ذکر است که پردازنده ای که استفاده کردند نیز Z80 بود. برای این کامپیوتر می توانید در اینترنت پیدا کنید اطلاعات زیاد. در دهه 80 اطلاعات کامپیوتری در این کشور توزیع شد مجلات. در حال حاضر چندین وجود دارد شبیه سازها کامپیوتر برای پلتفرم های مختلف

شبیه ساز رو دانلود کردم trs80gp و برای اولین بار توانستم ببینم این کامپیوتر چگونه کار می کند. البته کامپیوتر از خروجی رنگ پشتیبانی نمی کرد؛ وضوح صفحه نمایش تنها 128x48 پیکسل بود، اما پسوندها و تغییرات زیادی وجود داشت که می توانست وضوح صفحه را افزایش دهد. همچنین گزینه های زیادی برای سیستم عامل های این کامپیوتر و گزینه هایی برای پیاده سازی زبان بیسیک وجود داشت (که برخلاف ZX Spectrum، در برخی مدل ها حتی در رام فلش نمی شد و هر گزینه ای را می توان از فلاپی دیسک بارگذاری کرد، درست مانند خود سیستم عامل)

من هم پیدا کردم ابزار برای تبدیل صداهای ضبط شده به فرمت CAS، که توسط شبیه سازها پشتیبانی می شود، اما به دلایلی خواندن ضبط شده از کاست های من با استفاده از آنها ممکن نبود.

با فهمیدن فرمت فایل CAS (که معلوم شد فقط یک کپی بیت به بیت از داده های نواری است که قبلاً در دست داشتم، به جز هدر با حضور یک بایت همگام سازی)، یک تغییرات کمی در برنامه من انجام شد و توانستم یک فایل CAS کارآمد را که در شبیه ساز کار می کرد (TRS-80 Model III) خروجی بگیرم:

چگونه داده ها را با فرمت ناشناخته از نوار مغناطیسی بازیابی کردم

من آخرین نسخه ابزار تبدیل را با تعیین خودکار اولین پالس و فاصله بین پالس های مرجع به عنوان یک بسته GEM طراحی کردم، کد منبع در دسترس است گیتهاب.

نتیجه

مسیری که ما طی کرده‌ایم سفری جذاب به گذشته بود و خوشحالم که در پایان به پاسخ آن رسیدم. از جمله من:

  • من فرمت ذخیره داده ها را در ZX Spectrum کشف کردم و روال های ROM داخلی را برای ذخیره/خواندن داده ها از کاست های صوتی مطالعه کردم.
  • من با کامپیوتر TRS-80 و انواع آن آشنا شدم، سیستم عامل را مطالعه کردم، به برنامه های نمونه نگاه کردم و حتی این فرصت را داشتم که در کدهای ماشین اشکال زدایی کنم (به هر حال، تمام حافظه های Z80 برای من آشنا هستند)
  • یک ابزار کامل برای تبدیل صداهای ضبط شده به فرمت CAS نوشت که می تواند داده هایی را بخواند که توسط ابزار "رسمی" شناسایی نشده اند.

منبع: www.habr.com

اضافه کردن نظر