Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ COMMON_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(COMMON_SRC))
FFT_SRC = $(wildcard fft/*.c)
FFT_OBJ = $(patsubst %.c,$(BUILD_DIR)/%.o,$(FFT_SRC))

TARGETS = gen_ft8 decode_ft8 test_ft8
TARGETS = libft8.a gen_ft8 decode_ft8 test_ft8

CFLAGS = -fsanitize=address -O3 -ggdb3
CPPFLAGS = -std=c11 -I.
ifdef FT8_DEBUG
CFLAGS = -fsanitize=address -ggdb3 -DHAVE_STPCPY -I. -DFTX_DEBUG_PRINT
LDFLAGS = -fsanitize=address -lm
else
CFLAGS = -O3 -DHAVE_STPCPY -I.
LDFLAGS = -lm
endif

# Optionally, use Portaudio for live audio input
# Portaudio is a C++ library, so then you need to set CC=clang++ or CC=g++
ifdef PORTAUDIO_PREFIX
CPPFLAGS += -DUSE_PORTAUDIO -I$(PORTAUDIO_PREFIX)/include
CFLAGS += -DUSE_PORTAUDIO -I$(PORTAUDIO_PREFIX)/include
LDFLAGS += -lportaudio -L$(PORTAUDIO_PREFIX)/lib
endif

Expand All @@ -26,24 +31,28 @@ endif
all: $(TARGETS)

clean:
rm -rf $(BUILD_DIR) $(TARGETS)
rm -rf $(BUILD_DIR) $(TARGETS)

run_tests: test_ft8
@./test_ft8

install:
$(AR) rc libft8.a $(FT8_OBJ) $(COMMON_OBJ)
install: libft8.a
install libft8.a /usr/lib/libft8.a

gen_ft8: $(BUILD_DIR)/demo/gen_ft8.o $(FT8_OBJ) $(COMMON_OBJ) $(FFT_OBJ)
$(CC) $(LDFLAGS) -o $@ $^
gen_ft8: $(BUILD_DIR)/demo/gen_ft8.o libft8.a
$(CC) $(CFLAGS) -o $@ .build/demo/gen_ft8.o -lft8 -L. -lm

decode_ft8: $(BUILD_DIR)/demo/decode_ft8.o $(FT8_OBJ) $(COMMON_OBJ) $(FFT_OBJ)
$(CC) $(LDFLAGS) -o $@ $^
decode_ft8: $(BUILD_DIR)/demo/decode_ft8.o libft8.a $(FFT_OBJ)
$(CC) $(CFLAGS) -o $@ $(BUILD_DIR)/demo/decode_ft8.o $(FFT_OBJ) -lft8 -L. -lm

test_ft8: $(BUILD_DIR)/test/test.o $(FT8_OBJ)
$(CC) $(LDFLAGS) -o $@ $^
test_ft8: $(BUILD_DIR)/test/test.o libft8.a
$(CC) $(CFLAGS) -o $@ .build/test/test.o -lft8 -L. -lm

$(BUILD_DIR)/%.o: %.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $^
$(CC) $(CFLAGS) -o $@ -c $^

lib: libft8.a

libft8.a: $(FT8_OBJ) $(COMMON_OBJ)
$(AR) rc libft8.a $(FT8_OBJ) $(COMMON_OBJ)
9 changes: 5 additions & 4 deletions demo/decode_ft8.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#define _POSIX_C_SOURCE 199309L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
Expand Down Expand Up @@ -212,17 +213,17 @@ void decode(const monitor_t* mon, struct tm* tm_slot_start)
++num_decoded;

char text[FTX_MAX_MESSAGE_LENGTH];
ftx_message_rc_t unpack_status = ftx_message_decode(&message, &hash_if, text);
ftx_message_offsets_t offsets;
ftx_message_rc_t unpack_status = ftx_message_decode(&message, &hash_if, text, &offsets);
if (unpack_status != FTX_MESSAGE_RC_OK)
{
snprintf(text, sizeof(text), "Error [%d] while unpacking!", (int)unpack_status);
}

// Fake WSJT-X-like output for now
float snr = cand->score * 0.5f; // TODO: compute better approximation of SNR
printf("%02d%02d%02d %+05.1f %+4.2f %4.0f ~ %s\n",
printf("%02d%02d%02d %+d %+4.2f %4.0f ~ %s\n",
tm_slot_start->tm_hour, tm_slot_start->tm_min, tm_slot_start->tm_sec,
snr, time_sec, freq_hz, text);
cand->snr, time_sec, freq_hz, text);
}
}
LOG(LOG_INFO, "Decoded %d messages, callsign hashtable size %d\n", num_decoded, callsign_hashtable_size);
Expand Down
54 changes: 54 additions & 0 deletions ft8/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "ldpc.h"

#include <stdbool.h>
#include <stdlib.h>
#include <math.h>

// #define LOG_LEVEL LOG_DEBUG
Expand Down Expand Up @@ -124,6 +125,55 @@ static int ft8_sync_score(const ftx_waterfall_t* wf, const ftx_candidate_t* cand
return score;
}

// comparator for qsort
static int cmpint(const void *p1, const void *p2)
{
const int *i1 = (const int *)p1;
const int *i2 = (const int *)p2;
return (*i1 > *i2) - (*i1 < *i2);
}

static int get_snr(const ftx_waterfall_t* wf, ftx_candidate_t candidate)
{
// array with wf.num_blocks (row of waterfall) x 8*wf.freq_osr (signals width)
// Get this waterfall zoom on the candidate symbols
// Sort max to min and calculate max/min = ft8snr, subtract -26db for snr on 2500hz

float minC = 0.0f, maxC = 0.0f;

for (int i = 0; i < wf->num_blocks; ++i)
{
int candidate_zoom[8 * wf->freq_osr * wf->time_osr];

for (int j = 0; j < 8; j++)
{
for (int k = 0; k < wf->freq_osr * wf->time_osr; k++)
{
candidate_zoom[(j * wf->freq_osr * wf->time_osr) + k] =
wf->mag[(i * wf->block_stride) + candidate.freq_offset +
candidate.freq_sub + (j * wf->freq_osr * wf->time_osr) + k];
}
}

qsort(candidate_zoom, sizeof(candidate_zoom) / sizeof(int), sizeof(int), cmpint);

for (int j = 0; j < wf->freq_osr * wf->time_osr * 2; j++)
minC += candidate_zoom[j + (2 * wf->freq_osr * wf->time_osr)];

for (int j = 1; j <= wf->freq_osr * wf->time_osr; j++)
maxC += candidate_zoom[(8 * wf->freq_osr * wf->time_osr) - j];
}

minC = minC / (wf->num_blocks * wf->freq_osr * wf->time_osr * 2);
maxC = maxC / (wf->num_blocks * wf->freq_osr * wf->time_osr);

int min = (int)(minC / 2.0 - 240.0);
int max = (int)(maxC / 2.0 - 240.0);
int snr = max - min - 26;

return snr;
}

static int ft4_sync_score(const ftx_waterfall_t* wf, const ftx_candidate_t* candidate)
{
int score = 0;
Expand Down Expand Up @@ -247,6 +297,10 @@ int ftx_find_candidates(const ftx_waterfall_t* wf, int num_candidates, ftx_candi
heapify_down(heap, len_unsorted);
}

// Add SNR to each candidate
for (int i = 0; i < heap_size; i++)
heap[i].snr = (int16_t)get_snr(wf, heap[i]);

return heap_size;
}

Expand Down
1 change: 1 addition & 0 deletions ft8/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ typedef struct
int16_t freq_offset; ///< Index of the frequency bin
uint8_t time_sub; ///< Index of the time subdivision used
uint8_t freq_sub; ///< Index of the frequency subdivision used
int16_t snr; ///< Signal to noise ratio
} ftx_candidate_t;

/// Structure that contains the status of various steps during decoding of a message
Expand Down
Loading