summaryrefslogtreecommitdiff
path: root/gnuradio-core/src
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-core/src')
-rw-r--r--gnuradio-core/src/lib/io/CMakeLists.txt1
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_sink.cc455
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_sink.h169
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_sink.i80
-rw-r--r--gnuradio-core/src/lib/io/gr_file_sink_base.cc4
-rw-r--r--gnuradio-core/src/lib/io/io.i2
-rw-r--r--gnuradio-core/src/python/gnuradio/CMakeLists.txt1
-rw-r--r--gnuradio-core/src/python/gnuradio/parse_file_metadata.py174
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