Tempo di lettura: 5 minI risultati di un sabato pomeriggio passato a studiare la struttura del file binario .net del software di simulazione di propagazione delle onde elettromagnetiche Radio Mobile in cui sono racchiuse tutti i punti e le caratteristiche elettromagnetiche di ogni apparato che poi vengono usate da questo software per generare le simulazioni di copertura.

Per questo studio ho usato un file binario di esempio guardandolo con un xxd e di un file .bas di Visual Basic che si trova sul forum di supporto del programma.
Purtroppo il file .bas fa riferimento a file .net nella versione 2000 e 3000 mentre i file generati dalle ultime versioni di Radio Mobile mostrano come numero di versione 4000.
Scrivo qui le mie conclusioni e le parti ancora ignote che spero prima o poi di colmare.
Il file inizia con una struttura di intestazione formata da 10 byte
[code lang="perl"]# HEADER STRUCTURE - Len 10 bytes
# VERSION ([f] single-precision float - VB Single type - 4 bytes),
# NETWORK ELEMENTS ([s] signed short - VB Integer type - 2 bytes),
# UNIT ELEMENTS ([s] signed short - VB Integer type - 2 bytes),
# SYSTEM ELEMENTS ([s] signed short - VB Integer type - 2 bytes),[/code]
dove, tra parentesi, ho trascritto la struttura come vista dalla funziona pack/unpack di Perl e, di seguito, come vista dal Visual Basic.
Usando un po' di codice e, in particolare queste righe
[code lang="perl"]my $HeaderLen = 10;
my $HeaderPack = 'fsss';
my @HeaderItems = qw/version networkCount unitCount systemCount/;
struct( HeaderType => { map {$_ => '$'} @HeaderItems } );
# read and unpack the header
my @struct = unpack($HeaderPack,$f->get_bytes($HeaderLen));
my $header = new HeaderType(map {$HeaderItems[$_] => $struct[$_]} (0..@HeaderItems-1));
print Data::Dumper::Dumper($header);[/code]
dove $f è un oggetto della classe File::Binary che punta a un file .net aperto in lettura, si ottiene la seguente struttura autoesplicativa
[code lang="perl"]$VAR1 = bless( {
'HeaderType::systemCount' => 25,
'HeaderType::version' => '4000',
'HeaderType::unitCount' => 200,
'HeaderType::networkCount' => 50
}, 'HeaderType' );[/code]
che indica appunto il numero di versione e lo spazio assegnato e utilizabile per i network, i punti (unit) e i tipi di sistemi (system) gestibili da questo file .net.
Proseguendo nella lettura del file binario, subito dopo, si trova, ripetuta tante volte per quante sono il numero di punti nel file, la seguente struttura lunga 44 bytes
[code lang="perl"]# UNIT STRUCTURE - Len 44 bytes</pre>
# LON ([f] single-precision float - VB Single type - 4 bytes),
# LAT ([f] single-precision float - VB Single type - 4 bytes),
# H ([f] single-precision float - VB Single type - 4 bytes),
# ENABLED ([s] signed short - VB Integer type - 2 bytes),
# TRANSPARENT ([s] signed short - VB Integer type - 2 bytes),
# FORECOLOR ([l] signed long - VB Integer type - 4 bytes),
# BACKCOLOR ([l] signed long - VB Integer type - 4 bytes),
# NAME ([A] ASCII string - VB String*20 - 20 bytes),[/code]
quindi segue la definizione dei system, con la seguente struttura, lunga 50 byte
[code lang="perl"]# SYSTEM STRUCTURE - Len 50 bytes
# TX ([f] single-precision float - VB Single type - 4 bytes),
# RX ([f] single-precision float - VB Single type - 4 bytes),
# LOSS ([f] single-precision float - VB Single type - 4 bytes),
# ANT ([f] single-precision float - VB Single type - 4 bytes),
# H ([f] single-precision float - VB Single type - 4 bytes),
# NAME ([A] ASCII string - VB String*30 - 30 bytes),[/code]
ripetuta tante volte per il numero di system definiti nell'header.
Dopo di questo il file .bas riportava di due strutture di byte di forma matriciale bidimensionale di dimensione pari al prodotto tra il numero di unità e quello di reti. Io ne ho trovate invece, nella versione 4000, tre. Il file VB identifica la prima come net_role e la seconda come net_system. Probabilmente identificano in che network è situata ogni unit e con che sistema radio. Ovviamente è da investigare.
Subito dopo arriva la definizione dei network con una struttura di 72 byte
[code lang="perl"]# NET STRUCTURE - Len 72 bytes
# MINFX ([f] single-precision float - VB Single type - 4 bytes),
# MAXFX ([f] single-precision float - VB Single type - 4 bytes),
# POL ([s] signed short - VB Integer type - 2 bytes),
# EPS ([f] single-precision float - VB Single type - 4 bytes),
# SGM ([f] single-precision float - VB Single type - 4 bytes),
# ENS ([f] single-precision float - VB Single type - 4 bytes),
# CLIMATE ([s] signed short - VB Integer type - 2 bytes),
# MDVAR ([s] signed short - VB Integer type - 2 bytes),
# TIME ([f] single-precision float - VB Single type - 4 bytes),
# LOCATION ([f] single-precision float - VB Single type - 4 bytes),
# SITUATION ([f] single-precision float - VB Single type - 4 bytes),
# HOPS ([s] signed short - VB Integer type - 2 bytes),
# TOPOLOGY ([s] signed short - VB Integer type - 2 bytes),
# NAME ([A] ASCII string - VB String*30 - 30 bytes),[/code]
ripetuta tante volte per quanti network sono definiti nell'header.
Segue quindi una struttura denominata Coverage che, sostanzialmente contiene i parametri della finestra "Single Polar Radio Coverage" di Radio Mobile.

La struttura, lunga 74 byte è cosi fatta
[code lang="perl"]# COVERAGE STRUCTURE - Len 74 bytes
# DMAX ([f] single-precision float - VB Single type - 4 bytes),
# THMIN ([f] single-precision float - VB Single type - 4 bytes),
# THMAX ([f] single-precision float - VB Single type - 4 bytes),
# THINC ([f] single-precision float - VB Single type - 4 bytes),
# ANTAZT ([f] single-precision float - VB Single type - 4 bytes),
# FILE ([A] ASCII string - VB String*20 - 20 bytes),
# TRESHOLD ([s] signed short - VB Integer type - 2 bytes),
# LEVEL ([f] single-precision float - VB Single type - 4 bytes),
# AREA ([S] unsigned short - VB Boolean - 2 bytes, non credo bool)
# CAREA ([l] signed long - VB Integer type - 4 bytes),
# CONTOUR ([S] unsigned short - VB Boolean - 2 bytes)
# CCONTOUR ([l] signed long - VB Integer type - 4 bytes),
# VHS ([f] single-precision float - VB Single type - 4 bytes),
# VHT ([f] single-precision float - VB Single type - 4 bytes),
# DMIN ([f] single-precision float - VB Single type - 4 bytes),
# VCOL ([l] signed long - VB Integer type - 4 bytes),[/code]
e probabilmente è qui, per la gestione delle copertura in batch. Segue infine un signed-short di 2 byte con la lunghezza di una stringa che contiene il percorso al file .map che segue subito dopo e, successivamente a questa stringa, si entra in un loop finchè non finisce il file o i successivi 2 byte non sono zero, in cui si legge un signed-short di 2 byte che contiene la lunghezza della successiva stringa che contiene il percorso ad ogni singola picture da aprire all'apertura del file .net.
Indi, almeno per il file VB, c'è il caricamento di una matrice di single di grandezza pari al prodotto tra il numero di unità e quello di reti, quindi un elenco che identifica le icone di ogni unit, di tipo byte e di lunghezza uguale al numero di unit e, per finire, delle lineLossPerMeter di tipo single di lunghezza pari al numero di system.
Il problema con quest'ultima parte è che, a parte non aver identificato bene le strutture, e questo è da fare, ma il fatto è che, mentre per il file VB, il file .net dovrebbe essere finito, nella realtà c'è dell'altra roba, tra cui un percorso ad fantomatico file landheight.dat che sembra pure roba obsoleda.
C'è ancora da studiare ma ovviamente, se qualcuno ne sa più di me, si faccia avanti.