/* * util.cpp * Miscellaneous routines * * Copyright (c) 2015-2021 Tomasz Lemiech * * This program 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 of the License, or * (at your option) any later version. * * This program 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 program. If not, see . */ #include #include #include // uint32_t #include #include #include #include #include #include #include #include "config.h" #include "logging.h" #include "rtl_airband.h" int atomic_inc(volatile int* pv) { return __sync_fetch_and_add(pv, 1); } int atomic_dec(volatile int* pv) { return __sync_fetch_and_sub(pv, 1); } int atomic_get(volatile int* pv) { return __sync_fetch_and_add(pv, 0); } void tag_queue_put(device_t* dev, int freq, struct timeval tv) { pthread_mutex_lock(&dev->tag_queue_lock); dev->tq_head++; dev->tq_head %= TAG_QUEUE_LEN; if (dev->tq_head == dev->tq_tail) { log(LOG_WARNING, "tag_queue_put: queue overrun\n"); dev->tq_tail++; } dev->tag_queue[dev->tq_head].freq = freq; memcpy(&dev->tag_queue[dev->tq_head].tv, &tv, sizeof(struct timeval)); pthread_mutex_unlock(&dev->tag_queue_lock); } void tag_queue_get(device_t* dev, struct freq_tag* tag) { int i; if (!tag) return; pthread_mutex_lock(&dev->tag_queue_lock); if (dev->tq_head == dev->tq_tail) { /* empty queue */ tag->freq = -1; } else { // read queue entry at pos tq_tail+1 without dequeueing it i = dev->tq_tail + 1; i %= TAG_QUEUE_LEN; tag->freq = dev->tag_queue[i].freq; memcpy(&tag->tv, &dev->tag_queue[i].tv, sizeof(struct timeval)); } pthread_mutex_unlock(&dev->tag_queue_lock); } void tag_queue_advance(device_t* dev) { pthread_mutex_lock(&dev->tag_queue_lock); dev->tq_tail++; dev->tq_tail %= TAG_QUEUE_LEN; pthread_mutex_unlock(&dev->tag_queue_lock); } void* xcalloc(size_t nmemb, size_t size, const char* file, const int line, const char* func) { void* ptr = calloc(nmemb, size); if (ptr == NULL) { log(LOG_ERR, "%s:%d: %s(): calloc(%zu, %zu) failed: %s\n", file, line, func, nmemb, size, strerror(errno)); error(); } return ptr; } void* xrealloc(void* ptr, size_t size, const char* file, const int line, const char* func) { ptr = realloc(ptr, size); if (ptr == NULL) { log(LOG_ERR, "%s:%d: %s(): realloc(%zu) failed: %s\n", file, line, func, size, strerror(errno)); error(); } return ptr; } static float sin_lut[257], cos_lut[257]; void sincosf_lut_init() { for (uint32_t i = 0; i < 256; i++) SINCOSF(2.0F * M_PI * (float)i / 256.0f, sin_lut + i, cos_lut + i); sin_lut[256] = sin_lut[0]; cos_lut[256] = cos_lut[0]; } // phi range must be (0..1), rescaled to 0x0-0xFFFFFF void sincosf_lut(uint32_t phi, float* sine, float* cosine) { float v1, v2, fract; uint32_t idx; // get LUT index idx = phi >> 16; // cast fixed point fraction to float fract = (float)(phi & 0xffff) / 65536.0f; // get two adjacent values from LUT and interpolate v1 = sin_lut[idx]; v2 = sin_lut[idx + 1]; *sine = v1 + (v2 - v1) * fract; v1 = cos_lut[idx]; v2 = cos_lut[idx + 1]; *cosine = v1 + (v2 - v1) * fract; } /* librtlsdr-keenerd, (c) Kyle Keen */ double atofs(char* s) { char last; int len; double suff = 1.0; len = strlen(s); last = s[len - 1]; s[len - 1] = '\0'; switch (last) { case 'g': case 'G': suff *= 1e3; [[fallthrough]]; case 'm': case 'M': suff *= 1e3; [[fallthrough]]; case 'k': case 'K': suff *= 1e3; suff *= atof(s); s[len - 1] = last; return suff; } s[len - 1] = last; return atof(s); } double delta_sec(const timeval* start, const timeval* stop) { timeval delta; timersub(stop, start, &delta); return delta.tv_sec + delta.tv_usec / 1000000.0; } // level to/from dBFS conversion assumes level is nomalized to 1 and is based on: // https://kluedo.ub.uni-kl.de/frontdoor/deliver/index/docId/4293/file/exact_fft_measurements.pdf // // expanded form: // 20.0f * log10f(level / fft_size) + 7.54f + 10.0f * log10f(fft_size/2) - 2.38f const float& dBFS_offset(void) { static const float offset = 7.54f + 10.0f * log10f(fft_size / 2) - 2.38f; return offset; } float dBFS_to_level(const float& dBFS) { return pow(10.0, (dBFS - dBFS_offset()) / 20.0f) * fft_size; } float level_to_dBFS(const float& level) { return std::min(0.0f, 20.0f * log10f(level / fft_size) + dBFS_offset()); }