options: parameters: author: '' catch_exceptions: 'True' category: '[GRC Hier Blocks]' cmake_opt: '' comment: '' copyright: '' description: 'Headless IQ saver for HackRF over SSH' gen_cmake: 'On' gen_linking: dynamic generate_options: no_gui hier_block_src_path: '.:' id: data_saver max_nouts: '0' output_language: python placement: (0,0) qt_qss_theme: '' realtime_scheduling: '' run: 'True' run_command: '{python} -u {filename}' run_options: prompt sizing_mode: fixed thread_safe_setters: '' title: data_saver window_size: '' states: bus_sink: false bus_source: false bus_structure: null coordinate: [8, 8] rotation: 0 state: enabled blocks: - name: serial id: parameter parameters: category: Parameters comment: 'HackRF serial' hide: none label: serial short_id: s type: string value: '00000000000000000000000000000000' states: coordinate: [48, 48] rotation: 0 state: enabled - name: freq id: parameter parameters: category: Parameters comment: 'Center frequency in Hz' hide: none label: freq short_id: f type: real value: '1.160e9' states: coordinate: [48, 96] rotation: 0 state: enabled - name: save_dir id: parameter parameters: category: Parameters comment: 'Directory for IQ files' hide: none label: save_dir short_id: o type: string value: '/tmp/data_saver' states: coordinate: [48, 144] rotation: 0 state: enabled - name: file_tag id: parameter parameters: category: Parameters comment: 'File prefix' hide: none label: file_tag short_id: t type: string value: 'fragment_' states: coordinate: [48, 192] rotation: 0 state: enabled - name: samp_rate id: parameter parameters: category: Parameters comment: 'Sample rate in S/s' hide: none label: samp_rate short_id: r type: real value: '20e6' states: coordinate: [48, 240] rotation: 0 state: enabled - name: split_size id: parameter parameters: category: Parameters comment: 'Max complex samples per file' hide: none label: split_size short_id: n type: intx value: '400000' states: coordinate: [48, 288] rotation: 0 state: enabled - name: delay id: parameter parameters: category: Parameters comment: 'Delay between files in seconds' hide: none label: delay short_id: d type: real value: '0.1' states: coordinate: [48, 336] rotation: 0 state: enabled - name: rf_gain id: parameter parameters: category: Parameters comment: 'HackRF RF gain' hide: none label: rf_gain short_id: g type: real value: '12' states: coordinate: [48, 384] rotation: 0 state: enabled - name: if_gain id: parameter parameters: category: Parameters comment: 'HackRF IF gain' hide: none label: if_gain short_id: i type: real value: '30' states: coordinate: [48, 432] rotation: 0 state: enabled - name: bb_gain id: parameter parameters: category: Parameters comment: 'HackRF baseband gain' hide: none label: bb_gain short_id: b type: real value: '36' states: coordinate: [48, 480] rotation: 0 state: enabled - name: dev_args id: variable parameters: comment: '' value: '"hackrf=" + str(serial)' states: coordinate: [48, 540] rotation: 0 state: enabled - name: epy_block_0 id: epy_block parameters: Delay: delay FileTag: file_tag SaveDir: save_dir SplitSize: split_size _source_code: "\"\"\"\nEmbedded Python Blocks:\n\nHeadless IQ writer with file splitting.\n\"\"\"\n\nimport numpy as np\nfrom gnuradio import gr\nimport os\nimport time\n\n\nclass Simsi_Sink(gr.sync_block):\n def __init__(self, SaveDir=\"./signal\", FileTag=\"fragment_\", SplitSize=1000000, Delay=0.0):\n gr.sync_block.__init__(\n self,\n name='Simsi_Sink',\n in_sig=[np.complex64],\n out_sig=None\n )\n\n self.Delay = float(Delay)\n self.FileTag = str(FileTag)\n self.SplitSize = int(SplitSize)\n self.SaveDir = str(SaveDir)\n\n os.makedirs(self.SaveDir, exist_ok=True)\n\n self.it = 0\n self.last_file_len = 0\n self.last_fd = None\n\n self.reading_in_progress_file_path = os.path.join(self.SaveDir, 'reading_in_progress')\n self.reading_in_progress_file_fd = None\n\n self._open_next_file()\n\n def _touch_in_progress(self):\n if self.reading_in_progress_file_fd is not None:\n try:\n self.reading_in_progress_file_fd.close()\n except Exception:\n pass\n self.reading_in_progress_file_fd = open(self.reading_in_progress_file_path, 'wb')\n\n def _remove_in_progress(self):\n if self.reading_in_progress_file_fd is not None:\n try:\n self.reading_in_progress_file_fd.close()\n except Exception:\n pass\n self.reading_in_progress_file_fd = None\n if os.path.exists(self.reading_in_progress_file_path):\n try:\n os.remove(self.reading_in_progress_file_path)\n except Exception:\n pass\n\n def _open_next_file(self):\n self._touch_in_progress()\n self.last_file_path = os.path.join(self.SaveDir, f\"{self.FileTag}{self.it}\")\n self.last_fd = open(self.last_file_path, 'wb')\n self.last_file_len = 0\n print(f\"Opened file: {self.last_file_path}\", flush=True)\n\n def _rotate_file(self):\n if self.last_fd is not None:\n self.last_fd.close()\n self.last_fd = None\n self._remove_in_progress()\n if self.Delay > 0:\n time.sleep(self.Delay)\n self.it += 1\n self._open_next_file()\n\n def stop(self):\n try:\n if self.last_fd is not None:\n self.last_fd.close()\n self.last_fd = None\n finally:\n self._remove_in_progress()\n return True\n\n def work(self, input_items, output_items):\n data = input_items[0]\n offset = 0\n total = len(data)\n\n while offset < total:\n remaining = self.SplitSize - self.last_file_len\n chunk = min(remaining, total - offset)\n\n self.last_fd.write(data[offset:offset + chunk].copy())\n self.last_file_len += chunk\n offset += chunk\n\n if self.last_file_len >= self.SplitSize:\n print(f\"Saving file: {self.last_file_path}\", flush=True)\n self._rotate_file()\n\n return len(data)\n" affinity: '' alias: '' comment: '' maxoutbuf: '0' minoutbuf: '0' states: _io_cache: ('Simsi_Sink', 'Simsi_Sink', [('SaveDir', "'./signal'"), ('FileTag', "'fragment_'"), ('SplitSize', '1000000'), ('Delay', '0.0')], [('0', 'complex', 1)], [], 'Headless IQ writer with file splitting.', ['Delay', 'FileTag', 'SaveDir', 'SplitSize']) coordinate: [688, 208] rotation: 0 state: enabled - name: osmosdr_source_0 id: osmosdr_source parameters: affinity: '' alias: '' ant0: '' ant1: '' ant10: '' ant11: '' ant12: '' ant13: '' ant14: '' ant15: '' ant16: '' ant17: '' ant18: '' ant19: '' ant2: '' ant20: '' ant21: '' ant22: '' ant23: '' ant24: '' ant25: '' ant26: '' ant27: '' ant28: '' ant29: '' ant3: '' ant30: '' ant31: '' ant4: '' ant5: '' ant6: '' ant7: '' ant8: '' ant9: '' args: dev_args bb_gain0: bb_gain bb_gain1: '20' bb_gain10: '20' bb_gain11: '20' bb_gain12: '20' bb_gain13: '20' bb_gain14: '20' bb_gain15: '20' bb_gain16: '20' bb_gain17: '20' bb_gain18: '20' bb_gain19: '20' bb_gain2: '20' bb_gain20: '20' bb_gain21: '20' bb_gain22: '20' bb_gain23: '20' bb_gain24: '20' bb_gain25: '20' bb_gain26: '20' bb_gain27: '20' bb_gain28: '20' bb_gain29: '20' bb_gain3: '20' bb_gain30: '20' bb_gain31: '20' bb_gain4: '20' bb_gain5: '20' bb_gain6: '20' bb_gain7: '20' bb_gain8: '20' bb_gain9: '20' bw0: '0' bw1: '0' bw10: '0' bw11: '0' bw12: '0' bw13: '0' bw14: '0' bw15: '0' bw16: '0' bw17: '0' bw18: '0' bw19: '0' bw2: '0' bw20: '0' bw21: '0' bw22: '0' bw23: '0' bw24: '0' bw25: '0' bw26: '0' bw27: '0' bw28: '0' bw29: '0' bw3: '0' bw30: '0' bw31: '0' bw4: '0' bw5: '0' bw6: '0' bw7: '0' bw8: '0' bw9: '0' clock_source0: '' clock_source1: '' clock_source2: '' clock_source3: '' clock_source4: '' clock_source5: '' clock_source6: '' clock_source7: '' comment: '' corr0: '0' corr1: '0' corr10: '0' corr11: '0' corr12: '0' corr13: '0' corr14: '0' corr15: '0' corr16: '0' corr17: '0' corr18: '0' corr19: '0' corr2: '0' corr20: '0' corr21: '0' corr22: '0' corr23: '0' corr24: '0' corr25: '0' corr26: '0' corr27: '0' corr28: '0' corr29: '0' corr3: '0' corr30: '0' corr31: '0' corr4: '0' corr5: '0' corr6: '0' corr7: '0' corr8: '0' corr9: '0' dc_offset_mode0: '0' dc_offset_mode1: '0' dc_offset_mode10: '0' dc_offset_mode11: '0' dc_offset_mode12: '0' dc_offset_mode13: '0' dc_offset_mode14: '0' dc_offset_mode15: '0' dc_offset_mode16: '0' dc_offset_mode17: '0' dc_offset_mode18: '0' dc_offset_mode19: '0' dc_offset_mode2: '0' dc_offset_mode20: '0' dc_offset_mode21: '0' dc_offset_mode22: '0' dc_offset_mode23: '0' dc_offset_mode24: '0' dc_offset_mode25: '0' dc_offset_mode26: '0' dc_offset_mode27: '0' dc_offset_mode28: '0' dc_offset_mode29: '0' dc_offset_mode3: '0' dc_offset_mode30: '0' dc_offset_mode31: '0' dc_offset_mode4: '0' dc_offset_mode5: '0' dc_offset_mode6: '0' dc_offset_mode7: '0' dc_offset_mode8: '0' dc_offset_mode9: '0' freq0: freq freq1: 100e6 freq10: 100e6 freq11: 100e6 freq12: 100e6 freq13: 100e6 freq14: 100e6 freq15: 100e6 freq16: 100e6 freq17: 100e6 freq18: 100e6 freq19: 100e6 freq2: 100e6 freq20: 100e6 freq21: 100e6 freq22: 100e6 freq23: 100e6 freq24: 100e6 freq25: 100e6 freq26: 100e6 freq27: 100e6 freq28: 100e6 freq29: 100e6 freq3: 100e6 freq30: 100e6 freq31: 100e6 freq4: 100e6 freq5: 100e6 freq6: 100e6 freq7: 100e6 freq8: 100e6 freq9: 100e6 gain0: rf_gain gain1: '10' gain10: '10' gain11: '10' gain12: '10' gain13: '10' gain14: '10' gain15: '10' gain16: '10' gain17: '10' gain18: '10' gain19: '10' gain2: '10' gain20: '10' gain21: '10' gain22: '10' gain23: '10' gain24: '10' gain25: '10' gain26: '10' gain27: '10' gain28: '10' gain29: '10' gain3: '10' gain30: '10' gain31: '10' gain4: '10' gain5: '10' gain6: '10' gain7: '10' gain8: '10' gain9: '10' gain_mode0: 'False' gain_mode1: 'False' gain_mode10: 'False' gain_mode11: 'False' gain_mode12: 'False' gain_mode13: 'False' gain_mode14: 'False' gain_mode15: 'False' gain_mode16: 'False' gain_mode17: 'False' gain_mode18: 'False' gain_mode19: 'False' gain_mode2: 'False' gain_mode20: 'False' gain_mode21: 'False' gain_mode22: 'False' gain_mode23: 'False' gain_mode24: 'False' gain_mode25: 'False' gain_mode26: 'False' gain_mode27: 'False' gain_mode28: 'False' gain_mode29: 'False' gain_mode3: 'False' gain_mode30: 'False' gain_mode31: 'False' gain_mode4: 'False' gain_mode5: 'False' gain_mode6: 'False' gain_mode7: 'False' gain_mode8: 'False' gain_mode9: 'False' if_gain0: if_gain if_gain1: '20' if_gain10: '20' if_gain11: '20' if_gain12: '20' if_gain13: '20' if_gain14: '20' if_gain15: '20' if_gain16: '20' if_gain17: '20' if_gain18: '20' if_gain19: '20' if_gain2: '20' if_gain20: '20' if_gain21: '20' if_gain22: '20' if_gain23: '20' if_gain24: '20' if_gain25: '20' if_gain26: '20' if_gain27: '20' if_gain28: '20' if_gain29: '20' if_gain3: '20' if_gain30: '20' if_gain31: '20' if_gain4: '20' if_gain5: '20' if_gain6: '20' if_gain7: '20' if_gain8: '20' if_gain9: '20' iq_balance_mode0: '0' iq_balance_mode1: '0' iq_balance_mode10: '0' iq_balance_mode11: '0' iq_balance_mode12: '0' iq_balance_mode13: '0' iq_balance_mode14: '0' iq_balance_mode15: '0' iq_balance_mode16: '0' iq_balance_mode17: '0' iq_balance_mode18: '0' iq_balance_mode19: '0' iq_balance_mode2: '0' iq_balance_mode20: '0' iq_balance_mode21: '0' iq_balance_mode22: '0' iq_balance_mode23: '0' iq_balance_mode24: '0' iq_balance_mode25: '0' iq_balance_mode26: '0' iq_balance_mode27: '0' iq_balance_mode28: '0' iq_balance_mode29: '0' iq_balance_mode3: '0' iq_balance_mode30: '0' iq_balance_mode31: '0' iq_balance_mode4: '0' iq_balance_mode5: '0' iq_balance_mode6: '0' iq_balance_mode7: '0' iq_balance_mode8: '0' iq_balance_mode9: '0' maxoutbuf: '0' minoutbuf: '0' nchan: '1' num_mboards: '1' sample_rate: samp_rate sync: sync time_source0: '' time_source1: '' time_source2: '' time_source3: '' time_source4: '' time_source5: '' time_source6: '' time_source7: '' type: fc32 states: coordinate: [288, 208] rotation: 0 state: enabled connections: - [osmosdr_source_0, '0', epy_block_0, '0'] metadata: file_format: 1 grc_version: 3.10.9.2