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_file_sink.h
|
||||||
message_socket_sink.h
|
message_socket_sink.h
|
||||||
channelizer.h
|
channelizer.h
|
||||||
|
debugger.h
|
||||||
controller.h DESTINATION include/lora
|
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
|
message_socket_sink_impl.cc
|
||||||
channelizer_impl.cc
|
channelizer_impl.cc
|
||||||
controller_impl.cc
|
controller_impl.cc
|
||||||
|
debugger.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(lora_sources "${lora_sources}" PARENT_SCOPE)
|
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 "tables.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
|
||||||
//#define NO_TMP_WRITES 1 /// Debug output file write
|
//#define NO_TMP_WRITES 1 /// Debug output file write
|
||||||
//#define CFO_CORRECT 1 /// Correct shift fft estimation
|
//#define CFO_CORRECT 1 /// Correct shift fft estimation
|
||||||
|
|
||||||
|
@ -142,6 +143,9 @@ namespace gr {
|
||||||
|
|
||||||
// Whitening empty file
|
// Whitening empty file
|
||||||
// DBGR_QUICK_TO_FILE("/tmp/whitening_out", false, g, -1, "");
|
// DBGR_QUICK_TO_FILE("/tmp/whitening_out", false, g, -1, "");
|
||||||
|
#ifndef NDEBUG
|
||||||
|
d_dbg.attach();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <lora/debugger.h>
|
||||||
|
|
||||||
#define DECIMATOR_FILTER_SIZE (2*8*1 + 1) // 2*decim_factor*delay+1
|
#define DECIMATOR_FILTER_SIZE (2*8*1 + 1) // 2*decim_factor*delay+1
|
||||||
|
|
||||||
|
@ -64,6 +65,7 @@ namespace gr {
|
||||||
*/
|
*/
|
||||||
class decoder_impl : public decoder {
|
class decoder_impl : public decoder {
|
||||||
private:
|
private:
|
||||||
|
debugger d_dbg; ///< Debugger for plotting samples, printing output, etc.
|
||||||
DecoderState d_state; ///< Holds the current state of the decoder (state machine).
|
DecoderState d_state; ///< Holds the current state of the decoder (state machine).
|
||||||
|
|
||||||
std::vector<gr_complex> d_downchirp; ///< The complex ideal downchirp.
|
std::vector<gr_complex> d_downchirp; ///< The complex ideal downchirp.
|
||||||
|
|
Ładowanie…
Reference in New Issue