summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gnuradio-core/src/lib/io/CMakeLists.txt1
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_source.cc384
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_source.h117
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_source.i44
-rw-r--r--gnuradio-core/src/lib/io/io.i2
-rw-r--r--grc/blocks/block_tree.xml1
-rw-r--r--grc/blocks/gr_file_meta_source.xml88
7 files changed, 637 insertions, 0 deletions
diff --git a/gnuradio-core/src/lib/io/CMakeLists.txt b/gnuradio-core/src/lib/io/CMakeLists.txt
index c5b85d304..1cbcd2daf 100644
--- a/gnuradio-core/src/lib/io/CMakeLists.txt
+++ b/gnuradio-core/src/lib/io/CMakeLists.txt
@@ -88,6 +88,7 @@ set(gr_core_io_triple_threats
gr_file_meta_sink
gr_file_sink_base
gr_file_source
+ gr_file_meta_source
gr_file_descriptor_sink
gr_file_descriptor_source
gr_message_debug
diff --git a/gnuradio-core/src/lib/io/gr_file_meta_source.cc b/gnuradio-core/src/lib/io/gr_file_meta_source.cc
new file mode 100644
index 000000000..b0850593b
--- /dev/null
+++ b/gnuradio-core/src/lib/io/gr_file_meta_source.cc
@@ -0,0 +1,384 @@
+/* -*- 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_source.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_source_sptr
+gr_make_file_meta_source(const char *filename,
+ bool repeat,
+ bool detached_header,
+ const char *hdr_filename)
+{
+ return gnuradio::get_initial_sptr
+ (new gr_file_meta_source(filename,
+ repeat,
+ detached_header,
+ hdr_filename));
+}
+
+gr_file_meta_source::gr_file_meta_source(const char *filename,
+ bool repeat,
+ bool detached_header,
+ const char *hdr_filename)
+ : gr_sync_block("file_meta_source",
+ gr_make_io_signature(0, 0, 0),
+ gr_make_io_signature(1, 1, 1)),
+ d_itemsize(0), d_samp_rate(0),
+ d_seg_size(0),
+ d_updated(false), d_repeat(repeat)
+{
+ 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_source: can't open file\n");
+
+ do_update();
+
+ pmt_t hdr = PMT_NIL, extras = PMT_NIL;
+ if(read_header(hdr, extras))
+ parse_header(hdr);
+ else
+ throw std::runtime_error("file_meta_source: could not read header.\n");
+
+ // Set output signature based on itemsize info in header
+ set_output_signature(gr_make_io_signature(1, 1, d_itemsize));
+
+ // Convert from bytes to items
+ d_seg_size /= d_itemsize;
+}
+
+gr_file_meta_source::~gr_file_meta_source()
+{
+ 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_source::read_header(pmt_t &hdr, pmt_t &extras)
+{
+ size_t ret;
+ size_t size = 0;
+ std::string str;
+ char *hdr_buffer = new char[METADATA_HEADER_SIZE];
+ while(size < METADATA_HEADER_SIZE) {
+ ret = fread(&hdr_buffer[size], sizeof(char), METADATA_HEADER_SIZE-size, d_fp);
+ if(ret == 0) {
+ delete [] hdr_buffer;
+ if(feof(d_fp))
+ return false;
+ else {
+ std::stringstream s;
+ s << "file_meta_source: error occurred extracting header: "
+ << strerror(errno) << std::endl;
+ throw std::runtime_error(s.str());
+ }
+ }
+ size += ret;
+ }
+
+ // Convert to string or the char array gets confused by the \0
+ str.insert(0, hdr_buffer, METADATA_HEADER_SIZE);
+ hdr = pmt_deserialize_str(str);
+ delete [] hdr_buffer;
+
+ uint64_t seg_start, extra_len;
+ pmt_t r, dump;
+ if(pmt_dict_has_key(hdr, pmt_string_to_symbol("strt"))) {
+ r = pmt_dict_ref(hdr, pmt_string_to_symbol("strt"), dump);
+ seg_start = pmt_to_uint64(r);
+ extra_len = seg_start - METADATA_HEADER_SIZE;
+ }
+
+ if(extra_len > 0) {
+ size = 0;
+ hdr_buffer = new char[extra_len];
+ while(size < extra_len) {
+ ret = fread(&hdr_buffer[size], sizeof(char), extra_len-size, d_fp);
+ if(ret == 0) {
+ delete [] hdr_buffer;
+ if(feof(d_fp))
+ return false;
+ else {
+ std::stringstream s;
+ s << "file_meta_source: error occurred extracting extras: "
+ << strerror(errno) << std::endl;
+ throw std::runtime_error(s.str());
+ }
+ }
+ size += ret;
+ }
+
+ str.clear();
+ str.insert(0, hdr_buffer, extra_len);
+ extras = pmt_deserialize_str(str);
+ delete [] hdr_buffer;
+ }
+
+ return true;
+}
+
+void
+gr_file_meta_source::parse_header(pmt_t hdr)
+{
+ // GET SAMPLE RATE
+ if(pmt_dict_has_key(hdr, pmt_string_to_symbol("rx_rate"))) {
+ d_samp_rate = pmt_to_double(pmt_dict_ref(hdr, pmt_string_to_symbol("rx_rate"), PMT_NIL));
+ }
+ else {
+ throw std::runtime_error("file_meta_source: Could not extract sample rate.\n");
+ }
+
+ // GET TIME STAMP
+ if(pmt_dict_has_key(hdr, pmt_string_to_symbol("rx_time"))) {
+ d_time_stamp = pmt_dict_ref(hdr, pmt_string_to_symbol("rx_time"), PMT_NIL);
+ }
+ else {
+ throw std::runtime_error("file_meta_source: Could not extract time stamp.\n");
+ }
+
+ // GET DATA TYPE
+ if(pmt_dict_has_key(hdr, pmt_string_to_symbol("type"))) {
+ int t = pmt_to_long(pmt_dict_ref(hdr, pmt_string_to_symbol("type"), PMT_NIL));
+ switch(t) {
+ case(GR_FILE_CHAR): d_itemsize = sizeof(char); break;
+ case(GR_FILE_SHORT): d_itemsize = sizeof(short); break;
+ case(GR_FILE_INT): d_itemsize = sizeof(int); break;
+ case(GR_FILE_LONG): d_itemsize = sizeof(long int); break;
+ case(GR_FILE_LONG_LONG): d_itemsize = sizeof(long long int); break;
+ case(GR_FILE_FLOAT): d_itemsize = sizeof(float); break;
+ case(GR_FILE_DOUBLE): d_itemsize = sizeof(double); break;
+ default:
+ throw std::runtime_error("file_meta_source: Could not determine data type size.\n");
+ }
+ }
+ else {
+ throw std::runtime_error("file_meta_source: Could not extract data type.\n");
+ }
+
+ // GET COMPLEX INDICATOR
+ if(pmt_dict_has_key(hdr, pmt_string_to_symbol("cplx"))) {
+ bool cplx = pmt_to_bool(pmt_dict_ref(hdr, pmt_string_to_symbol("cplx"), PMT_NIL));
+ if(cplx)
+ d_itemsize *= 2;
+ }
+ else {
+ throw std::runtime_error("file_meta_source: Could not extract complex indicator.\n");
+ }
+
+ // GET FIRST SEGMENT SIZE
+ if(pmt_dict_has_key(hdr, pmt_string_to_symbol("size"))) {
+ d_seg_size = pmt_to_uint64(pmt_dict_ref(hdr, pmt_string_to_symbol("size"), PMT_NIL));
+ }
+ else {
+ throw std::runtime_error("file_meta_source: Could not extract segment size.\n");
+ }
+}
+
+bool
+gr_file_meta_source::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_source::_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_RDONLY|OUR_O_LARGEFILE|OUR_O_BINARY)) < 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, "rb")) == NULL) {
+ perror(filename);
+ ::close(fd); // don't leak file descriptor if fdopen fails.
+ }
+
+ ret = fp != 0;
+
+ return ret;
+}
+
+void
+gr_file_meta_source::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_source::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;
+ }
+}
+
+int
+gr_file_meta_source::work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ // We've reached the end of a segment; parse the next header and get
+ // the new tags to send and set the next segment size.
+ if((d_seg_size == 0) && (!feof(d_fp))) {
+ pmt_t hdr=PMT_NIL, extras=PMT_NIL;
+ if(read_header(hdr, extras))
+ parse_header(hdr);
+ else
+ return -1;
+ }
+
+ char *out = (char*)output_items[0];
+ int i;
+ int seg_size = std::min(noutput_items, (int)d_seg_size);
+ int size = seg_size;
+
+ do_update(); // update d_fp is reqd
+ if(d_fp == NULL)
+ throw std::runtime_error("work with file not open");
+
+ gruel::scoped_lock lock(d_mutex); // hold for the rest of this function
+ while(size) {
+ i = fread(out, d_itemsize, size, d_fp);
+
+ size -= i;
+ d_seg_size -= i;
+ out += i * d_itemsize;
+
+ if(size == 0) // done
+ break;
+
+ if(i > 0) // short read, try again
+ continue;
+
+ // We got a zero from fread. This is either EOF or error. In
+ // any event, if we're in repeat mode, seek back to the beginning
+ // of the file and try again, else break
+
+ if(!d_repeat)
+ break;
+
+ if(fseek(d_fp, 0, SEEK_SET) == -1) {
+ std::stringstream s;
+ s << "[" << __FILE__ << "]" << " fseek failed" << std::endl;
+ throw std::runtime_error(s.str());
+ }
+ }
+
+ if(size > 0) { // EOF or error
+ if(size == seg_size) // we didn't read anything; say we're done
+ return -1;
+ return seg_size - size; // else return partial result
+ }
+
+ return seg_size;
+}
diff --git a/gnuradio-core/src/lib/io/gr_file_meta_source.h b/gnuradio-core/src/lib/io/gr_file_meta_source.h
new file mode 100644
index 000000000..819f4ece2
--- /dev/null
+++ b/gnuradio-core/src/lib/io/gr_file_meta_source.h
@@ -0,0 +1,117 @@
+/* -*- 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_SOURCE_H
+#define INCLUDED_GR_FILE_META_SOURCE_H
+
+#include <gr_core_api.h>
+#include <gr_sync_block.h>
+#include <gruel/pmt.h>
+#include <gruel/thread.h>
+#include <cstdio>
+
+#include <gr_file_meta_sink.h>
+
+class gr_file_meta_source;
+typedef boost::shared_ptr<gr_file_meta_source> gr_file_meta_source_sptr;
+
+GR_CORE_API gr_file_meta_source_sptr
+gr_make_file_meta_source(const char *filename,
+ bool repeat=false,
+ bool detached_header=false,
+ const char *hdr_filename="");
+
+/*!
+ * \brief Reads stream from file with meta-data headers. Headers are
+ * parsed into tags.
+ * \ingroup source_blk
+ *
+ * The information in the metadata headers 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.
+ *
+ * Any item inside of the extra header dictionary is ready out and
+ * made into a stream tag.
+ */
+class GR_CORE_API gr_file_meta_source : public gr_sync_block
+{
+ /*!
+ * \brief Create a meta-data file source.
+ *
+ * \param filename (string): Name of file to write data to.
+ * \param repeate (bool): Repeats file when EOF is found.
+ * \param detached_header (bool): Set to true if header
+ * info is stored in a separate file (usually named filename.hdr)
+ * \param hdr_filename (string): Name of detached header file if used.
+ */
+ friend GR_CORE_API gr_file_meta_source_sptr
+ gr_make_file_meta_source(const char *filename,
+ bool repeat,
+ bool detached_header,
+ const char *hdr_filename);
+
+ private:
+ enum meta_state_t {
+ STATE_INLINE=0,
+ STATE_DETACHED
+ };
+
+ size_t d_itemsize;
+ double d_samp_rate;
+ pmt_t d_time_stamp;
+ size_t d_seg_size;
+ bool d_updated;
+ bool d_repeat;
+
+ gruel::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_source(const char *filename,
+ bool repeat=false,
+ bool detached_header=false,
+ const char *hdr_filename="");
+
+ bool _open(FILE **fp, const char *filename);
+ bool read_header(pmt_t &hdr, pmt_t &extras);
+ void parse_header(pmt_t hdr);
+
+ public:
+ ~gr_file_meta_source();
+
+ bool open(const char *filename);
+ void close();
+ void do_update();
+
+ int work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_GR_FILE_META_SOURCE_H */
diff --git a/gnuradio-core/src/lib/io/gr_file_meta_source.i b/gnuradio-core/src/lib/io/gr_file_meta_source.i
new file mode 100644
index 000000000..6cf089da1
--- /dev/null
+++ b/gnuradio-core/src/lib/io/gr_file_meta_source.i
@@ -0,0 +1,44 @@
+/* -*- 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_source)
+
+gr_file_meta_source_sptr
+gr_make_file_meta_source(const char *filename,
+ bool repeat=false,
+ bool detached_header=false,
+ const char *hdr_filename="");
+
+class gr_file_meta_source : public gr_sync_block
+{
+ protected:
+ gr_file_meta_source(const char *filename,
+ bool repeat,
+ bool detached_header,
+ const char *hdr_filename);
+
+ public:
+ ~gr_file_meta_source();
+
+ bool open(const char *filename);
+ void close();
+};
diff --git a/gnuradio-core/src/lib/io/io.i b/gnuradio-core/src/lib/io/io.i
index 055f28847..5885214d8 100644
--- a/gnuradio-core/src/lib/io/io.i
+++ b/gnuradio-core/src/lib/io/io.i
@@ -29,6 +29,7 @@
#include <gr_file_sink.h>
#include <gr_file_meta_sink.h>
#include <gr_file_source.h>
+#include <gr_file_meta_source.h>
#include <gr_file_descriptor_sink.h>
#include <gr_file_descriptor_source.h>
#include <gr_histo_sink_f.h>
@@ -58,6 +59,7 @@
%include "gr_file_sink.i"
%include "gr_file_meta_sink.i"
%include "gr_file_source.i"
+%include "gr_file_meta_source.i"
%include "gr_file_descriptor_sink.i"
%include "gr_file_descriptor_source.i"
%include "gr_histo_sink.i"
diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml
index 37a6321f5..f1c68c7c9 100644
--- a/grc/blocks/block_tree.xml
+++ b/grc/blocks/block_tree.xml
@@ -29,6 +29,7 @@
<block>gr_vector_sink_x</block>
<block>gr_null_sink</block>
<block>gr_file_sink</block>
+ <block>gr_file_meta_source</block>
<block>gr_file_meta_sink</block>
<block>blks2_tcp_sink</block>
<block>gr_udp_sink</block>
diff --git a/grc/blocks/gr_file_meta_source.xml b/grc/blocks/gr_file_meta_source.xml
new file mode 100644
index 000000000..9e5fcb0a2
--- /dev/null
+++ b/grc/blocks/gr_file_meta_source.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##File Source
+###################################################
+ -->
+<block>
+ <name>File Meta Source</name>
+ <key>gr_file_meta_source</key>
+ <import>from gnuradio import gr</import>
+ <make>gr.file_meta_source($file, $repeat, $detached, $hdr_file)</make>
+ <callback>open($file, $repeat)</callback>
+ <param>
+ <name>Output Type</name>
+ <key>type</key>
+ <type>enum</type>
+ <option>
+ <name>Complex</name>
+ <key>complex</key>
+ <opt>size:gr.sizeof_gr_complex</opt>
+ </option>
+ <option>
+ <name>Float</name>
+ <key>float</key>
+ <opt>size:gr.sizeof_float</opt>
+ </option>
+ <option>
+ <name>Int</name>
+ <key>int</key>
+ <opt>size:gr.sizeof_int</opt>
+ </option>
+ <option>
+ <name>Short</name>
+ <key>short</key>
+ <opt>size:gr.sizeof_short</opt>
+ </option>
+ <option>
+ <name>Byte</name>
+ <key>byte</key>
+ <opt>size:gr.sizeof_char</opt>
+ </option>
+ </param>
+ <param>
+ <name>File</name>
+ <key>file</key>
+ <value></value>
+ <type>file_open</type>
+ </param>
+ <param>
+ <name>Repeat</name>
+ <key>repeat</key>
+ <value>True</value>
+ <type>enum</type>
+ <option>
+ <name>Yes</name>
+ <key>True</key>
+ </option>
+ <option>
+ <name>No</name>
+ <key>False</key>
+ </option>
+ </param>
+ <param>
+ <name>Detached Header</name>
+ <key>detached</key>
+ <value>False</value>
+ <type>enum</type>
+ <option>
+ <name>Yes</name>
+ <key>True</key>
+ </option>
+ <option>
+ <name>No</name>
+ <key>False</key>
+ </option>
+ </param>
+ <param>
+ <name>Header File</name>
+ <key>hdr_file</key>
+ <value></value>
+ <type>file_open</type>
+ </param>
+ <source>
+ <name>out</name>
+ <type>$type</type>
+ <vlen>1</vlen>
+ </source>
+</block>