input-file.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * input-file.cpp
  3. * binary file specific routines
  4. *
  5. * Copyright (c) 2015-2021 Tomasz Lemiech <szpajder@gmail.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include "input-file.h" // file_dev_data_t
  21. #include <assert.h>
  22. #include <limits.h> // SCHAR_MAX
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <syslog.h> // FIXME: get rid of this
  26. #include <unistd.h> // usleep
  27. #include <libconfig.h++> // Setting
  28. #include "input-common.h" // input_t, sample_format_t, input_state_t, MODULE_EXPORT
  29. #include "input-helpers.h" // circbuffer_append
  30. #include "rtl_airband.h" // do_exit, fft_size, debug_print, XCALLOC, error()
  31. using namespace std;
  32. int file_parse_config(input_t* const input, libconfig::Setting& cfg) {
  33. assert(input != NULL);
  34. file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
  35. assert(dev_data != NULL);
  36. if (cfg.exists("filepath")) {
  37. dev_data->filepath = strdup(cfg["filepath"]);
  38. } else {
  39. cerr << "File configuration error: no 'filepath' given\n";
  40. error();
  41. }
  42. if (cfg.exists("speedup_factor")) {
  43. if (cfg["speedup_factor"].getType() == libconfig::Setting::TypeInt) {
  44. dev_data->speedup_factor = (int)cfg["speedup_factor"];
  45. } else if (cfg["speedup_factor"].getType() == libconfig::Setting::TypeFloat) {
  46. dev_data->speedup_factor = (float)cfg["speedup_factor"];
  47. } else {
  48. cerr << "File configuration error: 'speedup_factor' must be a float or int if set\n";
  49. error();
  50. }
  51. if (dev_data->speedup_factor <= 0.0) {
  52. cerr << "File configuration error: 'speedup_factor' must be >= 0.0\n";
  53. error();
  54. }
  55. } else {
  56. dev_data->speedup_factor = 4;
  57. }
  58. return 0;
  59. }
  60. int file_init(input_t* const input) {
  61. assert(input != NULL);
  62. file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
  63. assert(dev_data != NULL);
  64. dev_data->input_file = fopen(dev_data->filepath, "rb");
  65. if (!dev_data->input_file) {
  66. cerr << "File input failed to open '" << dev_data->filepath << "' - " << strerror(errno) << endl;
  67. error();
  68. }
  69. log(LOG_INFO, "File input %s initialized\n", dev_data->filepath);
  70. return 0;
  71. }
  72. void* file_rx_thread(void* ctx) {
  73. input_t* input = (input_t*)ctx;
  74. assert(input != NULL);
  75. assert(input->sample_rate != 0);
  76. file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
  77. assert(dev_data != NULL);
  78. assert(dev_data->input_file != NULL);
  79. assert(dev_data->speedup_factor != 0.0);
  80. size_t buf_len = (input->buf_size / 2) - 1;
  81. unsigned char* buf = (unsigned char*)XCALLOC(1, buf_len);
  82. float time_per_byte_ms = 1000 / (input->sample_rate * input->bytes_per_sample * 2 * dev_data->speedup_factor);
  83. log(LOG_DEBUG, "sample_rate: %d, bytes_per_sample: %d, speedup_factor: %f, time_per_byte_ms: %f\n", input->sample_rate, input->bytes_per_sample, dev_data->speedup_factor, time_per_byte_ms);
  84. input->state = INPUT_RUNNING;
  85. while (true) {
  86. if (do_exit) {
  87. break;
  88. }
  89. if (feof(dev_data->input_file)) {
  90. log(LOG_INFO, "File '%s': hit end of file at %d, disabling\n", dev_data->filepath, ftell(dev_data->input_file));
  91. input->state = INPUT_FAILED;
  92. break;
  93. }
  94. if (ferror(dev_data->input_file)) {
  95. log(LOG_ERR, "File '%s': read error (%d), disabling\n", dev_data->filepath, ferror(dev_data->input_file));
  96. input->state = INPUT_FAILED;
  97. break;
  98. }
  99. timeval start;
  100. gettimeofday(&start, NULL);
  101. size_t space_left;
  102. pthread_mutex_lock(&input->buffer_lock);
  103. if (input->bufe >= input->bufs) {
  104. space_left = input->bufs + (input->buf_size - input->bufe);
  105. } else {
  106. space_left = input->bufs - input->bufe;
  107. }
  108. pthread_mutex_unlock(&input->buffer_lock);
  109. if (space_left > buf_len) {
  110. size_t len = fread(buf, sizeof(unsigned char), buf_len, dev_data->input_file);
  111. circbuffer_append(input, buf, len);
  112. timeval end;
  113. gettimeofday(&end, NULL);
  114. int time_taken_ms = delta_sec(&start, &end) * 1000;
  115. int sleep_time_ms = len * time_per_byte_ms - time_taken_ms;
  116. if (sleep_time_ms > 0) {
  117. SLEEP(sleep_time_ms);
  118. }
  119. } else {
  120. SLEEP(10);
  121. }
  122. }
  123. free(buf);
  124. return 0;
  125. }
  126. int file_set_centerfreq(input_t* const /*input*/, int const /*centerfreq*/) {
  127. return 0;
  128. }
  129. int file_stop(input_t* const input) {
  130. assert(input != NULL);
  131. file_dev_data_t* dev_data = (file_dev_data_t*)input->dev_data;
  132. assert(dev_data != NULL);
  133. fclose(dev_data->input_file);
  134. dev_data->input_file = NULL;
  135. return 0;
  136. }
  137. MODULE_EXPORT input_t* file_input_new() {
  138. file_dev_data_t* dev_data = (file_dev_data_t*)XCALLOC(1, sizeof(file_dev_data_t));
  139. dev_data->input_file = NULL;
  140. dev_data->speedup_factor = 0.0;
  141. input_t* input = (input_t*)XCALLOC(1, sizeof(input_t));
  142. input->dev_data = dev_data;
  143. input->state = INPUT_UNKNOWN;
  144. input->sfmt = SFMT_U8;
  145. input->fullscale = (float)SCHAR_MAX - 0.5f;
  146. input->bytes_per_sample = sizeof(unsigned char);
  147. input->sample_rate = 0;
  148. input->parse_config = &file_parse_config;
  149. input->init = &file_init;
  150. input->run_rx_thread = &file_rx_thread;
  151. input->set_centerfreq = &file_set_centerfreq;
  152. input->stop = &file_stop;
  153. return input;
  154. }