Skip to main content

Binary Files Parser resulted from the script Logger from CRFS - Rfeye Node Equipament

Project description

RFPYE

Este módulo tem como objetivo o processamento e extração otimizada de dados dos arquivos .bin de monitoramento do espectro provenientes do script Logger executados nas estações de Monitoramento CRFS RFeye Node. Para tal utilizamos as várias funcionalidades da biblioteca fastcore, que expande e otimiza as estruturas de dados da linguagem python.

import warnings
with warnings.catch_warnings():
    warnings.simplefilter("ignore")
from rich import print

Instalação

Como parte dessa lib utiliza código c compilado com Cython, é preciso que um compilador C esteja instalado. Em Windows, uma opção é instalar a versão apropriada do Visual Studio seguindo as orientações do site da Microsoft. No entanto uma solução mais simples e a recomendada é utilizando o conda.

Primeiramente instale o miniconda. Com o conda instalado e disponível no seu PATH ou através do Anaconda Prompt execute o comando:

conda install -c intel libpython m2w64-toolchain -y

echo [build] > %CONDA_PREFIX%\Lib\distutils\distutils.cfg

echo compiler = mingw32 >> %CONDA_PREFIX%\Lib\distutils\distutils.cfg

Depois disso basta instalar normalmente a lib: python -m pip install rfpye

Em Linux normalmente o sistema já possui o compilador gcc instalado então basta executar o comando pip install acima.

Como utilizar

Abaixo mostramos as funcionalidades principais dos módulos, utilizando-os dentro de algum outro script ou REPL

Precisamos necessariamente de um diretório de entrada, contendo um ou mais arquivos .bin e um diretório de saída no qual iremos salvar os arquivos processados.

Mude os caminhos abaixo para suas pastas locais caso for executar o exemplo.

Ao utilizar o script process_bin, as pastas entrada e saída esses serão repassadas como parâmetros na linha de comando.

from fastcore.xtras import Path
VERBOSE = True
entrada = Path(r'D:\OneDrive - ANATEL\Backup_Rfeye_SP\CPV\2021')
saida = Path(r'C:\Users\rsilva\Downloads\saida')

Leitura de Arquivos

No módulo parser.py, há funções auxiliares para lidar com os arquivos .bin, pastas e para processar tais arquivos em formatos úteis. Nesse caso utilizaremos a função get_files que busca de maneira recursiva arquivos de dada extensão, inclusive links simbólicos se existirem O caráter recursivo e a busca em links, recurse e followlinks simbólicos pode ser desativados por meio dos parâmetros e opcionalmente pode ser varrido somente o conjunto de pastas indicado em folders

from rfpye.utils import get_files
arquivos = get_files(entrada, extensions=['.bin']) ; print(arquivos[:10])
[Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210204_T184230.bin'), 
Path('D:/OneDrive - 
ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210204_T184230_MaskBroken.bin'), 
Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210204_T184431.bin'), 
Path('D:/OneDrive - 
ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210204_T184431_MaskBroken.bin'), 
Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210206_T210901.bin'), 
Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210208_T233102.bin'), 
Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210211_T004502.bin'), 
Path('D:/OneDrive - 
ANATEL/Backup_Rfeye_SP/CPV/2021/rfeye002310_210211_T004502_MaskBroken.bin'), 
Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/RFeye002310_210211_T011350.bin'), 
Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CPV/2021/RFeye002310_210213_T033501.bin')]

O Objeto retornado L é uma extensão da lista python com funcionalidades adicionais, uma delas como podemos ver é que a representação da lista impressa mostra o comprimento da lista. Esse objeto pode ser usado de maneira idêntica à uma lista em python e sem substituição desta.

bin_file = arquivos[-1] ; print(bin_file.name)
RFeye002310_210520_T181500.bin

Processamento dos blocos

A função seguinte parse_bin recebe um arquivo .bin e mapeia os blocos contidos nele retornando um dicionário que tem como chave o tipo de bloco e os valores como uma lista com os blocos extraídos sequencialmente.

from rfpye.parser import parse_bin, extract_metadata, extract_level
%%time
map_bin = parse_bin(bin_file)['blocks']
Wall time: 4.48 s
for k, b in map_bin.items():
    print(f'Tipo de Bloco: {k[0]}, Fluxo (Thread ID): {k[1]}, #Blocos: {len(b)}')
Tipo de Bloco: 21, Fluxo (Thread ID): 0, #Blocos: 1
Tipo de Bloco: 42, Fluxo (Thread ID): 0, #Blocos: 2
Tipo de Bloco: 40, Fluxo (Thread ID): 1, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 50, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 60, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 70, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 90, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 110, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 130, #Blocos: 3023
Tipo de Bloco: 67, Fluxo (Thread ID): 10, #Blocos: 604
Tipo de Bloco: 67, Fluxo (Thread ID): 20, #Blocos: 604
Tipo de Bloco: 67, Fluxo (Thread ID): 40, #Blocos: 604
Tipo de Bloco: 42, Fluxo (Thread ID): 1, #Blocos: 50
gps = map_bin[(40,1)]
spec = map_bin[(67,20)]

A seguir é mostrado um exemplo dos atributos contidos num bloco de gps e num bloco de espectro

from rfpye.utils import getattrs
print(getattrs(gps[0][1]))
{
    'altitude': 571.6,
    'data_size': 40,
    'gps_datetime': datetime.datetime(2021, 5, 20, 18, 16),
    'gps_status': 'Standard GPS',
    'heading': 0.0,
    'latitude': -23.101629,
    'longitude': -45.7066,
    'num_satellites': 11,
    'speed': 0.04,
    'thread_id': 1,
    'type': 40,
    'wallclock_datetime': numpy.datetime64('2021-05-20T18:16:00.785620')
}
print(getattrs(spec[0][1]))
{
    'antenna_id': 0,
    'bw': 40,
    'data_size': 1156,
    'data_type': 1,
    'desclen': 28,
    'description': 'PRD 2021 (Faixa 2 de 4).',
    'dynamic_id': 0,
    'gerror': -1,
    'gflags': -1,
    'group_id': 0,
    'minimum': -56.5,
    'n_agc': 7,
    'n_tunning': 7,
    'namal': 1,
    'ndata': 1024,
    'npad': 1,
    'offset': 71,
    'padding': 0,
    'processing': 'peak',
    'resolution_bw': 73828,
    'sample': 5316,
    'start': 131,
    'start_channel': 0,
    'start_mega': 70,
    'start_mili': 0,
    'step': 0.039100684261974585,
    'stop': 1155,
    'stop_channel': 0,
    'stop_mega': 110,
    'stop_mili': 0,
    'thread_id': 20,
    'type': 67,
    'wallclock_datetime': numpy.datetime64('2021-05-20T18:20:00.107910')
}

Metadados

A função seguinte extrai os metadados META definidos no cabeçalho do arquivo constants.py e retorna um DataFrame.

gps_meta = extract_metadata(gps)
gps_meta.head()
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
start_byte stop_byte altitude data_size gps_datetime gps_status heading latitude longitude num_satellites speed thread_id type
wallclock_datetime
2021-05-20 18:16:00.785620 344 399 571.599976 40 2021-05-20 18:16:00 Standard GPS 0.0 -23.101629 -45.706600 11 0.040 1 40
2021-05-20 18:17:00.283280 31436 31491 574.799988 40 2021-05-20 18:17:00 Standard GPS 0.0 -23.101616 -45.706612 11 0.033 1 40
2021-05-20 18:18:00.183600 62528 62583 574.299988 40 2021-05-20 18:18:00 Standard GPS 0.0 -23.101612 -45.706612 11 0.037 1 40
2021-05-20 18:19:00.219400 93620 93675 577.200012 40 2021-05-20 18:19:00 Standard GPS 0.0 -23.101606 -45.706612 11 0.025 1 40
2021-05-20 18:20:00.683610 124712 124767 573.599976 40 2021-05-20 18:20:00 Standard GPS 0.0 -23.101625 -45.706608 11 0.053 1 40
spec_meta = extract_metadata(spec)
spec_meta.tail()
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
start_byte stop_byte antenna_id bw data_size data_type desclen description dynamic_id gerror ... start_channel start_mega start_mili step stop stop_channel stop_mega stop_mili thread_id type
wallclock_datetime
2021-05-22 20:15:00.160846 99227964 99229135 0 40 1156 1 28 PRD 2021 (Faixa 2 de 4). 0 -1 ... 0 70 0 0.039101 1155 0 110 0 20 67
2021-05-22 20:20:00.708650 99393348 99394519 0 40 1156 1 28 PRD 2021 (Faixa 2 de 4). 0 -1 ... 0 70 0 0.039101 1155 0 110 0 20 67
2021-05-22 20:25:00.230842 99558732 99559903 0 40 1156 1 28 PRD 2021 (Faixa 2 de 4). 0 -1 ... 0 70 0 0.039101 1155 0 110 0 20 67
2021-05-22 20:30:00.408390 99724116 99725287 0 40 1156 1 28 PRD 2021 (Faixa 2 de 4). 0 -1 ... 0 70 0 0.039101 1155 0 110 0 20 67
2021-05-22 20:35:00.180936 99889500 99890671 0 40 1156 1 28 PRD 2021 (Faixa 2 de 4). 0 -1 ... 0 70 0 0.039101 1155 0 110 0 20 67

5 rows × 34 columns

Frequência e Nível

A função seguinte extrai as frequências e nível num formato de Tabela Dinâmica:

  • Colunas: Frequências (MHz)
  • Índice: Números de Bloco
  • Valores: Níveis (dBm ou dBuV/m)
levels = extract_level(spec, dtype='float16')
levels.head()
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
70.000000 70.039101 70.078201 70.117302 70.156403 70.195503 70.234604 70.273705 70.312805 70.351906 ... 109.648094 109.687195 109.726295 109.765396 109.804497 109.843597 109.882698 109.921799 109.960899 110.000000
0 31.5 30.5 32.0 37.5 36.5 34.5 35.0 33.5 33.0 33.5 ... 19.0 19.0 20.5 21.0 19.5 8.0 1.0 19.5 22.5 23.0
1 30.5 32.0 31.5 27.5 29.5 29.0 27.5 27.5 27.5 29.5 ... 16.5 11.5 20.0 20.5 14.5 17.0 20.0 19.5 18.0 19.5
2 31.5 28.5 29.0 33.0 31.0 29.0 29.5 31.0 30.0 33.0 ... 13.5 14.5 17.5 16.5 6.0 20.5 20.5 20.5 19.5 17.5
3 31.5 31.5 31.5 32.0 33.5 35.0 34.0 33.5 33.0 33.5 ... 21.0 19.5 21.0 23.5 20.5 20.0 23.0 22.0 19.5 16.5
4 27.5 26.5 31.0 30.0 30.0 31.0 30.5 27.5 26.0 27.5 ... 10.0 0.5 9.5 14.0 19.0 21.0 10.5 20.0 23.0 22.0

5 rows × 1024 columns

Processamento, Extração e Salvamento dos Metadados e Espectro

A função a seguir é um wrapper de toda funcionalidade desta biblioteca. Ela recebe o caminho entrada para um arquivo .bin ou pasta contendo vários arquivos .bin, extrai os metadados e os dados de espectro. Mescla o timestamp dos metadados com o arquivo de espectro e salva ambos na pasta saida. Essa pasta é usada como repositório e cache dos dados processados que serão utilizados pela função extract_bin_stats.

from rfpye.filter import process_bin, extract_bin_stats
process_bin(bin_file, saida)
─────────────────────────── Lista de Arquivos a serem processados ───────────────────────────
['RFeye002310_210520_T181500.bin']                                                           
😴 Nenhum arquivo novo a processar 💤
☝ use --substituir no terminal ou substituir=True na chamada caso queira reprocessar os bins 
e sobrepôr os arquivos existentes 😉

Como vemos pela mensagem de saída, nada foi feito porque esse arquivo já foi processado anteriormente e todos os arquivos de metadados e espectros presentes já foram salvos na pasta saida

Resumo do arquivo

Se o que interessa é somente os dados estatísticos do arquivo como Min, Max e Mean basta utilizar:

stats = extract_bin_stats(bin_file, cache=saida)
stats
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
Frequency Min Max Mean
0 50.000000 -7.0 36.0 22.484375
1 50.039101 3.0 37.5 22.640625
2 50.078201 -3.5 38.5 22.593750
3 50.117302 -2.0 35.0 22.765625
4 50.156403 5.5 33.0 22.781250
... ... ... ... ...
26875 1218.844360 -137.0 -102.5 -112.500000
26876 1218.883301 -146.5 -101.0 -113.000000
26877 1218.922119 -136.5 -96.5 -112.500000
26878 1218.961060 -142.0 -95.0 -112.312500
26879 1219.000000 -146.0 -96.5 -112.500000

26880 rows × 4 columns

Podemos filtrar o intervalo tanto de frequência quanto de tempo da extração:

stats = extract_bin_stats(bin_file, cache=saida, freq_start=88, freq_stop=108)
stats
<style scoped> .dataframe tbody tr th:only-of-type { vertical-align: middle; }
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}
</style>
Frequency Min Max Mean
0 88.005867 1.0 33.0 21.109375
1 88.044968 -6.5 37.5 24.031250
2 88.084068 4.5 36.5 24.234375
3 88.123169 2.0 41.0 26.328125
4 88.162270 2.0 37.5 26.421875
... ... ... ... ...
866 107.959808 -6.0 30.0 18.140625
867 107.969582 -140.0 -87.5 -105.562500
868 107.979347 -143.5 -92.0 -105.875000
869 107.989113 -140.5 -94.0 -105.875000
870 107.998886 -133.5 -94.5 -106.000000

871 rows × 4 columns

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rfpye-0.0.6.tar.gz (213.0 kB view hashes)

Uploaded Source

Built Distribution

rfpye-0.0.6-cp37-cp37m-win_amd64.whl (202.9 kB view hashes)

Uploaded CPython 3.7m Windows x86-64

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page