From b8126663ab6a2d45d6bdd4ad27846e2ab1cee9f9 Mon Sep 17 00:00:00 2001 From: hayati ayguen Date: Sun, 10 Mar 2019 20:11:18 +0000 Subject: [PATCH] added option '-H' to write wave Header for rtl_sdr and rtl_fm * option is only used when output is to file - not with stdout * using multiple frequencies with rtl_fm .., will only write 1st frequency into wave header Signed-off-by: hayati ayguen --- CMakeLists.txt | 2 +- src/convenience/convenience.c | 150 ++++++++++++++++++++++++++++++++++ src/convenience/convenience.h | 19 +++++ src/rtl_fm.c | 20 ++++- src/rtl_sdr.c | 19 ++++- 5 files changed, 207 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bde3bf4..503f161 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ if(RTL_STATIC_BUILD) # Special MINGW stuff here # see https://cmake.org/pipermail/cmake/2012-September/051970.html # see http://stackoverflow.com/questions/13768515/how-to-do-static-linking-of-libwinpthread-1-dll-in-mingw - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static-libgcc -s") set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static-libgcc -static-libstdc++ -s") diff --git a/src/convenience/convenience.c b/src/convenience/convenience.c index 997f7fc..69e4a94 100755 --- a/src/convenience/convenience.c +++ b/src/convenience/convenience.c @@ -23,9 +23,12 @@ #include #include #include +#include +#include #ifndef _WIN32 #include +#include #else #include #include @@ -397,4 +400,151 @@ void executeInBackground( char * file, char * args, char * searchStr[], char * r #endif + +#pragma pack(push) +#pragma pack(1) + +typedef struct { + uint16_t wYear; /* 1601 through 30827 */ + uint16_t wMonth; /* 1..12 */ + uint16_t wDayOfWeek; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */ + uint16_t wDay; /* 1 .. 31 */ + uint16_t wHour; /* 0 .. 23 */ + uint16_t wMinute; /* 0 .. 59 */ + uint16_t wSecond; /* 0 .. 59 */ + uint16_t wMilliseconds; /* 0 .. 999 */ +} Wind_SystemTime; + + +typedef struct +{ + /* RIFF header */ + char riffID[4]; /* "RIFF" string */ + uint32_t riffSize; /* full filesize - 8 bytes (maybe with some byte missing...) */ + char waveID[4]; /* "WAVE" string */ + + /* FMT header */ + char fmtID[4]; /* = "FMT " */ + uint32_t fmtSize; + int16_t wFormatTag; + int16_t nChannels; + int32_t nSamplesPerSec; + int32_t nAvgBytesPerSec; + int16_t nBlockAlign; + int16_t nBitsPerSample; + + /* auxi header - used by SpectraVue / rfspace / HDSDR / .. */ + char auxiID[4]; /* ="auxi" (chunk rfspace) */ + uint32_t auxiSize; + Wind_SystemTime StartTime; + Wind_SystemTime StopTime; + uint32_t centerFreq; /* receiver center frequency */ + uint32_t ADsamplerate; /* A/D sample frequency before downsampling */ + uint32_t IFFrequency; /* IF freq if an external down converter is used */ + uint32_t Bandwidth; /* displayable BW if you want to limit the display to less than Nyquist band */ + int32_t IQOffset; /* DC offset of the I and Q channels in 1/1000's of a count */ + int32_t Unused2; + int32_t Unused3; + int32_t Unused4; + int32_t Unused5; + + /* DATA header */ + char dataID[4]; + uint32_t dataSize; +} waveFileHeader; + +static waveFileHeader waveHdr; + +#pragma pack(pop) + + +uint32_t waveDataSize = 0; +static int waveHdrStarted = 0; + +void waveSetTime(Wind_SystemTime *p) +{ + struct timeval tv; + struct tm t; + + gettimeofday(&tv, NULL); + p->wMilliseconds = tv.tv_usec / 1000; + +#ifdef _WIN32 + t = *gmtime(&tv.tv_sec); +#else + gmtime_r(&tv.tv_sec, &t); +#endif + + p->wYear = t.tm_year + 1900; /* 1601 through 30827 */ + p->wMonth = t.tm_mon + 1; /* 1..12 */ + p->wDayOfWeek = t.tm_wday; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */ + p->wDay = t.tm_mday; /* 1 .. 31 */ + p->wHour = t.tm_hour; /* 0 .. 23 */ + p->wMinute = t.tm_min; /* 0 .. 59 */ + p->wSecond = t.tm_sec; /* 0 .. 59 */ +} + +void wavePrepareHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels) +{ + int bytesPerSample = bitsPerSample / 8; + int bytesPerFrame = bytesPerSample * numChannels; + + strncpy( waveHdr.riffID, "RIFF", 4 ); + waveHdr.riffSize = sizeof(waveFileHeader) - 8; /* to fix */ + strncpy( waveHdr.waveID, "WAVE", 4 ); + + strncpy( waveHdr.fmtID, "fmt ", 4 ); + waveHdr.fmtSize = 16; + waveHdr.wFormatTag = 1; /* PCM */ + waveHdr.nChannels = numChannels; /* I and Q channels */ + waveHdr.nSamplesPerSec = samplerate; + waveHdr.nAvgBytesPerSec = samplerate * bytesPerFrame; + waveHdr.nBlockAlign = waveHdr.nChannels; + waveHdr.nBitsPerSample = bitsPerSample; + + strncpy( waveHdr.auxiID, "auxi", 4 ); + waveHdr.auxiSize = 2 * sizeof(Wind_SystemTime) + 9 * sizeof(int32_t); /* = 2 * 16 + 9 * 4 = 68 */ + waveSetTime( &waveHdr.StartTime ); + waveHdr.StopTime = waveHdr.StartTime; /* to fix */ + waveHdr.centerFreq = freq; + waveHdr.ADsamplerate = samplerate; + waveHdr.IFFrequency = 0; + waveHdr.Bandwidth = 0; + waveHdr.IQOffset = 0; + waveHdr.Unused2 = 0; + waveHdr.Unused3 = 0; + waveHdr.Unused4 = 0; + waveHdr.Unused5 = 0; + + strncpy( waveHdr.dataID, "data", 4 ); + waveHdr.dataSize = 0; /* to fix later */ + waveDataSize = 0; +} + +void waveWriteHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels, FILE * f) +{ + if (f != stdout) { + assert( !waveHdrStarted ); + wavePrepareHeader(samplerate, freq, bitsPerSample, numChannels); + fwrite(&waveHdr, sizeof(waveFileHeader), 1, f); + waveHdrStarted = 1; + } +} + +void waveFinalizeHeader(FILE * f) +{ + if (f != stdout) { + assert( waveHdrStarted ); + waveSetTime( &waveHdr.StopTime ); + waveHdr.dataSize = waveDataSize; + waveHdr.riffSize += waveDataSize; + + fseek(f, 0, SEEK_SET); + fwrite(&waveHdr, sizeof(waveFileHeader), 1, f); + waveHdrStarted = 0; + } +} + + + // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab diff --git a/src/convenience/convenience.h b/src/convenience/convenience.h index 7da641b..fa812ad 100644 --- a/src/convenience/convenience.h +++ b/src/convenience/convenience.h @@ -17,6 +17,8 @@ #ifndef __CONVENIENCE_H #define __CONVENIENCE_H +#include + /* a collection of user friendly tools */ @@ -157,4 +159,21 @@ int verbose_device_search(char *s); void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ); + +/*! + * helper functions to write and finalize wave headers + * with compatibility to some SDR programs - showing frequency: + * raw sample data still have to be written by caller to FILE*. + * call waveWriteHeader() before writing anything to to file + * and call waveFinalizeHeader() afterwards, + * AND count/increment the written raw size in variable 'waveDataSize'. + * stdout/stdout can't be used, because seek to begin isn't possible. + * + */ + +extern uint32_t waveDataSize; +void waveWriteHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels, FILE * f); +void waveFinalizeHeader(); + + #endif /*__CONVENIENCE_H*/ diff --git a/src/rtl_fm.c b/src/rtl_fm.c index 29ec51a..825518e 100644 --- a/src/rtl_fm.c +++ b/src/rtl_fm.c @@ -293,6 +293,8 @@ void usage(void) "\t dagc=:ds=:T=\n" "\t[-q dc_avg_factor for option rdc (default: 9)]\n" "\t[-n disables demodulation output to stdout/file]\n" + "\t[-H write wave Header to file (default: off)]\n" + "\t limitation: only 1st tuned frequency will be written into the header!\n" "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" @@ -1390,6 +1392,7 @@ static void *output_thread_fn(void *arg) safe_cond_wait(&s->ready, &s->ready_m); pthread_rwlock_rdlock(&s->rw); fwrite(s->result, 2, s->result_len, s->file); + waveDataSize += 2 * s->result_len; pthread_rwlock_unlock(&s->rw); } return 0; @@ -1695,6 +1698,7 @@ int main(int argc, char **argv) #endif int r, opt; int dev_given = 0; + int writeWav = 0; int custom_ppm = 0; int enable_biastee = 0; const char * rtlOpts = NULL; @@ -1708,7 +1712,7 @@ int main(int argc, char **argv) controller_init(&controller); cmd_init(&cmd); - while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:O:F:A:M:hTC:B:m:L:q:c:w:W:D:nv")) != -1) { + while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:E:O:F:A:M:hTC:B:m:L:q:c:w:W:D:nHv")) != -1) { switch (opt) { case 'd': dongle.dev_index = verbose_device_search(optarg); @@ -1847,6 +1851,9 @@ int main(int argc, char **argv) else ds_threshold = ds_temp; break; + case 'H': + writeWav = 1; + break; case 'v': ++verbosity; break; @@ -1972,7 +1979,15 @@ int main(int argc, char **argv) exit(1); } else + { fprintf(stderr, "Open %s for write\n", output.filename); + if (writeWav) { + int nChan = (demod.mode_demod == &raw_demod) ? 2 : 1; + int srate = (demod.rate_out2 > 0) ? demod.rate_out2 : demod.rate_out; + uint32_t f = controller.freqs[0]; /* only 1st frequency!!! */ + waveWriteHeader(srate, f, 16, nChan, output.file); + } + } } //r = rtlsdr_set_testmode(dongle.dev, 1); @@ -2019,6 +2034,9 @@ int main(int argc, char **argv) } if (output.file != stdout) { + if (writeWav) { + waveFinalizeHeader(output.file); + } fclose(output.file);} rtlsdr_close(dongle.dev); diff --git a/src/rtl_sdr.c b/src/rtl_sdr.c index 2390a07..8cc81f0 100644 --- a/src/rtl_sdr.c +++ b/src/rtl_sdr.c @@ -60,6 +60,7 @@ void usage(void) "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-n number of samples to read (default: 0, infinite)]\n" "\t[-S force sync output (default: async)]\n" + "\t[-H write wave Header to file (default: off)]\n" "\tfilename (a '-' dumps samples to stdout)\n\n"); exit(1); } @@ -101,6 +102,10 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) fprintf(stderr, "Short write, samples lost, exiting!\n"); rtlsdr_cancel_async(dev); } + else + { + waveDataSize += len; + } if (bytes_to_read > 0) bytes_to_read -= len; @@ -123,13 +128,14 @@ int main(int argc, char **argv) const char * rtlOpts = NULL; int dev_index = 0; int dev_given = 0; + int writeWav = 0; uint32_t frequency = 100000000; uint32_t bandwidth = DEFAULT_BANDWIDTH; uint32_t samp_rate = DEFAULT_SAMPLE_RATE; uint32_t out_block_size = DEFAULT_BUF_LENGTH; int verbosity = 0; - while ((opt = getopt(argc, argv, "d:f:g:s:w:b:n:p:O:Sv")) != -1) { + while ((opt = getopt(argc, argv, "d:f:g:s:w:b:n:p:O:SHv")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); @@ -162,6 +168,9 @@ int main(int argc, char **argv) case 'S': sync_mode = 1; break; + case 'H': + writeWav = 1; + break; case 'v': ++verbosity; break; @@ -249,6 +258,9 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to open %s\n", filename); goto out; } + if (writeWav) { + waveWriteHeader(samp_rate, frequency, 8, 2, file); + } } /* Reset endpoint before we start reading from it (mandatory) */ @@ -272,6 +284,7 @@ int main(int argc, char **argv) fprintf(stderr, "Short write, samples lost, exiting!\n"); break; } + waveDataSize += n_read; if ((uint32_t)n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); @@ -287,6 +300,10 @@ int main(int argc, char **argv) 0, out_block_size); } + if (writeWav) { + waveFinalizeHeader(file); + } + if (do_exit) fprintf(stderr, "\nUser cancel, exiting...\n"); else