Merge pull request #113 from Guenael/snr-fix

feat & dbg: .c2 files support, bench doc, normalize sig & dbg snr
pull/114/head
Guenael, VA2GKA 2021-12-23 18:06:41 -05:00 zatwierdzone przez GitHub
commit 2d8d938a24
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
4 zmienionych plików z 113 dodań i 18 usunięć

Wyświetl plik

@ -136,3 +136,18 @@ Some manufacturers integrate a 0.5ppm TCXO. It's the best second option, after a
- NooElec NESDR SMART : Works fine out of the box
- RTL-SDR Blog 1PPM TCXO : Works with some drift, require additional mass, or a better enclosure
- Other no-name like : RT820, E4000, FC0012, FC0013, can work, but require modification and usually drift a lot
## Performance & hardware tests
Some performance tests using:
- Raspbian GNU/Linux 11 (bullseye) for Raspberry Pi devices
- rtlsdr-wsprd version 0.4.2
- Build with `clang -O3 -std=gnu17`
| Hardware | Supported | RX Load | Decode burst |
| --------- | ------------------ | ------- | ------------ |
| RPi-1 | :heavy_check_mark: | 23.2% | 8.4s |
| RPi-2 | :heavy_check_mark: | 13.5% | 4.1s |
| RPi-3 | :heavy_check_mark: | 10.9% | 2.1s |
| RPi-4 | :heavy_check_mark: | 5.8% | 1.1s |
| i7-5820K | :heavy_check_mark: | 1.7% | 0.5s |

Wyświetl plik

@ -94,7 +94,7 @@ struct receiver_options {
bool selftest;
bool writefile;
bool readfile;
char filename[33];
char *filename;
};
@ -220,8 +220,8 @@ static void rtlsdr_callback(unsigned char *samples, uint32_t samples_count, void
/* Save the result in the buffer */
uint32_t idx = rx_state.bufferIndex;
if (rx_state.iqIndex[idx] < (SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE)) {
rx_state.iSamples[idx][rx_state.iqIndex[idx]] = Isum / (32768.0 * DOWNSAMPLING);
rx_state.qSamples[idx][rx_state.iqIndex[idx]] = Qsum / (32768.0 * DOWNSAMPLING);
rx_state.iSamples[idx][rx_state.iqIndex[idx]] = Isum;
rx_state.qSamples[idx][rx_state.iqIndex[idx]] = Qsum;
rx_state.iqIndex[idx]++;
}
}
@ -259,6 +259,29 @@ static void *decoder(void *arg) {
if (rx_state.iqIndex[prevBuffer] < ( (SIGNAL_LENGHT - 3) * SIGNAL_SAMPLE_RATE ) )
continue; /* Partial buffer during the first RX, skip it! */
/* Delete any previous samples tail */
for (int i = rx_state.iqIndex[prevBuffer]; i < SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE; i++) {
rx_state.iSamples[prevBuffer][i] = 0.0;
rx_state.qSamples[prevBuffer][i] = 0.0;
}
/* Normalize the sample @-3dB */
float maxSig = 0.0f;
for (int i = 0; i < SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE; i++) {
float absI = fabs(rx_state.iSamples[prevBuffer][i]);
float absQ = fabs(rx_state.qSamples[prevBuffer][i]);
if (absI > maxSig)
maxSig = absI;
if (absQ > maxSig)
maxSig = absQ;
}
maxSig = 0.5 / maxSig;
for (int i = 0; i < SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE; i++) {
rx_state.iSamples[prevBuffer][i] *= maxSig;
rx_state.qSamples[prevBuffer][i] *= maxSig;
}
/* Get the date at the beginning last recording session
with 1 second margin added, just to be sure to be on this even minute
*/
@ -324,7 +347,7 @@ void postSpots(uint32_t n_results) {
// "Table 'wsprnet_db.activity' doesn't exist" reported on web site...
// Anyone has doc about this?
if (n_results == 0) {
snprintf(url, sizeof(url) - 1, "http://wsprnet.org/post?function=wsprstat&rcall=%s&rgrid=%s&rqrg=%.6f&tpct=%.2f&tqrg=%.6f&dbm=%d&version=rtlsdr-050&mode=2",
snprintf(url, sizeof(url) - 1, "http://wsprnet.org/post?function=wsprstat&rcall=%s&rgrid=%s&rqrg=%.6f&tpct=%.2f&tqrg=%.6f&dbm=%d&version=rtlsdr-051&mode=2",
dec_options.rcall,
dec_options.rloc,
rx_options.realfreq / 1e6,
@ -347,7 +370,7 @@ void postSpots(uint32_t n_results) {
}
for (uint32_t i = 0; i < n_results; i++) {
snprintf(url, sizeof(url) - 1, "http://wsprnet.org/post?function=wspr&rcall=%s&rgrid=%s&rqrg=%.6f&date=%02d%02d%02d&time=%02d%02d&sig=%.0f&dt=%.1f&tqrg=%.6f&tcall=%s&tgrid=%s&dbm=%s&version=rtlsdr-050&mode=2",
snprintf(url, sizeof(url) - 1, "http://wsprnet.org/post?function=wspr&rcall=%s&rgrid=%s&rqrg=%.6f&date=%02d%02d%02d&time=%02d%02d&sig=%.0f&dt=%.1f&tqrg=%.6f&tcall=%s&tgrid=%s&dbm=%s&version=rtlsdr-051&mode=2",
dec_options.rcall,
dec_options.rloc,
dec_results[i].freq,
@ -552,13 +575,70 @@ int32_t writeRawIQfile(float *iSamples, float *qSamples, char *filename) {
}
int32_t readC2file(float *iSamples, float *qSamples, char *filename) {
float filebuffer[2 * SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE];
FILE *fd = fopen(filename, "rb");
int32_t nread;
double frequency;
int type;
char name[15];
if (fd == NULL) {
fprintf(stderr, "Cannot open data file...\n");
return 0;
}
/* Get the size of the file */
fseek(fd, 0L, SEEK_END);
int32_t recsize = ftell(fd) / (2 * sizeof(float)) - 26;
fseek(fd, 0L, SEEK_SET);
/* Limit the file/buffer to the max samples */
if (recsize > SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE) {
recsize = SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE;
}
/* Read the header */
nread = fread(name, sizeof(char), 14, fd);
nread = fread(&type, sizeof(int), 1, fd);
nread = fread(&frequency, sizeof(double), 1, fd);
dec_options.freq = frequency;
/* Read the IQ file */
nread = fread(filebuffer, sizeof(float), 2 * recsize, fd);
if (nread != 2 * recsize) {
fprintf(stderr, "Cannot read all the data! %d\n", nread);
fclose(fd);
return 0;
} else {
fclose(fd);
}
/* Convert the interleaved buffer into 2 buffers */
for (int32_t i = 0; i < recsize; i++) {
iSamples[i] = filebuffer[2 * i];
qSamples[i] = -filebuffer[2 * i + 1]; // neg, convention used by wsprsim
}
return recsize;
}
void decodeRecordedFile(char *filename) {
static float iSamples[SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE] = {0};
static float qSamples[SIGNAL_LENGHT * SIGNAL_SAMPLE_RATE] = {0};
static uint32_t samples_len;
int32_t n_results = 0;
samples_len = readRawIQfile(iSamples, qSamples, filename);
if (strcmp(&filename[strlen(filename)-3], ".iq") == 0) {
samples_len = readRawIQfile(iSamples, qSamples, filename);
} else if (strcmp(&filename[strlen(filename)-3], ".c2") == 0) {
samples_len = readC2file(iSamples, qSamples, filename);
} else {
fprintf(stderr, "Not a valid extension!! (only .iq & .c2 files)\n");
return;
}
printf("Number of samples: %d\n", samples_len);
if (samples_len) {
@ -691,7 +771,7 @@ void usage(void) {
"Debugging options:\n"
"\t-t decoder self-test (generate a signal & decode), no parameter\n"
"\t-w write received signal and exit [filename prefix]\n"
"\t-r read signal, decode and exit [filename]\n"
"\t-r read signal with .iq or .c2 format, decode and exit [filename]\n"
"\t (raw format: 375sps, float 32 bits, 2 channels)\n"
"Example:\n"
"\trtlsdr_wsprd -f 2m -c A1XYZ -l AB12cd -g 29 -o -4200\n");
@ -830,13 +910,13 @@ int main(int argc, char **argv) {
case 't': // Seft test (used in unit-test CI pipeline)
rx_options.selftest = true;
break;
case 'w': // Read a signal and decode
case 'w': // Write a signal and exit
rx_options.writefile = true;
snprintf(rx_options.filename, sizeof(rx_options.filename), "%.32s", optarg);
rx_options.filename = optarg;
break;
case 'r': // Write a signal and exit
case 'r': // Read a signal and decode
rx_options.readfile = true;
snprintf(rx_options.filename, sizeof(rx_options.filename), "%.32s", optarg);
rx_options.filename = optarg;
break;
default:
usage();
@ -986,7 +1066,7 @@ int main(int argc, char **argv) {
struct tm *gtm = gmtime(&rawtime);
/* Print used parameter */
printf("\nStarting rtlsdr-wsprd (%04d-%02d-%02d, %02d:%02dz) -- Version 0.5.0\n",
printf("\nStarting rtlsdr-wsprd (%04d-%02d-%02d, %02d:%02dz) -- Version 0.5.1\n",
gtm->tm_year + 1900, gtm->tm_mon + 1, gtm->tm_mday, gtm->tm_hour, gtm->tm_min);
printf(" Callsign : %s\n", dec_options.rcall);
printf(" Locator : %s\n", dec_options.rloc);
@ -1053,7 +1133,6 @@ int main(int argc, char **argv) {
/* Destroy the lock/cond/thread */
pthread_cond_destroy(&decState.ready_cond);
pthread_mutex_destroy(&decState.ready_mutex);
pthread_exit(NULL);
printf("Bye!\n");

Wyświetl plik

@ -42,6 +42,7 @@ double atofs(char *s);
int32_t parse_u64(char *s, uint64_t *const value);
int32_t readRawIQfile(float *iSamples, float *qSamples, char *filename);
int32_t writeRawIQfile(float *iSamples, float *qSamples, char *filename);
int32_t readC2file(float *iSamples, float *qSamples, char *filename);
void decodeRecordedFile(char *filename);
float whiteGaussianNoise(float factor);
int32_t decoderSelfTest();

Wyświetl plik

@ -454,10 +454,10 @@ int wspr_decode(float *idat,
/* Setup metric table */
int32_t mettab[2][256];
float bias = 0.42;
float bias = 0.45;
for (int i = 0; i < 256; i++) {
mettab[0][i] = round(10 * (metric_tables[2][i] - bias));
mettab[1][i] = round(10 * (metric_tables[2][255 - i] - bias));
mettab[0][i] = roundf(10.0 * (metric_tables[2][i] - bias));
mettab[1][i] = roundf(10.0 * (metric_tables[2][255 - i] - bias));
}
/* Setup/Load hash tables */
@ -573,12 +573,12 @@ int wspr_decode(float *idat,
* corresponding to -7-26.3=-33.3dB in 2500 Hz bandwidth.
* The corresponding threshold is -42.3 dB in 2500 Hz bandwidth for WSPR-15. */
float min_snr = powf(10.0, -7.0 / 10.0); // this is min snr in wspr bw
float min_snr = powf(10.0, -8.0 / 10.0); // this is min snr in wspr bw
float snr_scaling_factor = 26.3;
for (int j = 0; j < 411; j++) {
smspec[j] = smspec[j] / noise_level - 1.0;
if (smspec[j] < min_snr) smspec[j] = 0.1;
if (smspec[j] < min_snr) smspec[j] = 0.1 * min_snr;
continue;
}