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()
.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()
.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()
.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
.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
.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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for rfpye-0.0.6-cp37-cp37m-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d178b70c5d9b55e5d5bdc31840b91f5bb45b8f9e324e362021d9c4c7c9cbf40f |
|
MD5 | e5398066c15bffadf3f0248d7ed30b15 |
|
BLAKE2b-256 | 9ece30ef86d6493304f998883def92d739f9334660557039cc6a96cbc1295586 |