diff options
35 files changed, 1685 insertions, 1653 deletions
diff --git a/docs/doxygen/other/metadata.dox b/docs/doxygen/other/metadata.dox index 1b3c891a8..9fad7c584 100644 --- a/docs/doxygen/other/metadata.dox +++ b/docs/doxygen/other/metadata.dox @@ -9,8 +9,8 @@ the system state such as sample rate or if a receiver's frequency are not conveyed with the data in the file itself. Header of metadata solve this problem. -We write metadata files using gr_file_meta_sink and read metadata -files using gr_file_meta_source. +We write metadata files using blocks::file_meta_sink and read metadata +files using blocks::file_meta_source. Metadata files have headers that carry information about a segment of data within the file. The header structure is described in detail in @@ -88,12 +88,12 @@ keep the sample times exact. \subsection implementation Implementation -Metadata files are created using gr_file_meta_sink. The default +Metadata files are created using file_meta_sink. The default behavior is to create a single file with inline headers as metadata. An option can be set to switch to detached header mode. Metadata file are read into a flowgraph using -gr_file_meta_source. This source reads a metadata file, inline by +file_meta_source. This source reads a metadata file, inline by default with a settable option to use detached headers. The data from the segments is converted into a standard streaming output. The 'rx_rate' and 'rx_time' and all key:value pairs in the extra header @@ -250,13 +250,13 @@ manipulating metadata files. There is a general parser in Python that will convert the PMT header and extra header into Python dictionaries. This utility is: -- gnuradio-core/src/python/gnuradio/parse_file_metadata.py +- gr-blocks/python/parse_file_metadata.py This program is installed into the Python directory under the 'gnuradio' module, so it can be accessed with: \code -from gnuradio import parse_file_metadata +from gnuradio.blocks import parse_file_metadata \endcode It defines HEADER_LENGTH as the static length of the metadata header @@ -284,12 +284,14 @@ data with '.hdr' appended to it. Examples are located in: -- gnuradio-core/src/examples/metadata +- gr-blocks/examples/metadata -Currently, there are two GRC example programs. +Currently, there are a few GRC example programs. - file_metadata_sink: create a metadata file from UHD samples. - file_metadata_source: read the metadata file as input to a simple graph. +- file_metadata_vector_sink: create a metadata file from UHD samples. +- file_metadata_vector_source: read the metadata file as input to a simple graph. The file sink example can be switched to use a signal source instead of a UHD source, but no extra tagged data is used in this mode. @@ -298,6 +300,9 @@ The file source example pushes the data stream to a new raw file while a tag debugger block prints out any tags observed in the metedata file. A QT GUI time sink is used to look at the signal as well. +The versions with 'vector' in the name are similar except they use +vectors of data. + The following shows a simple way of creating extra metadata for a metadata file. This example is just showing how we can insert a date into the metadata to keep track of later. The date in this case is @@ -305,6 +310,7 @@ encoded as a vector of uint16 with [day, month, year]. \code from gruel import pmt + from gnuradio import blocks key = pmt.pmt_intern("date") val = pmt.pmt_init_u16vector(3, [13,12,2012]) @@ -312,11 +318,11 @@ encoded as a vector of uint16 with [day, month, year]. extras = pmt.pmt_make_dict() extras = pmt.pmt_dict_add(extras, key, val) extras_str = pmt.pmt_serialize_str(extras) - self.sink = gr.file_meta_sink(gr.sizeof_gr_complex, - "/tmp/metadat_file.out", - samp_rate, 1, - gr.GR_FILE_FLOAT, True, - 1000000, extra_str, False) + self.sink = blocks.file_meta_sink(gr.sizeof_gr_complex, + "/tmp/metadat_file.out", + samp_rate, 1, + blocks.GR_FILE_FLOAT, True, + 1000000, extra_str, False) \endcode diff --git a/gnuradio-core/src/examples/CMakeLists.txt b/gnuradio-core/src/examples/CMakeLists.txt index 36333425c..01d9eb83e 100644 --- a/gnuradio-core/src/examples/CMakeLists.txt +++ b/gnuradio-core/src/examples/CMakeLists.txt @@ -17,7 +17,6 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. -add_subdirectory(metadata) add_subdirectory(mp-sched) add_subdirectory(msg_passing) add_subdirectory(network) diff --git a/gnuradio-core/src/lib/io/CMakeLists.txt b/gnuradio-core/src/lib/io/CMakeLists.txt index 1cbcd2daf..59ca06b5a 100644 --- a/gnuradio-core/src/lib/io/CMakeLists.txt +++ b/gnuradio-core/src/lib/io/CMakeLists.txt @@ -85,10 +85,8 @@ 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_meta_source gr_file_descriptor_sink gr_file_descriptor_source gr_message_debug diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.cc b/gnuradio-core/src/lib/io/gr_file_meta_sink.cc deleted file mode 100644 index ab0acbdb4..000000000 --- a/gnuradio-core/src/lib/io/gr_file_meta_sink.cc +++ /dev/null @@ -1,457 +0,0 @@ -/* -*- 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 std::string &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 std::string &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("size"), pmt_from_long(d_itemsize)); - 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("bytes"), 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() -{ - 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 std::string &filename) -{ - bool ret = true; - if(d_state == STATE_DETACHED) { - std::string s = filename + ".hdr"; - ret = _open(&d_new_hdr_fp, s.c_str()); - } - - ret = ret && _open(&d_new_fp, filename.c_str()); - 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 - update_last_header(); - - 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"); - } - } - - fflush(fp); -} - -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("bytes"), 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("bytes"), 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("bytes"), 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 deleted file mode 100644 index 9b67cc4c8..000000000 --- a/gnuradio-core/src/lib/io/gr_file_meta_sink.h +++ /dev/null @@ -1,169 +0,0 @@ -/* -*- 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 <gruel/pmt.h> -#include <cstdio> - -using namespace pmt; - -const char METADATA_VERSION = 0; -const size_t METADATA_HEADER_SIZE = 149; - -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 std::string &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 std::string &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 std::string &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 std::string &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 deleted file mode 100644 index 6fa34913b..000000000 --- a/gnuradio-core/src/lib/io/gr_file_meta_sink.i +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- 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 = 149; - -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 std::string &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 -{ - protected: - gr_file_meta_sink(size_t itemsize, const std::string &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(); - - bool open(const std::string &filename); - void close(); - void set_unbuffered(bool unbuffered); -}; diff --git a/gnuradio-core/src/lib/io/gr_file_meta_source.cc b/gnuradio-core/src/lib/io/gr_file_meta_source.cc deleted file mode 100644 index d940e5edc..000000000 --- a/gnuradio-core/src/lib/io/gr_file_meta_source.cc +++ /dev/null @@ -1,427 +0,0 @@ -/* -*- 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 std::string &filename, - bool repeat, - bool detached_header, - const std::string &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 std::string &filename, - bool repeat, - bool detached_header, - const std::string &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, hdr_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, 0, d_tags); - parse_extras(extras, 0, d_tags); - } - 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)); -} - -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) -{ - // Select which file handle to read from. - FILE *fp; - if(d_state == STATE_DETACHED) - fp = d_hdr_fp; - else - fp = d_fp; - - 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, fp); - if(ret == 0) { - delete [] hdr_buffer; - if(feof(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, fp); - if(ret == 0) { - delete [] hdr_buffer; - if(feof(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, uint64_t offset, - std::vector<gr_tag_t> &tags) -{ - pmt_t r, key; - - // GET SAMPLE RATE - key = pmt_string_to_symbol("rx_rate"); - if(pmt_dict_has_key(hdr, key)) { - r = pmt_dict_ref(hdr, key, PMT_NIL); - d_samp_rate = pmt_to_double(r); - - gr_tag_t t; - t.offset = offset; - t.key = key; - t.value = r; - t.srcid = alias_pmt(); - tags.push_back(t); - } - else { - throw std::runtime_error("file_meta_source: Could not extract sample rate.\n"); - } - - // GET TIME STAMP - key = pmt_string_to_symbol("rx_time"); - if(pmt_dict_has_key(hdr, key)) { - d_time_stamp = pmt_dict_ref(hdr, key, PMT_NIL); - - gr_tag_t t; - t.offset = offset; - t.key = key; - t.value = d_time_stamp; - t.srcid = alias_pmt(); - tags.push_back(t); - } - else { - throw std::runtime_error("file_meta_source: Could not extract time stamp.\n"); - } - - // GET ITEM SIZE OF DATA - if(pmt_dict_has_key(hdr, pmt_string_to_symbol("size"))) { - d_itemsize = pmt_to_long(pmt_dict_ref(hdr, pmt_string_to_symbol("size"), PMT_NIL)); - } - else { - throw std::runtime_error("file_meta_source: Could not extract item size.\n"); - } - - // GET SEGMENT SIZE - if(pmt_dict_has_key(hdr, pmt_string_to_symbol("bytes"))) { - d_seg_size = pmt_to_uint64(pmt_dict_ref(hdr, pmt_string_to_symbol("bytes"), PMT_NIL)); - - // Convert from bytes to items - d_seg_size /= d_itemsize; - } - else { - throw std::runtime_error("file_meta_source: Could not extract segment size.\n"); - } -} - -void -gr_file_meta_source::parse_extras(pmt_t extras, uint64_t offset, - std::vector<gr_tag_t> &tags) -{ - pmt_t item, key, val; - - size_t nitems = pmt_length(extras); - for(size_t i = 0; i < nitems; i++) { - item = pmt_nth(i, extras); - key = pmt_car(item); - val = pmt_cdr(item); - - gr_tag_t t; - t.offset = offset; - t.key = key; - t.value = val; - t.srcid = alias_pmt(); - tags.push_back(t); - } -} - -bool -gr_file_meta_source::open(const std::string &filename, - const std::string &hdr_filename) -{ - bool ret = true; - if(d_state == STATE_DETACHED) { - std::string s; - if(hdr_filename == "") - s = filename + ".hdr"; - else - s = hdr_filename; - ret = _open(&d_new_hdr_fp, s.c_str()); - } - - ret = ret && _open(&d_new_fp, filename.c_str()); - 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) { - pmt_t hdr=PMT_NIL, extras=PMT_NIL; - if(read_header(hdr, extras)) { - parse_header(hdr, nitems_written(0), d_tags); - parse_extras(extras, nitems_written(0), d_tags); - } - 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"); - - // Push all tags onto the stream and remove them from the vector - while(!d_tags.empty()) { - add_item_tag(0, d_tags.back()); - d_tags.pop_back(); - } - - 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 deleted file mode 100644 index 95e466936..000000000 --- a/gnuradio-core/src/lib/io/gr_file_meta_source.h +++ /dev/null @@ -1,126 +0,0 @@ -/* -*- 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 <gr_tags.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 std::string &filename, - bool repeat=false, - bool detached_header=false, - const std::string &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 repeat (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. - * Defaults to 'filename.hdr' if detached_header is true but this - * field is an empty string. - */ - friend GR_CORE_API gr_file_meta_source_sptr - gr_make_file_meta_source(const std::string &filename, - bool repeat, - bool detached_header, - const std::string &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; - - std::vector<gr_tag_t> d_tags; - - protected: - gr_file_meta_source(const std::string &filename, - bool repeat=false, - bool detached_header=false, - const std::string &hdr_filename=""); - - bool _open(FILE **fp, const char *filename); - bool read_header(pmt_t &hdr, pmt_t &extras); - void parse_header(pmt_t hdr, uint64_t offset, - std::vector<gr_tag_t> &tags); - void parse_extras(pmt_t extras, uint64_t offset, - std::vector<gr_tag_t> &tags); - - public: - ~gr_file_meta_source(); - - bool open(const std::string &filename, - const std::string &hdr_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 deleted file mode 100644 index cb1281036..000000000 --- a/gnuradio-core/src/lib/io/gr_file_meta_source.i +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- 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 std::string &filename, - bool repeat=false, - bool detached_header=false, - const std::string &hdr_filename=""); - -class gr_file_meta_source : public gr_sync_block -{ - protected: - gr_file_meta_source(const std::string &filename, - bool repeat, - bool detached_header, - const std::string &hdr_filename); - - public: - ~gr_file_meta_source(); - - bool open(const std::string &filename, - const std::string &hdr_filename=""); - void close(); -}; diff --git a/gnuradio-core/src/lib/io/io.i b/gnuradio-core/src/lib/io/io.i index 5885214d8..e2de4eb97 100644 --- a/gnuradio-core/src/lib/io/io.i +++ b/gnuradio-core/src/lib/io/io.i @@ -27,9 +27,7 @@ #endif #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> @@ -57,9 +55,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_meta_source.i" %include "gr_file_descriptor_sink.i" %include "gr_file_descriptor_source.i" %include "gr_histo_sink.i" diff --git a/gnuradio-core/src/python/gnuradio/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/CMakeLists.txt index 31cde6921..bf696e0d3 100644 --- a/gnuradio-core/src/python/gnuradio/CMakeLists.txt +++ b/gnuradio-core/src/python/gnuradio/CMakeLists.txt @@ -32,7 +32,6 @@ 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/gr-blocks/CMakeLists.txt b/gr-blocks/CMakeLists.txt index 9c81ba6bf..7e2f43562 100644 --- a/gr-blocks/CMakeLists.txt +++ b/gr-blocks/CMakeLists.txt @@ -85,6 +85,7 @@ if(ENABLE_PYTHON) add_subdirectory(swig) add_subdirectory(grc) add_subdirectory(doc) + add_subdirectory(examples) endif(ENABLE_PYTHON) ######################################################################## diff --git a/gr-blocks/examples/CMakeLists.txt b/gr-blocks/examples/CMakeLists.txt new file mode 100644 index 000000000..79535daa0 --- /dev/null +++ b/gr-blocks/examples/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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. + +add_subdirectory(metadata) diff --git a/gnuradio-core/src/examples/metadata/CMakeLists.txt b/gr-blocks/examples/metadata/CMakeLists.txt index 182d9c543..53a54b9d5 100644 --- a/gnuradio-core/src/examples/metadata/CMakeLists.txt +++ b/gr-blocks/examples/metadata/CMakeLists.txt @@ -23,6 +23,8 @@ install( FILES file_metadata_sink.grc file_metadata_source.grc + file_metadata_vector_sink.grc + file_metadata_vector_source.grc DESTINATION ${GR_PKG_DATA_DIR}/examples/metadata COMPONENT "core_python" ) diff --git a/gnuradio-core/src/examples/metadata/file_metadata_sink.grc b/gr-blocks/examples/metadata/file_metadata_sink.grc index 197ff5572..198b0725f 100644 --- a/gnuradio-core/src/examples/metadata/file_metadata_sink.grc +++ b/gr-blocks/examples/metadata/file_metadata_sink.grc @@ -1,6 +1,65 @@ <?xml version='1.0' encoding='ASCII'?> <flow_graph> - <timestamp>Thu Dec 13 14:25:16 2012</timestamp> + <timestamp>Fri Dec 14 17:09:06 2012</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>file_metadata_sink</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> <block> <key>gr_sig_source_x</key> <param> @@ -100,37 +159,6 @@ </param> </block> <block> - <key>gr_head</key> - <param> - <key>id</key> - <value>gr_head_0</value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>type</key> - <value>complex</value> - </param> - <param> - <key>num_items</key> - <value>20000000</value> - </param> - <param> - <key>vlen</key> - <value>1</value> - </param> - <param> - <key>_coordinate</key> - <value>(309, 172)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> <key>variable</key> <param> <key>id</key> @@ -817,54 +845,30 @@ </param> </block> <block> - <key>gr_file_meta_sink</key> + <key>gr_head</key> <param> <key>id</key> - <value>gr_file_meta_sink_0</value> + <value>gr_head_0</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>file</key> - <value>/tmp/metadat_file.out</value> - </param> - <param> <key>type</key> <value>complex</value> </param> <param> - <key>samp_rate</key> - <value>samp_rate</value> - </param> - <param> - <key>rel_rate</key> - <value>1</value> + <key>num_items</key> + <value>20000000</value> </param> <param> <key>vlen</key> <value>1</value> </param> <param> - <key>max_seg_size</key> - <value>1000000</value> - </param> - <param> - <key>extra_dict</key> - <value>""</value> - </param> - <param> - <key>detached</key> - <value>False</value> - </param> - <param> - <key>unbuffered</key> - <value>False</value> - </param> - <param> <key>_coordinate</key> - <value>(555, 148)</value> + <value>(309, 172)</value> </param> <param> <key>_rotation</key> @@ -872,58 +876,54 @@ </param> </block> <block> - <key>options</key> + <key>blocks_file_meta_sink</key> <param> <key>id</key> - <value>file_metadata_sink</value> + <value>blocks_file_meta_sink_0</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>title</key> - <value></value> + <key>file</key> + <value>/tmp/metadat_file.out</value> </param> <param> - <key>author</key> - <value></value> + <key>type</key> + <value>complex</value> </param> <param> - <key>description</key> - <value></value> + <key>samp_rate</key> + <value>samp_rate</value> </param> <param> - <key>window_size</key> - <value>1280, 1024</value> + <key>rel_rate</key> + <value>1</value> </param> <param> - <key>generate_options</key> - <value>qt_gui</value> + <key>vlen</key> + <value>1</value> </param> <param> - <key>category</key> - <value>Custom</value> + <key>max_seg_size</key> + <value>1000000</value> </param> <param> - <key>run_options</key> - <value>prompt</value> + <key>extra_dict</key> + <value>""</value> </param> <param> - <key>run</key> + <key>detached</key> <value>True</value> </param> <param> - <key>max_nouts</key> - <value>0</value> - </param> - <param> - <key>realtime_scheduling</key> - <value></value> + <key>unbuffered</key> + <value>False</value> </param> <param> <key>_coordinate</key> - <value>(10, 10)</value> + <value>(569, 124)</value> </param> <param> <key>_rotation</key> @@ -931,20 +931,20 @@ </param> </block> <connection> - <source_block_id>gr_head_0</source_block_id> - <sink_block_id>gr_file_meta_sink_0</sink_block_id> + <source_block_id>gr_sig_source_x_0</source_block_id> + <sink_block_id>gr_head_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> <connection> - <source_block_id>gr_sig_source_x_0</source_block_id> + <source_block_id>uhd_usrp_source_0</source_block_id> <sink_block_id>gr_head_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> <connection> - <source_block_id>uhd_usrp_source_0</source_block_id> - <sink_block_id>gr_head_0</sink_block_id> + <source_block_id>gr_head_0</source_block_id> + <sink_block_id>blocks_file_meta_sink_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> diff --git a/gnuradio-core/src/examples/metadata/file_metadata_source.grc b/gr-blocks/examples/metadata/file_metadata_source.grc index cc1383ae1..23757881b 100644 --- a/gnuradio-core/src/examples/metadata/file_metadata_source.grc +++ b/gr-blocks/examples/metadata/file_metadata_source.grc @@ -1,62 +1,59 @@ <?xml version='1.0' encoding='ASCII'?> <flow_graph> - <timestamp>Thu Dec 13 14:25:01 2012</timestamp> + <timestamp>Fri Dec 14 17:08:09 2012</timestamp> <block> - <key>variable</key> + <key>options</key> <param> <key>id</key> - <value>samp_rate</value> + <value>file_metadata_source</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>value</key> - <value>200000</value> + <key>title</key> + <value></value> </param> <param> - <key>_coordinate</key> - <value>(208, 11)</value> + <key>author</key> + <value></value> </param> <param> - <key>_rotation</key> - <value>0</value> + <key>description</key> + <value></value> </param> - </block> - <block> - <key>gr_tag_debug</key> <param> - <key>id</key> - <value>gr_tag_debug_0</value> + <key>window_size</key> + <value>1280, 1024</value> </param> <param> - <key>_enabled</key> - <value>True</value> + <key>generate_options</key> + <value>qt_gui</value> </param> <param> - <key>type</key> - <value>complex</value> + <key>category</key> + <value>Custom</value> </param> <param> - <key>name</key> - <value>Test</value> + <key>run_options</key> + <value>prompt</value> </param> <param> - <key>num_inputs</key> - <value>1</value> + <key>run</key> + <value>True</value> </param> <param> - <key>vlen</key> - <value>1</value> + <key>max_nouts</key> + <value>0</value> </param> <param> - <key>display</key> - <value>True</value> + <key>realtime_scheduling</key> + <value></value> </param> <param> <key>_coordinate</key> - <value>(561, 290)</value> + <value>(10, 10)</value> </param> <param> <key>_rotation</key> @@ -64,30 +61,22 @@ </param> </block> <block> - <key>gr_throttle</key> + <key>variable</key> <param> <key>id</key> - <value>gr_throttle_0</value> + <value>samp_rate</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>type</key> - <value>complex</value> - </param> - <param> - <key>samples_per_second</key> - <value>samp_rate</value> - </param> - <param> - <key>vlen</key> - <value>1</value> + <key>value</key> + <value>200000</value> </param> <param> <key>_coordinate</key> - <value>(322, 223)</value> + <value>(208, 11)</value> </param> <param> <key>_rotation</key> @@ -95,10 +84,10 @@ </param> </block> <block> - <key>gr_file_meta_source</key> + <key>gr_tag_debug</key> <param> <key>id</key> - <value>gr_file_meta_source_0</value> + <value>gr_tag_debug_0</value> </param> <param> <key>_enabled</key> @@ -109,24 +98,24 @@ <value>complex</value> </param> <param> - <key>file</key> - <value>/tmp/metadat_file.out</value> + <key>name</key> + <value>Test</value> </param> <param> - <key>repeat</key> - <value>False</value> + <key>num_inputs</key> + <value>1</value> </param> <param> - <key>detached</key> - <value>False</value> + <key>vlen</key> + <value>1</value> </param> <param> - <key>hdr_file</key> - <value></value> + <key>display</key> + <value>True</value> </param> <param> <key>_coordinate</key> - <value>(56, 199)</value> + <value>(561, 290)</value> </param> <param> <key>_rotation</key> @@ -255,58 +244,73 @@ </param> </block> <block> - <key>options</key> + <key>gr_throttle</key> <param> <key>id</key> - <value>file_metadata_source</value> + <value>gr_throttle_0</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>title</key> - <value></value> + <key>type</key> + <value>complex</value> </param> <param> - <key>author</key> - <value></value> + <key>samples_per_second</key> + <value>samp_rate</value> </param> <param> - <key>description</key> - <value></value> + <key>vlen</key> + <value>1</value> </param> <param> - <key>window_size</key> - <value>1280, 1024</value> + <key>_coordinate</key> + <value>(322, 222)</value> </param> <param> - <key>generate_options</key> - <value>qt_gui</value> + <key>_rotation</key> + <value>0</value> </param> + </block> + <block> + <key>blocks_file_meta_source</key> <param> - <key>category</key> - <value>Custom</value> + <key>id</key> + <value>blocks_file_meta_source_0</value> </param> <param> - <key>run_options</key> - <value>prompt</value> + <key>_enabled</key> + <value>True</value> </param> <param> - <key>run</key> - <value>True</value> + <key>type</key> + <value>complex</value> </param> <param> - <key>max_nouts</key> - <value>0</value> + <key>file</key> + <value>/tmp/metadat_file.out</value> </param> <param> - <key>realtime_scheduling</key> + <key>repeat</key> + <value>False</value> + </param> + <param> + <key>detached</key> + <value>True</value> + </param> + <param> + <key>hdr_file</key> <value></value> </param> <param> + <key>vlen</key> + <value>1</value> + </param> + <param> <key>_coordinate</key> - <value>(10, 10)</value> + <value>(50, 198)</value> </param> <param> <key>_rotation</key> @@ -326,12 +330,6 @@ <sink_key>0</sink_key> </connection> <connection> - <source_block_id>gr_file_meta_source_0</source_block_id> - <sink_block_id>gr_throttle_0</sink_block_id> - <source_key>0</source_key> - <sink_key>0</sink_key> - </connection> - <connection> <source_block_id>gr_throttle_0</source_block_id> <sink_block_id>analog_agc2_xx_0</sink_block_id> <source_key>0</source_key> @@ -343,4 +341,10 @@ <source_key>0</source_key> <sink_key>0</sink_key> </connection> + <connection> + <source_block_id>blocks_file_meta_source_0</source_block_id> + <sink_block_id>gr_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> </flow_graph> diff --git a/gnuradio-core/src/examples/metadata/file_metadata_vector_sink.grc b/gr-blocks/examples/metadata/file_metadata_vector_sink.grc index d67af3a66..05b7cbc92 100644 --- a/gnuradio-core/src/examples/metadata/file_metadata_vector_sink.grc +++ b/gr-blocks/examples/metadata/file_metadata_vector_sink.grc @@ -1,6 +1,65 @@ <?xml version='1.0' encoding='ASCII'?> <flow_graph> - <timestamp>Fri Dec 14 13:52:31 2012</timestamp> + <timestamp>Fri Dec 14 17:08:29 2012</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>file_metadata_vector_sink</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>no_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> <block> <key>variable</key> <param> @@ -60,54 +119,30 @@ </param> </block> <block> - <key>gr_file_meta_sink</key> + <key>gr_head</key> <param> <key>id</key> - <value>gr_file_meta_sink_0</value> + <value>gr_head_0</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>file</key> - <value>/tmp/metadat_file.out</value> - </param> - <param> <key>type</key> <value>complex</value> </param> <param> - <key>samp_rate</key> - <value>samp_rate</value> - </param> - <param> - <key>rel_rate</key> - <value>1</value> + <key>num_items</key> + <value>10010000</value> </param> <param> <key>vlen</key> <value>10</value> </param> <param> - <key>max_seg_size</key> - <value>1000000</value> - </param> - <param> - <key>extra_dict</key> - <value></value> - </param> - <param> - <key>detached</key> - <value>False</value> - </param> - <param> - <key>unbuffered</key> - <value>False</value> - </param> - <param> <key>_coordinate</key> - <value>(559, 60)</value> + <value>(325, 108)</value> </param> <param> <key>_rotation</key> @@ -115,89 +150,54 @@ </param> </block> <block> - <key>options</key> + <key>blocks_file_meta_sink</key> <param> <key>id</key> - <value>file_metadata_vector_sink</value> + <value>blocks_file_meta_sink_0</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>title</key> - <value></value> - </param> - <param> - <key>author</key> - <value></value> - </param> - <param> - <key>description</key> - <value></value> - </param> - <param> - <key>window_size</key> - <value>1280, 1024</value> - </param> - <param> - <key>generate_options</key> - <value>no_gui</value> - </param> - <param> - <key>category</key> - <value>Custom</value> - </param> - <param> - <key>run_options</key> - <value>run</value> + <key>file</key> + <value>/tmp/metadat_vector.out</value> </param> <param> - <key>run</key> - <value>True</value> + <key>type</key> + <value>complex</value> </param> <param> - <key>max_nouts</key> - <value>0</value> + <key>samp_rate</key> + <value>samp_rate</value> </param> <param> - <key>realtime_scheduling</key> - <value></value> + <key>rel_rate</key> + <value>1</value> </param> <param> - <key>_coordinate</key> - <value>(10, 10)</value> + <key>vlen</key> + <value>10</value> </param> <param> - <key>_rotation</key> - <value>0</value> + <key>max_seg_size</key> + <value>1000000</value> </param> - </block> - <block> - <key>gr_head</key> <param> - <key>id</key> - <value>gr_head_0</value> + <key>extra_dict</key> + <value>""</value> </param> <param> - <key>_enabled</key> + <key>detached</key> <value>True</value> </param> <param> - <key>type</key> - <value>complex</value> - </param> - <param> - <key>num_items</key> - <value>10010000</value> - </param> - <param> - <key>vlen</key> - <value>10</value> + <key>unbuffered</key> + <value>False</value> </param> <param> <key>_coordinate</key> - <value>(325, 108)</value> + <value>(544, 60)</value> </param> <param> <key>_rotation</key> @@ -212,7 +212,7 @@ </connection> <connection> <source_block_id>gr_head_0</source_block_id> - <sink_block_id>gr_file_meta_sink_0</sink_block_id> + <sink_block_id>blocks_file_meta_sink_0</sink_block_id> <source_key>0</source_key> <sink_key>0</sink_key> </connection> diff --git a/gnuradio-core/src/examples/metadata/file_metadata_vector_source.grc b/gr-blocks/examples/metadata/file_metadata_vector_source.grc index 0662865f1..d52257e06 100644 --- a/gnuradio-core/src/examples/metadata/file_metadata_vector_source.grc +++ b/gr-blocks/examples/metadata/file_metadata_vector_source.grc @@ -1,11 +1,11 @@ <?xml version='1.0' encoding='ASCII'?> <flow_graph> - <timestamp>Fri Dec 14 13:55:43 2012</timestamp> + <timestamp>Fri Dec 14 17:11:02 2012</timestamp> <block> <key>options</key> <param> <key>id</key> - <value>file_metadata_source</value> + <value>file_metadata_vector_source</value> </param> <param> <key>_enabled</key> @@ -158,10 +158,10 @@ </param> </block> <block> - <key>gr_file_meta_source</key> + <key>qtgui_time_sink_x</key> <param> <key>id</key> - <value>gr_file_meta_source_0</value> + <value>qtgui_time_sink_x_0</value> </param> <param> <key>_enabled</key> @@ -172,28 +172,28 @@ <value>complex</value> </param> <param> - <key>file</key> - <value>/tmp/metadat_file.out</value> + <key>name</key> + <value>QT GUI Plot</value> </param> <param> - <key>repeat</key> - <value>False</value> + <key>size</key> + <value>1024</value> </param> <param> - <key>detached</key> - <value>False</value> + <key>bw</key> + <value>samp_rate</value> </param> <param> - <key>hdr_file</key> - <value>/tmp/metadat_file.out.hdr</value> + <key>nconnections</key> + <value>1</value> </param> <param> - <key>vlen</key> - <value>10</value> + <key>gui_hint</key> + <value></value> </param> <param> <key>_coordinate</key> - <value>(51, 199)</value> + <value>(746, 116)</value> </param> <param> <key>_rotation</key> @@ -201,10 +201,10 @@ </param> </block> <block> - <key>qtgui_time_sink_x</key> + <key>blocks_vector_to_stream</key> <param> <key>id</key> - <value>qtgui_time_sink_x_0</value> + <value>blocks_vector_to_stream_0</value> </param> <param> <key>_enabled</key> @@ -215,28 +215,16 @@ <value>complex</value> </param> <param> - <key>name</key> - <value>QT GUI Plot</value> - </param> - <param> - <key>size</key> - <value>1024</value> - </param> - <param> - <key>bw</key> - <value>samp_rate</value> + <key>num_items</key> + <value>10</value> </param> <param> - <key>nconnections</key> + <key>vlen</key> <value>1</value> </param> <param> - <key>gui_hint</key> - <value></value> - </param> - <param> <key>_coordinate</key> - <value>(746, 116)</value> + <value>(526, 132)</value> </param> <param> <key>_rotation</key> @@ -275,10 +263,10 @@ </param> </block> <block> - <key>blocks_vector_to_stream</key> + <key>blocks_file_meta_source</key> <param> <key>id</key> - <value>blocks_vector_to_stream_0</value> + <value>blocks_file_meta_source_0</value> </param> <param> <key>_enabled</key> @@ -289,16 +277,28 @@ <value>complex</value> </param> <param> - <key>num_items</key> - <value>10</value> + <key>file</key> + <value>/tmp/metadat_vector.out</value> + </param> + <param> + <key>repeat</key> + <value>False</value> + </param> + <param> + <key>detached</key> + <value>True</value> + </param> + <param> + <key>hdr_file</key> + <value></value> </param> <param> <key>vlen</key> - <value>1</value> + <value>10</value> </param> <param> <key>_coordinate</key> - <value>(526, 132)</value> + <value>(42, 199)</value> </param> <param> <key>_rotation</key> @@ -318,12 +318,6 @@ <sink_key>0</sink_key> </connection> <connection> - <source_block_id>gr_file_meta_source_0</source_block_id> - <sink_block_id>gr_throttle_0</sink_block_id> - <source_key>0</source_key> - <sink_key>0</sink_key> - </connection> - <connection> <source_block_id>blocks_vector_to_stream_0</source_block_id> <sink_block_id>qtgui_time_sink_x_0</sink_block_id> <source_key>0</source_key> @@ -335,4 +329,10 @@ <source_key>0</source_key> <sink_key>0</sink_key> </connection> + <connection> + <source_block_id>blocks_file_meta_source_0</source_block_id> + <sink_block_id>gr_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> </flow_graph> diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml index ea5e552d1..797b3dbc5 100644 --- a/gr-blocks/grc/blocks_block_tree.xml +++ b/gr-blocks/grc/blocks_block_tree.xml @@ -31,6 +31,11 @@ <cat> <name>Sources (New)</name> <block>blocks_file_source</block> + <block>blocks_file_meta_source</block> + </cat> + <cat> + <name>Sinks (New)</name> + <block>blocks_file_meta_sink</block> </cat> <cat> <name>Math Operations (New) </name> diff --git a/grc/blocks/gr_file_meta_sink.xml b/gr-blocks/grc/blocks_file_meta_sink.xml index ba9bf4ee1..f6490d507 100644 --- a/grc/blocks/gr_file_meta_sink.xml +++ b/gr-blocks/grc/blocks_file_meta_sink.xml @@ -6,9 +6,9 @@ --> <block> <name>File Meta Sink</name> - <key>gr_file_meta_sink</key> - <import>from gnuradio import gr</import> - <make>gr.file_meta_sink($type.size*$vlen, $file, $samp_rate, $rel_rate, $type.dtype, $type.cplx, $max_seg_size, $extra_dict, $detached) + <key>blocks_file_meta_sink</key> + <import>from gnuradio import gr, blocks</import> + <make>blocks.file_meta_sink($type.size*$vlen, $file, $samp_rate, $rel_rate, $type.dtype, $type.cplx, $max_seg_size, $extra_dict, $detached) self.$(id).set_unbuffered($unbuffered)</make> <callback>set_unbuffered($unbuffered)</callback> <callback>open($file)</callback> @@ -26,35 +26,35 @@ self.$(id).set_unbuffered($unbuffered)</make> <name>Complex</name> <key>complex</key> <opt>size:gr.sizeof_gr_complex</opt> - <opt>dtype:gr.GR_FILE_FLOAT</opt> + <opt>dtype:blocks.GR_FILE_FLOAT</opt> <opt>cplx:True</opt> </option> <option> <name>Float</name> <key>float</key> <opt>size:gr.sizeof_float</opt> - <opt>dtype:gr.GR_FILE_FLOAT</opt> + <opt>dtype:blocks.GR_FILE_FLOAT</opt> <opt>cplx:False</opt> </option> <option> <name>Int</name> <key>int</key> <opt>size:gr.sizeof_int</opt> - <opt>dtype:gr.GR_FILE_INT</opt> + <opt>dtype:blocks.GR_FILE_INT</opt> <opt>cplx:False</opt> </option> <option> <name>Short</name> <key>short</key> <opt>size:gr.sizeof_short</opt> - <opt>dtype:gr.GR_FILE_SHORT</opt> + <opt>dtype:blocks.GR_FILE_SHORT</opt> <opt>cplx:False</opt> </option> <option> <name>Byte</name> <key>byte</key> <opt>size:gr.sizeof_char</opt> - <opt>dtype:gr.GR_FILE_BYTE</opt> + <opt>dtype:blocks.GR_FILE_BYTE</opt> <opt>cplx:False</opt> </option> </param> diff --git a/grc/blocks/gr_file_meta_source.xml b/gr-blocks/grc/blocks_file_meta_source.xml index 8f667961e..aa7e34955 100644 --- a/grc/blocks/gr_file_meta_source.xml +++ b/gr-blocks/grc/blocks_file_meta_source.xml @@ -6,9 +6,9 @@ --> <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> + <key>blocks_file_meta_source</key> + <import>from gnuradio import gr, blocks</import> + <make>blocks.file_meta_source($file, $repeat, $detached, $hdr_file)</make> <callback>open($file, $repeat)</callback> <param> <name>Output Type</name> diff --git a/gr-blocks/include/blocks/CMakeLists.txt b/gr-blocks/include/blocks/CMakeLists.txt index 787fddba3..0459097b0 100644 --- a/gr-blocks/include/blocks/CMakeLists.txt +++ b/gr-blocks/include/blocks/CMakeLists.txt @@ -102,6 +102,8 @@ install(FILES conjugate_cc.h deinterleave.h file_source.h + file_meta_sink.h + file_meta_source.h float_to_char.h float_to_complex.h float_to_int.h diff --git a/gr-blocks/include/blocks/file_meta_sink.h b/gr-blocks/include/blocks/file_meta_sink.h new file mode 100644 index 000000000..ef64887cf --- /dev/null +++ b/gr-blocks/include/blocks/file_meta_sink.h @@ -0,0 +1,111 @@ +/* -*- 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_BLOCKS_FILE_META_SINK_H +#define INCLUDED_BLOCKS_FILE_META_SINK_H + +#include <blocks/api.h> +#include <gr_sync_block.h> + +namespace gr { + namespace blocks { + + const char METADATA_VERSION = 0; + const size_t METADATA_HEADER_SIZE = 149; + + 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, + }; + + /*! + * \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. + * size (uint32_t): item size in bytes. + * type (gr_file_types as int32_t): data type. + * cplx (bool): Is data complex? + * strt (uint64_t): Starting byte of data in this segment. + * bytes (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 BLOCKS_API file_meta_sink : virtual public gr_sync_block + { + public: + // gr::blocks::file_meta_sink::sptr + typedef boost::shared_ptr<file_meta_sink> sptr; + + /*! + * \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) + */ + static sptr make(size_t itemsize, const std::string &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); + + virtual bool open(const std::string &filename) = 0; + virtual void close() = 0; + virtual void do_update() = 0; + + virtual void set_unbuffered(bool unbuffered) = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_BLOCKS_FILE_META_SINK_H */ diff --git a/gr-blocks/include/blocks/file_meta_source.h b/gr-blocks/include/blocks/file_meta_source.h new file mode 100644 index 000000000..a992d5243 --- /dev/null +++ b/gr-blocks/include/blocks/file_meta_source.h @@ -0,0 +1,81 @@ +/* -*- 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_BLOCKS_FILE_META_SOURCE_H +#define INCLUDED_BLOCKS_FILE_META_SOURCE_H + +#include <blocks/api.h> +#include <gr_sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \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. + * size (uint32_t): item size in bytes. + * type (gr_file_types as int32_t): data type. + * cplx (bool): Is data complex? + * strt (uint64_t): Starting byte of data in this segment. + * bytes (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 BLOCKS_API file_meta_source : virtual public gr_sync_block + { + public: + // gr::blocks::file_meta_source::sptr + typedef boost::shared_ptr<file_meta_source> sptr; + + /*! + * \brief Create a meta-data file source. + * + * \param filename (string): Name of file to write data to. + * \param repeat (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. + * Defaults to 'filename.hdr' if detached_header is true but this + * field is an empty string. + */ + static sptr make(const std::string &filename, + bool repeat=false, + bool detached_header=false, + const std::string &hdr_filename=""); + + virtual bool open(const std::string &filename, + const std::string &hdr_filename="") = 0; + virtual void close() = 0; + virtual void do_update() = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_BLOCKS_FILE_META_SOURCE_H */ diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt index 3a8ffac75..ab989fc78 100644 --- a/gr-blocks/lib/CMakeLists.txt +++ b/gr-blocks/lib/CMakeLists.txt @@ -141,6 +141,8 @@ list(APPEND gr_blocks_sources conjugate_cc_impl.cc deinterleave_impl.cc file_source_impl.cc + file_meta_sink_impl.cc + file_meta_source_impl.cc float_to_char_impl.cc float_to_complex_impl.cc float_array_to_int.cc diff --git a/gr-blocks/lib/file_meta_sink_impl.cc b/gr-blocks/lib/file_meta_sink_impl.cc new file mode 100644 index 000000000..ad16e9fca --- /dev/null +++ b/gr-blocks/lib/file_meta_sink_impl.cc @@ -0,0 +1,464 @@ +/* -*- 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 "file_meta_sink_impl.h" +#include <gr_io_signature.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdexcept> +#include <cstdio> + +namespace gr { + namespace blocks { + +// 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 + + file_meta_sink::sptr + file_meta_sink::make(size_t itemsize, const std::string &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 file_meta_sink_impl(itemsize, filename, + samp_rate, relative_rate, + type, complex, + max_segment_size, + extra_dict, + detached_header)); + } + + file_meta_sink_impl::file_meta_sink_impl(size_t itemsize, + const std::string &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("size"), pmt_from_long(d_itemsize)); + 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("bytes"), 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); + } + + file_meta_sink_impl::~file_meta_sink_impl() + { + 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 + file_meta_sink_impl::open(const std::string &filename) + { + bool ret = true; + if(d_state == STATE_DETACHED) { + std::string s = filename + ".hdr"; + ret = _open(&d_new_hdr_fp, s.c_str()); + } + + ret = ret && _open(&d_new_fp, filename.c_str()); + d_updated = true; + return ret; + } + + bool + file_meta_sink_impl::_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 + file_meta_sink_impl::close() + { + gruel::scoped_lock guard(d_mutex); // hold mutex for duration of this function + update_last_header(); + + 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 + file_meta_sink_impl::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 + file_meta_sink_impl::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"); + } + } + + fflush(fp); + } + + void + file_meta_sink_impl::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 + file_meta_sink_impl::update_last_header() + { + if(d_state == STATE_DETACHED) + update_last_header_detached(); + else + update_last_header_inline(); + } + + void + file_meta_sink_impl::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("bytes"), 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 + file_meta_sink_impl::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("bytes"), 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 + file_meta_sink_impl::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("bytes"), 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 + file_meta_sink_impl::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 + file_meta_sink_impl::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; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/file_meta_sink_impl.h b/gr-blocks/lib/file_meta_sink_impl.h new file mode 100644 index 000000000..566c997b3 --- /dev/null +++ b/gr-blocks/lib/file_meta_sink_impl.h @@ -0,0 +1,96 @@ +/* -*- 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_BLOCKS_FILE_META_SINK_IMPL_H +#define INCLUDED_BLOCKS_FILE_META_SINK_IMPL_H + +#include <blocks/file_meta_sink.h> +#include <gruel/pmt.h> +#include <gruel/thread.h> + +using namespace pmt; + +namespace gr { + namespace blocks { + + class file_meta_sink_impl : public file_meta_sink + { + 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: + 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: + file_meta_sink_impl(size_t itemsize, const std::string &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); + ~file_meta_sink_impl(); + + bool open(const std::string &filename); + void close(); + void do_update(); + + void set_unbuffered(bool unbuffered) + { + d_unbuffered = unbuffered; + } + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_BLOCKS_FILE_META_SINK_IMPL_H */ diff --git a/gr-blocks/lib/file_meta_source_impl.cc b/gr-blocks/lib/file_meta_source_impl.cc new file mode 100644 index 000000000..fb39b205b --- /dev/null +++ b/gr-blocks/lib/file_meta_source_impl.cc @@ -0,0 +1,433 @@ +/* -*- 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 "file_meta_source_impl.h" +#include <gr_io_signature.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdexcept> +#include <cstdio> + +namespace gr { + namespace blocks { + +// 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 + + file_meta_source::sptr + file_meta_source::make(const std::string &filename, + bool repeat, + bool detached_header, + const std::string &hdr_filename) + { + return gnuradio::get_initial_sptr + (new file_meta_source_impl(filename, + repeat, + detached_header, + hdr_filename)); + } + + file_meta_source_impl::file_meta_source_impl(const std::string &filename, + bool repeat, + bool detached_header, + const std::string &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, hdr_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, 0, d_tags); + parse_extras(extras, 0, d_tags); + } + 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)); + } + + file_meta_source_impl::~file_meta_source_impl() + { + 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 + file_meta_source_impl::read_header(pmt_t &hdr, pmt_t &extras) + { + // Select which file handle to read from. + FILE *fp; + if(d_state == STATE_DETACHED) + fp = d_hdr_fp; + else + fp = d_fp; + + 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, fp); + if(ret == 0) { + delete [] hdr_buffer; + if(feof(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, fp); + if(ret == 0) { + delete [] hdr_buffer; + if(feof(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 + file_meta_source_impl::parse_header(pmt_t hdr, uint64_t offset, + std::vector<gr_tag_t> &tags) + { + pmt_t r, key; + + // GET SAMPLE RATE + key = pmt_string_to_symbol("rx_rate"); + if(pmt_dict_has_key(hdr, key)) { + r = pmt_dict_ref(hdr, key, PMT_NIL); + d_samp_rate = pmt_to_double(r); + + gr_tag_t t; + t.offset = offset; + t.key = key; + t.value = r; + t.srcid = alias_pmt(); + tags.push_back(t); + } + else { + throw std::runtime_error("file_meta_source: Could not extract sample rate.\n"); + } + + // GET TIME STAMP + key = pmt_string_to_symbol("rx_time"); + if(pmt_dict_has_key(hdr, key)) { + d_time_stamp = pmt_dict_ref(hdr, key, PMT_NIL); + + gr_tag_t t; + t.offset = offset; + t.key = key; + t.value = d_time_stamp; + t.srcid = alias_pmt(); + tags.push_back(t); + } + else { + throw std::runtime_error("file_meta_source: Could not extract time stamp.\n"); + } + + // GET ITEM SIZE OF DATA + if(pmt_dict_has_key(hdr, pmt_string_to_symbol("size"))) { + d_itemsize = pmt_to_long(pmt_dict_ref(hdr, pmt_string_to_symbol("size"), PMT_NIL)); + } + else { + throw std::runtime_error("file_meta_source: Could not extract item size.\n"); + } + + // GET SEGMENT SIZE + if(pmt_dict_has_key(hdr, pmt_string_to_symbol("bytes"))) { + d_seg_size = pmt_to_uint64(pmt_dict_ref(hdr, pmt_string_to_symbol("bytes"), PMT_NIL)); + + // Convert from bytes to items + d_seg_size /= d_itemsize; + } + else { + throw std::runtime_error("file_meta_source: Could not extract segment size.\n"); + } + } + + void + file_meta_source_impl::parse_extras(pmt_t extras, uint64_t offset, + std::vector<gr_tag_t> &tags) + { + pmt_t item, key, val; + + size_t nitems = pmt_length(extras); + for(size_t i = 0; i < nitems; i++) { + item = pmt_nth(i, extras); + key = pmt_car(item); + val = pmt_cdr(item); + + gr_tag_t t; + t.offset = offset; + t.key = key; + t.value = val; + t.srcid = alias_pmt(); + tags.push_back(t); + } + } + + bool + file_meta_source_impl::open(const std::string &filename, + const std::string &hdr_filename) + { + bool ret = true; + if(d_state == STATE_DETACHED) { + std::string s; + if(hdr_filename == "") + s = filename + ".hdr"; + else + s = hdr_filename; + ret = _open(&d_new_hdr_fp, s.c_str()); + } + + ret = ret && _open(&d_new_fp, filename.c_str()); + d_updated = true; + return ret; + } + + bool + file_meta_source_impl::_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 + file_meta_source_impl::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 + file_meta_source_impl::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 + file_meta_source_impl::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) { + pmt_t hdr=PMT_NIL, extras=PMT_NIL; + if(read_header(hdr, extras)) { + parse_header(hdr, nitems_written(0), d_tags); + parse_extras(extras, nitems_written(0), d_tags); + } + 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"); + + // Push all tags onto the stream and remove them from the vector + while(!d_tags.empty()) { + add_item_tag(0, d_tags.back()); + d_tags.pop_back(); + } + + 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; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/file_meta_source_impl.h b/gr-blocks/lib/file_meta_source_impl.h new file mode 100644 index 000000000..ca7ddc6e1 --- /dev/null +++ b/gr-blocks/lib/file_meta_source_impl.h @@ -0,0 +1,89 @@ +/* -*- 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_BLOCKS_FILE_META_SOURCE_IMPL_H +#define INCLUDED_BLOCKS_FILE_META_SOURCE_IMPL_H + +#include <blocks/file_meta_source.h> +#include <gr_tags.h> +#include <gruel/pmt.h> +#include <gruel/thread.h> + +#include <blocks/file_meta_sink.h> + +using namespace pmt; + +namespace gr { + namespace blocks { + + class file_meta_source_impl : public file_meta_source + { + 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; + + std::vector<gr_tag_t> d_tags; + + protected: + bool _open(FILE **fp, const char *filename); + bool read_header(pmt_t &hdr, pmt_t &extras); + void parse_header(pmt_t hdr, uint64_t offset, + std::vector<gr_tag_t> &tags); + void parse_extras(pmt_t extras, uint64_t offset, + std::vector<gr_tag_t> &tags); + + public: + file_meta_source_impl(const std::string &filename, + bool repeat=false, + bool detached_header=false, + const std::string &hdr_filename=""); + + ~file_meta_source_impl(); + + bool open(const std::string &filename, + const std::string &hdr_filename=""); + void close(); + void do_update(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_BLOCKS_FILE_META_SOURCE_IMPL_H */ diff --git a/gr-blocks/python/CMakeLists.txt b/gr-blocks/python/CMakeLists.txt index 710ab155c..cab0b956f 100644 --- a/gr-blocks/python/CMakeLists.txt +++ b/gr-blocks/python/CMakeLists.txt @@ -23,6 +23,7 @@ include(GrPython) GR_PYTHON_INSTALL( FILES __init__.py + parse_file_metadata.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/blocks COMPONENT "blocks_python" ) diff --git a/gnuradio-core/src/python/gnuradio/parse_file_metadata.py b/gr-blocks/python/parse_file_metadata.py index 0cee5a02c..ec7bf6e80 100644 --- a/gnuradio-core/src/python/gnuradio/parse_file_metadata.py +++ b/gr-blocks/python/parse_file_metadata.py @@ -24,6 +24,11 @@ import sys from gnuradio import gr from gruel import pmt +try: + import blocks_swig as blocks +except: + from gnuradio import blocks + ''' sr Sample rate (samples/second) time Time as uint64(secs), double(fractional secs) @@ -33,23 +38,23 @@ 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} +HEADER_LENGTH = blocks.METADATA_HEADER_SIZE + +ftype_to_string = {blocks.GR_FILE_BYTE: "bytes", + blocks.GR_FILE_SHORT: "short", + blocks.GR_FILE_INT: "int", + blocks.GR_FILE_LONG: "long", + blocks.GR_FILE_LONG_LONG: "long long", + blocks.GR_FILE_FLOAT: "float", + blocks.GR_FILE_DOUBLE: "double" } + +ftype_to_size = {blocks.GR_FILE_BYTE: gr.sizeof_char, + blocks.GR_FILE_SHORT: gr.sizeof_short, + blocks.GR_FILE_INT: gr.sizeof_int, + blocks.GR_FILE_LONG: gr.sizeof_int, + blocks.GR_FILE_LONG_LONG: 2*gr.sizeof_int, + blocks.GR_FILE_FLOAT: gr.sizeof_float, + blocks.GR_FILE_DOUBLE: gr.sizeof_double} def parse_header(p, VERBOSE=False): dump = pmt.PMT_NIL diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_file_metadata.py b/gr-blocks/python/qa_file_metadata.py index 849f32299..9f4a331d6 100644 --- a/gnuradio-core/src/python/gnuradio/gr/qa_file_metadata.py +++ b/gr-blocks/python/qa_file_metadata.py @@ -21,7 +21,8 @@ # from gnuradio import gr, gr_unittest -from gnuradio import parse_file_metadata +import parse_file_metadata +import blocks_swig as blocks import pmt import os, time @@ -46,10 +47,10 @@ class test_file_metadata(gr_unittest.TestCase): src = gr.sig_source_c(samp_rate, gr.GR_COS_WAVE, 1000, 1, 0) head = gr.head(gr.sizeof_gr_complex, 1000) - fsnk = gr.file_meta_sink(gr.sizeof_gr_complex, outfile, - samp_rate, 1, - gr.GR_FILE_FLOAT, True, - 1000000, extras_str, detached) + fsnk = blocks.file_meta_sink(gr.sizeof_gr_complex, outfile, + samp_rate, 1, + blocks.GR_FILE_FLOAT, True, + 1000000, extras_str, detached) fsnk.set_unbuffered(True) self.tb.connect(src, head, fsnk) @@ -86,7 +87,7 @@ class test_file_metadata(gr_unittest.TestCase): # Test file metadata source # Create a new sig source to start from the beginning src2 = gr.sig_source_c(samp_rate, gr.GR_COS_WAVE, 1000, 1, 0) - fsrc = gr.file_meta_source(outfile, False) + fsrc = blocks.file_meta_source(outfile, False) vsnk = gr.vector_sink_c() tsnk = gr.tag_debug(gr.sizeof_gr_complex, "QA") ssnk = gr.vector_sink_c() @@ -125,10 +126,10 @@ class test_file_metadata(gr_unittest.TestCase): src = gr.sig_source_c(samp_rate, gr.GR_COS_WAVE, 1000, 1, 0) head = gr.head(gr.sizeof_gr_complex, 1000) - fsnk = gr.file_meta_sink(gr.sizeof_gr_complex, outfile, - samp_rate, 1, - gr.GR_FILE_FLOAT, True, - 1000000, extras_str, detached) + fsnk = blocks.file_meta_sink(gr.sizeof_gr_complex, outfile, + samp_rate, 1, + blocks.GR_FILE_FLOAT, True, + 1000000, extras_str, detached) fsnk.set_unbuffered(True) self.tb.connect(src, head, fsnk) @@ -166,7 +167,7 @@ class test_file_metadata(gr_unittest.TestCase): # Test file metadata source # Create a new sig source to start from the beginning src2 = gr.sig_source_c(samp_rate, gr.GR_COS_WAVE, 1000, 1, 0) - fsrc = gr.file_meta_source(outfile, False, detached, outfile_hdr) + fsrc = blocks.file_meta_source(outfile, False, detached, outfile_hdr) vsnk = gr.vector_sink_c() tsnk = gr.tag_debug(gr.sizeof_gr_complex, "QA") ssnk = gr.vector_sink_c() diff --git a/gr-blocks/swig/blocks_swig.i b/gr-blocks/swig/blocks_swig.i index 7ec6bb423..53eea818f 100644 --- a/gr-blocks/swig/blocks_swig.i +++ b/gr-blocks/swig/blocks_swig.i @@ -62,6 +62,8 @@ #include "blocks/divide_ii.h" #include "blocks/divide_cc.h" #include "blocks/file_source.h" +#include "blocks/file_meta_sink.h" +#include "blocks/file_meta_source.h" #include "blocks/float_to_char.h" #include "blocks/float_to_complex.h" #include "blocks/float_to_int.h" @@ -147,6 +149,8 @@ %include "blocks/conjugate_cc.h" %include "blocks/deinterleave.h" %include "blocks/file_source.h" +%include "blocks/file_meta_sink.h" +%include "blocks/file_meta_source.h" %include "blocks/divide_ff.h" %include "blocks/divide_ss.h" %include "blocks/divide_ii.h" @@ -239,6 +243,8 @@ GR_SWIG_BLOCK_MAGIC2(blocks, divide_ss); GR_SWIG_BLOCK_MAGIC2(blocks, divide_ii); GR_SWIG_BLOCK_MAGIC2(blocks, divide_cc); GR_SWIG_BLOCK_MAGIC2(blocks, file_source); +GR_SWIG_BLOCK_MAGIC2(blocks, file_meta_sink); +GR_SWIG_BLOCK_MAGIC2(blocks, file_meta_source); GR_SWIG_BLOCK_MAGIC2(blocks, float_to_char); GR_SWIG_BLOCK_MAGIC2(blocks, float_to_complex); GR_SWIG_BLOCK_MAGIC2(blocks, float_to_int); diff --git a/gr-utils/src/python/gr_read_file_metadata b/gr-utils/src/python/gr_read_file_metadata index efbf8d15d..cf0cd5b11 100644 --- a/gr-utils/src/python/gr_read_file_metadata +++ b/gr-utils/src/python/gr_read_file_metadata @@ -23,8 +23,8 @@ import sys from optparse import OptionParser -from gnuradio import gr -from gnuradio import parse_file_metadata +from gruel import pmt +from gnuradio.blocks import parse_file_metadata def main(filename, detached=False): handle = open(filename, "rb") @@ -40,7 +40,7 @@ def main(filename, detached=False): # Convert from string to PMT (should be a dictionary) try: - header = gr.pmt_deserialize_str(header_str) + header = pmt.pmt_deserialize_str(header_str) except RuntimeError: sys.stderr.write("Could not deserialize header: invalid or corrupt data file.\n") sys.exit(1) @@ -54,7 +54,7 @@ def main(filename, detached=False): break try: - extra = gr.pmt_deserialize_str(extra_str) + extra = pmt.pmt_deserialize_str(extra_str) except RuntimeError: sys.stderr.write("Could not deserialize extras: invalid or corrupt data file.\n") sys.exit(1) diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml index f1c68c7c9..183883959 100644 --- a/grc/blocks/block_tree.xml +++ b/grc/blocks/block_tree.xml @@ -29,8 +29,6 @@ <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> <block>gr_wavfile_sink</block> |