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\CGH\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/CGH/2021/rfeye002279-SP-Congonhas_210319_T160137.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210325_T230001.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210401_T060001.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210404_T152752.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210410_T222501.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210416_T104037.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210422_T113942.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210428_T183501.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210505_T013201.bin'), Path('D:/OneDrive - ANATEL/Backup_Rfeye_SP/CGH/2021/rfeye002279-SP-Congonhas_210511_T083001.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[-3] ; print(bin_file.name)
rfeye002279-SP-Congonhas_210516_T144208.bin
from rfpye.parser import parse_bin, extract_metadata, extract_level
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.
%%time
map_bin = parse_bin(bin_file)['blocks']
Wall time: 5.26 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: 6341
Tipo de Bloco: 67, Fluxo (Thread ID): 300, #Blocos: 6341
Tipo de Bloco: 67, Fluxo (Thread ID): 310, #Blocos: 6341
Tipo de Bloco: 67, Fluxo (Thread ID): 100, #Blocos: 1268
Tipo de Bloco: 67, Fluxo (Thread ID): 110, #Blocos: 1268
Tipo de Bloco: 67, Fluxo (Thread ID): 130, #Blocos: 1268
Tipo de Bloco: 67, Fluxo (Thread ID): 210, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 220, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 230, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 240, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 320, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 340, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 360, #Blocos: 423
Tipo de Bloco: 67, Fluxo (Thread ID): 380, #Blocos: 423
Tipo de Bloco: 42, Fluxo (Thread ID): 1, #Blocos: 105
gps = map_bin[(40,1)]
spec = map_bin[(67,110)]
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': 815.3, 'data_size': 40, 'gps_datetime': datetime.datetime(2021, 5, 16, 14, 43), 'gps_status': 'Differential GPS', 'heading': 0.0, 'latitude': -23.635858, 'longitude': -46.654255, 'num_satellites': 12, 'speed': 0.014, 'thread_id': 1, 'type': 40, 'wallclock_datetime': numpy.datetime64('2021-05-16T14:43:00.532926') }
print(getattrs(spec[0][1]))
{ 'antenna_id': 0, 'bw': 40, 'data_size': 1164, 'data_type': 1, 'desclen': 36, 'description': 'PRD 2021 (Faixa principal 2 de 4).', 'dynamic_id': 0, 'gerror': -1, 'gflags': -1, 'group_id': 0, 'minimum': -32.5, 'n_agc': 7, 'n_tunning': 7, 'namal': 1, 'ndata': 1024, 'npad': 1, 'offset': 95, 'padding': 0, 'processing': 'peak', 'resolution_bw': 73828, 'sample': 6230, 'start': 139, 'start_channel': 0, 'start_mega': 70, 'start_mili': 0, 'step': 0.039100684261974585, 'stop': 1163, 'stop_channel': 0, 'stop_mega': 110, 'stop_mili': 0, 'thread_id': 110, 'type': 67, 'wallclock_datetime': numpy.datetime64('2021-05-16T14:45:00.790881') }
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-16 14:43:00.532926 | 344 | 399 | 815.299988 | 40 | 2021-05-16 14:43:00 | Differential GPS | 0.0 | -23.635859 | -46.654255 | 12 | 0.014 | 1 | 40 |
2021-05-16 14:44:00.330070 | 5324 | 5379 | 817.299988 | 40 | 2021-05-16 14:43:59 | Differential GPS | 0.0 | -23.635857 | -46.654263 | 12 | 0.025 | 1 | 40 |
2021-05-16 14:45:00.752895 | 10304 | 10359 | 816.200012 | 40 | 2021-05-16 14:45:00 | Differential GPS | 0.0 | -23.635859 | -46.654266 | 12 | 0.007 | 1 | 40 |
2021-05-16 14:46:00.402776 | 86092 | 86147 | 815.299988 | 40 | 2021-05-16 14:46:00 | Differential GPS | 0.0 | -23.635864 | -46.654263 | 12 | 0.005 | 1 | 40 |
2021-05-16 14:47:00.302816 | 91072 | 91127 | 818.099976 | 40 | 2021-05-16 14:47:00 | Differential GPS | 0.0 | -23.635859 | -46.654266 | 12 | 0.025 | 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-21 00:00:00.695624 | 69731068 | 69732247 | 0 | 40 | 1164 | 1 | 36 | PRD 2021 (Faixa principal 2 de 4). | 0 | -1 | ... | 0 | 70 | 0 | 0.039101 | 1163 | 0 | 110 | 0 | 110 | 67 |
2021-05-21 00:05:00.635638 | 69826776 | 69827955 | 0 | 40 | 1164 | 1 | 36 | PRD 2021 (Faixa principal 2 de 4). | 0 | -1 | ... | 0 | 70 | 0 | 0.039101 | 1163 | 0 | 110 | 0 | 110 | 67 |
2021-05-21 00:10:00.606404 | 69861632 | 69862811 | 0 | 40 | 1164 | 1 | 36 | PRD 2021 (Faixa principal 2 de 4). | 0 | -1 | ... | 0 | 70 | 0 | 0.039101 | 1163 | 0 | 110 | 0 | 110 | 67 |
2021-05-21 00:15:00.355210 | 69896488 | 69897667 | 0 | 40 | 1164 | 1 | 36 | PRD 2021 (Faixa principal 2 de 4). | 0 | -1 | ... | 0 | 70 | 0 | 0.039101 | 1163 | 0 | 110 | 0 | 110 | 67 |
2021-05-21 00:20:00.385654 | 69992196 | 69993375 | 0 | 40 | 1164 | 1 | 36 | PRD 2021 (Faixa principal 2 de 4). | 0 | -1 | ... | 0 | 70 | 0 | 0.039101 | 1163 | 0 | 110 | 0 | 110 | 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 | 48.5 | 47.5 | 46.0 | 42.5 | 32.0 | 39.0 | 32.5 | 41.0 | 44.5 | 45.5 | ... | 34.0 | 37.0 | 38.5 | 38.0 | 32.5 | 28.0 | 36.0 | 34.5 | 27.5 | 34.5 |
1 | 35.5 | 35.5 | 36.5 | 46.0 | 43.0 | 44.5 | 47.0 | 45.5 | 40.0 | 38.5 | ... | 35.5 | 34.5 | 31.0 | 34.5 | 35.5 | 28.5 | 21.5 | 28.5 | 27.0 | 37.5 |
2 | 47.0 | 44.5 | 44.0 | 41.5 | 40.5 | 42.0 | 40.0 | 40.0 | 43.0 | 39.5 | ... | 31.0 | 38.0 | 41.5 | 38.5 | 31.0 | 31.0 | 31.0 | 39.5 | 39.5 | 37.5 |
3 | 40.0 | 43.0 | 42.0 | 46.5 | 45.5 | 40.5 | 35.0 | 35.0 | 37.5 | 37.0 | ... | 37.0 | 38.5 | 39.0 | 35.5 | 36.5 | 32.5 | 26.5 | 33.5 | 35.5 | 33.0 |
4 | 40.0 | 35.0 | 32.0 | 42.0 | 41.0 | 40.5 | 36.5 | 32.5 | 46.0 | 47.5 | ... | 31.0 | 28.0 | 41.0 | 42.5 | 39.0 | 31.0 | 25.0 | 16.5 | 23.0 | 23.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 ───────────────────────────
['rfeye002279-SP-Congonhas_210516_T144208.bin']
Processando Blocos de: rfeye002279-SP-Congonhas_210516_T144208.bin
Extraindo Metadados de: rfeye002279-SP-Congonhas_210516_T144208.bin
kbô 😆
Se chamarmos a função novamente:
process_bin(bin_file, saida)
─────────────────────────── Lista de Arquivos a serem processados ───────────────────────────
['rfeye002279-SP-Congonhas_210516_T144208.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 | 18.0 | 48.5 | 37.81250 |
1 | 50.039101 | 10.5 | 49.0 | 37.62500 |
2 | 50.078201 | 8.5 | 48.5 | 37.59375 |
3 | 50.117302 | 17.5 | 49.5 | 37.96875 |
4 | 50.156403 | 17.5 | 50.0 | 38.00000 |
... | ... | ... | ... | ... |
43003 | 5159.843750 | -128.5 | -104.0 | -112.43750 |
43004 | 5159.882812 | -125.0 | -103.5 | -112.25000 |
43005 | 5159.921875 | -127.0 | -101.5 | -111.37500 |
43006 | 5159.960938 | -123.5 | -101.0 | -111.12500 |
43007 | 5160.000000 | -125.5 | -103.5 | -112.06250 |
43008 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.1, freq_stop=107.9)
print(stats.head(20))
Frequency Min Max Mean 0 88.123169 69.0 96.0 89.43750 1 88.162270 65.0 96.0 89.43750 2 88.201370 53.5 96.0 83.62500 3 88.240471 46.5 95.5 83.81250 4 88.279572 37.5 95.0 71.93750 5 88.318672 34.0 94.0 72.37500 6 88.357773 28.0 87.0 57.40625 7 88.396873 21.5 85.0 57.90625 8 88.435974 24.0 71.0 46.00000 9 88.475075 24.5 70.0 46.40625 10 88.514175 12.5 55.0 40.71875 11 88.553276 9.5 55.0 40.93750 12 88.592377 20.0 53.5 38.06250 13 88.631477 17.5 56.5 38.28125 14 88.670578 16.5 60.0 41.59375 15 88.709679 10.0 60.5 41.90625 16 88.748779 12.5 62.0 49.96875 17 88.787880 17.5 62.5 50.25000 18 88.826981 28.0 64.0 54.37500 19 88.866081 31.5 63.5 54.68750
print(stats.tail(20))
Frequency Min Max Mean 833 107.705833 -133.0 -64.0 -90.00000 834 107.715599 -120.5 -62.0 -88.81250 835 107.725372 -117.5 -60.5 -88.00000 836 107.735138 19.0 75.0 46.31250 837 107.744904 -128.5 -58.5 -86.68750 838 107.754677 -121.0 -57.0 -84.31250 839 107.764442 -116.5 -49.0 -80.68750 840 107.774208 -120.5 -41.0 -76.37500 841 107.783981 18.0 79.5 59.62500 842 107.793747 -121.5 -38.5 -72.37500 843 107.803520 -115.0 -38.5 -69.00000 844 107.813286 -116.5 -38.5 -66.68750 845 107.823051 -118.0 -40.5 -65.12500 846 107.832825 25.0 79.5 69.31250 847 107.842590 -124.5 -42.5 -62.59375 848 107.852356 -111.0 -41.5 -59.12500 849 107.862129 -119.5 -39.5 -55.75000 850 107.871895 -110.5 -38.0 -53.15625 851 107.881660 43.0 81.5 73.25000 852 107.891434 -111.0 -37.0 -51.71875
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.7-cp37-cp37m-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c2ebd4eb71038de9dea713c143c544d1cc32f09f6cad2cffae37baf19f1de822 |
|
MD5 | da05e2e4c898b19c9ce1bab003134f30 |
|
BLAKE2b-256 | 1c3f9e7b5c170524b523b5633495cfe18f51e61bfbe745e54d293ac948f4ea92 |