Некоторые компании, занимающиеся геофизикой, передают данные заказчику в формате PLN. Для того чтобы их посмотреть необходимо использовать программу О!Пиум. Бывают такие случаи, пока по неустановленным причинам, экспорт данных в LAS формат не производится. Обращение к "хозяевам" этого софта, для уточнения структуры файла, ни к чему не привело. По этой причине принято решение изучить структуру файла, с целью возможности открытия этого типа в других программных продуктах.
Статья будет пополняться по мере накопления материала, и будет особенно полезна начинающим программистам и людям интересующихся обработкой и интерпретацией данных. Любые комментарии категорически приветствуются.
Вступление
Для исследования структуры необходимо иметь возможность посмотреть исходный код файла. В этих случаях обычно пользуются различными hex (heximal) просмотровщиками. Я буду использовать режим просмотра файлового менеджера DN (Dos Navigator). Карандаш и блокнот чтобы записывать результаты. Для наглядной визуализации буду использовать табличный процессор ну и редактор растровой и векторной графики. Демонстрационный PLN файл идёт в комплекте в просмотровщиком LasView.
Этап 1
Основная задача найти и прочитать данные "кривулек", для дальнейшего экспорта.
Кривульки - здесь и далее по тексту, график отображения какого-либо геофизического параметра от глубины (иногда от времени).
Просмотр структуры привел к сегменту начинающегося с сигнатуры tGeoDataExHeader. За которым идёт значение указывающее на количество "кривулек" в файле, записанное в 16-ричном коде.
В данном случае байт 05 говорит о том что в файле находятся данные о пяти кривульках.
Следующая сигнатура до последовательности GeoDataMatrix во многих файлах одинакова 00 FF FF 01 00 0D 00. Любое изменение этой последовательности приводит к краху открывающегося приложения с ошибкой.
Есть предположение что по этой сигнатуре программа определяет тип файла. Вероятно работает и с другими типами. Но это уже лирика.
Этап 2
Окончание сегмента с данными символизирует сигнатура GeoDataHeader с двумя байтами в хвосте 13 00. Т.е. сегмент с данными заканчивается сигнатурой 47 65 6F 44 61 74 61 48 65 61 64 65 72 13 00 (hex).
Сами по себе данные кривулек располагаются секторами. Данные записаны парами: индексное значение - значение параметра. Например: глубина - значение, записанное в формате Single.
Например пара значений типа Single 00 00 70 41 и D0 44 D8 BB говорит о том что на глубине 00 00 70 41 (hex) т.е. 15.000 (dec) параметр имеет значение D0 44 D8 BB (hex) т.е. -0.0066 (dec). И так далее ...
Таким образом общая структура выглядит следующим образом:
Общий заголовок выглядит следующим образом (указан и код и значение):
Где: начало сегмента tGeoExHeader, далее байт количества кривулек, 7 байт 00 FF FF 01 00 0D 00 пока не известного назначения, начало данных GeoDataMatrix и байт границы заголовка 02 (иногда бывает 03, следствия исследуются).
За заголовком следуют сегменты с данными содержащие шапку, в общем случае вхождение первого сегмента данных это выглядит так:
Где: Между байтом границы заголовка 02 и маркером 10 B8 находятся (ближе к маркеру 10 B8) десять байт 01 00 00 00 00 00 00 00 00 00, в исследованных данных эти байты не изменялись, дальнейшая судьба этих байт стоит в очереди на анализ. В первых версиях анализа предполагался маркер 10 B8 01, однако исследования показали что есть файлы с маркером 10 B8 00, таким образом маркер уменьшился с трёх до двух байт 10 B8, однако для того чтобы избежать коллизии предлагается последовательность в три байта 00 10 B8 или даже 00 00 10 B8. Есть предположение что эти 10 байт и маркер составляют одно целое. Левее (раньше) расположена область названия кривульки типа String с размером поля, в данном случае длина поля = 2, а название кривульки (мнемоника) GZ.
Справа от маркера располагается поле где указывается дата и время (вероятно получения/записи данных), в этом примере оно пустое и содержит наборы нулей заканчивающихся байтом 02. За байтом 02 следуют два байта указывающие длину поля даты (описание будет ниже), в данном примере значения равны 00 00 т.е. отсутствуют. Следующие четыре байта указывают на количество пар записей данных в сегменте типа Integer, в данном примере 4 байта равны 27 55 00 00 что означает 21799 пар записей. Следующая информация идёт парами индекс - значение, в данном случае глубина - значение параметра GZ типа Single, и так 21799 раз до конца этого сегмента данных (в этом примере). Двойное слово нулевого значения всегда равно 00 00 80 80.
Следует отметить что оригинальный экспорт PLN в LAS указанной программой О!Пиум допускает некоторую неточность, а именно, в сегменте данных ~A (ASCII Data), значение NULL равно -999.0000, а в сегменте ~W (Well Information) параметр NULL описан как -999.000. А это написание не одно и тоже.
Несколько примеров первого вхождения сегмента данных приведена ниже:
Этап 3 (дата\время)
В некоторых кривульках присутствуют данные, вероятно, указывающие дату и время получения\записи.
После маркера 00 00 10 B8 идут 4 байта, 01 00 00 00 (бывают варианты и 00 00 00 00) на сейчас пока нет сведений о их значении. Следующие 4 байта вероятно сквозная нумерация кривой в проекте. Точной информации нет, однако в теле одного файла при наличии поля дата\время значения идут на увеличение (инкремент). В верхнем варианте (изображение см. выше) 4 байта 19 00 00 00 говорят что номер кривой №25. В последующей кривой этого же файла номер уже №26. Следующая пара тетрад описывает время 4D 47 EC BB (10:18:02) и дату 2D D4 E4 40 (14.10.2016). Пара байт, в этом примере это 00 00, заканчивает двоичное описание даты\времени. Значение этой пары уточняются.
За двоичным кодом времени\даты идёт текстовое описание комментария, которое начинается байтом 02 (темно зеленый цвет на изображении). Если за этим байтом идут пара нулей что текстовое описание отсутствует. В приведенном примере пара байт 00 0A указывает что длина текстового поля 10 знаков. За которыми идёт описание даты в текстовом виде в данном примере. За которым идёт тетрада количества записей в кривой (голубой цвет). За которой идут пары тетрад: индекс (глубина) - значение до конца сегмента кривульки (описано выше).