diff --git a/CMakeLists.txt b/CMakeLists.txt
index 758d3b3bc..ab808715f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -247,6 +247,7 @@ list(APPEND trunk_recorder_sources
trunk-recorder/recorders/p25_recorder_fsk4_demod.cc
trunk-recorder/recorders/p25_recorder_qpsk_demod.cc
trunk-recorder/recorders/p25_recorder_decode.cc
+ trunk-recorder/sources/iq_file_source.cc
trunk-recorder/csv_helper.cc
trunk-recorder/config.cc
trunk-recorder/talkgroup.cc
diff --git a/docs/CONFIGURE.md b/docs/CONFIGURE.md
index e5cd92992..e24b08d77 100644
--- a/docs/CONFIGURE.md
+++ b/docs/CONFIGURE.md
@@ -106,7 +106,7 @@ Here is a map of the different sections of the *config.json* file:
There is a list of available Plugins [here](./Plugins.md).
-### Global Configs
+## Global Configs
| Key | Required | Default Value | Type | Description |
@@ -142,18 +142,20 @@ There is a list of available Plugins [here](./Plugins.md).
-#### Source Object
+## Source Object
+
+### USRP or OSMOSDR Sources
| Key | Required | Default Value | Type | Description |
| :--------------- | :------: | :-----------: | --------------------------- | ------------------------------------------------------------ |
+| driver | ✓ | | **"usrp"**, **"osmosdr"** | The GNURadio block you wish to use for the SDR. |
+| device | | | **string**
See the [osmosdr page](http://sdr.osmocom.org/trac/wiki/GrOsmoSDR) for supported devices and parameters. | Osmosdr device name and possibly serial number or index of the device.
You only need to do add this key if there are more than one osmosdr devices being used.
Example: `bladerf=00001` for BladeRF with serial 00001 or `rtl=00923838` for RTL-SDR with serial 00923838, just `airspy` for an airspy.
It seems that when you have 5 or more RTLSDRs on one system you need to decrease the buffer size. I think it has something to do with the driver. Try adding buflen: `"device": "rtl=serial_num,buflen=65536"`, there should be no space between the comma and `buflen`. |
| center | ✓ | | number | The center frequency in Hz to tune the SDR to |
| rate | ✓ | | number | The sampling rate to set the SDR to, in samples / second |
| error | | 0 | number | The tuning error for the SDR, in Hz. This is the difference between the target value and the actual value. So if you wanted to recv 856MHz but you had to tune your SDR to 855MHz (when set to 0ppm) to actually receive it, you would set this to -1000000. You should also probably get a new SDR if it is off by this much. |
| gain | ✓ | | number | The RF gain setting for the SDR. Use a program like GQRX to find a good value. |
| digitalRecorders | | | number | The number of Digital Recorders to have attached to this source. This is essentially the number of simultaneous calls you can record at the same time in the frequency range that this Source will be tuned to. It is limited by the CPU power of the machine. Some experimentation might be needed to find the appropriate number. *This is only required for Trunk systems. Channels in Conventional systems have dedicated recorders and do not need to be included here.* |
| analogRecorders | | | number | The number of Analog Recorder to have attached to this source. The same as Digital Recorders except for Analog Voice channels. *This is only required for Trunk systems. Channels in Conventional systems have dedicated recorders and do not need to be included here.* |
-| driver | ✓ | | **"usrp"** or **"osmosdr"** | The GNURadio block you wish to use for the SDR. |
-| device | | | **string**
See the [osmosdr page](http://sdr.osmocom.org/trac/wiki/GrOsmoSDR) for supported devices and parameters. | Osmosdr device name and possibly serial number or index of the device.
You only need to do add this key if there are more than one osmosdr devices being used.
Example: `bladerf=00001` for BladeRF with serial 00001 or `rtl=00923838` for RTL-SDR with serial 00923838, just `airspy` for an airspy.
It seems that when you have 5 or more RTLSDRs on one system you need to decrease the buffer size. I think it has something to do with the driver. Try adding buflen: `"device": "rtl=serial_num,buflen=65536"`, there should be no space between the comma and `buflen`. |
| ppm | | 0 | number | The tuning error for the SDR in ppm (parts per million), as an alternative to `error` above. Use a program like GQRX to find an accurate value. |
| agc | | false | **true** / **false** | Whether or not to enable the SDR's automatic gain control (if supported). This is false by default. It is not recommended to set this as it often yields worse performance compared to a manual gain setting. |
| gainSettings | | | { "stageName": value} | Set the gain for any stage. The value for this setting should be passed as an object, where the key specifies the name of the gain stage and the value is the amount of gain, as an int. For example:
````"gainSettings": { "IF": 10, "BB": 11},```` |
@@ -166,8 +168,37 @@ There is a list of available Plugins [here](./Plugins.md).
| antenna | | | string, e.g.: **"TX/RX"** | *usrp only* selects which antenna jack to use |
| enabled | | true | **true** / **false** | control whether a configured source is enabled or disabled |
+***
+### SigMF Sources
+
+| Key | Required | Default Value | Type | Description |
+| :--------------- | :------: | :-----------: | --------------------------- | ------------------------------------------------------------ |
+| driver | ✓ | | **"sigmffile"**| Specify that you wish to use a SigMF based source block |
+| sigmfMeta | ✓ | | string | Path and filenme for the SigMF metadata File |
+| sigmfData | ✓ | | string | Path and filenme for the SigMF data File |
+| repeat | | false | **true** / **false** | whether to repeat playback of the IQ file when it reaches the end |
+| digitalRecorders | | | number | The number of Digital Recorders to have attached to this source. This is essentially the number of simultaneous calls you can record at the same time in the frequency range that this Source will be tuned to. It is limited by the CPU power of the machine. Some experimentation might be needed to find the appropriate number. *This is only required for Trunk systems. Channels in Conventional systems have dedicated recorders and do not need to be included here.* |
+| analogRecorders | | | number | The number of Analog Recorder to have attached to this source. The same as Digital Recorders except for Analog Voice channels. *This is only required for Trunk systems. Channels in Conventional systems have dedicated recorders and do not need to be included here.* |
+| enabled | | true | **true** / **false** | control whether a configured source is enabled or disabled |
+
+***
+
+### IQ File Sources
+
+| Key | Required | Default Value | Type | Description |
+| :--------------- | :------: | :-----------: | --------------------------- | ------------------------------------------------------------ |
+| driver | ✓ | | **"iqfile"**| Specify that you wish to use an IQ File based source block |
+| iqfile | ✓ | | string | Path and filenme for the IQ File |
+| repeat | | false | **true** / **false** | whether to repeat playback of the IQ file when it reaches the end |
+| center | ✓ | | number | The center frequency in Hz to tune the SDR to |
+| rate | ✓ | | number | The sampling rate to set the SDR to, in samples / second |
+| digitalRecorders | | | number | The number of Digital Recorders to have attached to this source. This is essentially the number of simultaneous calls you can record at the same time in the frequency range that this Source will be tuned to. It is limited by the CPU power of the machine. Some experimentation might be needed to find the appropriate number. *This is only required for Trunk systems. Channels in Conventional systems have dedicated recorders and do not need to be included here.* |
+| analogRecorders | | | number | The number of Analog Recorder to have attached to this source. The same as Digital Recorders except for Analog Voice channels. *This is only required for Trunk systems. Channels in Conventional systems have dedicated recorders and do not need to be included here.* |
+| enabled | | true | **true** / **false** | control whether a configured source is enabled or disabled |
+
-#### System Object
+
+## System Object
| Key | Required | Default Value | Type | Description |
| ---------------------- | :------: | -------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------ |
@@ -211,7 +242,9 @@ There is a list of available Plugins [here](./Plugins.md).
| decodeTPS | | false | **true** / **false** | *Conventional systems only* enable the Motorola Tactical Public Safety (aka FDNY Fireground) signaling decoder. |
| enabled | | true | **true** / **false** | control whether a configured system is enabled or disabled |
-#### System Object - Experimental Options
+***
+
+### System Object - Experimental Options
| Key | Required | Default Value | Type | Description |
| ---------------------- | :------: | --------------| ---------------------| --------------------------------------------------------------------------------- |
@@ -248,7 +281,7 @@ By default, Trunk Recorder will record the call from the first site to receive t
}
```
-#### Plugin Object
+## Plugin Object
| Key | Required | Default Value | Type | Description |
| ------- | :------: | ------------- | -------------------- | ------------------------------------------------------------ |
diff --git a/docs/SigMF.MD b/docs/SigMF.MD
new file mode 100644
index 000000000..07c326d2a
--- /dev/null
+++ b/docs/SigMF.MD
@@ -0,0 +1,7 @@
+---
+sidebar_label: 'SigMF'
+sidebar_position: 8
+---
+
+# SigMF Source & Recorder
+
diff --git a/trunk-recorder/config.cc b/trunk-recorder/config.cc
index 38f60b025..df5139465 100644
--- a/trunk-recorder/config.cc
+++ b/trunk-recorder/config.cc
@@ -227,7 +227,6 @@ bool load_config(string config_file, Config &config, gr::top_block_sptr &tb, std
BOOST_LOG_TRIVIAL(info) << "\n-------------------------------------\nSYSTEMS\n-------------------------------------\n";
-
for (json element : data["systems"]) {
bool system_enabled = element.value("enabled", true);
if (system_enabled) {
@@ -250,8 +249,8 @@ bool load_config(string config_file, Config &config, gr::top_block_sptr &tb, std
// If it is a conventional System
if ((system->get_system_type() == "conventional") || (system->get_system_type() == "conventionalP25") || (system->get_system_type() == "conventionalDMR")) {
- bool channel_file_exist = element.contains("channelFile");
- bool channels_exist = element.contains("channels");
+ bool channel_file_exist = element.contains("channelFile");
+ bool channels_exist = element.contains("channels");
if (channel_file_exist && channels_exist) {
BOOST_LOG_TRIVIAL(error) << "Both \"channels\" and \"channelFile\" cannot be defined for a system!";
@@ -261,7 +260,7 @@ bool load_config(string config_file, Config &config, gr::top_block_sptr &tb, std
if (channels_exist) {
BOOST_LOG_TRIVIAL(info) << "Conventional Channels: ";
std::vector channels = element["channels"];
- for (auto& channel : channels) {
+ for (auto &channel : channels) {
BOOST_LOG_TRIVIAL(info) << " " << format_freq(channel);
system->add_channel(channel);
}
@@ -277,7 +276,7 @@ bool load_config(string config_file, Config &config, gr::top_block_sptr &tb, std
} else if ((system->get_system_type() == "smartnet") || (system->get_system_type() == "p25")) {
BOOST_LOG_TRIVIAL(info) << "Control Channels: ";
std::vector control_channels = element["control_channels"];
- for (auto& control_channel : control_channels) {
+ for (auto &control_channel : control_channels) {
system->add_control_channel(control_channel);
}
for (unsigned int i = 0; i < control_channels.size(); i++) {
@@ -423,150 +422,173 @@ bool load_config(string config_file, Config &config, gr::top_block_sptr &tb, std
BOOST_LOG_TRIVIAL(info) << "\n\n-------------------------------------\nSOURCES\n-------------------------------------\n";
for (json element : data["sources"]) {
+
bool source_enabled = element.value("enabled", true);
if (source_enabled) {
+ Source *source;
bool gain_set = false;
- int silence_frames = element.value("silenceFrames", 0);
- double center = element.value("center", 0.0);
- double rate = element.value("rate", 0.0);
- double error = element.value("error", 0.0);
- double ppm = element.value("ppm", 0.0);
- bool agc = element.value("agc", false);
- int gain = element.value("gain", 0);
- int if_gain = element.value("ifGain", 0);
- int bb_gain = element.value("bbGain", 0);
- int mix_gain = element.value("mixGain", 0);
- int lna_gain = element.value("lnaGain", 0);
- int pga_gain = element.value("pgaGain", 0);
- int tia_gain = element.value("tiaGain", 0);
- int amp_gain = element.value("ampGain", 0);
- int vga_gain = element.value("vgaGain", 0);
- int vga1_gain = element.value("vga1Gain", 0);
- int vga2_gain = element.value("vga2Gain", 0);
-
- std::string antenna = element.value("antenna", "");
- int digital_recorders = element.value("digitalRecorders", 0);
- int sigmf_recorders = element.value("sigmfRecorders", 0);
- int analog_recorders = element.value("analogRecorders", 0);
-
std::string driver = element.value("driver", "");
- if ((driver != "osmosdr") && (driver != "usrp")) {
- BOOST_LOG_TRIVIAL(error) << "Driver specified in config.json not recognized, needs to be osmosdr or usrp";
+ if ((driver != "osmosdr") && (driver != "usrp") && (driver != "sigmf") && (driver != "iqfile")) {
+ BOOST_LOG_TRIVIAL(error) << "Driver specified in config.json not recognized, needs to be osmosdr, sigmf, iqfile or usrp";
+ return false;
}
- std::string device = element.value("device", "");
- BOOST_LOG_TRIVIAL(info) << "Driver: " << element.value("driver", "");
- BOOST_LOG_TRIVIAL(info) << "Center: " << format_freq(element.value("center", 0.0));
- BOOST_LOG_TRIVIAL(info) << "Rate: " << FormatSamplingRate(element.value("rate", 0.0));
- BOOST_LOG_TRIVIAL(info) << "Error: " << element.value("error", 0.0);
- BOOST_LOG_TRIVIAL(info) << "PPM Error: " << element.value("ppm", 0.0);
- BOOST_LOG_TRIVIAL(info) << "Auto gain control: " << element.value("agc", false);
- BOOST_LOG_TRIVIAL(info) << "Gain: " << element.value("gain", 0);
- BOOST_LOG_TRIVIAL(info) << "IF Gain: " << element.value("ifGain", 0);
- BOOST_LOG_TRIVIAL(info) << "BB Gain: " << element.value("bbGain", 0);
- BOOST_LOG_TRIVIAL(info) << "LNA Gain: " << element.value("lnaGain", 0);
- BOOST_LOG_TRIVIAL(info) << "PGA Gain: " << element.value("pgaGain", 0);
- BOOST_LOG_TRIVIAL(info) << "TIA Gain: " << element.value("tiaGain", 0);
- BOOST_LOG_TRIVIAL(info) << "MIX Gain: " << element.value("mixGain", 0);
- BOOST_LOG_TRIVIAL(info) << "AMP Gain: " << element.value("ampGain", 0);
- BOOST_LOG_TRIVIAL(info) << "VGA Gain: " << element.value("vgaGain", 0);
- BOOST_LOG_TRIVIAL(info) << "VGA1 Gain: " << element.value("vga1Gain", 0);
- BOOST_LOG_TRIVIAL(info) << "VGA2 Gain: " << element.value("vga2Gain", 0);
- BOOST_LOG_TRIVIAL(info) << "Idle Silence: " << element.value("silenceFrame", 0);
- BOOST_LOG_TRIVIAL(info) << "Digital Recorders: " << element.value("digitalRecorders", 0);
- BOOST_LOG_TRIVIAL(info) << "SigMF Recorders: " << element.value("sigmfRecorders", 0);
- BOOST_LOG_TRIVIAL(info) << "Analog Recorders: " << element.value("analogRecorders", 0);
+ int digital_recorders = element.value("digitalRecorders", 0);
+ int sigmf_recorders = element.value("sigmfRecorders", 0);
+ int analog_recorders = element.value("analogRecorders", 0);
- if ((ppm != 0) && (error != 0)) {
- BOOST_LOG_TRIVIAL(info) << "Both PPM and Error should not be set at the same time. Setting Error to 0.";
- error = 0;
- }
+ if (driver == "sigmf") {
+ string sigmf_data = element.value("sigmfData", "");
+ string sigmf_meta = element.value("sigmfMeta", "");
+ bool repeat = element.value("repeat", false);
+ source = new Source(sigmf_meta, sigmf_data, repeat, &config);
+ } else if (driver == "iqfile") {
+ string iq_file = element.value("iqFile", "");
+ string iq_type = element.value("iqType", "");
+ double center = element.value("center", 0.0);
+ double rate = element.value("rate", 0.0);
+ bool repeat = element.value("repeat", false);
+ if ((iq_type != "complex") && (iq_type != "float")) {
+ BOOST_LOG_TRIVIAL(error) << "IQ Type specified in config.json not recognized, needs to be complex or float";
+ return false;
+ }
+ source = new Source(iq_file, center, rate, repeat, &config);
+ } else {
- Source *source = new Source(center, rate, error, driver, device, &config);
- BOOST_LOG_TRIVIAL(info) << "Max Frequency: " << format_freq(source->get_max_hz());
- BOOST_LOG_TRIVIAL(info) << "Min Frequency: " << format_freq(source->get_min_hz());
+ std::string device = element.value("device", "");
+ BOOST_LOG_TRIVIAL(info) << "Driver: " << element.value("driver", "");
+ BOOST_LOG_TRIVIAL(info) << "Device: " << device;
+
+ int silence_frames = element.value("silenceFrames", 0);
+ double center = element.value("center", 0.0);
+ double rate = element.value("rate", 0.0);
+ double error = element.value("error", 0.0);
+ double ppm = element.value("ppm", 0.0);
+ bool agc = element.value("agc", false);
+ int gain = element.value("gain", 0);
+ int if_gain = element.value("ifGain", 0);
+ int bb_gain = element.value("bbGain", 0);
+ int mix_gain = element.value("mixGain", 0);
+ int lna_gain = element.value("lnaGain", 0);
+ int pga_gain = element.value("pgaGain", 0);
+ int tia_gain = element.value("tiaGain", 0);
+ int amp_gain = element.value("ampGain", 0);
+ int vga_gain = element.value("vgaGain", 0);
+ int vga1_gain = element.value("vga1Gain", 0);
+ int vga2_gain = element.value("vga2Gain", 0);
+
+ std::string antenna = element.value("antenna", "");
+
+ BOOST_LOG_TRIVIAL(info) << "Center: " << format_freq(element.value("center", 0.0));
+ BOOST_LOG_TRIVIAL(info) << "Rate: " << FormatSamplingRate(element.value("rate", 0.0));
+ BOOST_LOG_TRIVIAL(info) << "Error: " << element.value("error", 0.0);
+ BOOST_LOG_TRIVIAL(info) << "PPM Error: " << element.value("ppm", 0.0);
+ BOOST_LOG_TRIVIAL(info) << "Auto gain control: " << element.value("agc", false);
+ BOOST_LOG_TRIVIAL(info) << "Gain: " << element.value("gain", 0);
+ BOOST_LOG_TRIVIAL(info) << "IF Gain: " << element.value("ifGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "BB Gain: " << element.value("bbGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "LNA Gain: " << element.value("lnaGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "PGA Gain: " << element.value("pgaGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "TIA Gain: " << element.value("tiaGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "MIX Gain: " << element.value("mixGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "AMP Gain: " << element.value("ampGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "VGA Gain: " << element.value("vgaGain", 0);
+ BOOST_LOG_TRIVIAL(info) << "VGA1 Gain: " << element.value("vga1Gain", 0);
+ BOOST_LOG_TRIVIAL(info) << "VGA2 Gain: " << element.value("vga2Gain", 0);
+ BOOST_LOG_TRIVIAL(info) << "Idle Silence: " << element.value("silenceFrame", 0);
+
+ if ((ppm != 0) && (error != 0)) {
+ BOOST_LOG_TRIVIAL(info) << "Both PPM and Error should not be set at the same time. Setting Error to 0.";
+ error = 0;
+ }
+ source = new Source(center, rate, error, driver, device, &config);
- // SoapySDRPlay3 quirk: autogain must be disabled before any of the gains can be set
- if (source->get_device().find("sdrplay") != std::string::npos) {
- source->set_gain_mode(agc);
- }
+ // SoapySDRPlay3 quirk: autogain must be disabled before any of the gains can be set
+ if (source->get_device().find("sdrplay") != std::string::npos) {
+ source->set_gain_mode(agc);
+ }
+
+ if (element.contains("gainSettings")) {
+ for (auto it = element["gainSettings"].begin(); it != element["gainSettings"].end(); ++it) {
- if (element.contains("gainSettings")) {
- for (auto it = element["gainSettings"].begin(); it != element["gainSettings"].end(); ++it)
- {
-
source->set_gain_by_name(it.key(), it.value());
- gain_set = true;
+ gain_set = true;
+ }
}
- }
- if (if_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("IF", if_gain);
- }
+ if (if_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("IF", if_gain);
+ }
- if (bb_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("BB", bb_gain);
- }
+ if (bb_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("BB", bb_gain);
+ }
- if (mix_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("MIX", mix_gain);
- }
+ if (mix_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("MIX", mix_gain);
+ }
- if (lna_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("LNA", lna_gain);
- }
+ if (lna_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("LNA", lna_gain);
+ }
- if (tia_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("TIA", tia_gain);
- }
+ if (tia_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("TIA", tia_gain);
+ }
- if (pga_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("PGA", pga_gain);
- }
+ if (pga_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("PGA", pga_gain);
+ }
- if (amp_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("AMP", amp_gain);
- }
+ if (amp_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("AMP", amp_gain);
+ }
- if (vga_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("VGA", vga_gain);
- }
+ if (vga_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("VGA", vga_gain);
+ }
- if (vga1_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("VGA1", vga1_gain);
- }
+ if (vga1_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("VGA1", vga1_gain);
+ }
- if (vga2_gain != 0) {
- gain_set = true;
- source->set_gain_by_name("VGA2", vga2_gain);
- }
+ if (vga2_gain != 0) {
+ gain_set = true;
+ source->set_gain_by_name("VGA2", vga2_gain);
+ }
- if (gain != 0) {
- gain_set = true;
- source->set_gain(gain);
- }
+ if (gain != 0) {
+ gain_set = true;
+ source->set_gain(gain);
+ }
- if (!gain_set) {
- BOOST_LOG_TRIVIAL(error) << "! No Gain was specified! Things will probably not work";
- }
+ if (!gain_set) {
+ BOOST_LOG_TRIVIAL(error) << "! No Gain was specified! Things will probably not work";
+ }
- source->set_gain_mode(agc);
- source->set_antenna(antenna);
- source->set_silence_frames(silence_frames);
+ source->set_gain_mode(agc);
+ source->set_antenna(antenna);
+ source->set_silence_frames(silence_frames);
- if (ppm != 0) {
- source->set_freq_corr(ppm);
+ if (ppm != 0) {
+ source->set_freq_corr(ppm);
+ }
}
+ BOOST_LOG_TRIVIAL(info) << "Max Frequency: " << format_freq(source->get_max_hz());
+ BOOST_LOG_TRIVIAL(info) << "Min Frequency: " << format_freq(source->get_min_hz());
+ BOOST_LOG_TRIVIAL(info) << "Digital Recorders: " << element.value("digitalRecorders", 0);
+ BOOST_LOG_TRIVIAL(info) << "SigMF Recorders: " << element.value("sigmfRecorders", 0);
+ BOOST_LOG_TRIVIAL(info) << "Analog Recorders: " << element.value("analogRecorders", 0);
source->create_digital_recorders(tb, digital_recorders);
source->create_analog_recorders(tb, analog_recorders);
source->create_sigmf_recorders(tb, sigmf_recorders);
diff --git a/trunk-recorder/recorders/sigmf_recorder_impl.cc b/trunk-recorder/recorders/sigmf_recorder_impl.cc
index b800c7cc8..29a3134b6 100644
--- a/trunk-recorder/recorders/sigmf_recorder_impl.cc
+++ b/trunk-recorder/recorders/sigmf_recorder_impl.cc
@@ -4,6 +4,207 @@
// static int rec_counter=0;
+void sigmf_recorder_impl::generate_arb_taps() {
+
+ double arb_size = 32;
+ double arb_atten = 100;
+ // Create a filter that covers the full bandwidth of the output signal
+
+ // If rate >= 1, we need to prevent images in the output,
+ // so we have to filter it to less than half the channel
+ // width of 0.5. If rate < 1, we need to filter to less
+ // than half the output signal's bw to avoid aliasing, so
+ // the half-band here is 0.5*rate.
+ double percent = 0.80;
+
+ if (arb_rate <= 1) {
+ double halfband = 0.5 * arb_rate;
+ double bw = percent * halfband;
+ double tb = (percent / 2.0) * halfband;
+
+// BOOST_LOG_TRIVIAL(info) << "Arb Rate: " << arb_rate << " Half band: " << halfband << " bw: " << bw << " tb: " <<
+// tb;
+
+// As we drop the bw factor, the optfir filter has a harder time converging;
+// using the firdes method here for better results.
+#if GNURADIO_VERSION < 0x030900
+ arb_taps = gr::filter::firdes::low_pass_2(arb_size, arb_size, bw, tb, arb_atten, gr::filter::firdes::WIN_BLACKMAN_HARRIS);
+#else
+ arb_taps = gr::filter::firdes::low_pass_2(arb_size, arb_size, bw, tb, arb_atten, gr::fft::window::WIN_BLACKMAN_HARRIS);
+#endif
+ } else {
+ BOOST_LOG_TRIVIAL(error) << "Something is probably wrong! Resampling rate too low";
+ exit(1);
+ }
+}
+
+void sigmf_recorder_impl::initialize_prefilter() {
+ const float pi = M_PI;
+ double phase1_channel_rate = phase1_symbol_rate * phase1_samples_per_symbol;
+ if_rate = phase1_channel_rate;
+ int decimation = int(input_rate / if_rate);
+ double resampled_rate = float(input_rate) / float(decimation);
+
+ valve = gr::blocks::copy::make(sizeof(gr_complex));
+ valve->set_enabled(false);
+ squelch = gr::analog::pwr_squelch_cc::make(squelch_db, 0.0001, 0, true);
+ rms_agc = gr::blocks::rms_agc::make(0.45, 0.85);
+ double sps = floor(resampled_rate / phase1_symbol_rate);
+ double def_excess_bw = 0.2;
+ fll_band_edge = gr::digital::fll_band_edge_cc::make(sps, def_excess_bw, 2 * sps + 1, (2.0 * pi) / sps / 250); // OP25 has this set to 350 instead of 250
+}
+
+void sigmf_recorder_impl::initialize_prefilter_xlat() {
+
+ int decimation = int(input_rate / if_rate);
+ double resampled_rate = float(input_rate) / float(decimation);
+
+#if GNURADIO_VERSION < 0x030900
+ if_coeffs = gr::filter::firdes::low_pass(1.0, input_rate, resampled_rate / 2, resampled_rate / 2, gr::filter::firdes::WIN_HAMMING);
+#else
+ if_coeffs = gr::filter::firdes::low_pass(1.0, input_rate, resampled_rate / 2, resampled_rate / 2, gr::fft::window::WIN_HAMMING);
+#endif
+
+ freq_xlat = gr::filter::freq_xlating_fir_filter::make(decimation, if_coeffs, 0, input_rate); // inital_lpf_taps, 0, input_rate);
+ connect(valve, 0, freq_xlat, 0);
+
+ // ARB Resampler
+ arb_rate = if_rate / resampled_rate;
+ generate_arb_taps();
+ arb_resampler = gr::filter::pfb_arb_resampler_ccf::make(arb_rate, arb_taps);
+ connect(self(), 0, valve, 0);
+
+ if (arb_rate == 1.0) {
+ connect(freq_xlat, 0, squelch, 0);
+ } else {
+ connect(freq_xlat, 0, arb_resampler, 0);
+ connect(arb_resampler, 0, squelch, 0);
+ }
+
+ /*connect(squelch, 0, rms_agc, 0);
+ connect(rms_agc, 0, fll_band_edge, 0);*/
+}
+
+sigmf_recorder_impl::DecimSettings sigmf_recorder_impl::get_decim(long speed) {
+ long s = speed;
+ long if_freqs[] = {24000, 25000, 32000};
+ DecimSettings decim_settings = {-1, -1};
+ for (int i = 0; i < 3; i++) {
+ long if_freq = if_freqs[i];
+ if (s % if_freq != 0) {
+ continue;
+ }
+ long q = s / if_freq;
+ if (q & 1) {
+ continue;
+ }
+
+ if ((q >= 40) && ((q & 3) == 0)) {
+ decim_settings.decim = q / 4;
+ decim_settings.decim2 = 4;
+ } else {
+ decim_settings.decim = q / 2;
+ decim_settings.decim2 = 2;
+ }
+ BOOST_LOG_TRIVIAL(debug) << "P25 recorder Decim: " << decim_settings.decim << " Decim2: " << decim_settings.decim2;
+ return decim_settings;
+ }
+ BOOST_LOG_TRIVIAL(error) << "P25 recorder Decim: Nothing found";
+ return decim_settings;
+}
+
+void sigmf_recorder_impl::initialize_prefilter_decim() {
+ long fa = 0;
+ long fb = 0;
+ const float pi = M_PI;
+ if1 = 0;
+ if2 = 0;
+
+ int decimation = int(input_rate / if_rate);
+ double resampled_rate = float(input_rate) / float(decimation);
+ BOOST_LOG_TRIVIAL(info) << "\t P25 Recorder - decimation: " << decimation << " resampled rate " << resampled_rate << " if rate: " << if_rate << " System Rate: " << input_rate;
+
+#if GNURADIO_VERSION < 0x030900
+ inital_lpf_taps = gr::filter::firdes::low_pass_2(1.0, input_rate, 96000, 25000, 60, gr::filter::firdes::WIN_HANN);
+ if_coeffs = gr::filter::firdes::low_pass(1.0, input_rate, resampled_rate / 2, resampled_rate / 2, gr::filter::firdes::WIN_HAMMING);
+#else
+ inital_lpf_taps = gr::filter::firdes::low_pass_2(1.0, input_rate, 96000, 25000, 60, gr::fft::window::WIN_HANN);
+ if_coeffs = gr::filter::firdes::low_pass(1.0, input_rate, resampled_rate / 2, resampled_rate / 2, gr::fft::window::WIN_HAMMING);
+#endif
+
+ lo = gr::analog::sig_source_c::make(input_rate, gr::analog::GR_SIN_WAVE, 0, 1.0, 0.0);
+ mixer = gr::blocks::multiply_cc::make();
+
+ sigmf_recorder_impl::DecimSettings decim_settings = get_decim(input_rate);
+ if (decim_settings.decim != -1) {
+ double_decim = true;
+ decim = decim_settings.decim;
+ if1 = input_rate / decim_settings.decim;
+ if2 = if1 / decim_settings.decim2;
+ fa = 6250;
+ fb = if2 / 2;
+ BOOST_LOG_TRIVIAL(info) << "\t P25 Recorder two-stage decimator - Initial decimated rate: " << if1 << " Second decimated rate: " << if2 << " FA: " << fa << " FB: " << fb << " System Rate: " << input_rate;
+ bandpass_filter_coeffs = gr::filter::firdes::complex_band_pass(1.0, input_rate, -if1 / 2, if1 / 2, if1 / 2);
+#if GNURADIO_VERSION < 0x030900
+ lowpass_filter_coeffs = gr::filter::firdes::low_pass(1.0, if1, (fb + fa) / 2, fb - fa, gr::filter::firdes::WIN_HAMMING);
+#else
+ lowpass_filter_coeffs = gr::filter::firdes::low_pass(1.0, if1, (fb + fa) / 2, fb - fa, gr::fft::window::WIN_HAMMING);
+#endif
+ bandpass_filter = gr::filter::fft_filter_ccc::make(decim_settings.decim, bandpass_filter_coeffs);
+ lowpass_filter = gr::filter::fft_filter_ccf::make(decim_settings.decim2, lowpass_filter_coeffs);
+ resampled_rate = if2;
+ bfo = gr::analog::sig_source_c::make(if1, gr::analog::GR_SIN_WAVE, 0, 1.0, 0.0);
+ } else {
+ double_decim = false;
+ lo = gr::analog::sig_source_c::make(input_rate, gr::analog::GR_SIN_WAVE, 0, 1.0, 0.0);
+
+#if GNURADIO_VERSION < 0x030900
+ lowpass_filter_coeffs = gr::filter::firdes::low_pass(1.0, input_rate, 7250, 1450, gr::filter::firdes::WIN_HANN);
+#else
+ lowpass_filter_coeffs = gr::filter::firdes::low_pass(1.0, input_rate, 7250, 1450, gr::fft::window::WIN_HANN);
+#endif
+ decim = floor(input_rate / if_rate);
+ resampled_rate = input_rate / decim;
+ lowpass_filter = gr::filter::fft_filter_ccf::make(decim, lowpass_filter_coeffs);
+ BOOST_LOG_TRIVIAL(info) << "\t P25 Recorder single-stage decimator - Initial decimated rate: " << if1 << " Second decimated rate: " << if2 << " Initial Decimation: " << decim << " System Rate: " << input_rate;
+ }
+
+ // Cut-Off Filter
+ fa = 6250; // 3200; // sounds bad with FSK4 - WMATA
+ fb = fa + 1250; // 800; // sounds bad with FSK4 - WMATA
+#if GNURADIO_VERSION < 0x030900
+ cutoff_filter_coeffs = gr::filter::firdes::low_pass(1.0, if_rate, (fb + fa) / 2, fb - fa, gr::filter::firdes::WIN_HANN);
+#else
+ cutoff_filter_coeffs = gr::filter::firdes::low_pass(1.0, if_rate, (fb + fa) / 2, fb - fa, gr::fft::window::WIN_HANN);
+#endif
+ cutoff_filter = gr::filter::fft_filter_ccf::make(1.0, cutoff_filter_coeffs);
+
+ // ARB Resampler
+ arb_rate = if_rate / resampled_rate;
+ generate_arb_taps();
+ arb_resampler = gr::filter::pfb_arb_resampler_ccf::make(arb_rate, arb_taps);
+ connect(self(), 0, valve, 0);
+
+ if (double_decim) {
+ connect(valve, 0, bandpass_filter, 0);
+ connect(bandpass_filter, 0, mixer, 0);
+ connect(bfo, 0, mixer, 1);
+ } else {
+ connect(valve, 0, mixer, 0);
+ connect(lo, 0, mixer, 1);
+ }
+ connect(mixer, 0, lowpass_filter, 0);
+ if (arb_rate == 1.0) {
+ connect(lowpass_filter, 0, cutoff_filter, 0);
+ } else {
+ connect(lowpass_filter, 0, arb_resampler, 0);
+ connect(arb_resampler, 0, cutoff_filter, 0);
+ }
+ connect(cutoff_filter, 0, squelch, 0);
+ /*connect(squelch, 0, rms_agc, 0);
+ connect(rms_agc, 0, fll_band_edge, 0);*/
+}
+
sigmf_recorder_sptr make_sigmf_recorder(Source *src) {
sigmf_recorder *recorder = new sigmf_recorder_impl(src);
@@ -20,6 +221,8 @@ sigmf_recorder_impl::sigmf_recorder_impl(Source *src)
center = source->get_center();
config = source->get_config();
silence_frames = source->get_silence_frames();
+ squelch_db = 0;
+ input_rate = source->get_rate();
talkgroup = 0;
recording_count = 0;
recording_duration = 0;
@@ -33,8 +236,7 @@ sigmf_recorder_impl::sigmf_recorder_impl(Source *src)
timestamp = time(NULL);
starttime = time(NULL);
- valve = gr::blocks::copy::make(sizeof(gr_complex));
- valve->set_enabled(false);
+
// tm *ltm = localtime(&starttime);
@@ -45,8 +247,10 @@ sigmf_recorder_impl::sigmf_recorder_impl(Source *src)
}
raw_sink = gr::blocks::file_sink::make(sizeof(gr_complex), filename);
- connect(self(), 0, valve, 0);
- connect(valve, 0, raw_sink, 0);
+ initialize_prefilter();
+ initialize_prefilter_xlat();
+
+ connect(squelch, 0, raw_sink, 0);
}
int sigmf_recorder_impl::get_num() {
@@ -81,6 +285,7 @@ void sigmf_recorder_impl::tune_offset(double f) {
// have to flip this for 3.7
// BOOST_LOG_TRIVIAL(info) << "Offset set to: " << offset_amount << " Freq: "
// << freq;
+ freq_xlat->set_center_freq(-f);
}
State sigmf_recorder_impl::get_state() {
@@ -89,7 +294,8 @@ State sigmf_recorder_impl::get_state() {
void sigmf_recorder_impl::stop() {
if (state == ACTIVE) {
- BOOST_LOG_TRIVIAL(error) << "sigmf_recorder.cc: Stopping Logger \t[ " << rec_num << " ] - freq[ " << freq << "] \t talkgroup[ " << talkgroup << " ]";
+ BOOST_LOG_TRIVIAL(info) << "[" << call->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << this->call->get_talkgroup_display() << "\tFreq: " << format_freq(freq) << "\t\u001b[32mStopping SigMF Recorder Num [" << rec_num << "]\u001b[0m";
+
state = INACTIVE;
valve->set_enabled(false);
raw_sink->close();
@@ -104,27 +310,64 @@ bool sigmf_recorder_impl::start(Call *call) {
starttime = time(NULL);
int nchars;
tm *ltm = localtime(&starttime);
-
+ this->call = call;
+ System *system = call->get_system();
talkgroup = call->get_talkgroup();
freq = call->get_freq();
-
- BOOST_LOG_TRIVIAL(info) << "sigmf_recorder.cc: Starting Logger \t[ " << rec_num << " ] - freq[ " << freq << "] \t talkgroup[ " << talkgroup << " ]";
+ squelch_db = system->get_squelch_db();
+ squelch->set_threshold(squelch_db);
+ int offset_amount = (center - freq);
+ tune_offset(offset_amount);
+ BOOST_LOG_TRIVIAL(info) << "[" << call->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << this->call->get_talkgroup_display() << "\tFreq: " << format_freq(freq) << "\t\u001b[32mStarting SigMF Recorder Num [" << rec_num << "]\u001b[0m";
std::stringstream path_stream;
// Found some good advice on Streams and Strings here: https://blog.sensecodons.com/2013/04/dont-let-stdstringstreamstrcstr-happen.html
- path_stream << call->get_capture_dir() << "/" << call->get_short_name() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday;
+ path_stream << call->get_temp_dir() << "/" << call->get_short_name() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday;
std::string path_string = path_stream.str();
boost::filesystem::create_directories(path_string);
- nchars = snprintf(filename, 255, "%s/%ld-%ld_%.0f.raw", path_string.c_str(), talkgroup, starttime, call->get_freq());
+ nchars = snprintf(filename, 255, "%s/%ld-%ld_%.0f-call_%lu.sigmf-data", path_string.c_str(), talkgroup, starttime, call->get_freq(), call->get_call_num());
if (nchars >= 255) {
- BOOST_LOG_TRIVIAL(error) << "Call: Path longer than 255 charecters";
+ BOOST_LOG_TRIVIAL(error) << "SigMF-meta: Path longer than 255 charecters";
}
raw_sink->open(filename);
state = ACTIVE;
valve->set_enabled(true);
+ std::string src_description = source->get_driver() + ": " + source->get_device() + " - " + source->get_antenna();
+ time_t now;
+ time(&now);
+ char buf[sizeof "2011-10-08T07:07:09Z"];
+ strftime(buf, sizeof buf, "%FT%TZ", gmtime(&now));
+ std::string start_time(buf);
+ nlohmann::json j = {
+ {"global", {
+ {"core:datatype", "cf32_le"},
+ {"core:sample_rate", if_rate},
+ {"core:hw", src_description},
+ {"core:recorder", "Trunk Recorder"},
+ {"core:version", "1.0.0"}
+ }},
+ {"captures", nlohmann::json::array(
+ { nlohmann::json::object({
+ {"core:sample_start", 0},
+ {"core:frequency", freq},
+ {"core:datetime", start_time}
+ })
+ }
+ )},
+ {"annotations", nlohmann::json::array({})}
+ };
+
+ nchars = snprintf(filename, 255, "%s/%ld-%ld_%.0f-call_%lu.sigmf-meta", path_string.c_str(), talkgroup, starttime, call->get_freq(), call->get_call_num());
+ if (nchars >= 255) {
+ BOOST_LOG_TRIVIAL(error) << "SigMF-meta: Path longer than 255 charecters";
+ }
+ std::ofstream o(filename);
+ o << std::setw(4) << j << std::endl;
+ o.close();
+
} else {
BOOST_LOG_TRIVIAL(error) << "sigmf_recorder.cc: Trying to Start an already Active Logger!!!";
}
diff --git a/trunk-recorder/recorders/sigmf_recorder_impl.h b/trunk-recorder/recorders/sigmf_recorder_impl.h
index 00150fc8e..ca803c137 100644
--- a/trunk-recorder/recorders/sigmf_recorder_impl.h
+++ b/trunk-recorder/recorders/sigmf_recorder_impl.h
@@ -51,6 +51,7 @@
#endif
#endif
+#include
#include
#include
@@ -66,8 +67,8 @@
#include
#include
#include
-
-#include "../gr_blocks/freq_xlating_fft_filter.h"
+#include
+#include "../gr_blocks/rms_agc.h"
#include "recorder.h"
#include "../source.h"
@@ -94,31 +95,58 @@ class sigmf_recorder_impl : public sigmf_recorder {
// void forecast(int noutput_items, gr_vector_int &ninput_items_required);
private:
+ void generate_arb_taps();
+ void initialize_prefilter();
+ void initialize_prefilter_xlat();
+ sigmf_recorder_impl::DecimSettings get_decim(long speed);
+ void initialize_prefilter_decim();
+
+ bool double_decim;
double center, freq;
int silence_frames;
long talkgroup;
+ double arb_rate;
+ long input_rate;
+ long decim;
+ long if_rate;
+ double resampled_rate;
+ long if1;
+ long if2;
+ const int phase1_samples_per_symbol = 5;
+ const double phase1_symbol_rate = 4800;
+
+ double squelch_db;
time_t timestamp;
time_t starttime;
Config *config;
Source *source;
+ Call *call;
char filename[255];
// int num;
State state;
- // std::vector lpf_coeffs;
- std::vector lpf_coeffs;
- std::vector arb_taps;
- std::vector sym_taps;
- gr::filter::freq_xlating_fir_filter_ccf::sptr prefilter;
-
- gr::analog::quadrature_demod_cf::sptr fm_demod;
- gr::analog::feedforward_agc_cc::sptr agc;
- gr::analog::agc2_ff::sptr demod_agc;
- gr::analog::agc2_cc::sptr pre_demod_agc;
+ std::vector arb_taps;
+ std::vector bandpass_filter_coeffs;
+ std::vector inital_lpf_taps;
+ std::vector lowpass_filter_coeffs;
+ std::vector cutoff_filter_coeffs;
+ std::vector if_coeffs;
+
+ gr::analog::sig_source_c::sptr lo;
+ gr::analog::sig_source_c::sptr bfo;
+ gr::blocks::multiply_cc::sptr mixer;
+
+ gr::filter::fft_filter_ccc::sptr bandpass_filter;
+ gr::filter::fft_filter_ccf::sptr lowpass_filter;
+ gr::filter::fft_filter_ccf::sptr cutoff_filter;
+ gr::filter::freq_xlating_fir_filter::sptr freq_xlat;
+ gr::filter::pfb_arb_resampler_ccf::sptr arb_resampler;
+ gr::digital::fll_band_edge_cc::sptr fll_band_edge;
+ gr::blocks::rms_agc::sptr rms_agc;
+ gr::analog::pwr_squelch_cc::sptr squelch;
gr::blocks::file_sink::sptr raw_sink;
- gr::blocks::short_to_float::sptr converter;
gr::blocks::copy::sptr valve;
};
diff --git a/trunk-recorder/source.cc b/trunk-recorder/source.cc
index c2e13dee3..089b92f12 100644
--- a/trunk-recorder/source.cc
+++ b/trunk-recorder/source.cc
@@ -1,6 +1,8 @@
#include "source.h"
#include "formatter.h"
+using json = nlohmann::json;
+
static int src_counter = 0;
void Source::set_antenna(std::string ant) {
@@ -29,7 +31,6 @@ int Source::get_silence_frames() {
return silence_frames;
}
-
double Source::get_min_hz() {
return min_hz;
}
@@ -158,7 +159,7 @@ void Source::create_analog_recorders(gr::top_block_sptr tb, int r) {
Recorder *Source::get_analog_recorder(Talkgroup *talkgroup, int priority, Call *call) {
int num_available_recorders = get_num_available_analog_recorders();
- if(talkgroup && (priority == -1)){
+ if (talkgroup && (priority == -1)) {
call->set_state(MONITORING);
call->set_monitoring_state(IGNORED_TG);
BOOST_LOG_TRIVIAL(info) << "[" << call->get_system()->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << call->get_talkgroup_display() << "\tFreq: " << format_freq(call->get_freq()) << "\tNot recording talkgroup. Priority is -1.";
@@ -168,7 +169,7 @@ Recorder *Source::get_analog_recorder(Talkgroup *talkgroup, int priority, Call *
if (talkgroup && priority > num_available_recorders) { // a high priority is bad. You need at least the number of availalbe recorders to your priority
call->set_state(MONITORING);
call->set_monitoring_state(NO_RECORDER);
- BOOST_LOG_TRIVIAL(error) << "[" << call->get_system()->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << call->get_talkgroup_display() << "\tFreq: " << format_freq(call->get_freq()) << "\tNot recording talkgroup. Priority is " << priority << " but only " << num_available_recorders << " recorders are available.";
+ BOOST_LOG_TRIVIAL(error) << "[" << call->get_system()->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << call->get_talkgroup_display() << "\tFreq: " << format_freq(call->get_freq()) << "\tNot recording talkgroup. Priority is " << priority << " but only " << num_available_recorders << " recorders are available.";
return NULL;
}
@@ -203,7 +204,7 @@ void Source::create_digital_recorders(gr::top_block_sptr tb, int r) {
Recorder *Source::get_digital_recorder(Talkgroup *talkgroup, int priority, Call *call) {
int num_available_recorders = get_num_available_digital_recorders();
- if(talkgroup && (priority == -1)){
+ if (talkgroup && (priority == -1)) {
call->set_state(MONITORING);
call->set_monitoring_state(IGNORED_TG);
BOOST_LOG_TRIVIAL(info) << "[" << call->get_system()->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << call->get_talkgroup_display() << "\tFreq: " << format_freq(call->get_freq()) << "\tNot recording talkgroup. Priority is -1.";
@@ -213,7 +214,7 @@ Recorder *Source::get_digital_recorder(Talkgroup *talkgroup, int priority, Call
if (talkgroup && priority > num_available_recorders) { // a high priority is bad. You need at least the number of availalbe recorders to your priority
call->set_state(MONITORING);
call->set_monitoring_state(NO_RECORDER);
- BOOST_LOG_TRIVIAL(error) << "[" << call->get_system()->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << call->get_talkgroup_display() << "\tFreq: " << format_freq(call->get_freq()) << "\tNot recording talkgroup. Priority is " << priority << " but only " << num_available_recorders << " recorders are available.";
+ BOOST_LOG_TRIVIAL(error) << "[" << call->get_system()->get_short_name() << "]\t\033[0;34m" << call->get_call_num() << "C\033[0m\tTG: " << call->get_talkgroup_display() << "\tFreq: " << format_freq(call->get_freq()) << "\tNot recording talkgroup. Priority is " << priority << " but only " << num_available_recorders << " recorders are available.";
return NULL;
}
@@ -312,7 +313,7 @@ Recorder *Source::get_sigmf_recorder() {
}
void Source::print_recorders() {
- BOOST_LOG_TRIVIAL(info) << "[ Source " << src_num << ": " << format_freq(center) << " ] " << device ;
+ BOOST_LOG_TRIVIAL(info) << "[ Source " << src_num << ": " << format_freq(center) << " ] " << device;
for (std::vector::iterator it = digital_recorders.begin();
it != digital_recorders.end(); it++) {
@@ -526,6 +527,68 @@ Source::Source(double c, double r, double e, std::string drv, std::string dev, C
}
}
+void Source::set_iq_source(std::string iq_file, bool repeat, double center, double rate) {
+ this->rate = rate;
+ this->center = center;
+ error = 0;
+ min_hz = center - ((rate / 2));
+ max_hz = center + ((rate / 2));
+ driver = "iqfile";
+ device = "";
+ gain = 0;
+ lna_gain = 0;
+ tia_gain = 0;
+ pga_gain = 0;
+ mix_gain = 0;
+ if_gain = 0;
+ src_num = src_counter++;
+ max_digital_recorders = 0;
+ max_debug_recorders = 0;
+ max_sigmf_recorders = 0;
+ max_analog_recorders = 0;
+ debug_recorder_port = 0;
+
+ iq_file_source::sptr iq_file_src;
+ iq_file_src = iq_file_source::make(iq_file, this->rate, repeat);
+
+ BOOST_LOG_TRIVIAL(info) << "SOURCE TYPE IQ FILE";
+ BOOST_LOG_TRIVIAL(info) << "Setting Center to: " << FormatSamplingRate(center);
+ BOOST_LOG_TRIVIAL(info) << "Setting sample rate to: " << FormatSamplingRate(rate);
+
+ source_block = iq_file_src;
+}
+
+Source::Source(std::string sigmf_meta, std::string sigmf_data, bool repeat, Config *cfg) {
+ json data;
+ std::cout << sigmf_meta << std::endl;
+ try {
+ std::ifstream f(sigmf_meta);
+ data = json::parse(f);
+ } catch (const json::parse_error &e) {
+ // output exception information
+ std::cout << "message: " << e.what() << '\n'
+ << "exception id: " << e.id << '\n'
+ << "byte position of error: " << e.byte << std::endl;
+ exit(1);
+ }
+
+ std::cout << data.dump(4) << std::endl;
+ json global = data["global"];
+
+ config = cfg;
+ this->rate = global["core:sample_rate"];
+
+ json capture = data["captures"][0];
+ this->center = capture["core:frequency"];
+ std::cout << "Rate: " << rate << "Center: " << center << std::endl;
+ set_iq_source(sigmf_data, repeat, center, rate);
+}
+
+Source::Source(std::string iq_file, bool repeat, double center, double rate, Config *cfg) {
+ config = cfg;
+ set_iq_source(iq_file, repeat, center, rate);
+}
+
std::vector Source::get_recorders() {
std::vector recorders;
diff --git a/trunk-recorder/source.h b/trunk-recorder/source.h
index 02e58da79..03aee801f 100644
--- a/trunk-recorder/source.h
+++ b/trunk-recorder/source.h
@@ -13,6 +13,9 @@
#include "recorders/dmr_recorder.h"
#include "recorders/p25_recorder.h"
#include "recorders/sigmf_recorder.h"
+#include "sources/iq_file_source.h"
+#define JSON_DIAGNOSTICS 1
+#include
struct Gain_Stage_t {
std::string stage_name;
@@ -68,6 +71,9 @@ class Source {
int get_num_available_analog_recorders();
int get_num();
Source(double c, double r, double e, std::string driver, std::string device, Config *cfg);
+ Source(std::string sigmf_meta, std::string sigmf_data, bool repeat, Config *cfg);
+ Source(std::string iq_file, bool repeat, double center, double rate, Config *cfg);
+ void set_iq_source(std::string iq_file, bool repeat, double center, double rate);
gr::basic_block_sptr get_src_block();
double get_min_hz();
double get_max_hz();
diff --git a/trunk-recorder/sources/iq_file_source.cc b/trunk-recorder/sources/iq_file_source.cc
new file mode 100644
index 000000000..82f36fe98
--- /dev/null
+++ b/trunk-recorder/sources/iq_file_source.cc
@@ -0,0 +1,24 @@
+#include "iq_file_source.h"
+
+
+iq_file_source::sptr
+iq_file_source::make(std::string filename, double rate, bool repeat=false) {
+ return gnuradio::get_initial_sptr(new iq_file_source(filename, rate, repeat));
+}
+
+iq_file_source::iq_file_source(std::string filename, double rate, bool repeat=false)
+ : gr::hier_block2("iq_file_source",
+ gr::io_signature::make(0, 0, 0),
+ gr::io_signature::make(1, 1, sizeof(gr_complex))),
+ d_filename(filename),
+ d_rate(rate),
+ d_repeat(repeat) {
+
+ file_source = gr::blocks::file_source::make(sizeof(gr_complex), filename.c_str(), repeat);
+ throttle = gr::blocks::throttle::make(sizeof(gr_complex), rate);
+ connect(file_source, 0, throttle, 0);
+ connect(throttle, 0, self(), 0);
+}
+
+
+
\ No newline at end of file
diff --git a/trunk-recorder/sources/iq_file_source.h b/trunk-recorder/sources/iq_file_source.h
new file mode 100644
index 000000000..6156953b0
--- /dev/null
+++ b/trunk-recorder/sources/iq_file_source.h
@@ -0,0 +1,34 @@
+#ifndef IQ_FILE_SOURCE_H
+#define IQ_FILE_SOURCE_H
+
+#include
+#include
+#include
+
+
+
+class iq_file_source : public gr::hier_block2 {
+public:
+#if GNURADIO_VERSION < 0x030900
+ typedef boost::shared_ptr sptr;
+#else
+ typedef std::shared_ptr sptr;
+#endif
+ static sptr make(std::string filename, double rate, bool repeat);
+
+
+ iq_file_source(std::string filename, double rate, bool repeat);
+
+
+ private:
+ double d_rate;
+ std::string d_filename;
+ bool d_repeat;
+ gr::blocks::file_source::sptr file_source;
+ gr::blocks::throttle::sptr throttle;
+};
+
+
+
+
+#endif
\ No newline at end of file