Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

gr-smartnet for RTLSDR

By: a guest on Jun 10th, 2012  |  syntax: Bash  |  size: 24.57 KB  |  views: 518  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. git clone git://github.com/bistromath/gr-smartnet.git
  2. cd gr-smartnet
  3. git reset --hard b3335c1441ba5d2e9266e39f7c85faf65d546ba8
  4. git apply <<"END_PATCH"
  5. diff --git a/Makefile.common b/Makefile.common
  6. index 4b80811..299d1a9 100644
  7. --- a/Makefile.common
  8. +++ b/Makefile.common
  9. @@ -35,7 +35,8 @@ AM_CPPFLAGS = \
  10.  STD_DEFINES_AND_INCLUDES = \
  11.         $(DEFINES) \
  12.         -I$(GNURADIO_CORE_INCLUDEDIR) \
  13. -       -I$(GNURADIO_CORE_INCLUDEDIR)/swig
  14. +       -I$(GNURADIO_CORE_INCLUDEDIR)/swig \
  15. +       -I$(GNURADIO_CORE_INCLUDEDIR)/../gruel/swig
  16.  
  17.  # includes
  18.  grincludedir = $(includedir)/gnuradio
  19. diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
  20. index 702a898..1e9dc21 100644
  21. --- a/src/lib/Makefile.am
  22. +++ b/src/lib/Makefile.am
  23. @@ -31,7 +31,8 @@ grinclude_HEADERS =           \
  24.         smartnet_parse.h        \
  25.         smartnet_types.h        \
  26.         smartnet_subchannel_framer.h \
  27. -       smartnet_wavsink.h
  28. +       smartnet_wavsink.h \
  29. +       smartnet_wavfile.h
  30.  
  31.  ###################################
  32.  # SWIG Python interface and library
  33. @@ -59,6 +60,7 @@ smartnet_la_swig_sources =    \
  34.         smartnet_packetize.cc   \
  35.         smartnet_parse.cc       \
  36.         smartnet_subchannel_framer.cc \
  37. +       smartnet_wavfile.cc \
  38.         smartnet_wavsink.cc
  39.  
  40.  # additional arguments to the SWIG command
  41. diff --git a/src/lib/smartnet_wavfile.cc b/src/lib/smartnet_wavfile.cc
  42. new file mode 100644
  43. index 0000000..a782390
  44. --- /dev/null
  45. +++ b/src/lib/smartnet_wavfile.cc
  46. @@ -0,0 +1,313 @@
  47. +/* -*- c++ -*- */
  48. +/*
  49. + * Copyright 2004,2008 Free Software Foundation, Inc.
  50. + *
  51. + * This file is part of GNU Radio
  52. + *
  53. + * GNU Radio is free software; you can redistribute it and/or modify
  54. + * it under the terms of the GNU General Public License as published by
  55. + * the Free Software Foundation; either version 3, or (at your option)
  56. + * any later version.
  57. + *
  58. + * GNU Radio is distributed in the hope that it will be useful,
  59. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  60. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  61. + * GNU General Public License for more details.
  62. + *
  63. + * You should have received a copy of the GNU General Public License
  64. + * along with GNU Radio; see the file COPYING.  If not, write to
  65. + * the Free Software Foundation, Inc., 51 Franklin Street,
  66. + * Boston, MA 02110-1301, USA.
  67. + */
  68. +
  69. +#ifdef HAVE_CONFIG_H
  70. +#include "config.h"
  71. +#endif
  72. +
  73. +#include <smartnet_wavfile.h>
  74. +#include <cstring>
  75. +#include <stdint.h>
  76. +
  77. +# define VALID_COMPRESSION_TYPE 0x0001
  78. +
  79. +// WAV files are always little-endian, so we need some byte switching macros
  80. +
  81. +// FIXME: Use libgruel versions
  82. +
  83. +#ifdef WORDS_BIGENDIAN
  84. +
  85. +#ifdef HAVE_BYTESWAP_H
  86. +#include <byteswap.h>
  87. +#else
  88. +#warning Using non-portable code (likely wrong other than ILP32).
  89. +
  90. +static inline short int
  91. +bswap_16 (unsigned short int x)
  92. +{
  93. +  return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8));
  94. +}
  95. +
  96. +static inline unsigned int
  97. +bswap_32 (unsigned int x)
  98. +{
  99. +  return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8)      \
  100. +         | (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24));
  101. +}
  102. +#endif // HAVE_BYTESWAP_H
  103. +
  104. +static inline uint32_t
  105. +host_to_wav(uint32_t x)
  106. +{
  107. +  return bswap_32(x);
  108. +}
  109. +
  110. +static inline uint16_t
  111. +host_to_wav(uint16_t x)
  112. +{
  113. +  return bswap_16(x);
  114. +}
  115. +
  116. +static inline int16_t
  117. +host_to_wav(int16_t x)
  118. +{
  119. +  return bswap_16(x);
  120. +}
  121. +
  122. +static inline uint32_t
  123. +wav_to_host(uint32_t x)
  124. +{
  125. +  return bswap_32(x);
  126. +}
  127. +
  128. +static inline uint16_t
  129. +wav_to_host(uint16_t x)
  130. +{
  131. +  return bswap_16(x);
  132. +}
  133. +
  134. +static inline int16_t
  135. +wav_to_host(int16_t x)
  136. +{
  137. +  return bswap_16(x);
  138. +}
  139. +
  140. +#else
  141. +
  142. +static inline uint32_t
  143. +host_to_wav(uint32_t x)
  144. +{
  145. +  return x;
  146. +}
  147. +
  148. +static inline uint16_t
  149. +host_to_wav(uint16_t x)
  150. +{
  151. +  return x;
  152. +}
  153. +
  154. +static inline int16_t
  155. +host_to_wav(int16_t x)
  156. +{
  157. +  return x;
  158. +}
  159. +
  160. +static inline uint32_t
  161. +wav_to_host(uint32_t x)
  162. +{
  163. +  return x;
  164. +}
  165. +
  166. +static inline uint16_t
  167. +wav_to_host(uint16_t x)
  168. +{
  169. +  return x;
  170. +}
  171. +
  172. +static inline int16_t
  173. +wav_to_host(int16_t x)
  174. +{
  175. +  return x;
  176. +}
  177. +
  178. +#endif // WORDS_BIGENDIAN
  179. +
  180. +
  181. +bool
  182. +smartnet_wavheader_parse(FILE *fp,
  183. +                   unsigned int &sample_rate_o,
  184. +                   int &nchans_o,
  185. +                   int &bytes_per_sample_o,
  186. +                   int &first_sample_pos_o,
  187. +                   unsigned int &samples_per_chan_o)
  188. +{
  189. +  // _o variables take return values
  190. +  char str_buf[8] = {0};
  191. +
  192. +  uint32_t file_size;
  193. +  uint32_t fmt_hdr_skip;
  194. +  uint16_t compression_type;
  195. +  uint16_t nchans;
  196. +  uint32_t sample_rate;
  197. +  uint32_t avg_bytes_per_sec;
  198. +  uint16_t block_align;
  199. +  uint16_t bits_per_sample;
  200. +  uint32_t chunk_size;
  201. +
  202. +  size_t fresult;
  203. +
  204. +  fresult = fread(str_buf, 1, 4, fp);
  205. +  if (fresult != 4 || strncmp(str_buf, "RIFF", 4) || feof(fp)) {
  206. +    return false;
  207. +  }
  208. +
  209. +  fresult = fread(&file_size, 1, 4, fp);
  210. +
  211. +  fresult = fread(str_buf, 1, 8, fp);
  212. +  if (fresult != 8 || strncmp(str_buf, "WAVEfmt ", 8) || feof(fp)) {
  213. +    return false;
  214. +  }
  215. +
  216. +  fresult = fread(&fmt_hdr_skip, 1, 4, fp);
  217. +
  218. +  fresult = fread(&compression_type, 1, 2, fp);
  219. +  if (wav_to_host(compression_type) != VALID_COMPRESSION_TYPE) {
  220. +    return false;
  221. +  }
  222. +
  223. +  fresult = fread(&nchans,            1, 2, fp);
  224. +  fresult = fread(&sample_rate,       1, 4, fp);
  225. +  fresult = fread(&avg_bytes_per_sec, 1, 4, fp);
  226. +  fresult = fread(&block_align,       1, 2, fp);
  227. +  fresult = fread(&bits_per_sample,   1, 2, fp);
  228. +
  229. +  if (ferror(fp)) {
  230. +    return false;
  231. +  }
  232. +
  233. +  fmt_hdr_skip    = wav_to_host(fmt_hdr_skip);
  234. +  nchans          = wav_to_host(nchans);
  235. +  sample_rate     = wav_to_host(sample_rate);
  236. +  bits_per_sample = wav_to_host(bits_per_sample);
  237. +
  238. +  if (bits_per_sample != 8 && bits_per_sample != 16) {
  239. +    return false;
  240. +  }
  241. +
  242. +  fmt_hdr_skip -= 16;
  243. +  if (fmt_hdr_skip) {
  244. +    fseek(fp, fmt_hdr_skip, SEEK_CUR);
  245. +  }
  246. +
  247. +  // data chunk
  248. +  fresult = fread(str_buf, 1, 4, fp);
  249. +  if (strncmp(str_buf, "data", 4)) {
  250. +    return false;
  251. +  }
  252. +
  253. +  fresult = fread(&chunk_size, 1, 4, fp);
  254. +  if (ferror(fp)) {
  255. +    return false;
  256. +  }
  257. +
  258. +  // More byte swapping
  259. +  chunk_size = wav_to_host(chunk_size);
  260. +
  261. +  // Output values
  262. +  sample_rate_o      = (unsigned) sample_rate;
  263. +  nchans_o           = (int) nchans;
  264. +  bytes_per_sample_o = (int) (bits_per_sample / 8);
  265. +  first_sample_pos_o = (int) ftell(fp);
  266. +  samples_per_chan_o = (unsigned) (chunk_size / (bytes_per_sample_o * nchans));
  267. +  return true;
  268. +}
  269. +
  270. +
  271. +short int
  272. +smartnet_wav_read_sample(FILE *fp, int bytes_per_sample)
  273. +{
  274. +  int16_t buf = 0;
  275. +  size_t fresult;
  276. +
  277. +  fresult = fread(&buf, bytes_per_sample, 1, fp);
  278. +
  279. +  return (short) wav_to_host(buf);
  280. +}
  281. +
  282. +
  283. +bool
  284. +smartnet_wavheader_write(FILE *fp,
  285. +                   unsigned int sample_rate,
  286. +                   int nchans,
  287. +                   int bytes_per_sample)
  288. +{
  289. +  const int header_len = 44;
  290. +  char wav_hdr[header_len] = "RIFF\0\0\0\0WAVEfmt \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0data\0\0\0";
  291. +  uint16_t nchans_f        = (uint16_t) nchans;
  292. +  uint32_t sample_rate_f   = (uint32_t) sample_rate;
  293. +  uint16_t block_align     = bytes_per_sample * nchans;
  294. +  uint32_t avg_bytes       = sample_rate * block_align;
  295. +  uint16_t bits_per_sample = bytes_per_sample * 8;
  296. +
  297. +  nchans_f        = host_to_wav(nchans_f);
  298. +  sample_rate_f   = host_to_wav(sample_rate_f);
  299. +  block_align     = host_to_wav(block_align);
  300. +  avg_bytes       = host_to_wav(avg_bytes);
  301. +  bits_per_sample = host_to_wav(bits_per_sample);
  302. +
  303. +  wav_hdr[16] = 0x10; // no extra bytes
  304. +  wav_hdr[20] = 0x01; // no compression
  305. +  memcpy((void *) (wav_hdr + 22), (void *) &nchans_f,        2);
  306. +  memcpy((void *) (wav_hdr + 24), (void *) &sample_rate_f,   4);
  307. +  memcpy((void *) (wav_hdr + 28), (void *) &avg_bytes,       4);
  308. +  memcpy((void *) (wav_hdr + 32), (void *) &block_align,     2);
  309. +  memcpy((void *) (wav_hdr + 34), (void *) &bits_per_sample, 2);
  310. +
  311. +  fwrite(&wav_hdr, 1, header_len, fp);
  312. +  if (ferror(fp)) {
  313. +    return false;
  314. +  }
  315. +
  316. +  return true;
  317. +}
  318. +
  319. +
  320. +void
  321. +smartnet_wav_write_sample(FILE *fp, short int sample, int bytes_per_sample)
  322. +{
  323. +  void *data_ptr;
  324. +  unsigned char buf_8bit;
  325. +  int16_t       buf_16bit;
  326. +
  327. +  if (bytes_per_sample == 1) {
  328. +    buf_8bit = (unsigned char) sample;
  329. +    data_ptr = (void *) &buf_8bit;
  330. +  } else {
  331. +    buf_16bit = host_to_wav((int16_t) sample);
  332. +    data_ptr  = (void *) &buf_16bit;
  333. +  }
  334. +
  335. +  fwrite(data_ptr, 1, bytes_per_sample, fp);
  336. +}
  337. +
  338. +
  339. +bool
  340. +smartnet_wavheader_complete(FILE *fp, unsigned int byte_count)
  341. +{
  342. +  uint32_t chunk_size = (uint32_t) byte_count;
  343. +  chunk_size = host_to_wav(chunk_size);
  344. +
  345. +  fseek(fp, 40, SEEK_SET);
  346. +  fwrite(&chunk_size, 1, 4, fp);
  347. +
  348. +  chunk_size = (uint32_t) byte_count + 36; // fmt chunk and data header
  349. +  chunk_size = host_to_wav(chunk_size);
  350. +  fseek(fp, 4, SEEK_SET);
  351. +
  352. +  fwrite(&chunk_size, 1, 4, fp);
  353. +
  354. +  if (ferror(fp)) {
  355. +    return false;
  356. +  }
  357. +
  358. +  return true;
  359. +}
  360. diff --git a/src/lib/smartnet_wavfile.h b/src/lib/smartnet_wavfile.h
  361. new file mode 100644
  362. index 0000000..2c70d0b
  363. --- /dev/null
  364. +++ b/src/lib/smartnet_wavfile.h
  365. @@ -0,0 +1,101 @@
  366. +/* -*- c++ -*- */
  367. +/*
  368. + * Copyright 2008 Free Software Foundation, Inc.
  369. + *
  370. + * This file is part of GNU Radio
  371. + *
  372. + * GNU Radio is free software; you can redistribute it and/or modify
  373. + * it under the terms of the GNU General Public License as published by
  374. + * the Free Software Foundation; either version 3, or (at your option)
  375. + * any later version.
  376. + *
  377. + * GNU Radio is distributed in the hope that it will be useful,
  378. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  379. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  380. + * GNU General Public License for more details.
  381. + *
  382. + * You should have received a copy of the GNU General Public License
  383. + * along with GNU Radio; see the file COPYING.  If not, write to
  384. + * the Free Software Foundation, Inc., 51 Franklin Street,
  385. + * Boston, MA 02110-1301, USA.
  386. + */
  387. +
  388. +// This file stores all the RIFF file type knowledge for the gr_wavfile_*
  389. +// blocks.
  390. +
  391. +//#include <gr_core_api.h>
  392. +#include <cstdio>
  393. +
  394. +/*!
  395. + * \brief Read signal information from a given WAV file.
  396. + *
  397. + * \p fp File pointer to an opened, empty file.
  398. + * \p sample_rate Stores the sample rate [S/s]
  399. + * \p nchans      Number of channels
  400. + * \p bytes_per_sample Bytes per sample, can either be 1 or 2 (corresponding to
  401. + *         8 or 16 bit samples, respectively)
  402. + * \p first_sample_pos Number of the first byte containing a sample. Use this
  403. + *         with fseek() to jump from the end of the file to the first sample
  404. + *         when in repeat mode.
  405. + * \p samples_per_chan Number of samples per channel
  406. + * \p normalize_fac The normalization factor with which you need to divide the
  407. + *         integer values of the samples to get them within [-1;1]
  408. + * \p normalize_shift The value by which the sample values need to be shifted
  409. + *         after normalization (reason being, 8-bit WAV files store samples as
  410. + *         unsigned char and 16-bit as signed short int)
  411. + * \return True on a successful read, false if the file could not be read or is
  412. + *         not a valid WAV file.
  413. + */
  414. +bool
  415. +smartnet_wavheader_parse(FILE *fp,
  416. +                   unsigned int &sample_rate,
  417. +                   int &nchans,
  418. +                   int &bytes_per_sample,
  419. +                   int &first_sample_pos,
  420. +                   unsigned int &samples_per_chan);
  421. +
  422. +
  423. +/*!
  424. + * \brief Read one sample from an open WAV file at the current position.
  425. + *
  426. + * Takes care of endianness.
  427. + */
  428. +short int
  429. +smartnet_wav_read_sample(FILE *fp, int bytes_per_sample);
  430. +
  431. +
  432. +/*!
  433. + * \brief Write a valid RIFF file header
  434. + *
  435. + * Note: Some header values are kept blank because they're usually not known
  436. + * a-priori (file and chunk lengths). Use smartnet_wavheader_complete() to fill
  437. + * these in.
  438. + */
  439. +bool
  440. +smartnet_wavheader_write(FILE *fp,
  441. +                unsigned int sample_rate,
  442. +                int nchans,
  443. +                int bytes_per_sample);
  444. +
  445. +/*!
  446. + * \brief Write one sample to an open WAV file at the current position.
  447. + *
  448. + * Takes care of endianness.
  449. + */
  450. +void
  451. +smartnet_wav_write_sample(FILE *fp, short int sample, int bytes_per_sample);
  452. +
  453. +
  454. +/*!
  455. + * \brief Complete a WAV header
  456. + *
  457. + * Note: The stream position is changed during this function. If anything
  458. + * needs to be written to the WAV file after calling this function (which
  459. + * shouldn't happen), you  need to fseek() to the end of the file (or
  460. + * whereever).
  461. + *
  462. + * \p fp File pointer to an open WAV file with a blank header
  463. + * \p byte_count Length of all samples written to the file in bytes.
  464. + */
  465. +bool
  466. +smartnet_wavheader_complete(FILE *fp, unsigned int byte_count);
  467. diff --git a/src/lib/smartnet_wavsink.cc b/src/lib/smartnet_wavsink.cc
  468. index bc75e0c..d795fdf 100644
  469. --- a/src/lib/smartnet_wavsink.cc
  470. +++ b/src/lib/smartnet_wavsink.cc
  471. @@ -26,7 +26,7 @@
  472.  
  473.  #include <smartnet_wavsink.h>
  474.  #include <gr_io_signature.h>
  475. -#include <gri_wavfile.h>
  476. +#include <smartnet_wavfile.h>
  477.  #include <stdexcept>
  478.  #include <climits>
  479.  #include <cstring>
  480. @@ -140,7 +140,7 @@ smartnet_wavsink::open(const char* filename)
  481.                 int new_nchans, new_bytes_per_sample, new_first_sample_pos;
  482.  
  483.                 //validate the data, be sure it's set up the same as the current block (sample rate, mono/stereo, etc) and barf if it isn't
  484. -         if (!gri_wavheader_parse(d_new_fp,
  485. +         if (!smartnet_wavheader_parse(d_new_fp,
  486.                                    new_sample_rate,
  487.                                    new_nchans,
  488.                                    new_bytes_per_sample,
  489. @@ -180,7 +180,7 @@ smartnet_wavsink::open(const char* filename)
  490.         }
  491.                 d_updated = true;
  492.  
  493. -         if (!gri_wavheader_write(d_new_fp,
  494. +         if (!smartnet_wavheader_write(d_new_fp,
  495.                                    d_sample_rate,
  496.                                    d_nchans,
  497.                                    d_bytes_per_sample_new)) {
  498. @@ -211,7 +211,7 @@ void smartnet_wavsink::close_wav()
  499.  
  500.         //printf("Writing wav header with %f seconds of audio\n", ((float(d_sample_count)/d_sample_rate)/d_nchans)/d_bytes_per_sample);
  501.    
  502. -  if(!gri_wavheader_complete(d_fp, byte_count)) {
  503. +  if(!smartnet_wavheader_complete(d_fp, byte_count)) {
  504.                 throw std::runtime_error("Error writing wav header\n");
  505.         }
  506.    
  507. @@ -256,7 +256,7 @@ smartnet_wavsink::work (int noutput_items,
  508.                                 sample_buf_s = 0;
  509.        }
  510.        
  511. -      gri_wav_write_sample(d_fp, sample_buf_s, d_bytes_per_sample);
  512. +      smartnet_wav_write_sample(d_fp, sample_buf_s, d_bytes_per_sample);
  513.        
  514.        if (feof(d_fp) || ferror(d_fp)) {
  515.                                 fprintf(stderr, "[%s] file i/o error\n", __FILE__);
  516. diff --git a/src/python/smartnet2logging_rtl.py b/src/python/smartnet2logging_rtl.py
  517. new file mode 100755
  518. index 0000000..7ef2f42
  519. --- /dev/null
  520. +++ b/src/python/smartnet2logging_rtl.py
  521. @@ -0,0 +1,277 @@
  522. +#!/usr/bin/env python
  523. +"""
  524. +       This program decodes the Motorola SmartNet II trunking protocol from the control channel
  525. +       Tune it to the control channel center freq, and it'll spit out the decoded packets.
  526. +       In what format? Who knows.
  527. +
  528. +       This program does not include audio output support. It logs channels to disk by talkgroup name. If you don't specify what talkgroups to log, it logs EVERYTHING.
  529. +"""
  530. +
  531. +from gnuradio import gr, gru, blks2, optfir, digital
  532. +from grc_gnuradio import blks2 as grc_blks2
  533. +from gnuradio import audio
  534. +from gnuradio import eng_notation
  535. +#from gnuradio import uhd
  536. +from fsk_demod import fsk_demod
  537. +from logging_receiver import logging_receiver
  538. +from optparse import OptionParser
  539. +from gnuradio.eng_option import eng_option
  540. +from gnuradio import smartnet
  541. +#from gnuradio.wxgui import slider
  542. +#from gnuradio.wxgui import stdgui2, fftsink2, form
  543. +import osmosdr
  544. +from gnuradio import digital
  545. +
  546. +#from pkt import *
  547. +import time
  548. +import gnuradio.gr.gr_threading as _threading
  549. +import csv
  550. +import os
  551. +
  552. +class top_block_runner(_threading.Thread):
  553. +    def __init__(self, tb):
  554. +        _threading.Thread.__init__(self)
  555. +        self.setDaemon(1)
  556. +        self.tb = tb
  557. +        self.done = False
  558. +        self.start()
  559. +
  560. +    def run(self):
  561. +        self.tb.run()
  562. +        self.done = True
  563. +
  564. +class my_top_block(gr.top_block):
  565. +       def __init__(self, options, queue):
  566. +               gr.top_block.__init__(self)
  567. +
  568. +               self.rtl = osmosdr.source_c( args="nchan=" + str(1) + " " + ""  )
  569. +               self.rtl.set_sample_rate(options.rate)
  570. +               self.rtl.set_center_freq(options.centerfreq, 0)
  571. +               self.rtl.set_freq_corr(options.ppm, 0)
  572. +               self.rtl.set_gain_mode(1, 0)
  573. +              
  574. +               self.centerfreq = options.centerfreq
  575. +               print "Tuning to: %fMHz" % (self.centerfreq - options.error)
  576. +               if not(self.tune(options.centerfreq - options.error)):
  577. +                       print "Failed to set initial frequency"
  578. +
  579. +               if options.gain is None: #set to halfway
  580. +#                      g = self.u.get_gain_range()
  581. +#                      options.gain = (g.start()+g.stop()) / 2.0
  582. +                       options.gain = 10
  583. +                       # TODO FIX^
  584. +
  585. +               print "Setting gain to %i" % options.gain
  586. +               self.rtl.set_gain(options.gain, 0)
  587. +
  588. +               self.rate = options.rate
  589. +              
  590. +               print "Samples per second is %i" % self.rate
  591. +
  592. +               self._syms_per_sec = 3600;
  593. +
  594. +               options.audiorate = 11025
  595. +               options.rate = self.rate
  596. +
  597. +               options.samples_per_second = self.rate #yeah i know it's on the list
  598. +               options.syms_per_sec = self._syms_per_sec
  599. +               options.gain_mu = 0.01
  600. +               options.mu=0.5
  601. +               options.omega_relative_limit = 0.3
  602. +               options.syms_per_sec = self._syms_per_sec
  603. +               options.offset = options.centerfreq - options.freq
  604. +               print "Control channel offset: %f" % options.offset
  605. +
  606. +               self.demod = fsk_demod(options)
  607. +               self.start_correlator = digital.correlate_access_code_bb("10101100",0) #should mark start of packet
  608. +               self.smartnet_sync = smartnet.sync()
  609. +               self.smartnet_deinterleave = smartnet.deinterleave()
  610. +               self.smartnet_parity = smartnet.parity()
  611. +               self.smartnet_crc = smartnet.crc()
  612. +               self.smartnet_packetize = smartnet.packetize()
  613. +               self.parse = smartnet.parse(queue) #packet-based. this simply posts lightly-formatted messages to the queue.
  614. +
  615. +               self.connect(self.rtl, self.demod)
  616. +
  617. +               self.connect(self.demod, self.start_correlator, self.smartnet_sync, self.smartnet_deinterleave, self.smartnet_parity, self.smartnet_crc, self.smartnet_packetize, self.parse)
  618. +
  619. +              
  620. +       def tune(self, freq):
  621. +               result = self.rtl.set_center_freq(freq)
  622. +               return True
  623. +
  624. +def getfreq(chanlist, cmd):
  625. +       if chanlist is None: #if no chanlist file, make a guess. there are four extant bandplan schemes, and i believe this one is the most common.
  626. +#              if cmd < 0x2d0:        
  627. +# Changed for rebanding
  628. +               if cmd < 0x1b8:
  629. +                       freq = float(cmd * 0.025 + 851.0125)
  630. +               elif cmd < 0x230:
  631. +                       freq = float(cmd * 0.025 + 851.0125 - 10.9875)
  632. +               else:
  633. +                       freq = None
  634. +       else: #program your channel listings, get the right freqs.
  635. +               if chanlist.get(str(cmd), None) is not None:
  636. +                       freq = float(chanlist[str(cmd)])
  637. +               else:
  638. +                       freq = None
  639. +
  640. +       return freq
  641. +
  642. +def parsefreq(s, chanlist):
  643. +       retfreq = None
  644. +       [address, groupflag, command] = s.split(",")
  645. +       command = int(command)
  646. +       address = int(address) & 0xFFF0
  647. +       groupflag = bool(groupflag)
  648. +
  649. +       if chanlist is None:
  650. +               if command < 0x2d0:
  651. +                       retfreq = getfreq(chanlist, command)
  652. +
  653. +       else:
  654. +               if chanlist.get(str(command), None) is not None: #if it falls into the channel somewhere
  655. +                       retfreq = getfreq(chanlist, command)
  656. +       return [retfreq, address] # mask so the squelch opens up on the entire group
  657. +
  658. +
  659. +
  660. +def main():
  661. +       # Create Options Parser:
  662. +       parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
  663. +       expert_grp = parser.add_option_group("Expert")
  664. +
  665. +       parser.add_option("-f", "--freq", type="eng_float", default=866.9625e6,
  666. +                                               help="set control channel frequency to MHz [default=%default]", metavar="FREQ")
  667. +       parser.add_option("-c", "--centerfreq", type="eng_float", default=867.5e6,
  668. +                                               help="set center receive frequency to MHz [default=%default]. Set to center of 800MHz band for best results")
  669. +       parser.add_option("-g", "--gain", type="int", default=None,
  670. +                                               help="set RF gain", metavar="dB")
  671. +       parser.add_option("-r", "--rate", type="eng_float", default=64e6/18,
  672. +                                               help="set sample rate [default=%default]")
  673. +#      parser.add_option("-b", "--bandwidth", type="eng_float", default=3e6,
  674. +#                                              help="set bandwidth of DBS RX frond end [default=%default]")
  675. +       parser.add_option("-C", "--chanlistfile", type="string", default=None,
  676. +                                               help="read in list of Motorola channel frequencies (improves accuracy of frequency decoding) [default=%default]")
  677. +       parser.add_option("-E", "--error", type="eng_float", default=0,
  678. +                                               help="enter an offset error to compensate for USRP clock inaccuracy")
  679. +       parser.add_option("-m", "--monitor", type="int", default=None,
  680. +                                               help="monitor a specific talkgroup")
  681. +       parser.add_option("-v", "--volume", type="eng_float", default=3.0,
  682. +                                               help="set volume gain for audio output [default=%default]")
  683. +       parser.add_option("-s", "--squelch", type="eng_float", default=28,
  684. +                                               help="set audio squelch level (default=%default, play with it)")
  685. +       parser.add_option("-D", "--directory", type="string", default="./log",
  686. +                                               help="choose a directory in which to save log data [default=%default]")
  687. +#      parser.add_option("-a", "--addr", type="string", default="",
  688. +#                                              help="address options to pass to UHD")
  689. +#      parser.add_option("-s", "--subdev", type="string",
  690. +#                                              help="UHD subdev spec", default=None)
  691. +#      parser.add_option("-A", "--antenna", type="string", default=None,
  692. +#                                      help="select Rx Antenna where appropriate")
  693. +# FOR RTL
  694. +       parser.add_option("-p", "--ppm", type="eng_float", default=0,
  695. +                                               help="set RTL PPM frequency adjustment [default=%default]")
  696. +
  697. +       #receive_path.add_options(parser, expert_grp)
  698. +
  699. +       (options, args) = parser.parse_args ()
  700. +
  701. +       if len(args) != 0:
  702. +               parser.print_help(sys.stderr)
  703. +               sys.exit(1)
  704. +
  705. +
  706. +       if options.chanlistfile is not None:
  707. +               clreader=csv.DictReader(open(options.chanlistfile), quotechar='"')
  708. +               chanlist={"0": 0}
  709. +               for record in clreader:
  710. +                       chanlist[record['channel']] = record['frequency']
  711. +       else:
  712. +               chanlist = None
  713. +
  714. +       # build the graph
  715. +       queue = gr.msg_queue()
  716. +       tb = my_top_block(options, queue)
  717. +
  718. +       runner = top_block_runner(tb)
  719. +
  720. +       updaterate = 10 #main loop rate in Hz
  721. +       audiologgers = [] #this is the list of active audio sinks.
  722. +       rxfound = False #a flag to indicate whether or not an audio sink was found with the correct talkgroup ID; see below
  723. +
  724. +
  725. +       try:
  726. +               while 1:
  727. +                       if not queue.empty_p():
  728. +                               msg = queue.delete_head() # Blocking read
  729. +                               sentence = msg.to_string()
  730. +                              
  731. +                               [newfreq, newaddr] = parsefreq(sentence, chanlist)
  732. +
  733. +                               monaddr = newaddr & 0xFFF0 #last 8 bits are status flags for a given talkgroup
  734. +
  735. +                               if newfreq is not None and int(newfreq*1e6) == int(options.freq):
  736. +                                       newfreq = None #don't log the audio from the trunk itself
  737. +
  738. +                               #we have a new frequency assignment. look through the list of audio logger objects and find if any of them have been allocated to it
  739. +                               rxfound = False
  740. +
  741. +                               for rx in audiologgers:
  742. +
  743. +                                       #print "Logger info: %i @ %f idle for %fs" % (rx.talkgroup, rx.getfreq(options.centerfreq), rx.timeout()) #TODO: debug
  744. +
  745. +                                       #first look through the list to find out if there is a receiver assigned to this talkgroup
  746. +                                       if rx.talkgroup == monaddr: #here we've got one
  747. +                                               if newfreq != rx.getfreq(options.centerfreq) and newfreq is not None: #we're on a new channel, though
  748. +                                                       rx.tuneoffset(newfreq, options.centerfreq)
  749. +                                              
  750. +                                               rx.unmute() #this should be unnecessary but it does update the timestamp
  751. +                                               rxfound = True
  752. +                                               #print "New transmission on TG %i, updating timestamp" % rx.talkgroup
  753. +
  754. +                                       else:
  755. +                                               if rx.getfreq(options.centerfreq) == newfreq: #a different talkgroup, but a new assignment on that freq! time to mute.
  756. +                                                       rx.mute()
  757. +
  758. +                               if rxfound is False and newfreq is not None: #no existing receiver for this talkgroup. time to create one.
  759. +                                       #lock the flowgraph
  760. +                                       tb.lock()
  761. +                                       audiologgers.append( logging_receiver(newaddr, options) ) #create it
  762. +                                       audiologgers[-1].tuneoffset(newfreq, options.centerfreq) #tune it
  763. +                                       tb.connect(tb.rtl, audiologgers[-1]) #connect to the flowgraph
  764. +                                       tb.unlock()
  765. +                                       audiologgers[-1].unmute() #unmute it
  766. +
  767. +                               if newfreq is not None:
  768. +                                       print "TG %i @ %f, %i active loggers" % (newaddr, newfreq, len(audiologgers))
  769. +
  770. +
  771. +                       else:
  772. +                               time.sleep(1.0/updaterate)
  773. +
  774. +                       for rx in audiologgers:
  775. +                               if rx.timeout() >= 5.0: #if this receiver has been muted more than 3 seconds
  776. +                                       rx.close() #close the .wav file that the logger has been writing to
  777. +                                       tb.lock()
  778. +                                       tb.disconnect(rx)
  779. +                                       tb.unlock()
  780. +                                       audiologgers.remove(rx) #delete the audio logger object from the list
  781. +
  782. +       except KeyboardInterrupt:
  783. +               #perform cleanup: time to get out of Dodge
  784. +               for rx in audiologgers: #you probably don't need to lock, disconnect, unlock, remove. but you might as well.
  785. +                       rx.close()
  786. +                       #tb.lock()
  787. +                       #tb.disconnect(rx)
  788. +                       #tb.unlock()
  789. +                       audiologgers.remove(rx)
  790. +
  791. +               tb.stop()
  792. +
  793. +               runner = None
  794. +
  795. +if __name__ == '__main__':
  796. +       main()
  797. +
  798. +
  799. END_PATCH