Merge remote-tracking branch 'origin/record-iq' into integration_2025_w32 (!3487)
T hacks: add new tracer ant0 and a tool to plot ofdm data from usrp or rfsim (output or input) The tool ant0 dumps IQ data to a file (or stdout) as float complex or short. It is needed for a student's project. (Well, we could use record/extract, but it's simpler.) The tool ofdm-plot is used to plot output or input signal from gnb in the frequency domain. May be useful to debug, why not.
This commit is contained in:
@@ -3,7 +3,7 @@ CFLAGS=-Wall -g -pthread -DT_TRACER -I. -I..
|
||||
|
||||
LIBS=-lX11 -lm -lpng -lXft
|
||||
|
||||
all: dump_nack_signal time_meas timeplot multi-rru-clean
|
||||
all: dump_nack_signal time_meas timeplot multi-rru-clean ant0 plot-ofdm
|
||||
|
||||
dump_nack_signal: ../utils.o ../database.o ../configuration.o ../event.o \
|
||||
dump_nack_signal.o
|
||||
@@ -19,10 +19,25 @@ timplot: timeplot.o
|
||||
multi-rru-clean: ../utils.o ../database.o ../event.o ../configuration.o multi-rru-clean.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
ant0: ant0.o ../utils.o ../database.o ../configuration.o ../event.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lm
|
||||
|
||||
#ant0 needs good compilation, conversion to float is heavy
|
||||
ant0.o: ant0.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3
|
||||
|
||||
plot-ofdm: plot-ofdm.o ../utils.o
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LIBS) -lfftw3f
|
||||
|
||||
#plot-ofdm needs good compilation
|
||||
plot-ofdm.o: plot-ofdm.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $< -march=native -ffast-math -O3
|
||||
|
||||
.PHONY: all
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean
|
||||
rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean \
|
||||
ant0 plot-ofdm
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "database.h"
|
||||
//#include "utils.h"
|
||||
#include "configuration.h"
|
||||
#include "event.h"
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"options:\n"
|
||||
" -d <database file> this option is mandatory\n"
|
||||
" -o <output file> this option is mandatory (use - to dump to stdout)\n"
|
||||
" -ip <host> connect to given IP address (default %s)\n"
|
||||
" -p <port> connect to given port (default %d)\n"
|
||||
" -n <max> save maximum 'max' IQ points (may be a bit more)\n"
|
||||
" -nosig don't intercept ctrl+c, ctrl+z (it may mess up when piping to another tool)\n"
|
||||
" -e <event name> trace given event (default 'USRP_TX_ANT0')\n"
|
||||
" -b <buffer name> trace given buffer (default 'data')\n"
|
||||
" -no-timestamp don't use 'timestamp' to fill missing transmission\n"
|
||||
" -scale <scale value> divide 16bits input by given value for float output (default 32768.)\n"
|
||||
" -s16 output short instead of float\n",
|
||||
DEFAULT_REMOTE_IP,
|
||||
DEFAULT_REMOTE_PORT
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
volatile int run = 1;
|
||||
|
||||
static int socket = -1;
|
||||
|
||||
void force_stop(int x)
|
||||
{
|
||||
fprintf(stderr, "\ngently quit...\n");
|
||||
close(socket);
|
||||
socket = -1;
|
||||
run = 0;
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
char *database_filename = NULL;
|
||||
char *output_filename = NULL;
|
||||
FILE *out;
|
||||
void *database;
|
||||
char *ip = DEFAULT_REMOTE_IP;
|
||||
int port = DEFAULT_REMOTE_PORT;
|
||||
int *is_on;
|
||||
int number_of_events;
|
||||
int i;
|
||||
char mt;
|
||||
long max = -1;
|
||||
long dumped = 0;
|
||||
int nosig = 0;
|
||||
char *event_name = "USRP_TX_ANT0";
|
||||
char *buffer_name = "data";
|
||||
int no_timestamp = 0;
|
||||
float scale = 32768;
|
||||
int do_float = 1;
|
||||
|
||||
/* write on a socket fails if the other end is closed and we get SIGPIPE */
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();
|
||||
|
||||
for (i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
|
||||
if (!strcmp(v[i], "-d")) { if (i > n-2) usage(); database_filename = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-o")) { if (i > n-2) usage(); output_filename = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-p")) { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-n")) { if (i > n-2) usage(); max = atol(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-nosig")) { nosig = 1; continue; }
|
||||
if (!strcmp(v[i], "-e")) { if (i > n-2) usage(); event_name = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-b")) { if (i > n-2) usage(); buffer_name = v[++i]; continue; }
|
||||
if (!strcmp(v[i], "-no-timestamp")) { no_timestamp = 1; continue; }
|
||||
if (!strcmp(v[i], "-scale")) { if (i > n-2) usage(); scale = atof(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-s16")) { do_float = 0; continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
if (database_filename == NULL) {
|
||||
fprintf(stderr, "ERROR: provide a database file (-d)\n");
|
||||
exit(1);
|
||||
}
|
||||
if (output_filename == NULL) {
|
||||
fprintf(stderr, "ERROR: provide an output file (-o)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
database = parse_database(database_filename);
|
||||
load_config_file(database_filename);
|
||||
|
||||
/* get the event and fields from database */
|
||||
int tx_ant0_id = event_id_from_name(database, event_name);
|
||||
database_event_format f = get_format(database, tx_ant0_id);
|
||||
|
||||
#define G(var_name, var_type, var) \
|
||||
if (!strcmp(f.name[i], var_name)) { \
|
||||
if (strcmp(f.type[i], var_type)) abort(); \
|
||||
var = i; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
int timestamp_id = -1;
|
||||
int data_id = -1;
|
||||
for (i = 0; i < f.count; i++) {
|
||||
/* there is a typo in T_messages.txt, "timestap" instead of "timestamp" */
|
||||
if (!no_timestamp)
|
||||
G("timestap", "int", timestamp_id);
|
||||
G(buffer_name, "buffer", data_id);
|
||||
}
|
||||
if ((!no_timestamp && timestamp_id == -1) || data_id == -1) {
|
||||
if (data_id == -1)
|
||||
fprintf(stderr, "fatal: field '%s' of event '%s' does not exist\n",
|
||||
buffer_name, event_name);
|
||||
if (!no_timestamp && timestamp_id == -1)
|
||||
fprintf(stderr, "fatal: field 'timestamp' of event '%s' does not exist\n",
|
||||
event_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
number_of_events = number_of_ids(database);
|
||||
is_on = calloc(number_of_events, sizeof(int));
|
||||
if (is_on == NULL) abort();
|
||||
|
||||
on_off(database, event_name, is_on, 1);
|
||||
|
||||
/* connect to tracee */
|
||||
socket = connect_to(ip, port);
|
||||
|
||||
/* activate trace */
|
||||
mt = 1;
|
||||
if (socket_send(socket, &mt, 1) == -1 ||
|
||||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
|
||||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1)
|
||||
abort();
|
||||
|
||||
/* - means to output to stdout */
|
||||
if (!strcmp(output_filename, "-")) {
|
||||
out = stdout;
|
||||
} else {
|
||||
out = fopen(output_filename, "w");
|
||||
if (out == NULL) {
|
||||
perror(output_filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nosig) {
|
||||
/* exit on ctrl+c and ctrl+z */
|
||||
if (signal(SIGQUIT, force_stop) == SIG_ERR) abort();
|
||||
if (signal(SIGINT, force_stop) == SIG_ERR) abort();
|
||||
if (signal(SIGTSTP, force_stop) == SIG_ERR) abort();
|
||||
}
|
||||
|
||||
OBUF ebuf = {.osize = 0, .omaxsize = 0, .obuf = NULL};
|
||||
|
||||
/* read messages */
|
||||
unsigned int last_timestamp = 0;
|
||||
int last_count = 0;
|
||||
int first = 1;
|
||||
|
||||
/* stop if max reached */
|
||||
while ((max == -1 || dumped < max) && run) {
|
||||
event e;
|
||||
e = get_event(socket, &ebuf, database);
|
||||
|
||||
if (e.type == -1) break;
|
||||
if (e.type != tx_ant0_id) continue;
|
||||
|
||||
unsigned int timestamp = 0;
|
||||
if (!no_timestamp)
|
||||
timestamp = e.e[timestamp_id].i;
|
||||
int count = e.e[data_id].bsize / 4;
|
||||
|
||||
#if 0
|
||||
fprintf(stderr, "timestamp %u buffer %p count %d not transmitted %d delta %d\n",
|
||||
timestamp,
|
||||
e.e[data_id].b,
|
||||
count,
|
||||
timestamp - (last_timestamp + last_count),
|
||||
timestamp - last_timestamp);
|
||||
#endif
|
||||
|
||||
/* rfsimulator may send 1 sample with old time when a new client connects
|
||||
* let's reject this packet
|
||||
*/
|
||||
if (!first && timestamp < last_timestamp + last_count) {
|
||||
/* more than 1 sample is not accepted */
|
||||
if (count != 1) {
|
||||
fprintf(stderr, "fatal: past transmission detected (%d samples)\n", count);
|
||||
abort();
|
||||
}
|
||||
fprintf(stderr, "past transmission detected (%d samples), ignore\n", count);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* insert missing samples */
|
||||
if (!no_timestamp && !first) {
|
||||
int missing = timestamp - (last_timestamp + last_count);
|
||||
if (missing < 0) abort();
|
||||
while (missing) {
|
||||
/* limit size of array miss[], it's allocated on the stack */
|
||||
int count = missing > 65536 ? 65536 : missing;
|
||||
float miss[count * 2];
|
||||
memset(miss, 0, count * 2 * sizeof(float));
|
||||
|
||||
if (fwrite(miss, count * 2 * sizeof(float), 1, out) != 1) {
|
||||
fprintf(stderr, "error writing to %s\n", output_filename);
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
close(socket);
|
||||
exit(1);
|
||||
}
|
||||
dumped += count;
|
||||
missing -= count;
|
||||
}
|
||||
}
|
||||
|
||||
first = 0;
|
||||
|
||||
if (do_float) {
|
||||
/* transform to float */
|
||||
float iq[count * 2];
|
||||
short *iq_short = (short *)e.e[data_id].b;
|
||||
for (int i = 0; i < count * 2; i++)
|
||||
iq[i] = iq_short[i] / scale;
|
||||
|
||||
/* save */
|
||||
if (fwrite(iq, count * 2 * sizeof(float), 1, out) != 1) {
|
||||
fprintf(stderr, "error writing to %s\n", output_filename);
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
/* save directly as short */
|
||||
if (fwrite(e.e[data_id].b, count * 2 * 2, 1, out) != 1) {
|
||||
fprintf(stderr, "error writing to %s\n", output_filename);
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
dumped += count;
|
||||
|
||||
last_timestamp = timestamp;
|
||||
last_count = count;
|
||||
}
|
||||
|
||||
if (strcmp(output_filename, "-"))
|
||||
fclose(out);
|
||||
close(socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <complex.h>
|
||||
#include <fftw3.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
const int width = 14 * 20 * 4;
|
||||
int height;
|
||||
|
||||
/* when vzoom = 1 each pixel represents 1 RE
|
||||
* when 2, it is max of 2 vertical REs
|
||||
* useful if height of plot is to big
|
||||
* (with 106 RB we already reach 106*12 = 1272 pixels, so with even bigger
|
||||
* bandwidths...)
|
||||
*/
|
||||
int vzoom = 1;
|
||||
/* when hzoom = 1 each pixel represents 1 RE
|
||||
* when 2, it is max of 2 horizontal REs
|
||||
* useful to slow down display
|
||||
*/
|
||||
int hzoom = 1;
|
||||
/* intensity is to scale the input
|
||||
* normally, as float, cabs(input) is in [0..1]
|
||||
* to get pixel color we *255
|
||||
* but maybe it's too intense, or not enough,
|
||||
* so let's have a control the user can set at will
|
||||
*/
|
||||
float intensity = 255;
|
||||
|
||||
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&m);
|
||||
}
|
||||
|
||||
void unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&m);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Display *d;
|
||||
Window w;
|
||||
XImage *img;
|
||||
unsigned char *img_data;
|
||||
int img_pos;
|
||||
} gui_t;
|
||||
|
||||
/* delay management begin */
|
||||
|
||||
long double frame_time;
|
||||
|
||||
void time_start(void)
|
||||
{
|
||||
struct timespec l;
|
||||
if (clock_gettime(CLOCK_REALTIME, &l)) abort();
|
||||
frame_time = l.tv_sec * 1000000000. + l.tv_nsec;
|
||||
}
|
||||
|
||||
void delay(int fps)
|
||||
{
|
||||
struct timespec t;
|
||||
struct timespec delay;
|
||||
long double cur_time;
|
||||
|
||||
frame_time += 1000000000. / fps;
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &t)) abort();
|
||||
|
||||
cur_time = t.tv_sec * 1000000000. + t.tv_nsec;
|
||||
|
||||
if (frame_time < cur_time) { fprintf(stderr, "ERROR: we late\n"); return; }
|
||||
|
||||
delay.tv_nsec = ((uint64_t)(frame_time-cur_time)) % 1000000000;
|
||||
delay.tv_sec = ((uint64_t)(frame_time-cur_time)) / 1000000000;
|
||||
|
||||
if (pselect(0, NULL, NULL, NULL, &delay, NULL)) { fprintf(stderr, "ERROR: pselect fails\n"); exit(1); }
|
||||
}
|
||||
|
||||
/* delay management end */
|
||||
|
||||
static void *gui_thread(void *_g)
|
||||
{
|
||||
time_start();
|
||||
|
||||
gui_t *g = _g;
|
||||
while (1) {
|
||||
XEvent e; while (XPending(g->d)) XNextEvent(g->d, &e);
|
||||
lock();
|
||||
XPutImage(g->d, g->w, DefaultGC(g->d, DefaultScreen(g->d)), g->img, 0, 0, 0, 0,
|
||||
width, height/vzoom);
|
||||
unlock();
|
||||
delay(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char re_to_pixel(float re)
|
||||
{
|
||||
float v = fminf(255, re * intensity);
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned char mix(unsigned char a, unsigned char b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
printf("options:\n");
|
||||
printf(" -f input is float, not s16\n");
|
||||
printf(" -srate <sampling rate> set sampling rate (default 61440000)\n");
|
||||
printf(" -prb <prb> set number of PRBs (default 106)\n");
|
||||
printf(" -mu <mu> set mu (default 1, 0 is for 15KHz, 1 for 30KHz, etc.)\n");
|
||||
printf(" -no-fft don't do fft, input is in frequency domain\n");
|
||||
printf(" -vzoom <zoom> add some sort of vertical zoom, default 1, passing 2 divides height by 2, etc.\n");
|
||||
printf(" -hzoom <zoom> add some sort of speed control, default 1, passing 2 means 2 REs per pixel, etc.\n");
|
||||
printf(" -delay <delay> delay 'delay' microseconds between processing of each symbol\n");
|
||||
printf(" -intensity <intensity> set intensity of data pixel (default 255.)\n");
|
||||
printf(" -skip <count> skip 'count' samples at beginning\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int n, char **v)
|
||||
{
|
||||
int input_is_float = 0;
|
||||
int no_fft = 0;
|
||||
int delay = 0;
|
||||
int skip = 0;
|
||||
int symbols_per_subframe;
|
||||
long sampling_rate = 61440000;
|
||||
int mu = 1;
|
||||
int prb = 106;
|
||||
|
||||
/* parse command line arguments */
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (!strcmp(v[i], "-f")) { input_is_float = 1; continue; }
|
||||
if (!strcmp(v[i], "-no-fft")) { no_fft = 1; continue; }
|
||||
if (!strcmp(v[i], "-vzoom")) { if (i>n-2) usage(); vzoom = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-hzoom")) { if (i>n-2) usage(); hzoom = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-delay")) { if (i>n-2) usage(); delay = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-intensity")) { if (i>n-2) usage(); intensity = atof(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-srate")) { if (i>n-2) usage(); sampling_rate = atol(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-prb")) { if (i>n-2) usage(); prb = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-mu")) { if (i>n-2) usage(); mu = atoi(v[++i]); continue; }
|
||||
if (!strcmp(v[i], "-skip")) { if (i>n-2) usage(); skip = atoi(v[++i]); continue; }
|
||||
usage();
|
||||
}
|
||||
|
||||
symbols_per_subframe = 14 * (1 << mu);
|
||||
|
||||
/* compute durations from sampling rate and mu (scs) */
|
||||
/* this is 38.211 5.3.1, a bit simplified/reordered */
|
||||
int ofdm_duration = sampling_rate / 15000 / (1 << mu);
|
||||
int l0_cycle_prefix = sampling_rate / 480000 * (144*64 / (1 << mu) + 16*64) / 4096;
|
||||
int cycle_prefix = sampling_rate / 480000 * (144*64 / (1 << mu)) / 4096;
|
||||
|
||||
/* small sanity check */
|
||||
if (prb * 12 > ofdm_duration) {
|
||||
printf("fatal: too many PRBs for this sampling rate / mu\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
height = prb * 12;
|
||||
|
||||
if (height % vzoom) { fprintf(stderr, "bad vzoom for this height, try 1, 2, 4, ...\n"); return 1; }
|
||||
|
||||
gui_t g = { 0 };
|
||||
|
||||
/* open X11 display */
|
||||
g.d = XOpenDisplay(0);
|
||||
if (!g.d) {
|
||||
fprintf(stderr, "could not open display\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* create window */
|
||||
g.w = XCreateSimpleWindow(g.d, DefaultRootWindow(g.d), 0, 0,
|
||||
width, height / vzoom, 0, WhitePixel(g.d, DefaultScreen(g.d)),
|
||||
WhitePixel(g.d, DefaultScreen(g.d)));
|
||||
XStoreName(g.d, g.w, "IQ");
|
||||
/* enable backing store */
|
||||
{
|
||||
XSetWindowAttributes att;
|
||||
att.backing_store = Always;
|
||||
XChangeWindowAttributes(g.d, g.w, CWBackingStore, &att);
|
||||
}
|
||||
XSelectInput(g.d, g.w,
|
||||
KeyPressMask |
|
||||
ButtonPressMask |
|
||||
ButtonReleaseMask |
|
||||
PointerMotionMask |
|
||||
ExposureMask |
|
||||
StructureNotifyMask);
|
||||
XMapWindow(g.d, g.w);
|
||||
|
||||
g.img_data = calloc(1, width * height/vzoom * 4); if (!g.img_data) abort();
|
||||
g.img = XCreateImage(g.d, DefaultVisual(g.d, DefaultScreen(g.d)), 24 /*depth*/,
|
||||
ZPixmap, 0 /*offset*/, (char*)g.img_data, width, height/vzoom, 8, 0);
|
||||
|
||||
new_thread(gui_thread, &g);
|
||||
|
||||
/* fft data */
|
||||
int fft_size = ofdm_duration;
|
||||
float complex *in = fftwf_malloc(sizeof(float complex) * fft_size);
|
||||
float complex *out = fftwf_malloc(sizeof(float complex) * fft_size);
|
||||
fftwf_plan p_fft = fftwf_plan_dft_1d(fft_size, in, out,
|
||||
FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
memset(in, 0, sizeof(float complex) * fft_size);
|
||||
fftwf_execute(p_fft);
|
||||
|
||||
int symbol = 0;
|
||||
int back_color = 0;
|
||||
int hpixel = 0;
|
||||
|
||||
if (skip) {
|
||||
if (input_is_float) {
|
||||
float intime[skip * 2];
|
||||
if (fread(intime, skip * 8, 1, stdin) != 1) abort();
|
||||
} else {
|
||||
short intime[skip * 2];
|
||||
if (fread(intime, skip * 4, 1, stdin) != 1) abort();
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int cur_cycle_prefix;
|
||||
if (symbol == 0 || symbol == 7 * (1 << mu))
|
||||
cur_cycle_prefix = l0_cycle_prefix;
|
||||
else
|
||||
cur_cycle_prefix = cycle_prefix;
|
||||
|
||||
if (no_fft) goto without_fft;
|
||||
|
||||
/* input signal is in time domain, timing aligned, do FFT */
|
||||
|
||||
/* read time domain data */
|
||||
if (input_is_float) {
|
||||
float intime[(ofdm_duration + cur_cycle_prefix) * 2];
|
||||
if (fread(intime, (ofdm_duration + cur_cycle_prefix) * 8, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
/* skip cycle prefix */
|
||||
in[i] = intime[(cur_cycle_prefix + i) * 2] + I * intime[(cur_cycle_prefix + i) * 2 + 1];
|
||||
} else {
|
||||
short intime[(ofdm_duration + cur_cycle_prefix) * 2];
|
||||
if (fread(intime, (ofdm_duration + cur_cycle_prefix) * 4, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
/* skip cycle prefix */
|
||||
in[i] = intime[(cur_cycle_prefix + i) * 2] / 2048. + I * intime[(cur_cycle_prefix + i) * 2 + 1] / 2048.;
|
||||
}
|
||||
|
||||
/* go to freq domain */
|
||||
fftwf_execute(p_fft);
|
||||
|
||||
goto after_fft;
|
||||
|
||||
without_fft:
|
||||
/* input data is symbols, each containing full FFT data */
|
||||
if (input_is_float) {
|
||||
float infreq[fft_size * 2];
|
||||
if (fread(infreq, fft_size * 2 * 4, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
out[i] = infreq[i * 2] + I * infreq[i * 2 + 1];
|
||||
} else {
|
||||
short infreq[ofdm_duration * 2];
|
||||
if (fread(infreq, fft_size * 2 * 2, 1, stdin) != 1) break;
|
||||
|
||||
for (int i = 0; i < fft_size; i++)
|
||||
out[i] = infreq[i * 2] / 2048. + I * infreq[i * 2 + 1] / 2048.;
|
||||
}
|
||||
|
||||
after_fft:
|
||||
lock();
|
||||
|
||||
/* extract data of this ofdm symbol */
|
||||
unsigned char *p = g.img_data + g.img_pos * 4;
|
||||
int front_pos = (g.img_pos + 8) % width;
|
||||
unsigned char *pfront = g.img_data + front_pos * 4;
|
||||
for (int i = prb * 12 - 1; i >= 0; i -= vzoom) {
|
||||
/* take the max */
|
||||
float out_val = 0;
|
||||
for (int j = 0; j < vzoom; j++) {
|
||||
int start = - prb * 12 / 2;
|
||||
int pos = start + i + j;
|
||||
/* neg freq are at the end of fft data array */
|
||||
int pos_in_fft = pos >= 0
|
||||
? pos
|
||||
: pos + fft_size;
|
||||
float v = cabs(out[pos_in_fft]);
|
||||
if (v > out_val) out_val = v;
|
||||
}
|
||||
int color = re_to_pixel(out_val);
|
||||
/* grey level image mixed with some background changing every slot */
|
||||
unsigned char back_col[2][3] = {
|
||||
{ 100, 90, 90 },
|
||||
{ 100, 60, 60, }
|
||||
};
|
||||
if (hpixel == 0) {
|
||||
p[0] = p[1] = p[2] = color;
|
||||
} else {
|
||||
p[0] = mix(p[0], color);
|
||||
p[1] = mix(p[1], color);
|
||||
p[2] = mix(p[2], color);
|
||||
}
|
||||
if (hpixel == hzoom - 1) {
|
||||
p[0] = mix(p[0], back_col[back_color][0]);
|
||||
p[1] = mix(p[1], back_col[back_color][1]);
|
||||
p[2] = mix(p[2], back_col[back_color][2]);
|
||||
}
|
||||
p[3] = 255;
|
||||
p += width * 4;
|
||||
pfront[0] = 255;
|
||||
pfront[1] = 128;
|
||||
pfront[2] = 128;
|
||||
pfront[3] = 255;
|
||||
pfront += width * 4;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
hpixel++;
|
||||
if (hpixel == hzoom) {
|
||||
hpixel = 0;
|
||||
g.img_pos++;
|
||||
if (g.img_pos == width) g.img_pos = 0;
|
||||
}
|
||||
|
||||
symbol++;
|
||||
if (symbol == symbols_per_subframe) symbol = 0;
|
||||
|
||||
/* switch back color every slot */
|
||||
if (symbol % 14 == 0)
|
||||
back_color = 1 - back_color;
|
||||
|
||||
if (delay) usleep(delay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ int socket_send(int socket, void *buffer, int size) {
|
||||
|
||||
int get_connection(char *addr, int port) {
|
||||
int s, t;
|
||||
printf("waiting for connection on %s:%d\n", addr, port);
|
||||
fprintf(stderr, "waiting for connection on %s:%d\n", addr, port);
|
||||
s = create_listen_socket(addr, port);
|
||||
t = socket_accept(s);
|
||||
|
||||
@@ -169,7 +169,7 @@ int get_connection(char *addr, int port) {
|
||||
}
|
||||
|
||||
close(s);
|
||||
printf("connected\n");
|
||||
fprintf(stderr, "connected\n");
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -216,12 +216,12 @@ int try_connect_to(char *addr, int port) {
|
||||
|
||||
int connect_to(char *addr, int port) {
|
||||
int s;
|
||||
printf("connecting to %s:%d\n", addr, port);
|
||||
fprintf(stderr, "connecting to %s:%d\n", addr, port);
|
||||
again:
|
||||
s = try_connect_to(addr, port);
|
||||
|
||||
if (s == -1) {
|
||||
printf("trying again in 1s\n");
|
||||
fprintf(stderr, "trying again in 1s\n");
|
||||
sleep(1);
|
||||
goto again;
|
||||
}
|
||||
|
||||
@@ -733,6 +733,10 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t,
|
||||
timestamp,
|
||||
timestamp + nsamps,
|
||||
signal_energy(samplesVoid[0], nsamps));
|
||||
|
||||
/* trace only first antenna */
|
||||
T(T_USRP_TX_ANT0, T_INT(timestamp), T_BUFFER(samplesVoid[0], sampleToByte(nsamps, 1)));
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
@@ -1073,6 +1077,10 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest
|
||||
*ptimestamp,
|
||||
t->nextRxTstamp,
|
||||
signal_energy(samplesVoid[0], nsamps));
|
||||
|
||||
/* trace only first antenna */
|
||||
T(T_USRP_RX_ANT0, T_INT(t->nextRxTstamp), T_BUFFER(samplesVoid[0], sampleToByte(nsamps, 1)));
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user