From 6a2997ec9b8c579d9b5dab232cb92117fbefc77d Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 19 Jun 2025 16:41:02 +0200 Subject: [PATCH 1/5] T hacks: add ant0 utility to dump IQ data as float complex Or as int16. --- common/utils/T/tracer/hacks/Makefile | 12 +- common/utils/T/tracer/hacks/ant0.c | 259 +++++++++++++++++++++++++++ 2 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 common/utils/T/tracer/hacks/ant0.c diff --git a/common/utils/T/tracer/hacks/Makefile b/common/utils/T/tracer/hacks/Makefile index 7157406796..f693094fb6 100644 --- a/common/utils/T/tracer/hacks/Makefile +++ b/common/utils/T/tracer/hacks/Makefile @@ -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 dump_nack_signal: ../utils.o ../database.o ../configuration.o ../event.o \ dump_nack_signal.o @@ -19,10 +19,18 @@ 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 + .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 diff --git a/common/utils/T/tracer/hacks/ant0.c b/common/utils/T/tracer/hacks/ant0.c new file mode 100644 index 0000000000..3de4dac325 --- /dev/null +++ b/common/utils/T/tracer/hacks/ant0.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include "database.h" +//#include "utils.h" +#include "configuration.h" +#include "event.h" + +void usage(void) +{ + fprintf(stderr, +"options:\n" +" -d this option is mandatory\n" +" -o this option is mandatory (use - to dump to stdout)\n" +" -ip connect to given IP address (default %s)\n" +" -p connect to given port (default %d)\n" +" -n 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 trace given event (default 'USRP_TX_ANT0')\n" +" -b trace given buffer (default 'data')\n" +" -no-timestamp don't use 'timestamp' to fill missing transmission\n" +" -scale 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; +} From 4a0d8ce637e12c57c80a27c0968dcc67d0cac7d3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 19 Jun 2025 16:43:19 +0200 Subject: [PATCH 2/5] rfsim: add a T trace to dump IQ data Only for one antenna. --- radio/rfsimulator/simulator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 32648758c8..3a2a0ae2d5 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -738,6 +738,10 @@ static int rfsimulator_write_internal(rfsimulator_state_t *t, openair0_timestamp 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; } From 17b59413677f714cccc9a1c0d774c359c6fbfc82 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 19 Jun 2025 16:44:13 +0200 Subject: [PATCH 3/5] T: dump logs to stderr, not stdout This is a problem with the new tool ant0 when we dump IQ to stdout. The logs will mess up the recording. --- common/utils/T/tracer/utils.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/utils/T/tracer/utils.c b/common/utils/T/tracer/utils.c index 78b01a2516..66dd2a0b48 100644 --- a/common/utils/T/tracer/utils.c +++ b/common/utils/T/tracer/utils.c @@ -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; } From a10420e015e93826e83c9e11906631b8e508b4c5 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Sun, 22 Jun 2025 18:59:38 +0200 Subject: [PATCH 4/5] T hacks: add ofdm-plot Used to... surprise... plot OFDM data as output (or input) by gnb! Limitation: only one antenna. --- common/utils/T/tracer/hacks/Makefile | 11 +- common/utils/T/tracer/hacks/plot-ofdm.c | 351 ++++++++++++++++++++++++ 2 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 common/utils/T/tracer/hacks/plot-ofdm.c diff --git a/common/utils/T/tracer/hacks/Makefile b/common/utils/T/tracer/hacks/Makefile index f693094fb6..2c96e67823 100644 --- a/common/utils/T/tracer/hacks/Makefile +++ b/common/utils/T/tracer/hacks/Makefile @@ -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 ant0 +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 @@ -26,6 +26,13 @@ ant0: ant0.o ../utils.o ../database.o ../configuration.o ../event.o 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 @@ -33,4 +40,4 @@ ant0.o: ant0.c clean: rm -f *.o core dump_nack_signal time_meas timeplot multi-rru-clean \ - ant0 + ant0 plot-ofdm diff --git a/common/utils/T/tracer/hacks/plot-ofdm.c b/common/utils/T/tracer/hacks/plot-ofdm.c new file mode 100644 index 0000000000..3f6e0b9dce --- /dev/null +++ b/common/utils/T/tracer/hacks/plot-ofdm.c @@ -0,0 +1,351 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 set sampling rate (default 61440000)\n"); + printf(" -prb set number of PRBs (default 106)\n"); + printf(" -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 add some sort of vertical zoom, default 1, passing 2 divides height by 2, etc.\n"); + printf(" -hzoom add some sort of speed control, default 1, passing 2 means 2 REs per pixel, etc.\n"); + printf(" -delay delay 'delay' microseconds between processing of each symbol\n"); + printf(" -intensity set intensity of data pixel (default 255.)\n"); + printf(" -skip 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; +} From c607413eac6578a1570db34624138b75abc23cc3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Sun, 22 Jun 2025 19:08:35 +0200 Subject: [PATCH 5/5] rfsim: add a T trace to dump RX IQ data Only for one antenna. --- radio/rfsimulator/simulator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c index 3a2a0ae2d5..fca1b8a2e0 100644 --- a/radio/rfsimulator/simulator.c +++ b/radio/rfsimulator/simulator.c @@ -1077,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; }