summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_sink.cc128
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_sink.h51
-rw-r--r--gnuradio-core/src/lib/io/gr_file_meta_sink.i6
-rw-r--r--gnuradio-core/src/python/gnuradio/parse_file_metadata.py29
-rw-r--r--gr-utils/src/python/gr_read_file_metadata33
-rw-r--r--grc/blocks/gr_file_meta_sink.xml8
6 files changed, 203 insertions, 52 deletions
diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.cc b/gnuradio-core/src/lib/io/gr_file_meta_sink.cc
index aa34bf9b7..9d092d79f 100644
--- a/gnuradio-core/src/lib/io/gr_file_meta_sink.cc
+++ b/gnuradio-core/src/lib/io/gr_file_meta_sink.cc
@@ -28,25 +28,31 @@
#include <gr_io_signature.h>
#include <stdexcept>
+#define HEADER_SIZE 117
+
gr_file_meta_sink_sptr
gr_make_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string &extra_dict)
{
return gnuradio::get_initial_sptr
(new gr_file_meta_sink(itemsize, filename,
- samp_rate, type, complex,
+ samp_rate, relative_rate,
+ type, complex,
extra_dict));
}
gr_file_meta_sink::gr_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string &extra_dict)
: gr_sync_block("file_meta_sink",
gr_make_io_signature(1, 1, itemsize),
gr_make_io_signature(0, 0, 0)),
gr_file_sink_base(filename, true),
- d_itemsize(itemsize)
+ d_itemsize(itemsize), d_relative_rate(relative_rate),
+ d_total_seg_size(0)
{
if(!open(filename))
throw std::runtime_error("file_meta_sink: can't open file\n");
@@ -55,11 +61,11 @@ gr_file_meta_sink::gr_file_meta_sink(size_t itemsize, const char *filename,
pmt_from_double(0));
d_header = pmt_make_dict();
- d_header = pmt_dict_add(d_header, mp("sr"), mp(samp_rate));
- d_header = pmt_dict_add(d_header, mp("time"), timestamp);
+ d_header = pmt_dict_add(d_header, mp("rx_rate"), mp(samp_rate));
+ d_header = pmt_dict_add(d_header, mp("rx_time"), timestamp);
d_header = pmt_dict_add(d_header, mp("type"), pmt_from_long(type));
d_header = pmt_dict_add(d_header, mp("cplx"), complex ? PMT_T : PMT_F);
- d_header = pmt_dict_add(d_header, mp("strt"), pmt_from_uint64(109));
+ d_header = pmt_dict_add(d_header, mp("strt"), pmt_from_uint64(HEADER_SIZE));
d_header = pmt_dict_add(d_header, mp("size"), pmt_from_uint64(0));
// handle extra dictionary
@@ -72,6 +78,8 @@ gr_file_meta_sink::write_header(pmt_t header)
do_update();
std::string header_str = pmt_serialize_str(header);
+ if(header_str.size() != HEADER_SIZE)
+ throw std::runtime_error("file_meta_sink: header is wrong size.\n");
size_t nwritten = 0;
while(nwritten < header_str.size()) {
@@ -80,21 +88,55 @@ gr_file_meta_sink::write_header(pmt_t header)
nwritten += count;
if((count == 0) && (ferror(d_fp))) {
fclose(d_fp);
- throw std::runtime_error("file_meta_sink: error writing header to file\n");
+ throw std::runtime_error("file_meta_sink: error writing header to file.\n");
}
}
}
-gr_file_meta_sink::~gr_file_meta_sink()
+bool
+gr_file_meta_sink::update_header(pmt_t key, pmt_t value)
{
- fseek(d_fp, 0, SEEK_SET);
+ // Special handling caveat to transform rate from radio source into
+ // the rate at this sink.
+ if(pmt_eq(key, mp("rx_rate"))) {
+ double rate = pmt_to_double(value);
+ value = pmt_from_double(rate*d_relative_rate);
+ }
- // Replace the dictionary item with the data size now that we're
- // done.
- uint64_t s = nitems_read(0) * d_itemsize;
- d_header = pmt_dict_delete(d_header, mp("size"));
- d_header = pmt_dict_add(d_header, mp("size"), pmt_from_uint64(s));
- write_header(d_header);
+ if(pmt_dict_has_key(d_header, key)) {
+ d_header = pmt_dict_add(d_header, key, value);
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+uint64_t
+gr_file_meta_sink::get_last_header_loc()
+{
+ uint64_t loc = 0;
+ pmt_t v = pmt_dict_ref(d_header, mp("strt"), PMT_NIL);
+ if(!pmt_eq(v, PMT_NIL))
+ loc = pmt_to_uint64(v) - HEADER_SIZE;
+ return loc;
+}
+
+gr_file_meta_sink::~gr_file_meta_sink()
+{
+ // Replace the last header block with the final count of the number
+ // of items.
+ uint64_t loc = get_last_header_loc();
+ uint64_t seg_size = nitems_read(0) * d_itemsize - d_total_seg_size;
+ pmt_t s = pmt_from_uint64(seg_size);
+ //std::cerr << "Destructor" << std::endl;
+ //std::cerr << " location of last header: " << loc << std::endl;
+ //std::cerr << " nitems_read: " << nitems_read(0)*d_itemsize << std::endl;
+ //std::cerr << " Segment Size: " << seg_size << std::endl;
+ if(update_header(mp("size"), s)) {
+ fseek(d_fp, loc, SEEK_SET);
+ write_header(d_header);
+ }
}
int
@@ -110,6 +152,60 @@ gr_file_meta_sink::work(int noutput_items,
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++) {
+ // Special case where info is carried on the first tag, so we just
+ // overwrite the first header.
+ if(itr->offset == 0) {
+ if(update_header(itr->key, itr->value)) {
+ fseek(d_fp, 0, SEEK_SET);
+ write_header(d_header);
+ }
+ }
+ else {
+ // Update the last header info with the number of samples this
+ // block represents.
+ uint64_t loc = get_last_header_loc();
+ uint64_t seg_size = itr->offset * d_itemsize - d_total_seg_size;
+ pmt_t s = pmt_from_uint64(seg_size);
+ //std::cerr << "Found Tag at: " << itr->offset*d_itemsize << std::endl;
+ //std::cerr << " last header starts at: " << loc << std::endl;
+ //std::cerr << " segment size is: " << seg_size << std::endl;
+ if(update_header(mp("size"), s)) {
+ fseek(d_fp, loc, SEEK_SET);
+ write_header(d_header);
+ }
+
+ if(update_header(itr->key, itr->value)) {
+ // Otherwise, set current size of chunk to 0 and start of
+ // chunk based on current index + header size.
+ d_total_seg_size += seg_size;
+ s = pmt_from_uint64(0);
+ if(update_header(mp("size"), s)) {
+ // If we have multiple tags on the same offset, this makes
+ // sure we just overwrite the same header each time instead
+ // of creating a new header per tag.
+ uint64_t seg_start = loc;
+ if(seg_size != 0)
+ seg_start += HEADER_SIZE + seg_size;
+ pmt_t s = pmt_from_uint64(seg_start + HEADER_SIZE);
+ if(update_header(mp("strt"), s)) {
+ //std::cerr << "Adding new header" << std::endl;
+ //std::cerr << " new header start at: " << seg_start-HEADER_SIZE << std::endl;
+ //std::cerr << " new seg start at: " << seg_start << std::endl;
+ fseek(d_fp, seg_start, SEEK_SET);
+ write_header(d_header);
+ }
+ }
+ }
+ }
+ }
+
while(nwritten < noutput_items) {
int count = fwrite(inbuf, d_itemsize, noutput_items - nwritten, d_fp);
if(count == 0) // FIXME add error handling
diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.h b/gnuradio-core/src/lib/io/gr_file_meta_sink.h
index 741408de1..c0f30bdc0 100644
--- a/gnuradio-core/src/lib/io/gr_file_meta_sink.h
+++ b/gnuradio-core/src/lib/io/gr_file_meta_sink.h
@@ -46,34 +46,71 @@ typedef boost::shared_ptr<gr_file_meta_sink> gr_file_meta_sink_sptr;
GR_CORE_API gr_file_meta_sink_sptr
gr_make_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string &extra_dict="");
/*!
- * \brief Write stream to file.
+ * \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, public gr_file_sink_base
{
-
+ /*!
+ * \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 extra_dict (string): a serialized PMT dictionary of extra
+ * information. Currently not supported.
+ */
friend GR_CORE_API gr_file_meta_sink_sptr
gr_make_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string &extra_dict);
private:
size_t d_itemsize;
+ double d_relative_rate;
+ uint64_t d_total_seg_size;
pmt_t d_header;
pmt_t d_extra_dict;
protected:
gr_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string &extra_dict);
-
void write_header(pmt_t header);
+ bool update_header(pmt_t key, pmt_t value);
+ uint64_t get_last_header_loc();
public:
~gr_file_meta_sink();
diff --git a/gnuradio-core/src/lib/io/gr_file_meta_sink.i b/gnuradio-core/src/lib/io/gr_file_meta_sink.i
index 27fb4debb..7d90ca304 100644
--- a/gnuradio-core/src/lib/io/gr_file_meta_sink.i
+++ b/gnuradio-core/src/lib/io/gr_file_meta_sink.i
@@ -35,14 +35,16 @@ enum gr_file_types {
gr_file_meta_sink_sptr
gr_make_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string & extra_dict="");
class gr_file_meta_sink : public gr_sync_block, public gr_file_sink_base
{
protected:
gr_file_meta_sink(size_t itemsize, const char *filename,
- double samp_rate, gr_file_types type, bool complex,
+ double samp_rate, double relative_rate,
+ gr_file_types type, bool complex,
const std::string & extra_dict);
public:
diff --git a/gnuradio-core/src/python/gnuradio/parse_file_metadata.py b/gnuradio-core/src/python/gnuradio/parse_file_metadata.py
index 66cb4e447..cff7566e4 100644
--- a/gnuradio-core/src/python/gnuradio/parse_file_metadata.py
+++ b/gnuradio-core/src/python/gnuradio/parse_file_metadata.py
@@ -32,7 +32,7 @@ strt Start of data (or size of header) in bytes
size Size of data in bytes
'''
-HEADER_LENGTH = 109
+HEADER_LENGTH = 117
ftype_to_string = {gr.GR_FILE_BYTE: "bytes",
gr.GR_FILE_SHORT: "short",
gr.GR_FILE_INT: "int",
@@ -49,7 +49,7 @@ ftype_to_size = {gr.GR_FILE_BYTE: gr.sizeof_char,
gr.GR_FILE_FLOAT: gr.sizeof_float,
gr.GR_FILE_DOUBLE: gr.sizeof_double}
-def parse_header(p, VERBOSE=False):
+def parse_header(p, hdr_start, VERBOSE=False):
dump = gr.PMT_NIL
info = dict()
@@ -59,10 +59,10 @@ def parse_header(p, VERBOSE=False):
sys.exit(1)
# EXTRACT SAMPLE RATE
- if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("sr"))):
- r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("sr"), dump)
+ if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("rx_rate"))):
+ r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("rx_rate"), dump)
samp_rate = gr.pmt_to_double(r)
- info["sr"] = samp_rate
+ info["rx_rate"] = samp_rate
if(VERBOSE):
print "Sample Rate: {0} sps".format(samp_rate)
else:
@@ -70,14 +70,14 @@ def parse_header(p, VERBOSE=False):
sys.exit(1)
# EXTRACT TIME STAMP
- if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("time"))):
- r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("time"), dump)
+ if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("rx_time"))):
+ r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("rx_time"), dump)
pmt_secs = gr.pmt_tuple_ref(r, 0)
pmt_fracs = gr.pmt_tuple_ref(r, 1)
secs = float(gr.pmt_to_uint64(pmt_secs))
fracs = gr.pmt_to_double(pmt_fracs)
- t = secs + fracs/(1e9)
- info["time"] = t
+ t = secs + fracs
+ info["rx_time"] = t
if(VERBOSE):
print "Seconds: {0}".format(t)
else:
@@ -107,14 +107,15 @@ def parse_header(p, VERBOSE=False):
sys.stderr.write("Could not find key 'cplx': invalid or corrupt data file.\n")
sys.exit(1)
- # EXTRACT HEADER LENGTH
+ # EXTRACT WHERE CURRENT SEGMENT STARTS
if(gr.pmt_dict_has_key(p, gr.pmt_string_to_symbol("strt"))):
r = gr.pmt_dict_ref(p, gr.pmt_string_to_symbol("strt"), dump)
- hdr_len = gr.pmt_to_uint64(r)
- info["strt"] = hdr_len
+ seg_start = gr.pmt_to_uint64(r)
+ info["strt"] = seg_start
if(VERBOSE):
- print "Header Length: {0} bytes".format(hdr_len)
- print "Extra Header? {0}".format(hdr_len > HEADER_LENGTH)
+ print "Segment Start: {0} bytes".format(seg_start)
+ print "Header Length: {0}".format((seg_start-hdr_start))
+ print "Extra Header? {0}".format((seg_start-hdr_start) > HEADER_LENGTH)
else:
sys.stderr.write("Could not find key 'strt': invalid or corrupt data file.\n")
sys.exit(1)
diff --git a/gr-utils/src/python/gr_read_file_metadata b/gr-utils/src/python/gr_read_file_metadata
index 657ad7c2b..47109aead 100644
--- a/gr-utils/src/python/gr_read_file_metadata
+++ b/gr-utils/src/python/gr_read_file_metadata
@@ -29,19 +29,28 @@ from gnuradio import parse_file_metadata
def main(filename):
handle = open(filename, "rb")
- # just read out header bytes
- header_str = handle.read(parse_file_metadata.HEADER_LENGTH)
-
- # Convert from string to PMT (should be a dictionary)
- try:
- header = gr.pmt_deserialize_str(header_str)
- except RuntimeError:
- sys.stderr.write("Could not deserialize header: invalid or corrupt data file.\n")
- sys.exit(1)
- #gr.pmt_print(header)
+ nread = 0
+ while(True):
+ # read out next header bytes
+ hdr_start = handle.tell()
+ header_str = handle.read(parse_file_metadata.HEADER_LENGTH)
+ if(len(header_str) == 0):
+ break
+
+ # Convert from string to PMT (should be a dictionary)
+ try:
+ header = gr.pmt_deserialize_str(header_str)
+ except RuntimeError:
+ sys.stderr.write("Could not deserialize header: invalid or corrupt data file.\n")
+ sys.exit(1)
+ #gr.pmt_print(header)
+
+ info = parse_file_metadata.parse_header(header, hdr_start, True)
+ print "\n\n"
+
+ nread += info['nbytes'] + parse_file_metadata.HEADER_LENGTH
- info = parse_file_metadata.parse_header(header, True)
- #print info
+ handle.seek(nread, 0)
if __name__ == "__main__":
usage="%prog: [options] filename"
diff --git a/grc/blocks/gr_file_meta_sink.xml b/grc/blocks/gr_file_meta_sink.xml
index 13a7030b0..e6f5f2bd6 100644
--- a/grc/blocks/gr_file_meta_sink.xml
+++ b/grc/blocks/gr_file_meta_sink.xml
@@ -8,7 +8,7 @@
<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, $type.dtype, $type.cplx)
+ <make>gr.file_meta_sink($type.size*$vlen, $file, $samp_rate, $rel_rate, $type.dtype, $type.cplx)
self.$(id).set_unbuffered($unbuffered)</make>
<callback>set_unbuffered($unbuffered)</callback>
<callback>open($file)</callback>
@@ -65,6 +65,12 @@ self.$(id).set_unbuffered($unbuffered)</make>
<type>real</type>
</param>
<param>
+ <name>Relative Rate Change</name>
+ <key>rel_rate</key>
+ <value>1</value>
+ <type>real</type>
+ </param>
+ <param>
<name>Vec Length</name>
<key>vlen</key>
<value>1</value>