diff options
Diffstat (limited to 'gnuradio-core/src')
-rw-r--r-- | gnuradio-core/src/lib/io/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/gr_file_meta_sink.cc | 455 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/gr_file_meta_sink.h | 169 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/gr_file_meta_sink.i | 80 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/gr_file_sink_base.cc | 4 | ||||
-rw-r--r-- | gnuradio-core/src/lib/io/io.i | 2 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/parse_file_metadata.py | 174 |
8 files changed, 886 insertions, 0 deletions
diff --git a/gnuradio-core/src/lib/io/CMakeLists.txt b/gnuradio-core/src/lib/io/CMakeLists.txt index 59ca06b5a..c5b85d304 100644 --- a/gnuradio-core/src/lib/io/CMakeLists.txt +++ b/gnuradio-core/src/lib/io/CMakeLists.txt @@ -85,6 +85,7 @@ endif(ENABLE_PYTHON) ######################################################################## set(gr_core_io_triple_threats gr_file_sink + gr_file_meta_sink gr_file_sink_base gr_file_source gr_file_descriptor_sink diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.cc b/gnuradio-core/src/lib/io/gr_file_meta_sink.cc new file mode 100644 index 000000000..43900bcd9 --- /dev/null +++ b/gnuradio-core/src/lib/io/gr_file_meta_sink.cc @@ -0,0 +1,455 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_file_meta_sink.h> +#include <gr_io_signature.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <gruel/thread.h> +#include <stdexcept> + +// win32 (mingw/msvc) specific +#ifdef HAVE_IO_H +#include <io.h> +#endif +#ifdef O_BINARY +#define OUR_O_BINARY O_BINARY +#else +#define OUR_O_BINARY 0 +#endif + +// should be handled via configure +#ifdef O_LARGEFILE +#define OUR_O_LARGEFILE O_LARGEFILE +#else +#define OUR_O_LARGEFILE 0 +#endif + +gr_file_meta_sink_sptr +gr_make_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate, double relative_rate, + gr_file_types type, bool complex, + size_t max_segment_size, + const std::string &extra_dict, + bool detached_header) +{ + return gnuradio::get_initial_sptr + (new gr_file_meta_sink(itemsize, filename, + samp_rate, relative_rate, + type, complex, + max_segment_size, + extra_dict, + detached_header)); +} + +gr_file_meta_sink::gr_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate, double relative_rate, + gr_file_types type, bool complex, + size_t max_segment_size, + const std::string &extra_dict, + bool detached_header) + : gr_sync_block("file_meta_sink", + gr_make_io_signature(1, 1, itemsize), + gr_make_io_signature(0, 0, 0)), + d_itemsize(itemsize), + d_samp_rate(samp_rate), d_relative_rate(relative_rate), + d_max_seg_size(max_segment_size), d_total_seg_size(0), + d_updated(false), d_unbuffered(false) +{ + d_fp = 0; + d_new_fp = 0; + d_hdr_fp = 0; + d_new_hdr_fp = 0; + + if(detached_header == true) + d_state = STATE_DETACHED; + else + d_state = STATE_INLINE; + + if(!open(filename)) + throw std::runtime_error("file_meta_sink: can't open file\n"); + + pmt_t timestamp = pmt_make_tuple(pmt_from_uint64(0), + pmt_from_double(0)); + + // handle extra dictionary + d_extra = pmt_make_dict(); + if(extra_dict.size() > 0) { + pmt_t extras = pmt_deserialize_str(extra_dict); + pmt_t keys = pmt_dict_keys(extras); + pmt_t vals = pmt_dict_values(extras); + size_t nitems = pmt_length(keys); + for(size_t i = 0; i < nitems; i++) { + d_extra = pmt_dict_add(d_extra, + pmt_nth(i, keys), + pmt_nth(i, vals)); + } + } + + d_extra_size = pmt_serialize_str(d_extra).size(); + + d_header = pmt_make_dict(); + d_header = pmt_dict_add(d_header, mp("version"), mp(METADATA_VERSION)); + d_header = pmt_dict_add(d_header, mp("rx_rate"), mp(samp_rate)); + d_header = pmt_dict_add(d_header, mp("rx_time"), timestamp); + d_header = pmt_dict_add(d_header, mp("type"), pmt_from_long(type)); + d_header = pmt_dict_add(d_header, mp("cplx"), complex ? PMT_T : PMT_F); + d_header = pmt_dict_add(d_header, mp("strt"), pmt_from_uint64(METADATA_HEADER_SIZE+d_extra_size)); + d_header = pmt_dict_add(d_header, mp("size"), pmt_from_uint64(0)); + + do_update(); + + if(d_state == STATE_DETACHED) + write_header(d_hdr_fp, d_header, d_extra); + else + write_header(d_fp, d_header, d_extra); +} + +gr_file_meta_sink::~gr_file_meta_sink() +{ + update_last_header(); + + close(); + + if(d_fp) { + fclose(d_fp); + d_fp = 0; + } + + if(d_state == STATE_DETACHED) { + if(d_hdr_fp) { + fclose(d_hdr_fp); + d_hdr_fp = 0; + } + } +} + +bool +gr_file_meta_sink::open(const char *filename) +{ + bool ret = true; + if(d_state == STATE_DETACHED) { + std::stringstream s; + s << filename << ".hdr"; + ret = _open(&d_new_hdr_fp, s.str().c_str()); + } + + ret = ret && _open(&d_new_fp, filename); + d_updated = true; + return ret; +} + +bool +gr_file_meta_sink::_open(FILE **fp, const char *filename) +{ + gruel::scoped_lock guard(d_mutex); // hold mutex for duration of this function + + bool ret = true; + int fd; + + if((fd = ::open(filename, + O_WRONLY|O_CREAT|O_TRUNC|OUR_O_LARGEFILE|OUR_O_BINARY, + 0664)) < 0){ + perror(filename); + return false; + } + + if(*fp) { // if we've already got a new one open, close it + fclose(*fp); + fp = 0; + } + + if((*fp = fdopen(fd, "wb")) == NULL) { + perror(filename); + ::close(fd); // don't leak file descriptor if fdopen fails. + } + + ret = fp != 0; + + return ret; +} + +void +gr_file_meta_sink::close() +{ + gruel::scoped_lock guard(d_mutex); // hold mutex for duration of this function + if(d_state == STATE_DETACHED) { + if(d_new_hdr_fp) { + fclose(d_new_hdr_fp); + d_new_hdr_fp = 0; + } + } + + if(d_new_fp) { + fclose(d_new_fp); + d_new_fp = 0; + } + d_updated = true; +} + +void +gr_file_meta_sink::do_update() +{ + if(d_updated) { + gruel::scoped_lock guard(d_mutex); // hold mutex for duration of this block + if(d_state == STATE_DETACHED) { + if(d_hdr_fp) + fclose(d_hdr_fp); + d_hdr_fp = d_new_hdr_fp; // install new file pointer + d_new_hdr_fp = 0; + } + + if(d_fp) + fclose(d_fp); + d_fp = d_new_fp; // install new file pointer + d_new_fp = 0; + + d_updated = false; + } +} + +void +gr_file_meta_sink::write_header(FILE *fp, pmt_t header, pmt_t extra) +{ + std::string header_str = pmt_serialize_str(header); + std::string extra_str = pmt_serialize_str(extra); + + if((header_str.size() != METADATA_HEADER_SIZE) && (extra_str.size() != d_extra_size)) + throw std::runtime_error("file_meta_sink: header or extras is wrong size.\n"); + + size_t nwritten = 0; + while(nwritten < header_str.size()) { + std::string sub = header_str.substr(nwritten); + int count = fwrite(sub.c_str(), sizeof(char), sub.size(), fp); + nwritten += count; + if((count == 0) && (ferror(fp))) { + fclose(fp); + throw std::runtime_error("file_meta_sink: error writing header to file.\n"); + } + } + + nwritten = 0; + while(nwritten < extra_str.size()) { + std::string sub = extra_str.substr(nwritten); + int count = fwrite(sub.c_str(), sizeof(char), sub.size(), fp); + nwritten += count; + if((count == 0) && (ferror(fp))) { + fclose(fp); + throw std::runtime_error("file_meta_sink: error writing extra to file.\n"); + } + } +} + +void +gr_file_meta_sink::update_header(pmt_t key, pmt_t value) +{ + // Special handling caveat to transform rate from radio source into + // the rate at this sink. + if(pmt_eq(key, mp("rx_rate"))) { + d_samp_rate = pmt_to_double(value); + value = pmt_from_double(d_samp_rate*d_relative_rate); + } + + // If the tag is not part of the standard header, we put it into the + // extra data, which either updates the current dictionary or adds a + // new item. + if(pmt_dict_has_key(d_header, key)) { + d_header = pmt_dict_add(d_header, key, value); + } + else { + d_extra = pmt_dict_add(d_extra, key, value); + d_extra_size = pmt_serialize_str(d_extra).size(); + } +} + +void +gr_file_meta_sink::update_last_header() +{ + if(d_state == STATE_DETACHED) + update_last_header_detached(); + else + update_last_header_inline(); +} + +void +gr_file_meta_sink::update_last_header_inline() +{ + // Update the last header info with the number of samples this + // block represents. + + size_t hdrlen = pmt_to_uint64(pmt_dict_ref(d_header, mp("strt"), PMT_NIL)); + size_t seg_size = d_itemsize*d_total_seg_size; + pmt_t s = pmt_from_uint64(seg_size); + update_header(mp("size"), s); + update_header(mp("strt"), pmt_from_uint64(METADATA_HEADER_SIZE+d_extra_size)); + fseek(d_fp, -seg_size-hdrlen, SEEK_CUR); + write_header(d_fp, d_header, d_extra); + fseek(d_fp, seg_size, SEEK_CUR); +} + +void +gr_file_meta_sink::update_last_header_detached() +{ + // Update the last header info with the number of samples this + // block represents. + size_t hdrlen = pmt_to_uint64(pmt_dict_ref(d_header, mp("strt"), PMT_NIL)); + size_t seg_size = d_itemsize*d_total_seg_size; + pmt_t s = pmt_from_uint64(seg_size); + update_header(mp("size"), s); + update_header(mp("strt"), pmt_from_uint64(METADATA_HEADER_SIZE+d_extra_size)); + fseek(d_hdr_fp, -hdrlen, SEEK_CUR); + write_header(d_hdr_fp, d_header, d_extra); +} + +void +gr_file_meta_sink::write_and_update() +{ + // New header, so set current size of chunk to 0 and start of chunk + // based on current index + header size. + //uint64_t loc = get_last_header_loc(); + pmt_t s = pmt_from_uint64(0); + update_header(mp("size"), s); + + // If we have multiple tags on the same offset, this makes + // sure we just overwrite the same header each time instead + // of creating a new header per tag. + s = pmt_from_uint64(METADATA_HEADER_SIZE + d_extra_size); + update_header(mp("strt"), s); + + if(d_state == STATE_DETACHED) + write_header(d_hdr_fp, d_header, d_extra); + else + write_header(d_fp, d_header, d_extra); +} + +void +gr_file_meta_sink::update_rx_time() +{ + pmt_t rx_time = pmt_string_to_symbol("rx_time"); + pmt_t r = pmt_dict_ref(d_header, rx_time, PMT_NIL); + uint64_t secs = pmt_to_uint64(pmt_tuple_ref(r, 0)); + double fracs = pmt_to_double(pmt_tuple_ref(r, 1)); + double diff = d_total_seg_size / (d_samp_rate*d_relative_rate); + + //std::cerr << "old secs: " << secs << std::endl; + //std::cerr << "old fracs: " << fracs << std::endl; + //std::cerr << "seg size: " << d_total_seg_size << std::endl; + //std::cerr << "diff: " << diff << std::endl; + + fracs += diff; + uint64_t new_secs = static_cast<uint64_t>(fracs); + secs += new_secs; + fracs -= new_secs; + + //std::cerr << "new secs: " << secs << std::endl; + //std::cerr << "new fracs: " << fracs << std::endl << std::endl; + + r = pmt_make_tuple(pmt_from_uint64(secs), pmt_from_double(fracs)); + d_header = pmt_dict_add(d_header, rx_time, r); +} + +int +gr_file_meta_sink::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + char *inbuf = (char*)input_items[0]; + int nwritten = 0; + + do_update(); // update d_fp is reqd + + if(!d_fp) + return noutput_items; // drop output on the floor + + uint64_t abs_N = nitems_read(0); + uint64_t end_N = abs_N + (uint64_t)(noutput_items); + std::vector<gr_tag_t> all_tags; + get_tags_in_range(all_tags, 0, abs_N, end_N); + + std::vector<gr_tag_t>::iterator itr; + for(itr = all_tags.begin(); itr != all_tags.end(); itr++) { + int item_offset = (int)(itr->offset - abs_N); + + // Write date to file up to the next tag location + while(nwritten < item_offset) { + size_t towrite = std::min(d_max_seg_size - d_total_seg_size, + (size_t)(item_offset - nwritten)); + int count = fwrite(inbuf, d_itemsize, towrite, d_fp); + if(count == 0) // FIXME add error handling + break; + nwritten += count; + inbuf += count * d_itemsize; + + d_total_seg_size += count; + + // Only add a new header if we are not at the position of the + // next tag + if((d_total_seg_size == d_max_seg_size) && + (nwritten < item_offset)) { + update_last_header(); + update_rx_time(); + write_and_update(); + d_total_seg_size = 0; + } + } + + if(d_total_seg_size > 0) { + update_last_header(); + update_header(itr->key, itr->value); + write_and_update(); + d_total_seg_size = 0; + } + else { + update_header(itr->key, itr->value); + update_last_header(); + } + } + + // Finish up the rest of the data after tags + while(nwritten < noutput_items) { + size_t towrite = std::min(d_max_seg_size - d_total_seg_size, + (size_t)(noutput_items - nwritten)); + int count = fwrite(inbuf, d_itemsize, towrite, d_fp); + if(count == 0) // FIXME add error handling + break; + nwritten += count; + inbuf += count * d_itemsize; + + d_total_seg_size += count; + if(d_total_seg_size == d_max_seg_size) { + update_last_header(); + update_rx_time(); + write_and_update(); + d_total_seg_size = 0; + } + } + + if(d_unbuffered) + fflush(d_fp); + + return nwritten; +} diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.h b/gnuradio-core/src/lib/io/gr_file_meta_sink.h new file mode 100644 index 000000000..c0636d66a --- /dev/null +++ b/gnuradio-core/src/lib/io/gr_file_meta_sink.h @@ -0,0 +1,169 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_FILE_META_SINK_H +#define INCLUDED_GR_FILE_META_SINK_H + +#include <gr_core_api.h> +#include <gr_sync_block.h> +#include <gr_file_sink_base.h> +#include <gruel/pmt.h> + +using namespace pmt; + +const char METADATA_VERSION = 0; +const size_t METADATA_HEADER_SIZE = 134; + +enum gr_file_types { + GR_FILE_BYTE=0, + GR_FILE_CHAR=0, + GR_FILE_SHORT=1, + GR_FILE_INT, + GR_FILE_LONG, + GR_FILE_LONG_LONG, + GR_FILE_FLOAT, + GR_FILE_DOUBLE, +}; + +class gr_file_meta_sink; +typedef boost::shared_ptr<gr_file_meta_sink> gr_file_meta_sink_sptr; + +GR_CORE_API gr_file_meta_sink_sptr +gr_make_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate=1, double relative_rate=1, + gr_file_types type=GR_FILE_FLOAT, bool complex=true, + size_t max_segment_size=1000000, + const std::string &extra_dict="", + bool detached_header=false); + +/*! + * \brief Write stream to file with meta-data headers. + * \ingroup sink_blk + * + * These files represent data as binary information in between + * meta-data headers. The headers contain information about the type + * of data and properties of the data in the next segment of + * samples. The information includes: + * + * rx_rate (double): sample rate of data. + * rx_time (uint64_t, double): time stamp of first sample in segment. + * type (gr_file_types as int32_t): data type. + * cplx (bool): Is data complex? + * strt (uint64_t): Starting byte of data in this segment. + * size (uint64_t): Size in bytes of data in this segment. + * + * Tags can be sent to the file to update the information, which will + * create a new header. Headers are found by searching from the first + * header (at position 0 in the file) and reading where the data + * segment starts plus the data segment size. Following will either be + * a new header or EOF. + */ +class GR_CORE_API gr_file_meta_sink : public gr_sync_block +{ + /*! + * \brief Create a meta-data file sink. + * + * \param itemsize (size_t): Size of data type. + * \param filename (string): Name of file to write data to. + * \param samp_rate (double): Sample rate of data. If sample rate will be + * set by a tag, such as rx_tag from a UHD source, this is + * basically ignored. + * \param relative_rate (double): Rate chance from source of sample + * rate tag to sink. + * \param type (gr_file_types): Data type (int, float, etc.) + * \param complex (bool): If data stream is complex + * \param max_segment_size (size_t): Length of a single segment + * before the header is repeated (in items). + * \param extra_dict (string): a serialized PMT dictionary of extra + * information. Currently not supported. + * \param detached_header (bool): Set to true to store the header + * info in a separate file (named filename.hdr) + */ + friend GR_CORE_API gr_file_meta_sink_sptr + gr_make_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate, double relative_rate, + gr_file_types type, bool complex, + size_t max_segment_size, + const std::string &extra_dict, + bool detached_header); + + private: + enum meta_state_t { + STATE_INLINE=0, + STATE_DETACHED + }; + + + size_t d_itemsize; + double d_samp_rate; + double d_relative_rate; + size_t d_max_seg_size; + size_t d_total_seg_size; + pmt_t d_header; + pmt_t d_extra; + size_t d_extra_size; + bool d_updated; + bool d_unbuffered; + + boost::mutex d_mutex; + FILE *d_new_fp, *d_new_hdr_fp; + FILE *d_fp, *d_hdr_fp; + meta_state_t d_state; + + protected: + gr_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate=1, double relative_rate=1, + gr_file_types type=GR_FILE_FLOAT, bool complex=true, + size_t max_segment_size=1000000, + const std::string &extra_dict="", + bool detached_header=false); + + void write_header(FILE *fp, pmt_t header, pmt_t extra); + void update_header(pmt_t key, pmt_t value); + void update_last_header(); + void update_last_header_inline(); + void update_last_header_detached(); + void write_and_update(); + void update_rx_time(); + + bool _open(FILE **fp, const char *filename); + + public: + ~gr_file_meta_sink(); + + bool open(const char *filename); + void close(); + void do_update(); + + void set_unbuffered(bool unbuffered) + { + d_unbuffered = unbuffered; + } + + //FIXME: add setters/getters for properties. + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GR_FILE_META_SINK_H */ diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.i b/gnuradio-core/src/lib/io/gr_file_meta_sink.i new file mode 100644 index 000000000..ed3eda0f8 --- /dev/null +++ b/gnuradio-core/src/lib/io/gr_file_meta_sink.i @@ -0,0 +1,80 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +GR_SWIG_BLOCK_MAGIC(gr,file_meta_sink) + +const char METADATA_VERSION = 0; +const size_t METADATA_HEADER_SIZE = 134; + +enum gr_file_types { + GR_FILE_BYTE=0, + GR_FILE_CHAR=0, + GR_FILE_SHORT, + GR_FILE_INT, + GR_FILE_LONG, + GR_FILE_LONG_LONG, + GR_FILE_FLOAT, + GR_FILE_DOUBLE, +}; + +gr_file_meta_sink_sptr +gr_make_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate=1, double relative_rate=1, + gr_file_types type=GR_FILE_FLOAT, bool complex=true, + size_t max_segment_size=1000000, + const std::string & extra_dict="", + bool detached_header=false); + +class gr_file_meta_sink : public gr_sync_block, public gr_file_sink_base +{ + protected: + gr_file_meta_sink(size_t itemsize, const char *filename, + double samp_rate, double relative_rate, + gr_file_types type, bool complex, + size_t max_segment_size, + const std::string & extra_dict, + bool detached_header); + + public: + ~gr_file_meta_sink(); + + enum file_types { + FILE_BYTE=0, + FILE_CHAR=0, + FILE_SHORT, + FILE_INT, + FILE_LONG, + FILE_LONG_LONG, + FILE_FLOAT, + FILE_DOUBLE, + }; + + /*! + * \brief open filename and begin output to it. + */ + bool open(const char *filename); + + /*! + * \brief close current output file. + */ + void close(); +}; diff --git a/gnuradio-core/src/lib/io/gr_file_sink_base.cc b/gnuradio-core/src/lib/io/gr_file_sink_base.cc index 2dd896ae7..4eecf928e 100644 --- a/gnuradio-core/src/lib/io/gr_file_sink_base.cc +++ b/gnuradio-core/src/lib/io/gr_file_sink_base.cc @@ -79,6 +79,8 @@ gr_file_sink_base::open(const char *filename) perror (filename); return false; } + std::cerr << "OPENING NEW FILE: " << filename << std::endl; + std::cerr << "FD: " << fd << std::endl; if (d_new_fp){ // if we've already got a new one open, close it fclose(d_new_fp); @@ -90,6 +92,8 @@ gr_file_sink_base::open(const char *filename) ::close(fd); // don't leak file descriptor if fdopen fails. } + std::cerr << "D_NEW_FD: " << d_new_fp << std::endl; + d_updated = true; return d_new_fp != 0; } diff --git a/gnuradio-core/src/lib/io/io.i b/gnuradio-core/src/lib/io/io.i index e2de4eb97..055f28847 100644 --- a/gnuradio-core/src/lib/io/io.i +++ b/gnuradio-core/src/lib/io/io.i @@ -27,6 +27,7 @@ #endif #include <gr_file_sink.h> +#include <gr_file_meta_sink.h> #include <gr_file_source.h> #include <gr_file_descriptor_sink.h> #include <gr_file_descriptor_source.h> @@ -55,6 +56,7 @@ %include "gr_file_sink_base.i" %include "gr_file_sink.i" +%include "gr_file_meta_sink.i" %include "gr_file_source.i" %include "gr_file_descriptor_sink.i" %include "gr_file_descriptor_source.i" diff --git a/gnuradio-core/src/python/gnuradio/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/CMakeLists.txt index bf696e0d3..31cde6921 100644 --- a/gnuradio-core/src/python/gnuradio/CMakeLists.txt +++ b/gnuradio-core/src/python/gnuradio/CMakeLists.txt @@ -32,6 +32,7 @@ GR_PYTHON_INSTALL(FILES gr_unittest.py gr_xmlrunner.py optfir.py + parse_file_metadata.py window.py DESTINATION ${GR_PYTHON_DIR}/gnuradio COMPONENT "core_python" diff --git a/gnuradio-core/src/python/gnuradio/parse_file_metadata.py b/gnuradio-core/src/python/gnuradio/parse_file_metadata.py new file mode 100644 index 000000000..4f5547211 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/parse_file_metadata.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +# +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio 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, or (at your option) +# any later version. +# +# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from gnuradio import gr + +''' +sr Sample rate (samples/second) +time Time as uint64(secs), double(fractional secs) +type Type of data (see gr_file_types enum) +cplx is complex? (True or False) +strt Start of data (or size of header) in bytes +size Size of data in bytes +''' + +HEADER_LENGTH = gr.METADATA_HEADER_SIZE + +ftype_to_string = {gr.GR_FILE_BYTE: "bytes", + gr.GR_FILE_SHORT: "short", + gr.GR_FILE_INT: "int", + gr.GR_FILE_LONG: "long", + gr.GR_FILE_LONG_LONG: "long long", + gr.GR_FILE_FLOAT: "float", + gr.GR_FILE_DOUBLE: "double" } + +ftype_to_size = {gr.GR_FILE_BYTE: gr.sizeof_char, + gr.GR_FILE_SHORT: gr.sizeof_short, + gr.GR_FILE_INT: gr.sizeof_int, + gr.GR_FILE_LONG: gr.sizeof_int, + gr.GR_FILE_LONG_LONG: 2*gr.sizeof_int, + gr.GR_FILE_FLOAT: gr.sizeof_float, + gr.GR_FILE_DOUBLE: gr.sizeof_double} + +def parse_header(p, VERBOSE=False): + dump = gr.PMT_NIL + + info = dict() + + if(gr.pmt_is_dict(p) is False): + sys.stderr.write("Header is not a PMT dictionary: invalid or corrupt data file.\n") + sys.exit(1) + + # GET FILE FORMAT VERSION NUMBER + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("version"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("version"), dump) + version = gr.pmt_to_long(r) + if(VERBOSE): + print "Version Number: {0}".format(version) + else: + sys.stderr.write("Could not find key 'sr': invalid or corrupt data file.\n") + sys.exit(1) + + # EXTRACT SAMPLE RATE + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("rx_rate"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("rx_rate"), dump) + samp_rate = gr.pmt_to_double(r) + info["rx_rate"] = samp_rate + if(VERBOSE): + print "Sample Rate: {0:.2f} sps".format(samp_rate) + else: + sys.stderr.write("Could not find key 'sr': invalid or corrupt data file.\n") + sys.exit(1) + + # EXTRACT TIME STAMP + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("rx_time"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("rx_time"), dump) + pmt_secs = gr.pmt_tuple_ref(r, 0) + pmt_fracs = gr.pmt_tuple_ref(r, 1) + secs = float(gr.pmt_to_uint64(pmt_secs)) + fracs = gr.pmt_to_double(pmt_fracs) + t = secs + fracs + info["rx_time"] = t + if(VERBOSE): + print "Seconds: {0:.6f}".format(t) + else: + sys.stderr.write("Could not find key 'time': invalid or corrupt data file.\n") + sys.exit(1) + + # EXTRACT DATA TYPE + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("type"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("type"), dump) + dtype = gr.pmt_to_long(r) + stype = ftype_to_string[dtype] + info["type"] = stype + if(VERBOSE): + print "Data Type: {0} ({1})".format(stype, dtype) + else: + sys.stderr.write("Could not find key 'type': invalid or corrupt data file.\n") + sys.exit(1) + + # EXTRACT COMPLEX + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("cplx"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("cplx"), dump) + cplx = gr.pmt_to_bool(r) + info["cplx"] = cplx + if(VERBOSE): + print "Complex? {0}".format(cplx) + else: + sys.stderr.write("Could not find key 'cplx': invalid or corrupt data file.\n") + sys.exit(1) + + # EXTRACT WHERE CURRENT SEGMENT STARTS + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("strt"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("strt"), dump) + seg_start = gr.pmt_to_uint64(r) + info["hdr_len"] = seg_start + info["extra_len"] = seg_start - HEADER_LENGTH + info["has_extra"] = info["extra_len"] > 0 + if(VERBOSE): + print "Header Length: {0} bytes".format(info["hdr_len"]) + print "Extra Length: {0}".format((info["extra_len"])) + print "Extra Header? {0}".format(info["has_extra"]) + else: + sys.stderr.write("Could not find key 'strt': invalid or corrupt data file.\n") + sys.exit(1) + + # EXTRACT SIZE OF DATA + if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("size"))): + r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("size"), dump) + nbytes = gr.pmt_to_uint64(r) + + # Multiply itemsize by 2 if complex + if(cplx): mult=2 + else: mult=1 + + nitems = nbytes/(ftype_to_size[dtype]*mult) + info["nitems"] = nitems + info["nbytes"] = nbytes + + if(VERBOSE): + print "Size of Data: {0} bytes".format(nbytes) + print " {0} items".format(nitems) + else: + sys.stderr.write("Could not find key 'size': invalid or corrupt data file.\n") + sys.exit(1) + + return info + +# IF THERE IS EXTRA DATA, PULL OUT THE DICTIONARY AND PARSE IT +def parse_extra_dict(p, info, VERBOSE=False): + if(gr.pmt_is_dict(p) is False): + sys.stderr.write("Extra header is not a PMT dictionary: invalid or corrupt data file.\n") + sys.exit(1) + + items = gr.pmt_dict_items(p) + nitems = gr.pmt_length(items) + for i in xrange(nitems): + item = gr.pmt_nth(i, items) + key = gr.pmt_symbol_to_string(gr.pmt_car(item)) + val = gr.pmt_to_double(gr.pmt_cdr(item)) + info[key] = val + if(VERBOSE): + print "{0}: {1:.4f}".format(key, val) + + return info |