From 338407396caab379f44ee77d9f5bc8aa03e6fdac Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 27 Feb 2022 10:05:25 -0500 Subject: [PATCH 01/10] Getting started Added initial support in config --- trunk-recorder/main.cc | 65 ++++++++++++++------------------ trunk-recorder/systems/system.cc | 9 ----- trunk-recorder/systems/system.h | 4 -- 3 files changed, 29 insertions(+), 49 deletions(-) diff --git a/trunk-recorder/main.cc b/trunk-recorder/main.cc index 7b9418293..526b56707 100755 --- a/trunk-recorder/main.cc +++ b/trunk-recorder/main.cc @@ -229,49 +229,42 @@ bool load_config(string config_file) { system->set_system_type(node.second.get("type")); BOOST_LOG_TRIVIAL(info) << "System Type: " << system->get_system_type(); - if (system->get_system_type() == "conventional") { - BOOST_LOG_TRIVIAL(info) << "Conventional Channels: "; - BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("channels")) { - double channel = sub_node.second.get("", 0); - - BOOST_LOG_TRIVIAL(info) << " " << format_freq(channel); - system->add_channel(channel); - } + // If it is a conventional System + if ((system->get_system_type() == "conventional") || (system->get_system_type() == "conventionalP25") || (system->get_system_type() == "conventionalDMR")) { + + boost::optional channel_file_exist = node.second.get_optional("channelFile"); + boost::optional channels_exist = node.second.get_child_optional("channels"); - BOOST_LOG_TRIVIAL(info) << "Alpha Tags: "; - if (node.second.count("alphatags") != 0) { - int alphaIndex = 1; - BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("alphatags")) { - std::string alphaTag = sub_node.second.get("", ""); - BOOST_LOG_TRIVIAL(info) << " " << alphaTag; - system->talkgroups->add(alphaIndex, alphaTag); - alphaIndex++; - } + if (channel_file_exist && channels_exist) { + BOOST_LOG_TRIVIAL(error) << "Both \"channels\" and \"channelFile\" cannot be defined for a system!"; + return false; } - } else if ((system->get_system_type() == "conventionalP25") || (system->get_system_type() == "conventionalDMR") ) { - BOOST_LOG_TRIVIAL(info) << "Conventional Channels: "; - BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("channels")) { - double channel = sub_node.second.get("", 0); + if (channels_exist) { + BOOST_LOG_TRIVIAL(info) << "Conventional Channels: "; + BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("channels")) { + double channel = sub_node.second.get("", 0); - BOOST_LOG_TRIVIAL(info) << " " << format_freq(channel); - system->add_channel(channel); - } + BOOST_LOG_TRIVIAL(info) << " " << format_freq(channel); + system->add_channel(channel); + } - BOOST_LOG_TRIVIAL(info) << "Alpha Tags: "; - if (node.second.count("alphatags") != 0) { - int alphaIndex = 1; - BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("alphatags")) { - std::string alphaTag = sub_node.second.get("", ""); - BOOST_LOG_TRIVIAL(info) << " " << alphaTag; - system->talkgroups->add(alphaIndex, alphaTag); - alphaIndex++; + BOOST_LOG_TRIVIAL(info) << "Alpha Tags: "; + if (node.second.count("alphatags") != 0) { + int alphaIndex = 1; + BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("alphatags")) { + std::string alphaTag = sub_node.second.get("", ""); + BOOST_LOG_TRIVIAL(info) << " " << alphaTag; + system->talkgroups->add(alphaIndex, alphaTag); + alphaIndex++; + } } - } + } else if (channel_file_exist) { - system->set_delaycreateoutput(node.second.get("delayCreateOutput", false)); - BOOST_LOG_TRIVIAL(info) << "delayCreateOutput: " << system->get_delaycreateoutput(); + } else { + } + // If it is a Trunked System } else if ((system->get_system_type() == "smartnet") || (system->get_system_type() == "p25")) { BOOST_LOG_TRIVIAL(info) << "Control Channels: "; BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("control_channels")) { @@ -282,7 +275,7 @@ bool load_config(string config_file) { } } else { BOOST_LOG_TRIVIAL(error) << "System Type in config.json not recognized"; - exit(1); + return false; } bool qpsk_mod = true; diff --git a/trunk-recorder/systems/system.cc b/trunk-recorder/systems/system.cc index 8e444068d..a1ac08272 100644 --- a/trunk-recorder/systems/system.cc +++ b/trunk-recorder/systems/system.cc @@ -77,7 +77,6 @@ System::System(int sys_num) { // Setup the unit tags from the CSV file unit_tags = new UnitTags(); talkgroup_patches = {}; - d_delaycreateoutput = false; d_hideEncrypted = false; d_hideUnknown = false; d_mdc_enabled = false; @@ -418,14 +417,6 @@ System::TalkgroupDisplayFormat System::get_talkgroup_display_format() { return talkgroup_display_format; } -bool System::get_delaycreateoutput() { - return d_delaycreateoutput; -} - -void System::set_delaycreateoutput(bool delaycreateoutput) { - d_delaycreateoutput = delaycreateoutput; -} - bool System::get_hideEncrypted() { return d_hideEncrypted; } diff --git a/trunk-recorder/systems/system.h b/trunk-recorder/systems/system.h index 7c698da9f..6070bc885 100644 --- a/trunk-recorder/systems/system.h +++ b/trunk-recorder/systems/system.h @@ -197,9 +197,6 @@ class System { void set_talkgroup_display_format(TalkgroupDisplayFormat format); TalkgroupDisplayFormat get_talkgroup_display_format(); - bool get_delaycreateoutput(); - void set_delaycreateoutput(bool delaycreateoutput); - bool get_hideEncrypted(); void set_hideEncrypted(bool hideEncrypted); @@ -216,7 +213,6 @@ class System { private: TalkgroupDisplayFormat talkgroup_display_format; - bool d_delaycreateoutput; bool d_hideEncrypted; bool d_hideUnknown; From 2490879114ec431a07fecc985494829d39631520 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 27 Feb 2022 21:28:41 -0500 Subject: [PATCH 02/10] add new creation function --- trunk-recorder/talkgroup.cc | 33 ++++++++++++++++++++++++--------- trunk-recorder/talkgroup.h | 7 +++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/trunk-recorder/talkgroup.cc b/trunk-recorder/talkgroup.cc index f8227d042..397400770 100644 --- a/trunk-recorder/talkgroup.cc +++ b/trunk-recorder/talkgroup.cc @@ -1,14 +1,29 @@ #include "talkgroup.h" -Talkgroup::Talkgroup(long num, std::string m, std::string a, std::string d, std::string t, std::string g, int p) { - number = num; - mode = m; - alpha_tag = a; - description = d; - tag = t; - group = g; - priority = p; - active = false; +Talkgroup::Talkgroup(long num, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group, int priority) { + this.number = num; + this.mode = m; + this.alpha_tag = a; + this.description = d; + this.tag = t; + this.group = g; + this.priority = p; + this.active = false; + this.channel = 0; + this.tone = 0; +} + +Talkgroup::Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { + this.number = num; + this.mode = m; + this.alpha_tag = a; + this.description = d; + this.tag = t; + this.group = g; + this.priority = p; + this.active = false; + this.channel = 0; + this.tone = 0; } std::string Talkgroup::menu_string() { diff --git a/trunk-recorder/talkgroup.h b/trunk-recorder/talkgroup.h index 453c37041..a38bfab5a 100644 --- a/trunk-recorder/talkgroup.h +++ b/trunk-recorder/talkgroup.h @@ -15,6 +15,13 @@ class Talkgroup { std::string tag; std::string group; int priority; + + // For Conventional + double channel; + double tone; + + Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group); + Talkgroup(long num, std::string m, std::string a, std::string d, std::string t, std::string g, int p); bool is_active(); int get_priority(); From 131671308f8dcc96ed0d7767367e621b661e28eb Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 27 Feb 2022 22:18:44 -0500 Subject: [PATCH 03/10] Load a Channel File into Talkgroup Objects --- trunk-recorder/main.cc | 7 +-- trunk-recorder/systems/system.cc | 7 +++ trunk-recorder/systems/system.h | 2 + trunk-recorder/talkgroup.cc | 39 +++++++++-------- trunk-recorder/talkgroups.cc | 73 ++++++++++++++++++++++++++++++++ trunk-recorder/talkgroups.h | 1 + 6 files changed, 106 insertions(+), 23 deletions(-) diff --git a/trunk-recorder/main.cc b/trunk-recorder/main.cc index 526b56707..2e029c007 100755 --- a/trunk-recorder/main.cc +++ b/trunk-recorder/main.cc @@ -260,7 +260,8 @@ bool load_config(string config_file) { } } } else if (channel_file_exist) { - + std::string channel_file = node.second.get("channelFile"); + BOOST_LOG_TRIVIAL(info) << "Channel File: " << channel_file; } else { } @@ -272,6 +273,8 @@ bool load_config(string config_file) { BOOST_LOG_TRIVIAL(info) << " " << format_freq(control_channel); system->add_control_channel(control_channel); + system->set_talkgroups_file(node.second.get("talkgroupsFile", "")); + BOOST_LOG_TRIVIAL(info) << "Talkgroups File: " << system->get_talkgroups_file(); } } else { BOOST_LOG_TRIVIAL(error) << "System Type in config.json not recognized"; @@ -331,8 +334,6 @@ bool load_config(string config_file) { BOOST_LOG_TRIVIAL(info) << "Audio Archive: " << system->get_audio_archive(); system->set_transmission_archive(node.second.get("transmissionArchive", false)); BOOST_LOG_TRIVIAL(info) << "Transmission Archive: " << system->get_transmission_archive(); - system->set_talkgroups_file(node.second.get("talkgroupsFile", "")); - BOOST_LOG_TRIVIAL(info) << "Talkgroups File: " << system->get_talkgroups_file(); system->set_unit_tags_file(node.second.get("unitTagsFile", "")); BOOST_LOG_TRIVIAL(info) << "Unit Tags File: " << system->get_unit_tags_file(); system->set_record_unknown(node.second.get("recordUnknown", true)); diff --git a/trunk-recorder/systems/system.cc b/trunk-recorder/systems/system.cc index a1ac08272..da222ee23 100644 --- a/trunk-recorder/systems/system.cc +++ b/trunk-recorder/systems/system.cc @@ -253,6 +253,13 @@ std::string System::get_unit_tags_file() { return this->unit_tags_file; } +void System::set_channel_file(std::string channel_file) { + BOOST_LOG_TRIVIAL(info) << "Loading Talkgroups..."; + this->channel_file = channel_file; + this->talkgroups->load_talkgroups(channel_file); +} + + void System::set_talkgroups_file(std::string talkgroups_file) { BOOST_LOG_TRIVIAL(info) << "Loading Talkgroups..."; this->talkgroups_file = talkgroups_file; diff --git a/trunk-recorder/systems/system.h b/trunk-recorder/systems/system.h index 6070bc885..d404c910c 100644 --- a/trunk-recorder/systems/system.h +++ b/trunk-recorder/systems/system.h @@ -55,6 +55,7 @@ class System { p25p2_lfsr *lfsr; Source *source; std::string talkgroups_file; + std::string channel_file; std::string unit_tags_file; std::string short_name; std::string api_key; @@ -166,6 +167,7 @@ class System { Talkgroup *find_talkgroup(long tg); UnitTag *find_unit_tag(long unitID); void set_talkgroups_file(std::string); + void set_channel_file(std::string channel_file); void set_unit_tags_file(std::string); int control_channel_count(); void add_control_channel(double channel); diff --git a/trunk-recorder/talkgroup.cc b/trunk-recorder/talkgroup.cc index 397400770..42137094e 100644 --- a/trunk-recorder/talkgroup.cc +++ b/trunk-recorder/talkgroup.cc @@ -1,29 +1,28 @@ #include "talkgroup.h" Talkgroup::Talkgroup(long num, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group, int priority) { - this.number = num; - this.mode = m; - this.alpha_tag = a; - this.description = d; - this.tag = t; - this.group = g; - this.priority = p; - this.active = false; - this.channel = 0; - this.tone = 0; + this->number = num; + this->mode = mode; + this->alpha_tag = alpha_tag; + this->description = description; + this->tag = tag; + this->group = group; + this->priority = priority; + this->active = false; + this->channel = 0; + this->tone = 0; } Talkgroup::Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { - this.number = num; - this.mode = m; - this.alpha_tag = a; - this.description = d; - this.tag = t; - this.group = g; - this.priority = p; - this.active = false; - this.channel = 0; - this.tone = 0; + this->number = num; + this->mode = mode; + this->alpha_tag = alpha_tag; + this->description = description; + this->tag = tag; + this->group = group; + this->active = false; + this->channel = channel; + this->tone = tone; } std::string Talkgroup::menu_string() { diff --git a/trunk-recorder/talkgroups.cc b/trunk-recorder/talkgroups.cc index 10bc989f1..b57cf3277 100644 --- a/trunk-recorder/talkgroups.cc +++ b/trunk-recorder/talkgroups.cc @@ -116,6 +116,79 @@ void Talkgroups::load_talkgroups(std::string filename) { } } +void Talkgroups::load_channels(std::string filename) { + if (filename == "") { + return; + } + + std::ifstream in(filename.c_str()); + + if (!in.is_open()) { + BOOST_LOG_TRIVIAL(error) << "Error Opening Channel File: " << filename << std::endl; + return; + } + + boost::char_separator sep(",\t"); + typedef boost::tokenizer> t_tokenizer; + + std::vector vec; + std::string line; + + int lines_read = 0; + int lines_pushed = 0; + int priority = 1; + + while (!safeGetline(in, line).eof()) // this works with \r, \n, or \r\n + { + if (line.size() && (line[line.size() - 1] == '\r')) { + line = line.substr(0, line.size() - 1); + } + + lines_read++; + + if (line == "") + continue; + + t_tokenizer tok(line, sep); + + vec.assign(tok.begin(), tok.end()); + + Talkgroup *tg = NULL; + + // Channel File columns: + // + // [0] - talkgroup number + // [1] - channel freq + // [2] - tone + // [3] - mode + // [4] - alpha_tag + // [5] - description + // [6] - tag + // [7] - group + + if (!((vec.size() == 8) || (vec.size() == 7))) { + BOOST_LOG_TRIVIAL(error) << "Malformed channel entry at line " << lines_read << "."; + continue; + } + // TODO(nkw): more sanity checking here. + +//Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { + tg = new Talkgroup(atoi(vec[0].c_str()), std::stod(vec[1]), std::stod(vec[2]), vec[3].c_str(), vec[4].c_str(), vec[5].c_str(), vec[6].c_str(),vec[7].c_str()); + + talkgroups.push_back(tg); + lines_pushed++; + } + + if (lines_pushed != lines_read) { + // The parser above is pretty brittle. This will help with debugging it, for + // now. + BOOST_LOG_TRIVIAL(error) << "Warning: skipped " << lines_read - lines_pushed << " of " << lines_read << " channel entries! Invalid format?"; + BOOST_LOG_TRIVIAL(error) << "The format is very particular. See https://github.com/robotastic/trunk-recorder for example input."; + } else { + BOOST_LOG_TRIVIAL(info) << "Read " << lines_pushed << " channels."; + } +} + Talkgroup *Talkgroups::find_talkgroup(long tg_number) { Talkgroup *tg_match = NULL; diff --git a/trunk-recorder/talkgroups.h b/trunk-recorder/talkgroups.h index 8c69a34c0..b7a34ca14 100644 --- a/trunk-recorder/talkgroups.h +++ b/trunk-recorder/talkgroups.h @@ -12,6 +12,7 @@ class Talkgroups { public: Talkgroups(); void load_talkgroups(std::string filename); + void load_channels(std::string filename); Talkgroup *find_talkgroup(long tg); void add(long num, std::string alphaTag); }; From d5255f6b5bbbeb5b10b274740d8953a9f223a0c5 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sat, 5 Mar 2022 10:00:27 -0500 Subject: [PATCH 04/10] Support for channels.csv --- docs/CONFIGURE.md | 13 +++- trunk-recorder/main.cc | 114 +++++++++++++++++++------------ trunk-recorder/systems/system.cc | 13 ++++ trunk-recorder/systems/system.h | 3 + trunk-recorder/talkgroup.cc | 9 +-- trunk-recorder/talkgroup.h | 7 +- trunk-recorder/talkgroups.cc | 33 ++++++--- trunk-recorder/talkgroups.h | 3 +- 8 files changed, 131 insertions(+), 64 deletions(-) diff --git a/docs/CONFIGURE.md b/docs/CONFIGURE.md index 6828d7f7e..8d7180bca 100644 --- a/docs/CONFIGURE.md +++ b/docs/CONFIGURE.md @@ -163,6 +163,7 @@ Here is a map of the different sections of the *config.json* file: | type | ✓ | | **"smartnet" "p25" "conventional, conventionalDMR"** or **"conventionalP25"** | The type of radio system. | | control_channels | ✓ | | array of numbers;
[496537500, 496437500] | **For trunked systems** The control channel frequencies for the system, in Hz. The frequencies will automatically be cycled through if the system moves to an alternate channel. | | channels | ✓ | | array of numbers;
[166725000, 166925000, 167075000, 166850000] | **For conventional systems** The channel frequencies, in Hz, used for the system. The channels get assigned a virtual talkgroup number based upon their position in the array. Squelch levels need to be specified for the Source(s) being used. | +| channelFile | ✓ | | string | **For conventional systems** The filename for a CSV file that provides information about the conventional channels. The format for the file is described below. Squelch levels need to be specified for the Source(s) being used. *Use channels or channelFile, not both*. | | modulation | | "qpsk" | **"qpsk"** or **"fsk4"** | The type of digital modulation that the system uses. You do not need to specify this with **conventionalDMR** systems. | | squelch | | -160 | number | Squelch in DB, this needs to be set for all conventional systems. The squelch setting is also used for analog talkgroups in a SmartNet system. I generally use -60 for my rtl-sdr. The closer the squelch is to 0, the stronger the signal has to be to unmute it. | | talkgroupsFile | | | string | The filename for a CSV file that provides information about the talkgroups. It determines whether a talkgroup is analog or digital, and what priority it should have. This file should be located in the same directory as the trunk-recorder executable. | @@ -178,7 +179,6 @@ Here is a map of the different sections of the *config.json* file: | analogLevels | | 8 | number (1-32) | The amount of amplification that will be applied to the analog audio. | | maxDev | | 4000 | number | Allows you to set the maximum deviation for analog channels. If you analog recordings sound good or if you have a completely digital system, then there is no need to touch this. | | digitalLevels | | 1 | number (1-16) | The amount of amplification that will be applied to the digital audio. | -| alphatags | | | array of strings
e.g.: ["police", "fire", "ems"] | [*For conventional systems*] these tags will be displayed in the log files to show what each frequency is used for. They will be applied to the *channels* in the order the values appear in the array. | | unitTagsFile | | | string | This is the filename of a CSV files that provides information about the unit tags. It allows a Unit ID to be assigned a name. This file should be located in the same directory as the trunk-recorder executable. The format is 2 columns, the first being the decimal number of the Unit ID, the second is the Unit Name, | | recordUnknown | | true | **true** / **false** | Record talkgroups if they are not listed in the Talkgroups File. | | hideEncrypted | | false | **true** / **false** | Hide encrypted talkgroups log entries | @@ -355,3 +355,14 @@ Here are the column headers and some sample data: |101 | 065 | D | DCFD 01 Disp | 01 Dispatch | Fire Dispatch | Fire | 1 | |2227 | 8b3 | D | DC StcarYard | Streetcar Yard | Transportation | Services | 3 | + + +## channelFile + +*Currently Tone base squelch is not supported.* + +| TG Number | Frequency | Tone | Alpha Tag | Description | Tag | Group | +| --------- | --------- | -------- | ------------- | ---------------------- | ------ | ------ | +| 300 | 462275000 | 94.8 PL | Town A Police | Town A Police Dispatch | Police | Town A | +| 325 | 462275000 | 151.4 PL | Town B DPW | Town B Trash Dispatch | DPW | Town B | + diff --git a/trunk-recorder/main.cc b/trunk-recorder/main.cc index 2e029c007..efc2af359 100755 --- a/trunk-recorder/main.cc +++ b/trunk-recorder/main.cc @@ -248,22 +248,13 @@ bool load_config(string config_file) { BOOST_LOG_TRIVIAL(info) << " " << format_freq(channel); system->add_channel(channel); } - - BOOST_LOG_TRIVIAL(info) << "Alpha Tags: "; - if (node.second.count("alphatags") != 0) { - int alphaIndex = 1; - BOOST_FOREACH (boost::property_tree::ptree::value_type &sub_node, node.second.get_child("alphatags")) { - std::string alphaTag = sub_node.second.get("", ""); - BOOST_LOG_TRIVIAL(info) << " " << alphaTag; - system->talkgroups->add(alphaIndex, alphaTag); - alphaIndex++; - } - } } else if (channel_file_exist) { std::string channel_file = node.second.get("channelFile"); BOOST_LOG_TRIVIAL(info) << "Channel File: " << channel_file; + system->set_channel_file(channel_file); } else { - + BOOST_LOG_TRIVIAL(error) << "Either \"channels\" or \"channelFile\" need to be defined for a conventional system!"; + return false; } // If it is a Trunked System } else if ((system->get_system_type() == "smartnet") || (system->get_system_type() == "p25")) { @@ -1242,49 +1233,37 @@ void monitor_messages() { } } -bool setup_systems() { - - Source *source = NULL; - - for (vector::iterator sys_it = systems.begin(); sys_it != systems.end(); sys_it++) { - System *system = *sys_it; - //bool source_found = false; - bool system_added = false; - if ((system->get_system_type() == "conventional") || (system->get_system_type() == "conventionalP25") || (system->get_system_type() == "conventionalDMR")) { - std::vector channels = system->get_channels(); - int tg_iterate_index = 0; - - for (vector::iterator chan_it = channels.begin(); chan_it != channels.end(); chan_it++) { - double channel = *chan_it; - ++tg_iterate_index; - bool channel_added = false; - +bool setup_convetional_channel(System *system, double frequency, long channel_index) { + bool channel_added = false; + Source *source = NULL; for (vector::iterator src_it = sources.begin(); src_it != sources.end(); src_it++) { source = *src_it; - if ((source->get_min_hz() <= channel) && (source->get_max_hz() >= channel)) { + if ((source->get_min_hz() <= frequency) && (source->get_max_hz() >= frequency)) { channel_added = true; if (system->get_squelch_db() == -160) { BOOST_LOG_TRIVIAL(error) << "[" << system->get_short_name() << "]\tSquelch needs to be specified for the Source for Conventional Systems"; - system_added = false; + return false; } else { - system_added = true; + channel_added = true; } - // This source can be used for this channel (and a squelch is set) - BOOST_LOG_TRIVIAL(info) << "[" << system->get_short_name() << "]\tMonitoring Conventional Channel: " << format_freq(channel) << " Talkgroup: " << tg_iterate_index; - Call_conventional *call = new Call_conventional(tg_iterate_index, channel, system, config); - Talkgroup *talkgroup = system->find_talkgroup(call->get_talkgroup()); - - if (talkgroup) { - call->set_talkgroup_tag(talkgroup->alpha_tag); + + + Call_conventional *call = NULL; + if (system->has_channel_file()) { + Talkgroup *tg = system->find_talkgroup_by_freq(frequency); + call = new Call_conventional(tg->number, tg->freq, system, config); + } else { + call = new Call_conventional(channel_index, frequency, system, config); + BOOST_LOG_TRIVIAL(info) << "[" << system->get_short_name() << "]\tMonitoring Conventional Channel: " << format_freq(frequency) << " Talkgroup: " << channel_index; + } - if (system->get_system_type() == "conventional") { analog_recorder_sptr rec; rec = source->create_conventional_recorder(tb); rec->start(call); - call->set_is_analog(true); + call->set_is_analog(true); call->set_recorder((Recorder *)rec.get()); call->set_state(RECORDING); system->add_conventional_recorder(rec); @@ -1315,11 +1294,56 @@ bool setup_systems() { break; } } - if (!channel_added) { - BOOST_LOG_TRIVIAL(error) << "[" << system->get_short_name() << "]\t Unable to find a source for this conventional channel! Channel not added: " << format_freq(channel) << " Talkgroup: " << tg_iterate_index; - //return false; - } + return channel_added; +} + + +bool setup_conventional_system(System *system) { + bool system_added = false; + + if (system->has_channel_file()) { + std::vector talkgroups = system->get_talkgroups(); + for (vector::iterator tg_it = talkgroups.begin(); tg_it != talkgroups.end(); tg_it++) { + Talkgroup *tg = *tg_it; + + bool channel_added = setup_convetional_channel(system, tg->freq, tg->number); + + if (!channel_added) { + BOOST_LOG_TRIVIAL(error) << "[" << system->get_short_name() << "]\t Unable to find a source for this conventional channel! Channel not added: " << format_freq(tg->freq) << " Talkgroup: " << tg->number; + //return false; + } else { + system_added = true; + } + } + } else { + std::vector channels = system->get_channels(); + int channel_index = 0; + for (vector::iterator chan_it = channels.begin(); chan_it != channels.end(); chan_it++) { + double channel = *chan_it; + ++channel_index; + bool channel_added = setup_convetional_channel(system, channel, channel_index); + + if (!channel_added) { + BOOST_LOG_TRIVIAL(error) << "[" << system->get_short_name() << "]\t Unable to find a source for this conventional channel! Channel not added: " << format_freq(channel) << " Talkgroup: " << channel_index; + //return false; + } else { + system_added = true; } + } + } + return system_added; +} + +bool setup_systems() { + + Source *source = NULL; + + for (vector::iterator sys_it = systems.begin(); sys_it != systems.end(); sys_it++) { + System *system = *sys_it; + //bool source_found = false; + bool system_added = false; + if ((system->get_system_type() == "conventional") || (system->get_system_type() == "conventionalP25") || (system->get_system_type() == "conventionalDMR")) { + system_added = setup_conventional_system(system); } else { // If it's not a conventional system, then it's a trunking system double control_channel_freq = system->get_current_control_channel(); diff --git a/trunk-recorder/systems/system.cc b/trunk-recorder/systems/system.cc index da222ee23..76d1f7092 100644 --- a/trunk-recorder/systems/system.cc +++ b/trunk-recorder/systems/system.cc @@ -259,6 +259,13 @@ void System::set_channel_file(std::string channel_file) { this->talkgroups->load_talkgroups(channel_file); } +bool System::has_channel_file() { + if (this->channel_file.length() > 0) { + return true; + } else { + return false; + } +} void System::set_talkgroups_file(std::string talkgroups_file) { BOOST_LOG_TRIVIAL(info) << "Loading Talkgroups..."; @@ -284,6 +291,9 @@ Talkgroup *System::find_talkgroup(long tg_number) { return talkgroups->find_talkgroup(tg_number); } +Talkgroup *System::find_talkgroup_by_freq(double freq) { + return talkgroups->find_talkgroup_by_freq(freq); +} UnitTag *System::find_unit_tag(long unitID) { return unit_tags->find_unit_tag(unitID); } @@ -292,6 +302,9 @@ std::vector System::get_channels() { return channels; } +std::vector System::get_talkgroups() { + return talkgroups->get_talkgroups(); +} int System::channel_count() { return channels.size(); } diff --git a/trunk-recorder/systems/system.h b/trunk-recorder/systems/system.h index d404c910c..ae5e91739 100644 --- a/trunk-recorder/systems/system.h +++ b/trunk-recorder/systems/system.h @@ -165,9 +165,11 @@ class System { Source *get_source(); void set_source(Source *); Talkgroup *find_talkgroup(long tg); + Talkgroup *find_talkgroup_by_freq(double freq); UnitTag *find_unit_tag(long unitID); void set_talkgroups_file(std::string); void set_channel_file(std::string channel_file); + bool has_channel_file(); void set_unit_tags_file(std::string); int control_channel_count(); void add_control_channel(double channel); @@ -183,6 +185,7 @@ class System { std::vector get_conventionalDMR_recorders(); std::vector get_channels(); std::vector get_control_channels(); + std::vector get_talkgroups(); System(int sys_id); void set_bandplan(std::string); std::string get_bandplan(); diff --git a/trunk-recorder/talkgroup.cc b/trunk-recorder/talkgroup.cc index 42137094e..f9b5e0520 100644 --- a/trunk-recorder/talkgroup.cc +++ b/trunk-recorder/talkgroup.cc @@ -9,20 +9,21 @@ Talkgroup::Talkgroup(long num, std::string mode, std::string alpha_tag, std::str this->group = group; this->priority = priority; this->active = false; - this->channel = 0; + this->freq = 0; this->tone = 0; } -Talkgroup::Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { +Talkgroup::Talkgroup(long num, double freq, double tone, std::string alpha_tag, std::string description, std::string tag, std::string group) { this->number = num; - this->mode = mode; + this->mode = "Z"; this->alpha_tag = alpha_tag; this->description = description; this->tag = tag; this->group = group; this->active = false; - this->channel = channel; + this->freq = freq; this->tone = tone; + this->priority = 0; } std::string Talkgroup::menu_string() { diff --git a/trunk-recorder/talkgroup.h b/trunk-recorder/talkgroup.h index a38bfab5a..058c15753 100644 --- a/trunk-recorder/talkgroup.h +++ b/trunk-recorder/talkgroup.h @@ -17,12 +17,13 @@ class Talkgroup { int priority; // For Conventional - double channel; + double freq; double tone; - Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group); - Talkgroup(long num, std::string m, std::string a, std::string d, std::string t, std::string g, int p); + Talkgroup(long num, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group, int priority); + Talkgroup(long num, double freq, double tone, std::string alpha_tag, std::string description, std::string tag, std::string group); + bool is_active(); int get_priority(); void set_priority(int new_priority); diff --git a/trunk-recorder/talkgroups.cc b/trunk-recorder/talkgroups.cc index b57cf3277..a17547482 100644 --- a/trunk-recorder/talkgroups.cc +++ b/trunk-recorder/talkgroups.cc @@ -160,11 +160,10 @@ void Talkgroups::load_channels(std::string filename) { // [0] - talkgroup number // [1] - channel freq // [2] - tone - // [3] - mode - // [4] - alpha_tag - // [5] - description - // [6] - tag - // [7] - group + // [3] - alpha_tag + // [4] - description + // [5] - tag + // [6] - group if (!((vec.size() == 8) || (vec.size() == 7))) { BOOST_LOG_TRIVIAL(error) << "Malformed channel entry at line " << lines_read << "."; @@ -172,8 +171,8 @@ void Talkgroups::load_channels(std::string filename) { } // TODO(nkw): more sanity checking here. -//Talkgroup(long num, double channel, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { - tg = new Talkgroup(atoi(vec[0].c_str()), std::stod(vec[1]), std::stod(vec[2]), vec[3].c_str(), vec[4].c_str(), vec[5].c_str(), vec[6].c_str(),vec[7].c_str()); +//Talkgroup(long num, double freq, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { + tg = new Talkgroup(atoi(vec[0].c_str()), std::stod(vec[1]), std::stod(vec[2]), vec[3].c_str(), vec[4].c_str(), vec[5].c_str(), vec[6].c_str()); talkgroups.push_back(tg); lines_pushed++; @@ -203,7 +202,21 @@ Talkgroup *Talkgroups::find_talkgroup(long tg_number) { return tg_match; } -void Talkgroups::add(long num, std::string alphaTag) { - Talkgroup *tg = new Talkgroup(num, "X", alphaTag, "", "", "", 0); - talkgroups.push_back(tg); +Talkgroup *Talkgroups::find_talkgroup_by_freq(double freq) { + Talkgroup *tg_match = NULL; + + for (std::vector::iterator it = talkgroups.begin(); it != talkgroups.end(); ++it) { + Talkgroup *tg = (Talkgroup *)*it; + + if (tg->freq == freq) { + tg_match = tg; + break; + } + } + return tg_match; } + +std::vector Talkgroups::get_talkgroups() { + return talkgroups; +} + diff --git a/trunk-recorder/talkgroups.h b/trunk-recorder/talkgroups.h index b7a34ca14..a820c58e5 100644 --- a/trunk-recorder/talkgroups.h +++ b/trunk-recorder/talkgroups.h @@ -14,6 +14,7 @@ class Talkgroups { void load_talkgroups(std::string filename); void load_channels(std::string filename); Talkgroup *find_talkgroup(long tg); - void add(long num, std::string alphaTag); + Talkgroup *find_talkgroup_by_freq(double freq); + std::vector get_talkgroups(); }; #endif // TALKGROUPS_H From d24baa3ebacb77c1eb441ab9b9439a90c2cb1db6 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 6 Mar 2022 09:49:13 -0500 Subject: [PATCH 05/10] Examples --- examples/channel.csv | 22 ++++++++++++++++++++++ examples/config-frs-single.json | 25 +++++++++++++++++++++++++ examples/config-frs.json | 22 ++++++++++++++++++++++ trunk-recorder/systems/system.cc | 2 +- trunk-recorder/talkgroups.cc | 4 ++-- 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 examples/channel.csv create mode 100644 examples/config-frs-single.json create mode 100644 examples/config-frs.json diff --git a/examples/channel.csv b/examples/channel.csv new file mode 100644 index 000000000..fae40d083 --- /dev/null +++ b/examples/channel.csv @@ -0,0 +1,22 @@ +1,462562500,0,FRS 01,Channel 1,FMN,Other +2,462587500,0,FRS 02,Channel 2,FMN,Other +3,462612500,0,FRS 03,Channel 3,FMN,Other +4,462637500,0,FRS 04,Channel 4,FMN,Other +5,462662500,0,FRS 05,Channel 5,FMN,Other +6,462687500,0,FRS 06,Channel 6,FMN,Other +7,462712500,0,FRS 07,Channel 7,FMN,Other +8,467562500,0,FRS 08,Channel 8,FMN,Other +9,467587500,0,FRS 09,Channel 9,FMN,Other +10,467612500,0,FRS 10,Channel 10,FMN,Other +11,467637500,0,FRS 11,Channel 11,FMN,Other +12,467662500,0,FRS 12,Channel 12,FMN,Other +13,467687500,0,FRS 13,Channel 13,FMN,Other +14,467712500,0,FRS 14,Channel 14,FMN,Other +15,462550000,0,FRS 15,Channel 15,FMN,Other +16,462575000,0,FRS 16,Channel 16,FMN,Other +17,462600000,0,FRS 17,Channel 17,FMN,Other +18,462625000,0,FRS 18,Channel 18,FMN,Other +19,462650000,0,FRS 19,Channel 19,FMN,Other +20,462675000,0,FRS 20,Channel 20,FMN,Other +21,462700000,0,FRS 21,Channel 21,FMN,Other +22,462725000,0,FRS 22,Channel 22,FMN,Other \ No newline at end of file diff --git a/examples/config-frs-single.json b/examples/config-frs-single.json new file mode 100644 index 000000000..702229a92 --- /dev/null +++ b/examples/config-frs-single.json @@ -0,0 +1,25 @@ +{ + "ver": 2, + "sources": [{ + "center": 462500000, + "rate": 2048000, + "error": 0, + "gain": 36, + "debugRecorders": 0, + "digitalRecoders": 5, + "driver": "osmosdr" + } + ], + "systems": [ + { + "type": "conventional", + "channels": [462562500 ], + "callLog": true, + "digitalLevels": 14, + "squelch": -20 + } + ], + "controlWarnRate": 5, + "callTimeout": 0.75 + + } diff --git a/examples/config-frs.json b/examples/config-frs.json new file mode 100644 index 000000000..2fdf99ed5 --- /dev/null +++ b/examples/config-frs.json @@ -0,0 +1,22 @@ +{ + "ver": 2, + "sources": [{ + "center": 462500000, + "rate": 2048000, + "error": 0, + "gain": 36, + "debugRecorders": 0, + "digitalRecoders": 0, + "driver": "osmosdr" + } + ], + "systems": [ + { + "type": "conventional", + "channelFile": "channel.csv", + "callLog": true, + "squelch": -20 + } + ] + + } diff --git a/trunk-recorder/systems/system.cc b/trunk-recorder/systems/system.cc index 76d1f7092..f6ff67e95 100644 --- a/trunk-recorder/systems/system.cc +++ b/trunk-recorder/systems/system.cc @@ -256,7 +256,7 @@ std::string System::get_unit_tags_file() { void System::set_channel_file(std::string channel_file) { BOOST_LOG_TRIVIAL(info) << "Loading Talkgroups..."; this->channel_file = channel_file; - this->talkgroups->load_talkgroups(channel_file); + this->talkgroups->load_channels(channel_file); } bool System::has_channel_file() { diff --git a/trunk-recorder/talkgroups.cc b/trunk-recorder/talkgroups.cc index a17547482..1d1f3edd0 100644 --- a/trunk-recorder/talkgroups.cc +++ b/trunk-recorder/talkgroups.cc @@ -165,8 +165,8 @@ void Talkgroups::load_channels(std::string filename) { // [5] - tag // [6] - group - if (!((vec.size() == 8) || (vec.size() == 7))) { - BOOST_LOG_TRIVIAL(error) << "Malformed channel entry at line " << lines_read << "."; + if (vec.size() != 7) { + BOOST_LOG_TRIVIAL(error) << "Malformed channel entry at line " << lines_read << ". Found: " << vec.size() << " Expected 7"; continue; } // TODO(nkw): more sanity checking here. From 3f10740e142afd1a45082cf08fe0de30a97de326 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 6 Mar 2022 10:24:18 -0500 Subject: [PATCH 06/10] Update nonstop_wavfile_sink_impl.cc --- lib/gr_blocks/nonstop_wavfile_sink_impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gr_blocks/nonstop_wavfile_sink_impl.cc b/lib/gr_blocks/nonstop_wavfile_sink_impl.cc index fec116d1f..90f9cbc80 100644 --- a/lib/gr_blocks/nonstop_wavfile_sink_impl.cc +++ b/lib/gr_blocks/nonstop_wavfile_sink_impl.cc @@ -267,7 +267,7 @@ void nonstop_wavfile_sink_impl::stop_recording() { } if (state == RECORDING) { - BOOST_LOG_TRIVIAL(error) << "stop_recording() - stopping wavfile sink but recorder state is: " << state << std::endl; + BOOST_LOG_TRIVIAL(trace) << "stop_recording() - stopping wavfile sink but recorder state is: " << state << std::endl; } d_current_call = NULL; d_first_work = true; From 79e50e2d0f426f3a25db5893cd33df333c35e63d Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 6 Mar 2022 10:26:03 -0500 Subject: [PATCH 07/10] cleaned up formatting --- examples/config-frs-single.json | 41 ++++++++++++++++----------------- examples/config-frs.json | 34 +++++++++++++-------------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/examples/config-frs-single.json b/examples/config-frs-single.json index 702229a92..c338c3eca 100644 --- a/examples/config-frs-single.json +++ b/examples/config-frs-single.json @@ -1,25 +1,24 @@ { "ver": 2, - "sources": [{ - "center": 462500000, - "rate": 2048000, - "error": 0, - "gain": 36, - "debugRecorders": 0, - "digitalRecoders": 5, - "driver": "osmosdr" - } + "sources": [ + { + "center": 462500000, + "rate": 2048000, + "error": 0, + "gain": 36, + "debugRecorders": 0, + "digitalRecoders": 0, + "driver": "osmosdr" + } ], "systems": [ - { - "type": "conventional", - "channels": [462562500 ], - "callLog": true, - "digitalLevels": 14, - "squelch": -20 - } - ], - "controlWarnRate": 5, - "callTimeout": 0.75 - - } + { + "type": "conventional", + "channels": [ + 462562500 + ], + "callLog": true, + "squelch": -20 + } + ] +} \ No newline at end of file diff --git a/examples/config-frs.json b/examples/config-frs.json index 2fdf99ed5..6508ea63f 100644 --- a/examples/config-frs.json +++ b/examples/config-frs.json @@ -1,22 +1,22 @@ { "ver": 2, - "sources": [{ - "center": 462500000, - "rate": 2048000, - "error": 0, - "gain": 36, - "debugRecorders": 0, - "digitalRecoders": 0, - "driver": "osmosdr" - } + "sources": [ + { + "center": 462500000, + "rate": 2048000, + "error": 0, + "gain": 36, + "debugRecorders": 0, + "digitalRecoders": 0, + "driver": "osmosdr" + } ], "systems": [ - { - "type": "conventional", - "channelFile": "channel.csv", - "callLog": true, - "squelch": -20 - } + { + "type": "conventional", + "channelFile": "channel.csv", + "callLog": true, + "squelch": -20 + } ] - - } +} \ No newline at end of file From be65dee59c8e4c8d873d84e7ce2ff4c9cb0c1926 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sat, 12 Mar 2022 09:58:21 -0500 Subject: [PATCH 08/10] Channel Enable column supported --- docs/CONFIGURE.md | 14 +++++++++----- examples/channel.csv | 2 +- trunk-recorder/talkgroups.cc | 18 +++++++++++++++--- trunk-recorder/talkgroups.h | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/CONFIGURE.md b/docs/CONFIGURE.md index 8d7180bca..3067b6f4e 100644 --- a/docs/CONFIGURE.md +++ b/docs/CONFIGURE.md @@ -359,10 +359,14 @@ Here are the column headers and some sample data: ## channelFile -*Currently Tone base squelch is not supported.* +This file allows for you to specify additional information about conventional channels. A recorder is started for each line in the file and set the to frequency specified. The type of recorder is based on the type of System. A *Conventional* system would have Analog Recorders, while a *ConventionalP25* or *ConventionalDMR* would have digital recorders. -| TG Number | Frequency | Tone | Alpha Tag | Description | Tag | Group | -| --------- | --------- | -------- | ------------- | ---------------------- | ------ | ------ | -| 300 | 462275000 | 94.8 PL | Town A Police | Town A Police Dispatch | Police | Town A | -| 325 | 462275000 | 151.4 PL | Town B DPW | Town B Trash Dispatch | DPW | Town B | +*Tone based squelch is currently not supported.* + +The **Enable** Column is optional and defaults to *True*. It only needs to be added to rows that you do not want to have recorded. For those rows, set **Enable** to *False*. + +| TG Number | Frequency | Tone | Alpha Tag | Description | Tag | Group | Enable (*optional*) | +| --------- | --------- | -------- | ------------- | ---------------------- | ------ | ------ | ------------------- | +| 300 | 462275000 | 94.8 PL | Town A Police | Town A Police Dispatch | Police | Town A | | +| 325 | 462275000 | 151.4 PL | Town B DPW | Town B Trash Dispatch | DPW | Town B | False | diff --git a/examples/channel.csv b/examples/channel.csv index fae40d083..d3d6d3788 100644 --- a/examples/channel.csv +++ b/examples/channel.csv @@ -4,7 +4,7 @@ 4,462637500,0,FRS 04,Channel 4,FMN,Other 5,462662500,0,FRS 05,Channel 5,FMN,Other 6,462687500,0,FRS 06,Channel 6,FMN,Other -7,462712500,0,FRS 07,Channel 7,FMN,Other +7,462712500,0,FRS 07,Channel 7,FMN,Other, False 8,467562500,0,FRS 08,Channel 8,FMN,Other 9,467587500,0,FRS 09,Channel 9,FMN,Other 10,467612500,0,FRS 10,Channel 10,FMN,Other diff --git a/trunk-recorder/talkgroups.cc b/trunk-recorder/talkgroups.cc index 1d1f3edd0..3a83fd112 100644 --- a/trunk-recorder/talkgroups.cc +++ b/trunk-recorder/talkgroups.cc @@ -128,7 +128,8 @@ void Talkgroups::load_channels(std::string filename) { return; } - boost::char_separator sep(",\t"); + + boost::char_separator sep(",", "\t", boost::keep_empty_tokens); typedef boost::tokenizer> t_tokenizer; std::vector vec; @@ -164,18 +165,29 @@ void Talkgroups::load_channels(std::string filename) { // [4] - description // [5] - tag // [6] - group + // [7] - enable (Optional, default is True) - if (vec.size() != 7) { - BOOST_LOG_TRIVIAL(error) << "Malformed channel entry at line " << lines_read << ". Found: " << vec.size() << " Expected 7"; + if ((vec.size() != 7) && (vec.size() != 8)) { + BOOST_LOG_TRIVIAL(error) << "Malformed channel entry at line " << lines_read << ". Found: " << vec.size() << " Expected 7 or 8"; continue; } // TODO(nkw): more sanity checking here. + bool enable = true; + if (vec.size() == 8 ) { + boost::trim(vec[7]); + if (boost::iequals(vec[7], "false")) { + enable = false; + } + } //Talkgroup(long num, double freq, double tone, std::string mode, std::string alpha_tag, std::string description, std::string tag, std::string group) { + if (enable) { tg = new Talkgroup(atoi(vec[0].c_str()), std::stod(vec[1]), std::stod(vec[2]), vec[3].c_str(), vec[4].c_str(), vec[5].c_str(), vec[6].c_str()); talkgroups.push_back(tg); + } lines_pushed++; + } if (lines_pushed != lines_read) { diff --git a/trunk-recorder/talkgroups.h b/trunk-recorder/talkgroups.h index a820c58e5..4a8a21348 100644 --- a/trunk-recorder/talkgroups.h +++ b/trunk-recorder/talkgroups.h @@ -2,7 +2,7 @@ #define TALKGROUPS_H #include "talkgroup.h" - +#include #include #include From b33935887cf9da0cf564991b0059c89268cfb292 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 20 Mar 2022 09:04:28 -0400 Subject: [PATCH 09/10] updated version --- CHANGELOG.md | 7 ++++++- CMakeLists.txt | 2 +- README.md | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8786f7999..28a03a44b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Trunk Recorder ChangeLog ======================== -### Version 4.3.0 + +### Version 4.4.0 +* Add support for using a .csv file for adding for conventional channels +* Removed alphaTags option from config + +### Version 4.3.0 * Add support for DMR / MotoTRBO diff --git a/CMakeLists.txt b/CMakeLists.txt index ac264449e..662a03e62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) # Set the version information here set(VERSION_MAJOR 4) -set(VERSION_API 3) +set(VERSION_API 4) set(VERSION_ABI 0) set(VERSION_PATCH git) diff --git a/README.md b/README.md index dac3ff2c2..d15d67ac1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Trunk Recorder - v4.3.1 +Trunk Recorder - v4.3.2 ======================= ## Sponsors From 447e8327ca0f22c6d49c2b89913a2189f43f5ccf Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Sun, 20 Mar 2022 09:04:55 -0400 Subject: [PATCH 10/10] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28a03a44b..052befe6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Trunk Recorder ChangeLog ### Version 4.4.0 * Add support for using a .csv file for adding for conventional channels -* Removed alphaTags option from config +* Removed alphaTags support from config.json, use the channel.csv instead ### Version 4.3.0 * Add support for DMR / MotoTRBO