kopia lustrzana https://github.com/rpp0/gr-lora
New tool for real-time debugging and analysis of signals
rodzic
88047f2a9e
commit
d41580cbdf
|
@ -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)
|
|
@ -26,5 +26,6 @@ install(FILES
|
|||
message_file_sink.h
|
||||
message_socket_sink.h
|
||||
channelizer.h
|
||||
debugger.h
|
||||
controller.h DESTINATION include/lora
|
||||
)
|
||||
|
|
|
@ -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 */
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
Ładowanie…
Reference in New Issue