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