1 /* 2 * Copyright (C) 2018 3 * Matthias P. Braendli (matthias.braendli@mpb.li) 4 * 5 * Copyright (C) 2017 6 * Albrecht Lohofener (albrechtloh@gmx.de) 7 * 8 * This file is based on SDR-J 9 * Copyright (C) 2010, 2011, 2012 10 * Jan van Katwijk (J.vanKatwijk@gmail.com) 11 * 12 * This file is part of the welle.io. 13 * Many of the ideas as implemented in welle.io are derived from 14 * other work, made available through the GNU general Public License. 15 * All copyrights of the original authors are recognized. 16 * 17 * welle.io is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License as published by 19 * the Free Software Foundation; either version 2 of the License, or 20 * (at your option) any later version. 21 * 22 * welle.io is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU General Public License for more details. 26 * 27 * You should have received a copy of the GNU General Public License 28 * along with welle.io; if not, write to the Free Software 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 30 * 31 */ 32 33 #include <string> 34 #include <iostream> 35 #include <fcntl.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <sys/time.h> 39 #include <time.h> 40 #include <unistd.h> 41 42 #include "raw_file.h" 43 44 // For Qt translation if Qt is exisiting 45 #ifdef QT_CORE_LIB 46 #include <QtGlobal> 47 #else 48 #define QT_TRANSLATE_NOOP(x,y) (y) 49 #endif 50 51 static inline int64_t getMyTime(void) 52 { 53 struct timeval tv; 54 55 gettimeofday(&tv, NULL); 56 return ((int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec); 57 } 58 59 #define INPUT_FRAMEBUFFERSIZE 8 * 32768 60 61 CRAWFile::CRAWFile(RadioControllerInterface& radioController, 62 bool throttle, bool rewind) : 63 radioController(radioController), 64 throttle(throttle), 65 autoRewind(rewind), 66 fileName(""), 67 fileFormat(CRAWFileFormat::Unknown), 68 IQByteSize(1), 69 SampleBuffer(INPUT_FRAMEBUFFERSIZE), 70 SpectrumSampleBuffer(8*2048) 71 { 72 } 73 74 CRAWFile::~CRAWFile(void) 75 { 76 ExitCondition = true; 77 if (readerOK) { 78 if (thread.joinable()) { 79 thread.join(); 80 } 81 82 if (filePointer) { 83 fclose(filePointer); 84 } 85 } 86 } 87 88 void CRAWFile::setFrequency(int Frequency) 89 { 90 (void)Frequency; 91 } 92 93 int CRAWFile::getFrequency() const 94 { 95 return 0; 96 } 97 98 bool CRAWFile::restart(void) 99 { 100 if (readerOK) 101 readerPausing = false; 102 return readerOK; 103 } 104 105 bool CRAWFile::is_ok() 106 { 107 return readerOK; 108 } 109 110 void CRAWFile::stop(void) 111 { 112 if (readerOK) 113 readerPausing = true; 114 } 115 116 void CRAWFile::reset() 117 { 118 } 119 120 void CRAWFile::rewind() 121 { 122 if (filePointer) { 123 fseek(filePointer, 0, SEEK_SET); 124 endReached = false; 125 } 126 } 127 128 float CRAWFile::getGain() const 129 { 130 return 0; 131 } 132 133 float CRAWFile::setGain(int Gain) 134 { 135 (void)Gain; 136 137 return 0; 138 } 139 140 int CRAWFile::getGainCount() 141 { 142 return 0; 143 } 144 145 void CRAWFile::setAgc(bool AGC) 146 { 147 (void)AGC; 148 } 149 150 std::string CRAWFile::getDescription() 151 { 152 return "rawfile (" + fileName + ")"; 153 } 154 155 CDeviceID CRAWFile::getID() 156 { 157 return CDeviceID::RAWFILE; 158 } 159 160 bool ends_with(const std::string& value, const std::string& ending) 161 { 162 if (ending.size() > value.size()) return false; 163 return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); 164 } 165 166 void CRAWFile::setFileName(const std::string& fileName, 167 const std::string& fileFormat) 168 { 169 this->fileName = fileName; 170 171 setFileFormat(fileFormat); 172 173 filePointer = fopen(fileName.c_str(), "rb"); 174 if (filePointer == nullptr) { 175 std::clog << "RAWFile: Cannot open file: " << fileName << std::endl; 176 radioController.onMessage(message_level_t::Error, 177 QT_TRANSLATE_NOOP("CRadioController", "Cannot open file "), fileName); 178 return; 179 } 180 181 readerOK = true; 182 readerPausing = true; 183 currPos = 0; 184 thread = std::thread(&CRAWFile::run, this); 185 } 186 187 void CRAWFile::setFileHandle(int handle, const std::string& fileFormat) 188 { 189 this->fileName = "unknown"; 190 191 setFileFormat(fileFormat); 192 193 filePointer = fdopen(handle, "rb"); 194 if (filePointer == nullptr) { 195 std::clog << "RAWFile: Cannot open file: " << fileName << std::endl; 196 radioController.onMessage(message_level_t::Error, 197 QT_TRANSLATE_NOOP("CRadioController", "Cannot open file "), fileName); 198 return; 199 } 200 201 readerOK = true; 202 readerPausing = true; 203 currPos = 0; 204 thread = std::thread(&CRAWFile::run, this); 205 } 206 207 std::string CRAWFile::getFileName() const 208 { 209 return fileName; 210 } 211 212 // size is in I/Q pairs, file contains 8 bits values 213 int32_t CRAWFile::getSamples(DSPCOMPLEX* V, int32_t size) 214 { 215 if (filePointer == nullptr) 216 return 0; 217 218 while ((int32_t)(SampleBuffer.GetRingBufferReadAvailable()) < IQByteSize * size) 219 if (readerPausing) 220 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 221 else 222 std::this_thread::sleep_for(std::chrono::milliseconds(100)); 223 224 return convertSamples(SampleBuffer, V, size); 225 } 226 227 std::vector<DSPCOMPLEX> CRAWFile::getSpectrumSamples(int size) 228 { 229 std::vector<DSPCOMPLEX> buffer(size); 230 231 int sizeRead = convertSamples(SpectrumSampleBuffer, buffer.data(), size); 232 if (sizeRead < size) { 233 buffer.resize(sizeRead); 234 } 235 236 return buffer; 237 } 238 239 int32_t CRAWFile::getSamplesToRead(void) 240 { 241 return SampleBuffer.GetRingBufferReadAvailable() / 2; 242 } 243 244 void CRAWFile::run(void) 245 { 246 int32_t t; 247 int32_t bufferSize = 32768; 248 int32_t period; 249 int64_t nextStop; 250 251 if (!readerOK) 252 return; 253 254 ExitCondition = false; 255 256 period = (32768 * 1000) / (IQByteSize * 2048); // full IQs read 257 258 std::clog << "RAWFile" << "Period =" << period << std::endl; 259 std::vector<uint8_t> bi(bufferSize); 260 nextStop = getMyTime(); 261 while (!ExitCondition) { 262 if (readerPausing) { 263 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 264 nextStop = getMyTime(); 265 continue; 266 } 267 268 while (SampleBuffer.WriteSpace() < bufferSize + 10) { 269 if (ExitCondition) 270 break; 271 std::this_thread::sleep_for(std::chrono::microseconds(100)); 272 } 273 274 nextStop += period; 275 t = readBuffer(bi.data(), bufferSize); 276 if (t <= 0) { 277 for (int i = 0; i < bufferSize; i++) 278 bi[i] = 0; 279 t = bufferSize; 280 } 281 SampleBuffer.putDataIntoBuffer(bi.data(), t); 282 SpectrumSampleBuffer.putDataIntoBuffer(bi.data(), t); 283 putIntoRecordBuffer(*bi.data(), t); 284 int64_t t_to_wait = nextStop - getMyTime(); 285 if (throttle and t_to_wait > 0) 286 std::this_thread::sleep_for(std::chrono::microseconds(t_to_wait)); 287 } 288 289 std::clog << "RAWFile:" << "Read threads ends" << std::endl; 290 } 291 292 /* 293 * length is number of uints that we read. 294 */ 295 int32_t CRAWFile::readBuffer(uint8_t* data, int32_t length) 296 { 297 int32_t n; 298 299 if (!filePointer) { 300 return 0; 301 } 302 303 n = fread(data, sizeof(uint8_t), length, filePointer); 304 currPos += n; 305 if (n < length) { 306 if (autoRewind) { 307 fseek(filePointer, 0, SEEK_SET); 308 std::clog << "RAWFile:" << "End of file, restarting" << std::endl; 309 radioController.onMessage(message_level_t::Information, 310 QT_TRANSLATE_NOOP("CRadioController", "End of file, restarting")); 311 } 312 else { 313 radioController.onMessage(message_level_t::Information, QT_TRANSLATE_NOOP("CRadioController", "End of file")); 314 endReached = true; 315 return 0; 316 } 317 } 318 return n & ~01; 319 } 320 321 int32_t CRAWFile::convertSamples(RingBuffer<uint8_t>& Buffer, DSPCOMPLEX *V, int32_t size) 322 { 323 // Native endianness complex<float> requires no conversion 324 if (fileFormat == CRAWFileFormat::COMPLEXF) { 325 int32_t amount = Buffer.getDataFromBuffer(V, IQByteSize * size); 326 return amount / IQByteSize; 327 } 328 329 std::vector<uint8_t> temp((size_t)IQByteSize * (size_t)size); 330 331 int32_t amount = Buffer.getDataFromBuffer(temp.data(), IQByteSize * size); 332 333 // Unsigned 8-bit 334 if (fileFormat == CRAWFileFormat::U8) { 335 for (int i = 0; i < amount / 2; i++) 336 V[i] = DSPCOMPLEX(float(temp[2 * i] - 128) / 128.0, 337 float(temp[2 * i + 1] - 128) / 128.0); 338 } 339 // Signed 8-bit 340 else if (fileFormat == CRAWFileFormat::S8) { 341 for (int i = 0; i < amount / 2; i++) 342 V[i] = DSPCOMPLEX(float((int8_t)temp[2 * i]) / 128.0, 343 float((int8_t)temp[2 * i + 1]) / 128.0); 344 } 345 // Signed 16-bit little endian 346 else if (fileFormat == CRAWFileFormat::S16LE) { 347 for (int i = 0, j = 0; i < amount / 4; i++, j+= IQByteSize) { 348 int16_t IQ_I = (int16_t)(temp[j + 0] << 8) | temp[j + 1]; 349 int16_t IQ_Q = (int16_t)(temp[j + 2] << 8) | temp[j + 3]; 350 V[i] = DSPCOMPLEX((float)(IQ_I), (float)(IQ_Q)); 351 } 352 } 353 // Signed 16-bit big endian 354 else if (fileFormat == CRAWFileFormat::S16BE) { 355 for (int i = 0, j = 0; i < amount / 4; i++, j += IQByteSize) { 356 int16_t IQ_I = (int16_t)(temp[j + 1] << 8) | temp[j + 0]; 357 int16_t IQ_Q = (int16_t)(temp[j + 3] << 8) | temp[j + 2]; 358 V[i] = DSPCOMPLEX((float)(IQ_I), (float)(IQ_Q)); 359 } 360 } 361 362 return amount / IQByteSize; 363 } 364 365 void CRAWFile::setFileFormat(const std::string &fileFormat) 366 { 367 if (fileFormat == "u8" or 368 (fileFormat == "auto" and ends_with(fileName, ".u8.iq"))) { 369 this->fileFormat = CRAWFileFormat::U8; 370 IQByteSize = 2; 371 } 372 else if (fileFormat == "s8" or 373 (fileFormat == "auto" and ends_with(fileName, ".s8.iq"))) { 374 this->fileFormat = CRAWFileFormat::S8; 375 IQByteSize = 2; 376 } 377 else if(fileFormat == "s16le" or 378 (fileFormat == "auto" and ends_with(fileName, ".s16le.iq"))) { 379 this->fileFormat = CRAWFileFormat::S16LE; 380 IQByteSize = 4; 381 } 382 else if(fileFormat == "s16be" or 383 (fileFormat == "auto" and ends_with(fileName, ".s16be.iq"))) { 384 this->fileFormat = CRAWFileFormat::S16BE; 385 IQByteSize = 4; 386 } 387 else if(fileFormat == "cf32" or 388 (fileFormat == "auto" and ends_with(fileName, ".cf32.iq"))) { 389 this->fileFormat = CRAWFileFormat::COMPLEXF; 390 IQByteSize = 8; 391 } 392 else if (fileFormat == "auto") { 393 // Default to u8 for backward compatibility 394 this->fileFormat = CRAWFileFormat::U8; 395 IQByteSize = 2; 396 } 397 else { 398 this->fileFormat = CRAWFileFormat::Unknown; 399 std::clog << "RAWFile: unknown file format" << std::endl; 400 radioController.onMessage(message_level_t::Error, 401 QT_TRANSLATE_NOOP("CRadioController", "Unknown RAW file format")); 402 } 403 } 404