Выходные закончились, не знаю как пойдёт дальше, но за последнюю неделю успел многое.
==
a) Проработал архитектуру стека декодеров.
Декодеров 4 слоя:
1) Преобразование битового потока в массив интервалов. Сейчас сидит в драйвере spidev.c.
Для тестов также написал простой конвертор из sql3-файлов fluxengine в формат близкий к тому, что даёт spidev.c.
(есть несколько давно снятых образов; чтобы при отладке иметь хорошую повторяемость ошибок важно чтобы и данные были каждый раз одинаковыми). Слоистая структура цепочки декодирования позволяет легко менять слои.
2) Преобразование массива интервалов в массив исходных нулей и единичек MFM/GCR-кодера.
Т.е., например, для GCR: интервалы 2.0-6.0 mks - это "1", 6.0-10.0 mks - "10" и т.д.
Этот слой - самая замысловатая часть. Именно тут происходит компенсация ошибок скоростей дисководов, учёт скоростей (300/360 rpm), фазовые ошибки и всё такое.
У меня есть код для флюксы, написанный год-два назад на pascal, заточенный под offline-обработку образов флюксы на PC, широко использует плавающую точку. Этот код - не слоистый, архитектурно там "всё в одном". Я переписывал его под arm, под слои, стараясь перевести все на целочисленную арифметику.
После первоначальной отладки ещё пару дней ковырялся с финальными косяками.
Во первых, долго не мог понять одно место в собственном старом коде. Вроде было похоже на ошибку, даже на специально синтезированном массиве данных явно старый код работал хуже нового. Но когда дело дошло до тестов на реальных образах - всё стало наоборот: старый код работает хорошо, новый валит ошибки. В итоге, перерыв большую статью по контроллерам дисководов и ещё раз крепко подумав, понял, в чём тут тонкость.
Всё дело в том, что, в зависимости от типа ошибок (джиттер vs систематическая ошибка) алгоритмы коррекции будут противоположными. Джиттер: один импульс сдвинулся, значит, если интервал времени до него уменьшился, то после него увеличился. Если систематическая ошибка, то рост интервала до импульса показывает на то, что нужно ожидать роста интервала и после импульса.
Систематическая ошибка на дисководах довольно незначительная: 1-2%, полная ошибка (если пишущий дисковод имеет ошибку противоположную читающему) - 2-4%. Это почти незаметно и легко компенсируется тем, что, фактически, каждый новый импульс восстанавливает синхронизацию между устройством записи и устройством чтения. Джиттер же гораздо существеннее и именно его должен компенсировать умный алгоритм восстановления интервалов. Но джиттер случаен. А есть ещё фазовые искажения: один импульс "убегает" от другого на коротких интервалах или "притягивается" к другому на длинных. Это должно частично устраняться предкомпенсацией записи, но она работает с 2-3 зонами (у 840ки агата их три, у PC-совместимых - две, вроде бы), внутри зоны каждая соседняя дорожка немного отличается от предыдущей, а значит, если даже какая-то одна дорожка будет идеальной, то соседние всё равно будет иметь более или менее заметные фазовые искажения.
В погоне за объёмом хранимых данных пара дисковод+дискета проектируются так, чтобы сократить интервалы между импульсами. Но фазовая ошибка при этом становится существенной по сравнению с интервалом между импульсами. И тут как раз становится понятным, почему некоторые алгоритмы восстановления интервалов хорошо работают с 140кой, а другие - с 840кой.
140ка имеет бОльшие интервалы, чем 840ка, и большее окно захвата очередного бита. В результате, она практически нечувствительна к фазовым искажениям (они заметно меньше окна захвата), не требует предкомпенсации записи, но отказ от последних 5 дорожек (35-40) в 140ке, вероятно, как раз и вызван тем, что там уже проще было не хранить данные, чем накручивать сложности для борьбы с фазовыми искажениями. Соответственно, она требует только борьбы с джиттером. Всё в угоду снижению ценника. Итог: 4.00 кб / дорожку.
840ка наоборот: тут пытались вытащить всё, что можно получить от механники и физики, пусть ценой усложнения схем. Интервалы сокращены, окна захвата в два (!!!) раза меньше, фазовые ошибки становятся заметны и их коррекциях играет большую роль как при записи (предкомпенсация записи), так и при чтении. Итог: 5.25 кб / дорожку.
Для меня всё равно ещё не всё прозрачно в этой теме, я уже пару раз брался за съёмки сцены для ролика, где пытался объяснить все эти сложности, каждый раз заходя в логические тупики.
Но сейчас я хотя бы понял, почему вообще к 140ке и 840ке лучше подходят разные алгоритмы коррекции ошибок.
Вторая проблема отладки слоя 2 была в отказе (точнее, вынесении "за скобки") арифметики с плавающей точкой. Там тоже пришлось повозится, чтобы выловить все потери точностей. В pascal-версии все интервалы времени считались в mks, теперь же все характерные времена заранее пересчитываются в тики задающего генератора семплера
и дальше вся математика работает с ними.
3) Слой 3 уже проще: тут из нулей и единиц, полученных от слоя 2 собирается изначальный байтовый поток дорожки. Если в слоях 1 и 2 алгоритмы не отличаются для различных форматов записи (140/840/PC-совместимые), меняются только настройки для слоя 2, то слой 3 - это уже разные алгоритмы, в зависимости от формата записи.
840 и PC похожи, различие в синхросимволах и их интерпретации. 140ка - сама по себе.
В этом слое и кода меньше и он устроен однозначнее, чем в слое 2.
Слой 3 может на выходе синтезировать старый eim-формат, использовавшийся в Мостах 2.
Для PC-формата слой 3 тоже выдаёт что-то вроде старого eim, но, вряд ли имеет смысл сохранять его в таком виде файлах, он будет использоваться только для передачи данных слою 4.
4) Слой 4 - это уже формирование DSK-образа для любого из форматов. Алгоритмы разные для разных форматов, но тоже однозначные. Можно как синтезировать DSK, так и только получить отчёт о корректности CRC или более развёрнутый отчёт (сколько раз встречались сектора, насколько успешно их удалось прочитать, были ли расхождения (многократные успешные чтения с совпадением CRC, но при этом с разными данным - такое бывает на 140ках)).
Отчёт о корректности без создания dsk важен для читалки: мы можем снимать образ в Eim3 (массивы интервалов времени - то, что снимают и флюкса и supercardpro), но при этом уже зная, что данные прочитались успешно.
Фактически, я тут честно тырил собственный код из RawEdit - программы для Мостов2, которые интерактивно работают со старыми eim. Тырить очень приятно, потому что rawedit - штука старая и хорошо вылизанная. Формально, читать образцовую дискету не сложно, но, в случае ошибок, этим алгоритмам есть где запутаться (Например: нашли поле адреса, за ним поле данных успешно прочиталось. Всё хорошо ? Нет. Возможно, нужное поле данных имеет повреждённый пролог, также повреждено и следующее поле адреса. А мы успешно прочитали поле данных другого сектора. Нужно было проверять расстояния между полями). Так что готовый код - это много готовых подсказок.
Для тестов сохранял из слоя 3 образы Eim, потом прогонял их через rawedit и через свеженаписанный 4 слой декодеров и сравнивал финальные dsk. Наловил несколько хитрых багов в новом коде (что-то вроде: что будет если аппартный индекс пройдёт строго между синхросимволом и прологом поля адреса или поля данных), но это всё быстро исправлялось. В самом rawedit ошибок вроде не нашлось :)
Код rawedit и "слоя4" не совпадают 1:1. RawEdit немного более сложен. Например, он очень подробно учитывает сигнал индекса (рисует его прямо над дампом дорожки), который для "слоя4" пока вообще не нужен. О некоторых особенностях дорожки "слой4" сообщает в лог и не хранит затем эту информацию, в то время как rawedit собирает подобную информацию и может, например, быстро гонять курсор между разными полями, экземплярами дорожки и т.д.
Слой 4 пока не закончен, есть только 840ка. Нужно ещё 140ку и PC-формат.
==
b) Собрал на плате RTC; всё таки убедился, что на плате неправильно подключено посадочное место для holder'а батарейки (+/- перепутал). Вроде часики отзываются, надо будет батарейку воткнуть и погонять.
Собрал индикаторор читаемого трека: двухцветные светодиоды. Чтобы ровно запаять их все, привлёк супругу: у неё глазомер отличный и руки не дрожжат. Она смотрит сверху на диод и позиционирует его, я смотрю сбоку. Ей хорошо видно положение диода, а мне хорошо видна точка пайки. Так вдвоём всю линейку собрали довольно быстро.
Написал прикольную короткую демку и поддержку линейки в ui-server'е.
/*
Когда только начинал осваивать электронику, мечтал о переключателе гирлян для ёлки.
Первая конструкция в школьном радиокружке - простые бегущие (перемигивающиеся) огни.
Я мало понимал, как это работает, но увидел весь стек разработки: от простой схемы мультивибратора на бумаге
до законченного устройства. Две печатные платы: диодный мост+кондёр и сам мультивибратор с релюшкой на выходе. До сих пор люблю всякие бегающие огоньки :))
*/
Собрал интерфейс 140: семплер и силовые части. Но пока не проверял.
==
Дальше надо закончить слой 4 для 140ки и PC.
Проверить интерфейс 140ки и дописать управление этим приводом, включая всякие хитрые алгоритмы управления его головой.
И самое-самое: процедуру/библиотку автоопределения формата записи.
Декодеры L4 могут сообщать о том, похожа ли дорожка на известный им формат.
Также они сообщают, сколько секторов удалось вытащить при данном запросе и кумулятивно по всем попыткам.
Теперь нужно продумать decoders suites, как -то их описать в виде массива и вокруг этого выстроить перебор вариантов.
Комбинаций немало: (форматы 140+840+PC) x (скорости флопика 300/360) x (битрейты SD/HD для PC) x (таблицы ФАПЧ слоя2 - 5 штук) x (мелкая подстройка под скорость флопа: 0.96, 0.97, 0.98, 1.0, 1.02, 1.03, 1.04 - иногда внезапно это помогает, причём перебирать нужно не по порядку, выгоднее как-то так: 1.0, 0.97, 0.98, 0.96, 1.03, 1.02, 1.04...).
Перебирать нужно тоже не совсем по порядку: сперва грубо понять с каким исходным форматом имеем дело, потом уже подгонять детали. Исходить из того, что suitе, которая успешно прочитала предыдущую дорожку, скорее всего прочитает и следующую.
Можно накапливать долговременную статистику внутри устройства: какие suit'ы чаще всего успешно читали разные дорожки на разных приводах. И исходить из неё. Такой себе ИИ.
Не совсем ясно, что выгоднее: несколько раз перечитать дорожку или сменить suite ? Один оборот, проверка с текущей цепочкой, ещё оборот, опять пробуем текущую, затем ещё два оборота и пробуем перебор разных цепочек для всех 4 оборотов ?...
Данные не обязательно прогонять через весь стек для каждого формата: сперва слой 1 - универсальный для всех форматов. Потом слой 2 - отдельные настройки для 140кб, отдельные настройки для 840кб и PC/SD (т.е. можно для этих двух форматов не повторять вызов слоя 2, но это не всегда прокатит: небольшая поправка на то, что у 840ки битрейт немного сдвинут из-за вариантов кварцев, иногда заметно улучшает результат чтения), отдельные настройки для PC/HD. Но отдельные обращения к слою 2 в зависимости от скорости вращения флопика.
Кроме того у PC-совместимых может быть разное количество секторов даже в рамках одной плотности записи.
И разный размер сектора.
Также список должен будет как-то учитывать возможные расширения поддерживаемых форматов.
Поскольку, софт пишется как набор отдельных библиотек-слоёв, то где-то как-то это должно позволять заменой самого нижнего слоя (т.е. источника массива интервалов) использовать весь стек не только для декодирования данных с Моста 3, но и для офлайновой работы, в т.ч. с другими устройствами захвата.