New tool for real-time debugging and analysis of signals

pull/61/head
Pieter Robyns 2017-08-10 13:49:00 +02:00
rodzic 88047f2a9e
commit d41580cbdf
7 zmienionych plików z 275 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,129 @@
#!/usr/bin/python3
# -----------------------------------------------------------------------------
# grlora_analyze.py
# A Python tool capable of performing a real-time analysis of signals passed
# by GNU Radio.
#
# Author: Pieter Robyns
# -----------------------------------------------------------------------------
import os
import socket
import numpy as np
import matplotlib.pyplot as plt
import struct
import argparse
from threading import Thread
# Pop num bytes from l
def fetch(l, num):
fet = l[0:num]
del l[0:num]
return fet
class State:
READ_HEADER = 0
READ_DATA = 1
class Plotter(Thread):
def __init__(self):
Thread.__init__(self)
self.setDaemon(True)
self.socket_address = "/tmp/gr_lora.sock"
self.socket = None
self.init_socket()
plt.ion()
def init_socket(self):
if self.socket is None:
try:
os.unlink(self.socket_address)
except OSError:
if os.path.exists(self.socket_address):
raise FileExistsError
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.socket.bind(self.socket_address)
self.socket.listen(1)
self.socket.setblocking(0)
print("[gr-lora analyzer]: listening at " + self.socket_address)
else:
return # Socket is already initialized
def get_data(self, connection):
buffer = connection.recv(1024)
if buffer:
return buffer
else:
raise ConnectionResetError
def run(self):
while True:
plt.pause(0.0001) # Give some time to Qt GUI to render
try:
client_socket, client_address = self.socket.accept()
try:
self.state = State.READ_HEADER
data = bytearray()
data_len = 0
draw_over = False
while True:
plt.pause(0.0001)
try:
# Parse buffer data
if self.state == State.READ_HEADER:
while len(data) < 5:
data += self.get_data(client_socket)
data_len, draw_over = struct.unpack(">I?", fetch(data, 5))
self.state = State.READ_DATA
elif self.state == State.READ_DATA:
while len(data) < data_len:
data += self.get_data(client_socket)
plot_data = np.frombuffer(fetch(data, data_len), dtype=np.complex64)
if not draw_over:
plt.gcf().clear()
plt.plot(np.arange(len(plot_data)), np.real(plot_data), "b", np.arange(len(plot_data)), np.imag(plot_data), "g")
self.state = State.READ_HEADER
except ConnectionResetError:
print("[gr-lora analyzer]: connection reset")
break
finally:
# Clean up the connection
client_socket.close()
except BlockingIOError:
pass
# Test operation of the Plotter class
def test():
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect("/tmp/gr_lora.sock")
try:
print("[fake client]: sending!")
with open(os.path.expanduser("~/usrpsf7.cfile"),"rb") as f:
data = f.read()
chunk_size = 524288
for i in range(0, len(data), chunk_size):
chunk = data[i:i+chunk_size]
data_len = len(chunk)
draw_over = False
client.sendall(struct.pack(">I?", data_len, draw_over) + chunk)
finally:
print("[fake client]: done sending!")
client.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='gr-lora debugging / analysis tool for interfacing with GNU Radio over Unix sockets')
parser.add_argument('--test', dest='test', help='Peform a test', action='store_true')
args, unknown = parser.parse_known_args()
plotter = Plotter()
plotter.start()
if args.test:
test()
plotter.join()
exit(0)

Wyświetl plik

@ -26,5 +26,6 @@ install(FILES
message_file_sink.h
message_socket_sink.h
channelizer.h
debugger.h
controller.h DESTINATION include/lora
)

Wyświetl plik

@ -0,0 +1,55 @@
/* -*- c++ -*- */
/*
* Copyright 2017 Pieter Robyns.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_DEBUGGER_H
#define INCLUDED_DEBUGGER_H
#include <gnuradio/gr_complex.h>
#include <vector>
#include <stdint.h>
#define PACKED __attribute__((packed, aligned(1)))
namespace gr {
namespace lora {
class debugger {
public:
debugger();
virtual ~debugger();
void attach(std::string path = "/tmp/gr_lora.sock");
void detach(void);
void analyze_samples(bool clear, bool draw_over);
void store_samples(const gr_complex* samples, uint32_t length);
private:
typedef struct header {
uint32_t length;
bool draw_over;
} PACKED header;
void send_samples();
std::vector<gr_complex> d_samples;
int d_socket;
bool d_attached;
};
}
}
#endif /* INCLUDED_DEBUGGER_H */

Wyświetl plik

@ -31,6 +31,7 @@ list(APPEND lora_sources
message_socket_sink_impl.cc
channelizer_impl.cc
controller_impl.cc
debugger.cc
)
set(lora_sources "${lora_sources}" PARENT_SCOPE)

83
lib/debugger.cc 100644
Wyświetl plik

@ -0,0 +1,83 @@
#include <iostream>
#include <lora/debugger.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
namespace gr {
namespace lora {
debugger::debugger() : d_attached(false) {
}
debugger::~debugger() {
}
/*
* Attach to a UNIX domain socket.
*/
void debugger::attach(std::string path) {
if((d_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
std::cerr << "Failed to create UNIX domain socket." << std::endl;
return;
}
struct sockaddr_un address;
memset(&address, 0, sizeof(address));
address.sun_family = AF_UNIX;
strncpy(address.sun_path, path.c_str(), sizeof(address.sun_path)-1);
if(connect(d_socket, (struct sockaddr*)&address, sizeof(address)) == -1) {
std::cerr << "Failed to connect to analyzer." << std::endl;
} else {
d_attached = true;
}
}
void debugger::detach(void) {
if(d_attached) {
close(d_socket);
d_attached = false;
}
}
/*
TODO: For some vague reason, making a template member function breaks swig. This only happens if we separate declaration and definition of the function (i.e. in utilities.h, the functions don't have this issue). Therefore, it seems like a bug. To resolve, we can port LoRa Receiver to C++, removing the SWIG dependency.
See: https://github.com/gnuradio/gnuradio/search?utf8=%E2%9C%93&q=quicksort_index.h&type=
*/
void debugger::analyze_samples(bool clear, bool draw_over) {
if(d_attached) {
uint32_t num_payload_bytes = d_samples.size() * sizeof(gr_complex);
uint32_t num_bytes_sent;
debugger::header hdr;
hdr.length = htonl(num_payload_bytes);
hdr.draw_over = draw_over;
// Send header
if((num_bytes_sent = send(d_socket, &hdr, sizeof(hdr), 0)) == -1) {
std::cerr << "Failed to send header." << std::endl;
return;
}
// Send payload
if((num_bytes_sent = send(d_socket, &d_samples[0], num_payload_bytes, 0)) == -1) {
std::cerr << "Failed to send payload." << std::endl;
return;
}
if(clear)
d_samples.clear();
}
}
void debugger::store_samples(const gr_complex* samples, uint32_t length) {
if(d_attached) {
d_samples.insert(d_samples.end(), samples, samples + length);
}
}
}
}

Wyświetl plik

@ -31,6 +31,7 @@
#include "tables.h"
#include "utilities.h"
//#define NO_TMP_WRITES 1 /// Debug output file write
//#define CFO_CORRECT 1 /// Correct shift fft estimation
@ -142,6 +143,9 @@ namespace gr {
// Whitening empty file
// DBGR_QUICK_TO_FILE("/tmp/whitening_out", false, g, -1, "");
#ifndef NDEBUG
d_dbg.attach();
#endif
}
/**

Wyświetl plik

@ -26,6 +26,7 @@
#include <string>
#include <vector>
#include <fstream>
#include <lora/debugger.h>
#define DECIMATOR_FILTER_SIZE (2*8*1 + 1) // 2*decim_factor*delay+1
@ -64,6 +65,7 @@ namespace gr {
*/
class decoder_impl : public decoder {
private:
debugger d_dbg; ///< Debugger for plotting samples, printing output, etc.
DecoderState d_state; ///< Holds the current state of the decoder (state machine).
std::vector<gr_complex> d_downchirp; ///< The complex ideal downchirp.