Last active
August 27, 2018 06:43
-
-
Save dangpzanco/fd7267c1d9a2aaf95f915c7cccd6a36c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import serial | |
import sys | |
import argparse | |
import csv | |
import pathlib | |
from datetime import datetime | |
def list_serial_ports(): | |
""" Lists serial port names | |
:raises EnvironmentError: | |
On unsupported or unknown platforms | |
:returns: | |
A list of the serial ports available on the system | |
:reference: | |
https://stackoverflow.com/a/14224477/2801287 | |
""" | |
if sys.platform.startswith('win'): | |
ports = ['COM%s' % (i + 1) for i in range(256)] | |
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'): | |
# this excludes your current terminal "/dev/tty" | |
ports = glob.glob('/dev/tty[A-Za-z]*') | |
elif sys.platform.startswith('darwin'): | |
ports = glob.glob('/dev/tty.*') | |
else: | |
raise EnvironmentError('Unsupported platform') | |
result = [] | |
for port in ports: | |
try: | |
with serial.Serial(port) as s: | |
result.append(port) | |
except (OSError, serial.SerialException): | |
pass | |
return result | |
def parse_rssi(line): | |
str_line = str(line.decode("utf-8")) | |
rssi = int(float(str_line[5:10])) | |
error_count = int(float(str_line[10:])) | |
return rssi, error_count | |
def log_rssi_data(filename,num_samples,overwrite): | |
# List all the available serial ports | |
serial_ports = list_serial_ports() | |
if len(serial_ports) == 0: | |
print('Nenhuma porta serial disponível!') | |
sys.exit() | |
else: | |
print(f'Portas serial disponíveis: {serial_ports}') | |
# Set CC1350's serial port (change this index if there are multiple devices) | |
# A placa tem duas portas serial, a segunda é a que envia os dados | |
com_port = serial_ports[1] | |
# CSV header keynames (in order) | |
keys = ['index', 'time', 'rssi', 'error_count'] | |
# Initialize dictionary from keys (with 0) | |
data = dict.fromkeys(keys, 0) | |
data['rssi'] = -120 | |
# Set CSV filename | |
csv_name = pathlib.Path(filename) | |
if len(csv_name.suffix) == 0: | |
csv_name = csv_name.with_suffix('.csv') | |
# Avoid overwriting a file | |
if not overwrite and csv_name.exists(): | |
timestamp = datetime.now().isoformat().replace(':','') | |
csv_name = csv_name.with_name(csv_name.stem + timestamp + csv_name.suffix) | |
# Just write the CSV header | |
with open(csv_name, 'w', newline='') as f: | |
w = csv.DictWriter(f, keys) | |
w.writeheader() | |
# Print some information | |
print('Gravando {n} amostras no arquivo {f}.'.format(f=filename,n=num_samples)) | |
# Print header | |
print('index\ttime\t\t\t\trssi\terror_count') | |
for i in range(num_samples): | |
# Indexing | |
data['index'] = i | |
data['time'] = datetime.now().isoformat() | |
with serial.Serial(com_port, baudrate=115200) as ser: | |
# Read the RSSI data, try again if it fails | |
successful = False | |
while not successful: | |
try: | |
line = ser.readline() | |
rssi, error_count = parse_rssi(line) | |
# Error counter | |
data['error_count'] = error_count | |
# RSSI [dB] | |
data['rssi'] = rssi | |
# Leave loop | |
successful = True | |
except: | |
pass | |
# Save dictionary to a CSV line and print line | |
with open(csv_name, 'a', newline='') as f: | |
w = csv.DictWriter(f, keys) | |
w.writerow(data) | |
# Printing stuff | |
l = list(w._dict_to_list(data)) | |
string_to_print = '{}\t{}\t{}\t{}'.format(*l) | |
print(string_to_print,end='\r') | |
print() | |
def cmdline_args(): | |
# Make parser object | |
p = argparse.ArgumentParser(description= | |
""" | |
Script para gravar os dados de RSSI lidos via serial do CC1350. | |
Os dados são salvos em um arquivo CSV, que pode ser lido com | |
Python (se referir ao script `plot_cc1350.py`), MATLAB ou Excel, entre outros. | |
""", | |
formatter_class=argparse.ArgumentDefaultsHelpFormatter) | |
p.add_argument("-f", "--filename", type=str, default='CC1350_data.csv', | |
help="Nome do arquivo CSV no qual os dados serão salvos.") | |
p.add_argument("-n", "--num_amostras", type=int, default=100, | |
help="Número de amostras a serem salvas.") | |
p.add_argument("-o", "--overwrite", action="store_true", | |
help="Sobreescrever o arquivo CSV.") | |
return(p.parse_args()) | |
if __name__ == '__main__': | |
if sys.version_info < (3,5,0): | |
sys.stderr.write("Você precisa de Python 3.5+ para rodar esse script.\n") | |
sys.exit(1) | |
try: | |
args = cmdline_args() | |
log_rssi_data(filename=args.filename,num_samples=args.num_amostras,overwrite=args.overwrite) | |
print('Fim do script.') | |
except: | |
print('\nTente o comando:\npython log_cc1350.py --filename log.txt --num_amostras 1000') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import argparse | |
import pathlib | |
import numpy as np | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
def plot_rssi_data(filename): | |
# Abrir arquivo CSV | |
filename = pathlib.Path(filename) | |
data = pd.read_csv(filename) | |
# Ler colunas da tabela | |
index = data['index'].values | |
rssi = data['rssi'].values | |
error_count = data['error_count'].values | |
# Converter o tempo para segundos decorridos | |
dt = pd.to_datetime(data['time'].values) | |
elapsed_time = dt - dt[0] | |
time = elapsed_time.total_seconds().values | |
# Dados úteis | |
num_amostras = index[-1] | |
erros = np.hstack([0,np.diff(error_count)]) | |
FER = erros.mean() | |
fig = plt.figure(figsize=(8,7)) | |
plt.subplot(2, 1, 1) | |
plt.plot(time, rssi) | |
plt.title('RSSI x Amostras - {a:.2f} amostras/s'.format(a=num_amostras/time[-1])) | |
plt.xlabel('Tempo [s]') | |
plt.ylabel('RSSI [dB]') | |
plt.grid() | |
plt.subplot(2, 1, 2) | |
plt.stem(time, erros) | |
plt.title('ERRO de Pacote x Amostras - FER = ' + str(FER)) | |
plt.xlabel('Tempo [s]') | |
plt.ylabel('ERRO') | |
plt.tight_layout() | |
plt.show() | |
def cmdline_args(): | |
# Make parser object | |
p = argparse.ArgumentParser(description= | |
""" | |
Script teste para plotar os dados lidos do CC1350. | |
""", | |
formatter_class=argparse.ArgumentDefaultsHelpFormatter) | |
p.add_argument("-f", "--filename", type=str, default='CC1350_data.csv', | |
help="Nome do arquivo CSV no qual os dados foram salvos.") | |
return(p.parse_args()) | |
if __name__ == '__main__': | |
if sys.version_info < (3,5,0): | |
sys.stderr.write("Você precisa de Python 3.5+ para rodar esse script.\n") | |
sys.exit(1) | |
try: | |
args = cmdline_args() | |
plot_rssi_data(filename=args.filename) | |
print('Fim do script.') | |
except: | |
print('\nTente o comando:\npython plot_cc1350.py --filename log.txt') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Passo a passo de instalação do Python (no Windows):
1 - Miniconda: https://conda.io/miniconda.html
Python 3.6 -> https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe
Na instalação, escolher colocar o caminho do Miniconda/Anaconda no PATH (garante acesso aos comandos
python
eipython
via cmd)2 - Instalar alguns pacotes necessários:
a) pyserial - Acesso às portas serial
pip install pyserial
b) pandas - Manipulação de tabelas
conda install pandas
c) matplotlib - Biblioteca de plotagem de gráficos
conda install matplotlib
Como usar os scripts:
$ python log_cc1350.py --help
$ python plot_cc1350.py --help
Extra (programas úteis para programação em Python):
1 - Usar o ConEmu como substituto do cmd no Windows
https://conemu.github.io/
2 - Instalar o Sublime Text 3 (Se o Python estiver no PATH, o atalho Ctrl+B executa os scripts.)
a) Instalar a extensão Package Control.
https://packagecontrol.io/installation
b) Usar a extensão JEDI, para autocompletar e tirar dúvidas de funções em Python direto do editor de texto.