Merge pull request #126 from sh123/module_frn
Add support for FRN - The Free Radio Network The new module Frn add support for the [Free Radio Network](http://www.freeradionetwork.eu/).
This commit is contained in:
commit
852c32e1b9
|
|
@ -25,6 +25,7 @@ either C++ or TCL. Examples of modules are:
|
|||
* *PropagationMonitor* -- Announce propagation warnings from dxmaps.com
|
||||
* *SelCall* -- Send selective calling sequences by entering DTMF codes
|
||||
* *MetarInformation* -- Play airport weather information
|
||||
* *Frn* -- Connect to Free Radio Network (FRN) servers
|
||||
|
||||
== Qtel ==
|
||||
Qtel, the Qt EchoLink client, is a graphical application used to access the
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ set(MAN_PAGES svxlink.1 remotetrx.1 siglevdetcal.1 svxlink.conf.5
|
|||
ModuleHelp.conf.5 remotetrx.conf.5 ModuleParrot.conf.5 ModuleEchoLink.conf.5
|
||||
ModuleTclVoiceMail.conf.5 ModuleDtmfRepeater.conf.5
|
||||
ModulePropagationMonitor.conf.5 ModuleSelCallEnc.conf.5 qtel.1 devcal.1
|
||||
ModuleFrn.conf.5
|
||||
)
|
||||
|
||||
# Search for the gzip and groff programs. Error out if not found.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
.TH MODULEFRN.CONF 5 "MARS 2013" Linux "File Formats"
|
||||
.
|
||||
.SH NAME
|
||||
.
|
||||
ModuleFrn.conf \- Configuration file for the SvxLink server
|
||||
Free Radio Network (FRN) module
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.
|
||||
.B svxlink
|
||||
is a general purpose voice service system for ham radio use. This man-page
|
||||
describe the SvxLink server configuration for the FRN module.
|
||||
.P
|
||||
The FRN module is used to connect to Free Radio Network (FRN) servers.
|
||||
.
|
||||
.SH CONFIGURATION VARIABLES
|
||||
.
|
||||
There are a couple of configuration variables that are common to all modules.
|
||||
The documentation for these can be found in the
|
||||
.BR svxlink.conf (5)
|
||||
manual page.
|
||||
.P
|
||||
Here is the description of all module specific configuration
|
||||
variables that the SvxLink FRN module understands.
|
||||
.
|
||||
.SS ModuleFrn
|
||||
.
|
||||
.TP
|
||||
.B SERVER
|
||||
Free Radio Network (FRN) server hostname or ip address, you can find a list
|
||||
of active servers using FRN desktop application or at
|
||||
http://freeradionetwork.eu web page.
|
||||
.TP
|
||||
.B PORT
|
||||
FRN server port number
|
||||
.TP
|
||||
.B VERSION
|
||||
Identify itself as a FRN client with this version.
|
||||
.TP
|
||||
.B EMAIL_ADDRESS
|
||||
Your email address, which was used during registration.
|
||||
.TP
|
||||
.B DYN_PASSWORD
|
||||
Password provided to you by FRN on registration.
|
||||
.TP
|
||||
.B CLIENT_TYPE
|
||||
Type of the client:
|
||||
.br
|
||||
0 - crosslink
|
||||
.br
|
||||
1 - gateway
|
||||
.br
|
||||
2 - pc only
|
||||
|
||||
Read more at http://freeradionetwork.eu/frnprotocol.htm web page.
|
||||
.TP
|
||||
.B CALLSIGN_AND_USER
|
||||
Your callsign and user name used during registration, for example "1234, Bob".
|
||||
.TP
|
||||
.B BAND_AND_CHANNEL
|
||||
In case of PC only user, this is 'PC Only'.
|
||||
In case of a crosslink this is 'Crosslink'.
|
||||
In case of a gateway this is the frequancy in full Mhz and kHz seperated by a
|
||||
'.' (dot), followed by the mode 'FM', 'AM' or 'DIG', followed by a space and
|
||||
the squelch type 'CTC', 'DSC' or 'NONE' followed by the CTC frequency or ID
|
||||
code.
|
||||
|
||||
Examples:
|
||||
.br
|
||||
446.03125FM CTC131.8
|
||||
.br
|
||||
28.12500AM
|
||||
.br
|
||||
145.43750FM DCS828
|
||||
.br
|
||||
446.02625DIG CID123
|
||||
.TP
|
||||
.B DESCRIPTION
|
||||
Custom extra information about the node.
|
||||
.TP
|
||||
.B COUNTRY
|
||||
Full name of the country.
|
||||
.TP
|
||||
.B CITY_CITY_PART
|
||||
The city and the part of the city, or the part of the country and the city
|
||||
divided by a minus sign, for example "City \- Street".
|
||||
.TP
|
||||
.B NET
|
||||
Network (room) where to connect, each server holds up to 32 networks (rooms).
|
||||
.TP
|
||||
.B FRN_DEBUG
|
||||
Define to enable extensive module logging, including FRN request and response
|
||||
printout.
|
||||
.TP
|
||||
.B DISABLE_RF
|
||||
Define to disable sending voice from FRN to RIG. Nothing will be transmitted
|
||||
by radio (could be switched ON by DTMF command later if needed)
|
||||
.
|
||||
.SH FRN NEW USER REGISTRATION
|
||||
To register at FRN service it is possible to use native desktop client or just
|
||||
register using netcat:
|
||||
|
||||
echo 'IG:\\
|
||||
.br
|
||||
<ON>[CallsignAndUser]</ON>\\
|
||||
.br
|
||||
<EA>[EMailAddress]</EA>\\
|
||||
.br
|
||||
<BC>[BandAndChannel]</BC>\\
|
||||
.br
|
||||
<DS>[Description]</DS>\\
|
||||
.br
|
||||
<NN>[Country]</NN>\\
|
||||
.br
|
||||
<CT>[CityCityPart]</CT>' | nc -v sysman.freeradionetwork.eu 10025
|
||||
|
||||
FRN System Manager should return OK in case of successful registration.
|
||||
|
||||
More information about new user registration is available at
|
||||
http://freeradionetwork.eu/frnprotocol.htm
|
||||
.
|
||||
.SH FILES
|
||||
.
|
||||
.TP
|
||||
.IR /etc/svxlink/svxlink.conf " (or deprecated " /etc/svxlink.conf ")"
|
||||
The system wide configuration file.
|
||||
.TP
|
||||
.IR ~/.svxlink/svxlink.conf
|
||||
Per user configuration file.
|
||||
.TP
|
||||
.I /etc/svxlink/svxlink.d/ModuleFrn.conf
|
||||
Global modularized configuration file. Depends on the CFG_DIR configuration
|
||||
variable setting.
|
||||
.TP
|
||||
.I ~/.svxlink/svxlink.d/ModuleFrn.conf
|
||||
Per user modularized configuration file. Depends on the CFG_DIR configuration
|
||||
variable setting.
|
||||
.
|
||||
.SH AUTHOR
|
||||
.
|
||||
Tobias Blomberg (SM0SVX) <sm0svx at users dot sourceforge dot net>
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
.
|
||||
.BR svxlink.conf (5)
|
||||
|
|
@ -8,3 +8,4 @@ add_subdirectory(dtmf_repeater)
|
|||
add_subdirectory(metarinfo)
|
||||
add_subdirectory(propagation_monitor)
|
||||
add_subdirectory(selcallenc)
|
||||
add_subdirectory(frn)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
/makefile.root
|
||||
/depend
|
||||
/Makefile
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# The name of the module without the Module prefix
|
||||
set(MODNAME Frn)
|
||||
|
||||
# Module source code
|
||||
set(MODSRC QsoFrn.cpp Utils.cpp)
|
||||
|
||||
# Project libraries to link to
|
||||
#set(LIBS ${LIBS} echolib)
|
||||
|
||||
# Find the GSM codec library and include directory
|
||||
find_package(GSM REQUIRED)
|
||||
if(NOT GSM_FOUND)
|
||||
message(FATAL_ERROR "libgsm not found")
|
||||
endif(NOT GSM_FOUND)
|
||||
include_directories(${GSM_INCLUDE_DIR})
|
||||
set(LIBS ${LIBS} ${GSM_LIBRARY})
|
||||
|
||||
# The version tag name without the VER_ prefix
|
||||
set(VERNAME ${MODNAME})
|
||||
string(REGEX REPLACE \(.\)\([A-Z]\) \\1_\\2 VERNAME ${VERNAME})
|
||||
string(TOUPPER MODULE_${VERNAME} VERNAME)
|
||||
|
||||
# Add targets for version files
|
||||
set(VERSION_DEPENDS)
|
||||
add_version_target(${VERNAME} VERSION_DEPENDS)
|
||||
add_version_target(SVXLINK VERSION_DEPENDS)
|
||||
|
||||
# Build the plugin
|
||||
add_library(Module${MODNAME} MODULE Module${MODNAME}.cpp ${MODSRC}
|
||||
${VERSION_DEPENDS}
|
||||
)
|
||||
set_target_properties(Module${MODNAME} PROPERTIES PREFIX "")
|
||||
target_link_libraries(Module${MODNAME} ${LIBS})
|
||||
|
||||
# Install targets
|
||||
install(TARGETS Module${MODNAME} DESTINATION ${SVX_MODULE_INSTALL_DIR})
|
||||
install(FILES ${MODNAME}.tcl DESTINATION ${SVX_SHARE_INSTALL_DIR}/events.d)
|
||||
install_if_not_exists(Module${MODNAME}.conf
|
||||
${SVX_SYSCONF_INSTALL_DIR}/svxlink.d
|
||||
)
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
###############################################################################
|
||||
#
|
||||
# Frn module event handlers
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
#
|
||||
# This is the namespace in which all functions and variables below will exist.
|
||||
# The name must match the configuration variable "NAME" in the
|
||||
# [ModuleFrn] section in the configuration file. The name may be changed
|
||||
# but it must be changed in both places.
|
||||
#
|
||||
namespace eval Frn {
|
||||
|
||||
#
|
||||
# Check if this module is loaded in the current logic core
|
||||
#
|
||||
if {![info exists CFG_ID]} {
|
||||
return;
|
||||
}
|
||||
|
||||
#
|
||||
# Extract the module name from the current namespace
|
||||
#
|
||||
set module_name [namespace tail [namespace current]];
|
||||
|
||||
|
||||
#
|
||||
# An "overloaded" playMsg that eliminates the need to write the module name
|
||||
# as the first argument.
|
||||
#
|
||||
proc playMsg {msg} {
|
||||
variable module_name;
|
||||
::playMsg $module_name $msg;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# A convenience function for printing out information prefixed by the
|
||||
# module name
|
||||
#
|
||||
proc printInfo {msg} {
|
||||
variable module_name;
|
||||
puts "$module_name: $msg";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when this module is being activated
|
||||
#
|
||||
proc activating_module {} {
|
||||
variable module_name;
|
||||
Module::activating_module $module_name;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when this module is being deactivated.
|
||||
#
|
||||
proc deactivating_module {} {
|
||||
variable module_name;
|
||||
Module::deactivating_module $module_name;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the inactivity timeout for this module has expired.
|
||||
#
|
||||
proc timeout {} {
|
||||
variable module_name;
|
||||
Module::timeout $module_name;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when playing of the help message for this module has been requested.
|
||||
#
|
||||
proc play_help {} {
|
||||
variable module_name;
|
||||
Module::play_help $module_name;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the state of this module should be reported on the radio
|
||||
# channel. Typically this is done when a manual identification has been
|
||||
# triggered by the user by sending a "*".
|
||||
# This function will only be called if this module is active.
|
||||
#
|
||||
proc status_report {} {
|
||||
printInfo "status_report called...";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when an entered command failed or have bad syntax.
|
||||
#
|
||||
proc command_failed {cmd} {
|
||||
spellWord $cmd;
|
||||
playMsg "operation_failed";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when an unrecognized command has been received.
|
||||
#
|
||||
proc unknown_command {cmd} {
|
||||
spellWord $cmd;
|
||||
playMsg "unknown_command";
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when command to count nodes on the channel is called
|
||||
#
|
||||
proc count_clients {count_clients} {
|
||||
playNumber $count_clients;
|
||||
playSilence 50;
|
||||
playMsg "connected_clients";
|
||||
playSilence 250;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Executed when the rf disable feature is activated or deactivated
|
||||
# status - The current status of the feature (0=deactivated, 1=activated)
|
||||
# activate - The requested new status of the feature
|
||||
# (0=deactivate, 1=activate)
|
||||
#
|
||||
proc rf_disable {status activate} {
|
||||
variable module_name;
|
||||
puts "$module_name: [expr {$activate ? "Activating" : "Deactivating"}]\
|
||||
listen only mode.";
|
||||
playMsg [expr {$activate ? "activating" : "deactivating"}];
|
||||
playMsg "rf_disable";
|
||||
}
|
||||
|
||||
# end of namespace
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# This file has not been truncated
|
||||
#
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
[ModuleFrn]
|
||||
NAME=Frn
|
||||
PLUGIN_NAME=Frn
|
||||
ID=7
|
||||
TIMEOUT=300
|
||||
|
||||
# Details http://freeradionetwork.eu/frnprotocol.htm
|
||||
SERVER=127.0.0.1
|
||||
PORT=10024
|
||||
VERSION=2014000
|
||||
EMAIL_ADDRESS=your@example.com
|
||||
DYN_PASSWORD=12345
|
||||
CLIENT_TYPE=1
|
||||
CALLSIGN_AND_USER="callsign, user"
|
||||
BAND_AND_CHANNEL="446.03125FM CTC131.8"
|
||||
DESCRIPTION="SvxLink FreeRadioNetwork Station"
|
||||
COUNTRY=Antarctica
|
||||
CITY_CITY_PART="City - Street"
|
||||
NET=Test
|
||||
|
||||
#FRN_DEBUG=1
|
||||
#DISABLE_RF=1
|
||||
|
|
@ -0,0 +1,490 @@
|
|||
/**
|
||||
@file ModuleFrn.cpp
|
||||
@brief Free Radio Network (FRN) module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
\verbatim
|
||||
A module (plugin) for the multi purpose tranciever frontend system.
|
||||
Copyright (C) 2004 Tobias Blomberg
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* System Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Project Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <AsyncConfig.h>
|
||||
#include <AsyncAudioSplitter.h>
|
||||
#include <AsyncAudioValve.h>
|
||||
#include <AsyncAudioSelector.h>
|
||||
#include <AsyncAudioFifo.h>
|
||||
#include <AsyncAudioJitterFifo.h>
|
||||
#include <AsyncAudioDecimator.h>
|
||||
#include <AsyncAudioInterpolator.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <version/MODULE_FRN.h>
|
||||
#include "ModuleFrn.h"
|
||||
#include "multirate_filter_coeff.h"
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Namespaces to use
|
||||
*
|
||||
****************************************************************************/
|
||||
using namespace std;
|
||||
using namespace Async;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Defines & typedefs
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local class definitions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Prototypes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Exported Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Pure C-functions
|
||||
*
|
||||
****************************************************************************/
|
||||
extern "C" {
|
||||
Module *module_init(void *dl_handle, Logic *logic, const char *cfg_name)
|
||||
{
|
||||
return new ModuleFrn(dl_handle, logic, cfg_name);
|
||||
}
|
||||
} /* extern "C" */
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Public member functions
|
||||
*
|
||||
****************************************************************************/
|
||||
ModuleFrn::ModuleFrn(void *dl_handle, Logic *logic, const string& cfg_name)
|
||||
: Module(dl_handle, logic, cfg_name)
|
||||
, qso(0)
|
||||
, audio_valve(0)
|
||||
, audio_splitter(0)
|
||||
, audio_selector(0)
|
||||
{
|
||||
cout << "\tModule Frn v" MODULE_FRN_VERSION " starting...\n";
|
||||
|
||||
} /* ModuleFrn */
|
||||
|
||||
|
||||
ModuleFrn::~ModuleFrn(void)
|
||||
{
|
||||
moduleCleanup();
|
||||
} /* ~ModuleFrn */
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Protected member functions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
*------------------------------------------------------------------------
|
||||
* Method:
|
||||
* Purpose:
|
||||
* Input:
|
||||
* Output:
|
||||
* Author:
|
||||
* Created:
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Private member functions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: initialize
|
||||
* Purpose: Called by the core system right after the object has been
|
||||
* constructed. As little of the initialization should be done in
|
||||
* the constructor. It's easier to handle errors here.
|
||||
* Input: None
|
||||
* Output: Return \em true on success or else \em false should be returned
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2005-08-28
|
||||
* Remarks: The base class initialize method must be called from here.
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
bool ModuleFrn::initialize(void)
|
||||
{
|
||||
if (!Module::initialize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qso = new QsoFrn(this);
|
||||
qso->error.connect(
|
||||
mem_fun(*this, &ModuleFrn::onQsoError));
|
||||
|
||||
// rig/mic -> frn
|
||||
audio_valve = new AudioValve;
|
||||
audio_splitter = new AudioSplitter;
|
||||
|
||||
AudioSink::setHandler(audio_valve);
|
||||
audio_valve->registerSink(audio_splitter);
|
||||
#if INTERNAL_SAMPLE_RATE == 16000
|
||||
AudioDecimator *down_sampler = new AudioDecimator(
|
||||
2, coeff_16_8, coeff_16_8_taps);
|
||||
audio_splitter->addSink(down_sampler, true);
|
||||
down_sampler->registerSink(qso);
|
||||
#else
|
||||
audio_splitter->addSink(qso);
|
||||
#endif
|
||||
|
||||
// frn -> rig/speaker
|
||||
audio_selector = new AudioSelector;
|
||||
audio_fifo = new Async::AudioFifo(100 * 320 * 5);
|
||||
|
||||
#if INTERNAL_SAMPLE_RATE == 16000
|
||||
AudioInterpolator *up_sampler = new AudioInterpolator(
|
||||
2, coeff_16_8, coeff_16_8_taps);
|
||||
qso->registerSink(up_sampler, true);
|
||||
audio_selector->addSource(up_sampler);
|
||||
audio_selector->enableAutoSelect(up_sampler, 0);
|
||||
#else
|
||||
audio_selector->addSource(qso);
|
||||
audio_selector->enableAutoSelect(qso, 0);
|
||||
#endif
|
||||
audio_fifo->registerSource(audio_selector);
|
||||
AudioSource::setHandler(audio_fifo);
|
||||
|
||||
if (!qso->initOk())
|
||||
{
|
||||
delete qso;
|
||||
cerr << "*** ERROR: Creation of Qso object failed\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} /* initialize */
|
||||
|
||||
|
||||
void ModuleFrn::moduleCleanup()
|
||||
{
|
||||
AudioSource::clearHandler();
|
||||
audio_fifo->unregisterSource();
|
||||
|
||||
audio_splitter->removeSink(qso);
|
||||
audio_valve->unregisterSink();
|
||||
AudioSink::clearHandler();
|
||||
|
||||
delete qso;
|
||||
qso = 0;
|
||||
|
||||
delete audio_fifo;
|
||||
audio_fifo = 0;
|
||||
|
||||
delete audio_splitter;
|
||||
audio_splitter = 0;
|
||||
|
||||
delete audio_valve;
|
||||
audio_valve = 0;
|
||||
|
||||
delete audio_selector;
|
||||
audio_selector = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: activateInit
|
||||
* Purpose: Called by the core system when this module is activated.
|
||||
* Input: None
|
||||
* Output: None
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2004-03-07
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
void ModuleFrn::activateInit(void)
|
||||
{
|
||||
audio_valve->setOpen(true);
|
||||
qso->connect();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: deactivateCleanup
|
||||
* Purpose: Called by the core system when this module is deactivated.
|
||||
* Input: None
|
||||
* Output: None
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2004-03-07
|
||||
* Remarks: Do NOT call this function directly unless you really know what
|
||||
* you are doing. Use Module::deactivate() instead.
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
void ModuleFrn::deactivateCleanup(void)
|
||||
{
|
||||
audio_valve->setOpen(true);
|
||||
qso->disconnect();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: dtmfDigitReceived
|
||||
* Purpose: Called by the core system when a DTMF digit has been
|
||||
* received. This function will only be called if the module
|
||||
* is active.
|
||||
* Input: digit - The DTMF digit received (0-9, A-D, *, #)
|
||||
* duration - The length in milliseconds of the received digit
|
||||
* Output: Return true if the digit is handled or false if not
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2004-03-07
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
bool ModuleFrn::dtmfDigitReceived(char digit, int duration)
|
||||
{
|
||||
cout << "DTMF digit received in module " << name() << ": " << digit << endl;
|
||||
|
||||
return false;
|
||||
|
||||
} /* dtmfDigitReceived */
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: dtmfCmdReceived
|
||||
* Purpose: Called by the core system when a DTMF command has been
|
||||
* received. A DTMF command consists of a string of digits ended
|
||||
* with a number sign (#). The number sign is not included in the
|
||||
* command string. This function will only be called if the module
|
||||
* is active.
|
||||
* Input: cmd - The received command.
|
||||
* Output: None
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2004-03-07
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
void ModuleFrn::dtmfCmdReceived(const string& cmd)
|
||||
{
|
||||
cout << "DTMF command received in module " << name() << ": " << cmd << endl;
|
||||
|
||||
if (cmd == "")
|
||||
{
|
||||
deactivateMe();
|
||||
return;
|
||||
}
|
||||
|
||||
stringstream ss;
|
||||
|
||||
switch (cmd[0])
|
||||
{
|
||||
case CMD_HELP:
|
||||
playHelpMsg();
|
||||
break;
|
||||
|
||||
case CMD_COUNT_CLIENTS:
|
||||
{
|
||||
if (!validateCommand(cmd, 1))
|
||||
return;
|
||||
ss << "count_clients ";
|
||||
ss << qso->clientsCount();
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_RF_DISABLE:
|
||||
{
|
||||
if (!validateCommand(cmd, 2))
|
||||
return;
|
||||
|
||||
bool disable = (cmd[1] != '0');
|
||||
qso->setRfDisabled(disable);
|
||||
cout << "rf disable: " << disable << endl;
|
||||
ss << "rf_disable " << (qso->isRfDisabled() ? "1 " : "0 ")
|
||||
<< (disable ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ss << "unknown_command " << cmd;
|
||||
break;
|
||||
}
|
||||
|
||||
processEvent(ss.str());
|
||||
|
||||
} /* dtmfCmdReceived */
|
||||
|
||||
|
||||
bool ModuleFrn::validateCommand(const string& cmd, size_t argc)
|
||||
{
|
||||
if (cmd.size() == argc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "command_failed " << cmd;
|
||||
processEvent(ss.str());
|
||||
return false;
|
||||
}
|
||||
} /* ModulrFrn::commandFailed */
|
||||
|
||||
|
||||
#if 0
|
||||
void ModuleFrn::dtmfCmdReceivedWhenIdle(const std::string &cmd)
|
||||
{
|
||||
|
||||
} /* dtmfCmdReceivedWhenIdle */
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: squelchOpen
|
||||
* Purpose: Called by the core system when the squelch open or close.
|
||||
* Input: is_open - Set to \em true if the squelch is open or \em false
|
||||
* if it's not.
|
||||
* Output: None
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2005-08-28
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
void ModuleFrn::squelchOpen(bool is_open)
|
||||
{
|
||||
qso->squelchOpen(is_open);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: allMsgsWritten
|
||||
* Purpose: Called by the core system when all announcement messages has
|
||||
* been played. Note that this function also may be called even
|
||||
* if it wasn't this module that initiated the message playing.
|
||||
* Input: None
|
||||
* Output: None
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2005-08-28
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
void ModuleFrn::allMsgsWritten(void)
|
||||
{
|
||||
|
||||
} /* allMsgsWritten */
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
* Method: reportState
|
||||
* Purpose: This function is called by the logic core when it wishes the
|
||||
* module to report its state on the radio channel. Typically this
|
||||
* is done when a manual identification has been triggered by the
|
||||
* user by sending a "*".
|
||||
* This function will only be called if this module is active.
|
||||
* Input: None
|
||||
* Output: None
|
||||
* Author: Tobias Blomberg / SM0SVX
|
||||
* Created: 2005-08-28
|
||||
* Remarks:
|
||||
* Bugs:
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
void ModuleFrn::reportState(void)
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "count_clients " << qso->clientsCount();
|
||||
processEvent(ss.str());
|
||||
} /* reportState */
|
||||
|
||||
|
||||
void ModuleFrn::onQsoError(void)
|
||||
{
|
||||
cerr << "QSO errored, deactivating module" << endl;
|
||||
deactivateMe();
|
||||
}
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
@file ModuleFrn.h
|
||||
@brief Free Radio Network (FRN) QSO module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
\verbatim
|
||||
A module (plugin) for the svxlink server, a multi purpose tranciever
|
||||
frontend system.
|
||||
Copyright (C) 2004-2005 Tobias Blomberg / SM0SVX
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MODULE_FRN_INCLUDED
|
||||
#define MODULE_FRN_INCLUDED
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* System Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Project Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <Module.h>
|
||||
#include <version/SVXLINK.h>
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "QsoFrn.h"
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations
|
||||
*
|
||||
****************************************************************************/
|
||||
namespace Async
|
||||
{
|
||||
class AudioSplitter;
|
||||
class AudioValve;
|
||||
class AudioSelector;
|
||||
class AudioFifo;
|
||||
class AudioJitterFifo;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Namespace
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
//namespace MyNameSpace
|
||||
//{
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations of classes inside of the declared namespace
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Defines & typedefs
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Exported Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Class definitions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
@brief Free Radio Network (FRN) module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
*/
|
||||
class ModuleFrn : public Module
|
||||
{
|
||||
public:
|
||||
ModuleFrn(void *dl_handle, Logic *logic, const std::string& cfg_name);
|
||||
~ModuleFrn(void);
|
||||
const char *compiledForVersion(void) const { return SVXLINK_VERSION; }
|
||||
|
||||
private:
|
||||
void moduleCleanup();
|
||||
bool initialize(void);
|
||||
void activateInit(void);
|
||||
void deactivateCleanup(void);
|
||||
bool dtmfDigitReceived(char digit, int duration);
|
||||
void dtmfCmdReceived(const std::string& cmd);
|
||||
void squelchOpen(bool is_open);
|
||||
void allMsgsWritten(void);
|
||||
void reportState(void);
|
||||
bool validateCommand(const std::string& cmd, size_t argc);
|
||||
void onQsoError(void);
|
||||
|
||||
private:
|
||||
QsoFrn *qso;
|
||||
Async::AudioValve *audio_valve;
|
||||
Async::AudioSplitter *audio_splitter;
|
||||
Async::AudioSelector *audio_selector;
|
||||
Async::AudioFifo *audio_fifo;
|
||||
|
||||
static const char CMD_HELP = '0';
|
||||
static const char CMD_COUNT_CLIENTS = '1';
|
||||
static const char CMD_RF_DISABLE = '2';
|
||||
|
||||
}; /* class ModuleFrn */
|
||||
|
||||
|
||||
//} /* namespace */
|
||||
|
||||
#endif /* MODULE_FRN_INCLUDED */
|
||||
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
|
@ -0,0 +1,970 @@
|
|||
/**
|
||||
@file QsoFrn.cpp
|
||||
@brief Free Radio Network (FRN) QSO module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
This file contains a class that implementes the things needed for one
|
||||
EchoLink Qso.
|
||||
|
||||
\verbatim
|
||||
A module (plugin) for the multi purpose tranciever frontend system.
|
||||
Copyright (C) 2004-2014 Tobias Blomberg / SM0SVX
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* System Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sigc++/bind.h>
|
||||
#include <sstream>
|
||||
#include <regex.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Project Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <AsyncConfig.h>
|
||||
#include <AsyncAudioPacer.h>
|
||||
#include <AsyncAudioSelector.h>
|
||||
#include <AsyncAudioPassthrough.h>
|
||||
#include <AsyncAudioFifo.h>
|
||||
#include <AsyncAudioDecimator.h>
|
||||
#include <AsyncAudioInterpolator.h>
|
||||
#include <AsyncAudioDebugger.h>
|
||||
#include <AsyncTcpClient.h>
|
||||
#include <AsyncTimer.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "Utils.h"
|
||||
#include "ModuleFrn.h"
|
||||
#include "QsoFrn.h"
|
||||
#include "multirate_filter_coeff.h"
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Namespaces to use
|
||||
*
|
||||
****************************************************************************/
|
||||
using namespace std;
|
||||
using namespace Async;
|
||||
using namespace sigc;
|
||||
using namespace FrnUtils;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Defines & typedefs
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local class definitions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Prototypes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Exported Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Public member functions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
QsoFrn::QsoFrn(ModuleFrn *module)
|
||||
: init_ok(false)
|
||||
, tcp_client(new TcpClient(TCP_BUFFER_SIZE))
|
||||
, rx_timeout_timer(new Timer(RX_TIMEOUT_TIME, Timer::TYPE_PERIODIC))
|
||||
, con_timeout_timer(new Timer(CON_TIMEOUT_TIME, Timer::TYPE_PERIODIC))
|
||||
, keepalive_timer(new Timer(KEEPALIVE_TIMEOUT_TIME, Timer::TYPE_PERIODIC))
|
||||
, reconnect_timer(new Timer(KEEPALIVE_TIMEOUT_TIME, Timer::TYPE_ONESHOT))
|
||||
, state(STATE_DISCONNECTED)
|
||||
, connect_retry_cnt(0)
|
||||
, send_buffer_cnt(0)
|
||||
, gsmh(gsm_create())
|
||||
, lines_to_read(-1)
|
||||
, is_receiving_voice(false)
|
||||
, is_rf_disabled(false)
|
||||
, reconnect_timeout_ms(RECONNECT_TIMEOUT_TIME)
|
||||
, opt_frn_debug(false)
|
||||
{
|
||||
assert(module != 0);
|
||||
|
||||
Config &cfg = module->cfg();
|
||||
const string &cfg_name = module->cfgName();
|
||||
|
||||
if (cfg.getValue(cfg_name, "FRN_DEBUG", opt_frn_debug))
|
||||
cout << "frn debugging is enabled" << endl;
|
||||
|
||||
if (cfg.getValue(cfg_name, "DISABLE_RF", is_rf_disabled))
|
||||
cout << "rf is disabled" << endl;
|
||||
|
||||
if (!cfg.getValue(cfg_name, "SERVER", opt_server))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/SERVER not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "PORT", opt_port))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/PORT not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "EMAIL_ADDRESS", opt_email_address))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/EMAIL_ADDRESS not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "DYN_PASSWORD", opt_dyn_password))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/DYN_PASSWORD not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "CALLSIGN_AND_USER", opt_callsign_and_user))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/CALLSIGN_AND_USER not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "CLIENT_TYPE", opt_client_type))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/CLIENT_TYPE not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "BAND_AND_CHANNEL", opt_band_and_channel))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/BAND_AND_CHANNEL not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "DESCRIPTION", opt_description))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/DESCRIPTION not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "COUNTRY", opt_country))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/COUNTRY not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "CITY_CITY_PART", opt_city_city_part))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/CITY_CITY_PART not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "NET", opt_net))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/NET not set\n";
|
||||
return;
|
||||
}
|
||||
if (!cfg.getValue(cfg_name, "VERSION", opt_version))
|
||||
{
|
||||
cerr << "*** ERROR: Config variable " << cfg_name
|
||||
<< "/VERSION not set\n";
|
||||
return;
|
||||
}
|
||||
|
||||
int gsm_one = 1;
|
||||
assert(gsm_option(gsmh, GSM_OPT_WAV49, &gsm_one) != -1);
|
||||
|
||||
tcp_client->connected.connect(
|
||||
mem_fun(*this, &QsoFrn::onConnected));
|
||||
tcp_client->disconnected.connect(
|
||||
mem_fun(*this, &QsoFrn::onDisconnected));
|
||||
tcp_client->dataReceived.connect(
|
||||
mem_fun(*this, &QsoFrn::onDataReceived));
|
||||
tcp_client->sendBufferFull.connect(
|
||||
mem_fun(*this, &QsoFrn::onSendBufferFull));
|
||||
|
||||
this->rxVoiceStarted.connect(
|
||||
mem_fun(*this, &QsoFrn::onRxVoiceStarted));
|
||||
this->frnListReceived.connect(
|
||||
mem_fun(*this, &QsoFrn::onFrnListReceived));
|
||||
this->frnClientListReceived.connect(
|
||||
mem_fun(*this, &QsoFrn::onFrnClientListReceived));
|
||||
|
||||
con_timeout_timer->setEnable(false);
|
||||
con_timeout_timer->expired.connect(
|
||||
mem_fun(*this, &QsoFrn::onConnectTimeout));
|
||||
|
||||
rx_timeout_timer->setEnable(false);
|
||||
rx_timeout_timer->expired.connect(
|
||||
mem_fun(*this, &QsoFrn::onRxTimeout));
|
||||
|
||||
keepalive_timer->setEnable(true);
|
||||
keepalive_timer->expired.connect(
|
||||
mem_fun(*this, &QsoFrn::onKeepaliveTimeout));
|
||||
|
||||
reconnect_timer->setEnable(false);
|
||||
reconnect_timer->expired.connect(
|
||||
mem_fun(*this, &QsoFrn::onDelayedReconnect));
|
||||
|
||||
init_ok = true;
|
||||
}
|
||||
|
||||
|
||||
QsoFrn::~QsoFrn(void)
|
||||
{
|
||||
AudioSink::clearHandler();
|
||||
AudioSource::clearHandler();
|
||||
|
||||
delete con_timeout_timer;
|
||||
con_timeout_timer = 0;
|
||||
|
||||
delete rx_timeout_timer;
|
||||
con_timeout_timer = 0;
|
||||
|
||||
delete tcp_client;
|
||||
tcp_client = 0;
|
||||
|
||||
delete keepalive_timer;
|
||||
keepalive_timer = 0;
|
||||
|
||||
gsm_destroy(gsmh);
|
||||
gsmh = 0;
|
||||
}
|
||||
|
||||
|
||||
bool QsoFrn::initOk(void)
|
||||
{
|
||||
return init_ok;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::connect(void)
|
||||
{
|
||||
setState(STATE_CONNECTING);
|
||||
|
||||
cout << "connecting to " << opt_server << ":" << opt_port << endl;
|
||||
tcp_client->connect(opt_server, atoi(opt_port.c_str()));
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::disconnect(void)
|
||||
{
|
||||
setState(STATE_DISCONNECTED);
|
||||
|
||||
con_timeout_timer->setEnable(false);
|
||||
|
||||
if (tcp_client->isConnected())
|
||||
{
|
||||
tcp_client->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string QsoFrn::stateToString(State state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case STATE_DISCONNECTED:
|
||||
return "DISCONNECTED";
|
||||
case STATE_CONNECTING:
|
||||
return "CONNECTING";
|
||||
case STATE_CONNECTED:
|
||||
return "CONNECTED";
|
||||
case STATE_LOGGING_IN_1:
|
||||
return "LOGGING_IN_1";
|
||||
case STATE_LOGGING_IN_2:
|
||||
return "LOGGIN_IN_2";
|
||||
case STATE_IDLE:
|
||||
return "IDLE";
|
||||
case STATE_ERROR:
|
||||
return "ERROR";
|
||||
case STATE_TX_AUDIO_WAITING:
|
||||
return "TX_AUDIO_WAITING";
|
||||
case STATE_TX_AUDIO_APPROVED:
|
||||
return "TX_AUDIO_APPROVED";
|
||||
case STATE_TX_AUDIO:
|
||||
return "TX_AUDIO";
|
||||
case STATE_RX_AUDIO:
|
||||
return "RX_AUDIO";
|
||||
case STATE_RX_CLIENT_LIST_HEADER:
|
||||
return "RX_CLIENT_LIST_HEADER";
|
||||
case STATE_RX_CLIENT_LIST:
|
||||
return "RX_CLIENT_LIST";
|
||||
case STATE_RX_LIST:
|
||||
return "RX_LIST";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::writeSamples(const float *samples, int count)
|
||||
{
|
||||
//cout << __FUNCTION__ << " " << count << endl;
|
||||
int samples_read = 0;
|
||||
con_timeout_timer->reset();
|
||||
|
||||
while (samples_read < count)
|
||||
{
|
||||
int read_cnt = min(BUFFER_SIZE - send_buffer_cnt, count-samples_read);
|
||||
for (int i = 0; i < read_cnt; i++)
|
||||
{
|
||||
float sample = samples[samples_read++];
|
||||
if (sample > 1)
|
||||
send_buffer[send_buffer_cnt++] = 32767;
|
||||
else if (sample < -1)
|
||||
send_buffer[send_buffer_cnt++] = -32767;
|
||||
else
|
||||
send_buffer[send_buffer_cnt++] = static_cast<int16_t>(32767.0 * sample);
|
||||
}
|
||||
if (send_buffer_cnt == BUFFER_SIZE)
|
||||
{
|
||||
if (state == STATE_TX_AUDIO)
|
||||
{
|
||||
sendVoiceData(send_buffer, send_buffer_cnt);
|
||||
send_buffer_cnt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
samples_read = count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return samples_read;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::flushSamples(void)
|
||||
{
|
||||
//cout << __FUNCTION__ << " " << stateToString(state) << endl;
|
||||
|
||||
if (state == STATE_TX_AUDIO)
|
||||
{
|
||||
if (send_buffer_cnt > 0)
|
||||
{
|
||||
memset(send_buffer + send_buffer_cnt, 0,
|
||||
sizeof(send_buffer) - sizeof(*send_buffer) * send_buffer_cnt);
|
||||
send_buffer_cnt = BUFFER_SIZE;
|
||||
|
||||
sendVoiceData(send_buffer, send_buffer_cnt);
|
||||
send_buffer_cnt = 0;
|
||||
}
|
||||
sendRequest(RQ_TX0);
|
||||
}
|
||||
sourceAllSamplesFlushed();
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::resumeOutput(void)
|
||||
{
|
||||
//cout << __FUNCTION__ << endl;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::squelchOpen(bool is_open)
|
||||
{
|
||||
if (is_open && state == STATE_IDLE)
|
||||
{
|
||||
sendRequest(RQ_TX0);
|
||||
setState(STATE_TX_AUDIO_WAITING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Protected member functions
|
||||
*
|
||||
****************************************************************************/
|
||||
void QsoFrn::allSamplesFlushed(void)
|
||||
{
|
||||
//cout << __FUNCTION__ << endl;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Private member functions
|
||||
*
|
||||
****************************************************************************/
|
||||
void QsoFrn::setState(State newState)
|
||||
{
|
||||
if (newState != state)
|
||||
{
|
||||
if (opt_frn_debug)
|
||||
cout << "state: " << stateToString(newState) << endl;
|
||||
state = newState;
|
||||
stateChange(newState);
|
||||
if (state == STATE_ERROR)
|
||||
error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::login(void)
|
||||
{
|
||||
assert(state == STATE_CONNECTED);
|
||||
|
||||
setState(STATE_LOGGING_IN_1);
|
||||
|
||||
std::stringstream s;
|
||||
s << "CT:";
|
||||
s << "<VX>" << opt_version << "</VX>";
|
||||
s << "<EA>" << opt_email_address << "</EA>";
|
||||
s << "<PW>" << opt_dyn_password << "</PW>";
|
||||
s << "<ON>" << opt_callsign_and_user << "</ON>";
|
||||
s << "<CL>" << opt_client_type << "</CL>";
|
||||
s << "<BC>" << opt_band_and_channel << "</BC>";
|
||||
s << "<DS>" << opt_description << "</DS>";
|
||||
s << "<NN>" << opt_country << "</NN>";
|
||||
s << "<CT>" << opt_city_city_part << "</CT>";
|
||||
s << "<NT>" << opt_net << "</NT>";
|
||||
s << endl;
|
||||
|
||||
std::string req = s.str();
|
||||
tcp_client->write(req.c_str(), req.length());
|
||||
}
|
||||
|
||||
void QsoFrn::sendVoiceData(short *data, int len)
|
||||
{
|
||||
assert(len == BUFFER_SIZE);
|
||||
|
||||
size_t nbytes = 0;
|
||||
unsigned char gsm_data[FRN_AUDIO_PACKET_SIZE];
|
||||
|
||||
for (int nframe = 0; nframe < FRAME_COUNT; nframe++)
|
||||
{
|
||||
short * src = data + nframe * PCM_FRAME_SIZE;
|
||||
unsigned char * dst = gsm_data + nframe * GSM_FRAME_SIZE;
|
||||
|
||||
// GSM_OPT_WAV49, produce alternating frames 32, 33, 32, 33, ..
|
||||
gsm_encode(gsmh, src, dst);
|
||||
gsm_encode(gsmh, src + PCM_FRAME_SIZE / 2, dst + 32);
|
||||
|
||||
nbytes += GSM_FRAME_SIZE;
|
||||
}
|
||||
sendRequest(RQ_TX1);
|
||||
size_t written = tcp_client->write(gsm_data, nbytes);
|
||||
if (written != nbytes)
|
||||
{
|
||||
cerr << "not all voice data was written to FRN: "
|
||||
<< written << "\\" << nbytes << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::reconnect(void)
|
||||
{
|
||||
reconnect_timeout_ms *= RECONNECT_BACKOFF;
|
||||
if (connect_retry_cnt++ < MAX_CONNECT_RETRY_CNT)
|
||||
{
|
||||
cout << "reconnecting #" << connect_retry_cnt << endl;
|
||||
connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "failed to reconnect " << MAX_CONNECT_RETRY_CNT << " times" << endl;
|
||||
setState(STATE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::sendRequest(Request rq)
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
switch(rq)
|
||||
{
|
||||
case RQ_RX0:
|
||||
s << "RX0";
|
||||
break;
|
||||
|
||||
case RQ_TX0:
|
||||
s << "TX0";
|
||||
break;
|
||||
|
||||
case RQ_TX1:
|
||||
s << "TX1";
|
||||
break;
|
||||
|
||||
case RQ_P:
|
||||
s << "P";
|
||||
break;
|
||||
|
||||
default:
|
||||
cerr << "unknown request " << rq << endl;
|
||||
return;
|
||||
}
|
||||
if (opt_frn_debug)
|
||||
cout << "req: " << s.str() << endl;
|
||||
if (tcp_client->isConnected())
|
||||
{
|
||||
s << "\r\n";
|
||||
std::string rq_s = s.str();
|
||||
size_t written = tcp_client->write(rq_s.c_str(), rq_s.length());
|
||||
if (written != rq_s.length())
|
||||
{
|
||||
cerr << "request " << rq_s << " was not written to FRN: "
|
||||
<< written << "\\" << rq_s.length() << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::handleAudioData(unsigned char *data, int len)
|
||||
{
|
||||
unsigned char *gsm_data = data + CLIENT_INDEX_SIZE;
|
||||
short *pcm_buffer = receive_buffer;
|
||||
float pcm_samples[PCM_FRAME_SIZE];
|
||||
|
||||
if (len < FRN_AUDIO_PACKET_SIZE + CLIENT_INDEX_SIZE)
|
||||
return 0;
|
||||
|
||||
if (!is_receiving_voice)
|
||||
{
|
||||
unsigned short client_index = data[1] | data[0] << 8;
|
||||
is_receiving_voice = true;
|
||||
if (client_index > 0 && client_index <= client_list.size())
|
||||
rxVoiceStarted(client_list[client_index - 1]);
|
||||
}
|
||||
|
||||
if (!is_rf_disabled)
|
||||
{
|
||||
for (int frameno = 0; frameno < FRAME_COUNT; frameno++)
|
||||
{
|
||||
unsigned char *src = gsm_data + frameno * GSM_FRAME_SIZE;
|
||||
short *dst = pcm_buffer;
|
||||
bool is_gsm_decode_success = true;
|
||||
|
||||
// GSM_OPT_WAV49, consume alternating frames of size 33, 32, 33, 32, ..
|
||||
if (gsm_decode(gsmh, src, dst) == -1)
|
||||
is_gsm_decode_success = false;
|
||||
|
||||
if (gsm_decode(gsmh, src + 33, dst + PCM_FRAME_SIZE / 2) == -1)
|
||||
is_gsm_decode_success = false;
|
||||
|
||||
if (!is_gsm_decode_success)
|
||||
cerr << "gsm decoder failed to decode frame " << frameno << endl;
|
||||
|
||||
for (int i = 0; i < PCM_FRAME_SIZE; i++)
|
||||
pcm_samples[i] = static_cast<float>(pcm_buffer[i]) / 32768.0;
|
||||
|
||||
int all_written = 0;
|
||||
while (all_written < PCM_FRAME_SIZE)
|
||||
{
|
||||
int written = sinkWriteSamples(pcm_samples + all_written,
|
||||
PCM_FRAME_SIZE - all_written);
|
||||
if (written == 0)
|
||||
{
|
||||
cerr << "cannot write frame to sink, dropping sample "
|
||||
<< (PCM_FRAME_SIZE - all_written) << endl;
|
||||
break;
|
||||
}
|
||||
all_written += written;
|
||||
}
|
||||
pcm_buffer += PCM_FRAME_SIZE;
|
||||
}
|
||||
}
|
||||
setState(STATE_IDLE);
|
||||
rx_timeout_timer->setEnable(true);
|
||||
rx_timeout_timer->reset();
|
||||
sendRequest(RQ_P);
|
||||
return FRN_AUDIO_PACKET_SIZE + CLIENT_INDEX_SIZE;
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::handleCommand(unsigned char *data, int len)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
Response cmd = (Response)data[0];
|
||||
if (opt_frn_debug)
|
||||
cout << "cmd: " << cmd << endl;
|
||||
|
||||
keepalive_timer->reset();
|
||||
|
||||
bytes_read += 1;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case DT_IDLE:
|
||||
sendRequest(RQ_P);
|
||||
setState(STATE_IDLE);
|
||||
break;
|
||||
|
||||
case DT_DO_TX:
|
||||
setState(STATE_TX_AUDIO_APPROVED);
|
||||
break;
|
||||
|
||||
case DT_VOICE_BUFFER:
|
||||
setState(STATE_RX_AUDIO);
|
||||
rx_timeout_timer->setEnable(true);
|
||||
rx_timeout_timer->reset();
|
||||
break;
|
||||
|
||||
case DT_TEXT_MESSAGE:
|
||||
case DT_NET_NAMES:
|
||||
case DT_ADMIN_LIST:
|
||||
case DT_ACCESS_LIST:
|
||||
case DT_BLOCK_LIST:
|
||||
case DT_MUTE_LIST:
|
||||
case DT_ACCESS_MODE:
|
||||
setState(STATE_RX_LIST);
|
||||
break;
|
||||
|
||||
case DT_CLIENT_LIST:
|
||||
setState(STATE_RX_CLIENT_LIST_HEADER);
|
||||
break;
|
||||
|
||||
default:
|
||||
cout << "unknown command " << cmd << endl;
|
||||
break;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::handleListHeader(unsigned char *data, int len)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
|
||||
if (len >= CLIENT_INDEX_SIZE)
|
||||
{
|
||||
bytes_read += CLIENT_INDEX_SIZE;
|
||||
setState(STATE_RX_CLIENT_LIST);
|
||||
lines_to_read = -1;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::handleList(unsigned char *data, int len)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
std::string line;
|
||||
std::istringstream lines(std::string((char*)data, len));
|
||||
bool has_win_newline = hasWinNewline(lines);
|
||||
|
||||
if (hasLine(lines) && safeGetline(lines, line))
|
||||
{
|
||||
if (lines_to_read == -1)
|
||||
{
|
||||
lines_to_read = atoi(line.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_item_list.push_back(line);
|
||||
lines_to_read--;
|
||||
}
|
||||
bytes_read += line.length() + (has_win_newline ? 2 : 1);
|
||||
}
|
||||
if (lines_to_read == 0)
|
||||
{
|
||||
if (state == STATE_RX_CLIENT_LIST)
|
||||
frnClientListReceived(cur_item_list);
|
||||
frnListReceived(cur_item_list);
|
||||
cur_item_list.clear();
|
||||
lines_to_read = -1;
|
||||
setState(STATE_IDLE);
|
||||
}
|
||||
//cout << "got " << len << " read " << bytes_read << endl;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::handleLogin(unsigned char *data, int len, bool stage_one)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
std::string line;
|
||||
std::istringstream lines(std::string((char*)data, len));
|
||||
bool has_win_newline = hasWinNewline(lines);
|
||||
|
||||
if (hasLine(lines) && safeGetline(lines, line))
|
||||
{
|
||||
if (stage_one)
|
||||
{
|
||||
if (line.length() == std::string("2014003").length() ||
|
||||
line.length() == std::string("0").length())
|
||||
{
|
||||
setState(STATE_LOGGING_IN_2);
|
||||
cout << "login stage 1 completed: " << line << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
setState(STATE_ERROR);
|
||||
cerr << "login stage 1 failed: " << line << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line.find("<AL>BLOCK</AL>") == std::string::npos &&
|
||||
line.find("<AL>WRONG</AL>") == std::string::npos)
|
||||
{
|
||||
setState(STATE_IDLE);
|
||||
sendRequest(RQ_RX0);
|
||||
cout << "login stage 2 completed: " << line << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
setState(STATE_ERROR);
|
||||
cerr << "login stage 2 failed: " << line << endl;
|
||||
}
|
||||
}
|
||||
bytes_read += line.length() + (has_win_newline ? 2 : 1);
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onConnected(void)
|
||||
{
|
||||
//cout << __FUNCTION__ << endl;
|
||||
setState(STATE_CONNECTED);
|
||||
|
||||
connect_retry_cnt = 0;
|
||||
reconnect_timeout_ms = RECONNECT_TIMEOUT_TIME;
|
||||
con_timeout_timer->setEnable(true);
|
||||
login();
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onDisconnected(TcpConnection *conn,
|
||||
TcpConnection::DisconnectReason reason)
|
||||
{
|
||||
//cout << __FUNCTION__ << " ";
|
||||
bool needs_reconnect = false;
|
||||
|
||||
setState(STATE_DISCONNECTED);
|
||||
|
||||
con_timeout_timer->setEnable(false);
|
||||
|
||||
switch (reason)
|
||||
{
|
||||
case TcpConnection::DR_HOST_NOT_FOUND:
|
||||
cout << "DR_HOST_NOT_FOUND" << endl;
|
||||
needs_reconnect = true;
|
||||
break;
|
||||
|
||||
case TcpConnection::DR_REMOTE_DISCONNECTED:
|
||||
cout << "DR_REMOTE_DISCONNECTED" << ", "
|
||||
<< conn->disconnectReasonStr(reason) << endl;
|
||||
needs_reconnect = true;
|
||||
break;
|
||||
|
||||
case TcpConnection::DR_SYSTEM_ERROR:
|
||||
cout << "DR_SYSTEM_ERROR" << ", "
|
||||
<< conn->disconnectReasonStr(reason) << endl;
|
||||
needs_reconnect = true;
|
||||
break;
|
||||
|
||||
case TcpConnection::DR_RECV_BUFFER_OVERFLOW:
|
||||
cout << "DR_RECV_BUFFER_OVERFLOW" << endl;
|
||||
setState(STATE_ERROR);
|
||||
break;
|
||||
|
||||
case TcpConnection::DR_ORDERED_DISCONNECT:
|
||||
cout << "DR_ORDERED_DISCONNECT" << endl;
|
||||
break;
|
||||
|
||||
default:
|
||||
cout << "DR_UNKNOWN" << endl;
|
||||
setState(STATE_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (needs_reconnect)
|
||||
{
|
||||
cout << "reconnecting in " << reconnect_timeout_ms << " ms" << endl;
|
||||
reconnect_timer->setEnable(true);
|
||||
reconnect_timer->setTimeout(reconnect_timeout_ms);
|
||||
reconnect_timer->reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int QsoFrn::onDataReceived(TcpConnection *con, void *data, int len)
|
||||
{
|
||||
//cout << __FUNCTION__ << " len: " << len << endl;
|
||||
unsigned char *p_data = (unsigned char*)data;
|
||||
|
||||
con_timeout_timer->reset();
|
||||
int remaining_bytes = len;
|
||||
|
||||
while (remaining_bytes > 0)
|
||||
{
|
||||
int bytes_read = 0;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case STATE_LOGGING_IN_1:
|
||||
bytes_read += handleLogin(p_data, remaining_bytes, true);
|
||||
break;
|
||||
|
||||
case STATE_LOGGING_IN_2:
|
||||
bytes_read += handleLogin(p_data, remaining_bytes, false);
|
||||
break;
|
||||
|
||||
case STATE_TX_AUDIO_APPROVED:
|
||||
if (remaining_bytes >= CLIENT_INDEX_SIZE)
|
||||
bytes_read += CLIENT_INDEX_SIZE;
|
||||
setState(STATE_TX_AUDIO);
|
||||
break;
|
||||
|
||||
case STATE_TX_AUDIO:
|
||||
case STATE_TX_AUDIO_WAITING:
|
||||
case STATE_IDLE:
|
||||
bytes_read += handleCommand(p_data, remaining_bytes);
|
||||
break;
|
||||
|
||||
case STATE_RX_AUDIO:
|
||||
bytes_read += handleAudioData(p_data, remaining_bytes);
|
||||
break;
|
||||
|
||||
case STATE_RX_CLIENT_LIST_HEADER:
|
||||
bytes_read += handleListHeader(p_data, remaining_bytes);
|
||||
break;
|
||||
|
||||
case STATE_RX_LIST:
|
||||
case STATE_RX_CLIENT_LIST:
|
||||
bytes_read += handleList(p_data, remaining_bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//cout << bytes_read << " " << remaining_bytes << " " << len << endl;
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
remaining_bytes -= bytes_read;
|
||||
p_data += bytes_read;
|
||||
}
|
||||
return len - remaining_bytes;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onSendBufferFull(bool is_full)
|
||||
{
|
||||
cerr << "send buffer is full " << is_full << endl;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onConnectTimeout(Timer *timer)
|
||||
{
|
||||
//cout << __FUNCTION__ << endl;
|
||||
if (state == STATE_IDLE)
|
||||
{
|
||||
disconnect();
|
||||
reconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onRxTimeout(Timer *timer)
|
||||
{
|
||||
//cout << __FUNCTION__ << endl;
|
||||
sinkFlushSamples();
|
||||
rx_timeout_timer->setEnable(false);
|
||||
is_receiving_voice = false;
|
||||
//setState(STATE_IDLE);
|
||||
sendRequest(RQ_P);
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onKeepaliveTimeout(Timer *timer)
|
||||
{
|
||||
if (state == STATE_IDLE)
|
||||
sendRequest(RQ_P);
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onRxVoiceStarted(const string &client_descritpion) const
|
||||
{
|
||||
if (is_rf_disabled)
|
||||
cout << "[listen only] ";
|
||||
cout << "voice started: " << client_descritpion << endl;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onFrnListReceived(const FrnList &list) const
|
||||
{
|
||||
cout << "FRN list received:" << endl;
|
||||
for (FrnList::const_iterator it = list.begin(); it != list.end(); ++it)
|
||||
{
|
||||
cout << "-- " << *it << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onFrnClientListReceived(const FrnList &list)
|
||||
{
|
||||
cout << "FRN active client list updated" << endl;
|
||||
client_list = list;
|
||||
}
|
||||
|
||||
|
||||
void QsoFrn::onDelayedReconnect(Async::Timer *timer)
|
||||
{
|
||||
reconnect();
|
||||
}
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,584 @@
|
|||
/**
|
||||
@file QsoFrn.h
|
||||
@brief Free Radio Network (FRN) QSO module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
This file contains a class that implementes the things needed for one
|
||||
Frn Qso.
|
||||
|
||||
\verbatim
|
||||
A module (plugin) for the multi purpose tranciever frontend system.
|
||||
Copyright (C) 2004-2014 Tobias Blomberg / SM0SVX
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
#ifndef QSO_FRN_INCLUDED
|
||||
#define QSO_FRN_INCLUDED
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* System Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Project Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
extern "C" {
|
||||
#include <gsm.h>
|
||||
}
|
||||
|
||||
#include <AsyncAudioSink.h>
|
||||
#include <AsyncAudioSource.h>
|
||||
#include <AsyncTcpConnection.h>
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Local Includes
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations
|
||||
*
|
||||
****************************************************************************/
|
||||
namespace Async
|
||||
{
|
||||
class Config;
|
||||
class AudioPacer;
|
||||
class AudioFifo;
|
||||
class AudioPassthrough;
|
||||
class TcpClient;
|
||||
class TcpConnection;
|
||||
};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Namespace
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
//namespace MyNameSpace
|
||||
//{
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Forward declarations of classes inside of the declared namespace
|
||||
*
|
||||
****************************************************************************/
|
||||
class MsgHandler;
|
||||
class EventHandler;
|
||||
class AsyncTimer;
|
||||
class AudioFifo;
|
||||
class ModuleFrn;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Defines & typedefs
|
||||
*
|
||||
****************************************************************************/
|
||||
typedef std::vector<std::string> FrnList;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Exported Global Variables
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Class definitions
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
@brief Free Radio Network (FRN) QSO module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
A class that implementes the things needed for FRN QSO.
|
||||
*/
|
||||
class QsoFrn
|
||||
: public Async::AudioSink, public Async::AudioSource, public sigc::trackable
|
||||
{
|
||||
public:
|
||||
typedef enum {
|
||||
STATE_ERROR,
|
||||
STATE_DISCONNECTED,
|
||||
STATE_CONNECTING,
|
||||
STATE_CONNECTED,
|
||||
STATE_LOGGING_IN_1,
|
||||
STATE_LOGGING_IN_2,
|
||||
STATE_IDLE,
|
||||
STATE_TX_AUDIO_WAITING,
|
||||
STATE_TX_AUDIO_APPROVED,
|
||||
STATE_TX_AUDIO,
|
||||
STATE_RX_AUDIO,
|
||||
STATE_RX_CLIENT_LIST_HEADER,
|
||||
STATE_RX_CLIENT_LIST,
|
||||
STATE_RX_LIST
|
||||
} State;
|
||||
|
||||
typedef enum {
|
||||
DT_IDLE = 0,
|
||||
DT_DO_TX,
|
||||
DT_VOICE_BUFFER,
|
||||
DT_CLIENT_LIST,
|
||||
DT_TEXT_MESSAGE,
|
||||
DT_NET_NAMES,
|
||||
DT_ADMIN_LIST,
|
||||
DT_ACCESS_LIST,
|
||||
DT_BLOCK_LIST,
|
||||
DT_MUTE_LIST,
|
||||
DT_ACCESS_MODE
|
||||
} Response;
|
||||
|
||||
typedef enum {
|
||||
RQ_RX0,
|
||||
RQ_TX0,
|
||||
RQ_TX1,
|
||||
RQ_P
|
||||
} Request;
|
||||
|
||||
/**
|
||||
* @brief Default constuctor
|
||||
*/
|
||||
QsoFrn(ModuleFrn *module);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~QsoFrn(void);
|
||||
|
||||
/**
|
||||
* @brief Call to check if QSO was ctor-ed succesfully
|
||||
*
|
||||
* @return true if QSO was constructed correctly
|
||||
*
|
||||
* Call after qso contruction to make sure that it was ctor-ed correctly.
|
||||
*/
|
||||
bool initOk(void);
|
||||
|
||||
/**
|
||||
* @brief Method to put QSO to live
|
||||
*/
|
||||
void connect(void);
|
||||
|
||||
/**
|
||||
* @brief Disconnect QSO from server, put it to disconnected state
|
||||
*/
|
||||
void disconnect(void);
|
||||
|
||||
/**
|
||||
* @brief Called by FRN module to notify that squelch is opened
|
||||
*
|
||||
* @param true if squelch is open or false if closed
|
||||
*/
|
||||
void squelchOpen(bool is_open);
|
||||
|
||||
/**
|
||||
* @brief Write samples into this audio sink
|
||||
* @param samples The buffer containing the samples
|
||||
* @param count The number of samples in the buffer
|
||||
* @return Returns the number of samples that has been taken care of
|
||||
*
|
||||
* This function is used to write audio into this audio sink. If it
|
||||
* returns 0, no more samples could be written.
|
||||
* If the returned number of written samples is lower than the count
|
||||
* parameter value, the sink is not ready to accept more samples.
|
||||
* In this case, the audio source requires sample buffering to temporarily
|
||||
* store samples that are not immediately accepted by the sink.
|
||||
* The writeSamples function should be called on source buffer updates
|
||||
* and after a source output request has been received through the
|
||||
* requestSamples function.
|
||||
* This function is normally only called from a connected source object.
|
||||
*/
|
||||
virtual int writeSamples(const float *samples, int count);
|
||||
|
||||
/**
|
||||
* @brief Tell the sink to flush the previously written samples
|
||||
*
|
||||
* This function is used to tell the sink to flush previously written
|
||||
* samples. When done flushing, the sink should call the
|
||||
* sourceAllSamplesFlushed function.
|
||||
* This function is normally only called from a connected source object.
|
||||
*/
|
||||
virtual void flushSamples(void);
|
||||
|
||||
/**
|
||||
* @brief Return true if qso is connected to the server
|
||||
*
|
||||
* @return true if qso is connected
|
||||
*/
|
||||
bool isConnected() const { return state >= STATE_IDLE; }
|
||||
|
||||
/**
|
||||
* @brief return number of clients on the channel
|
||||
*
|
||||
* @return count of clients on the channel including myself
|
||||
*/
|
||||
int clientsCount() const { return client_list.size(); }
|
||||
|
||||
/**
|
||||
* @brief Resume audio output to the sink
|
||||
*
|
||||
* This function will be called when the registered audio sink is ready
|
||||
* to accept more samples.
|
||||
* This function is normally only called from a connected sink object.
|
||||
*/
|
||||
virtual void resumeOutput(void);
|
||||
|
||||
/**
|
||||
* @brief Do not send anything to rig
|
||||
*
|
||||
* @param true to disable audio stream to rig
|
||||
*/
|
||||
void setRfDisabled(bool disabled) { is_rf_disabled = disabled; }
|
||||
|
||||
/**
|
||||
* @brief Return true if rf tx disabled for the qso
|
||||
*
|
||||
* @return true if rf tx is disabled
|
||||
*/
|
||||
bool isRfDisabled() const { return is_rf_disabled; }
|
||||
|
||||
/**
|
||||
* @brief QSO is erroring out and cannot recover itself
|
||||
*/
|
||||
sigc::signal<void> error;
|
||||
|
||||
/**
|
||||
* @brief QSO internal state has been changed
|
||||
* @param QSO state enum value
|
||||
*/
|
||||
sigc::signal<void, State> stateChange;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The registered sink has flushed all samples
|
||||
*
|
||||
* This function will be called when all samples have been flushed in the
|
||||
*
|
||||
* registered sink. If it is not reimplemented, a handler must be set
|
||||
* that handle the function call.
|
||||
* This function is normally only called from a connected sink object.
|
||||
*/
|
||||
virtual void allSamplesFlushed(void);
|
||||
|
||||
/**
|
||||
* @brief Converts QSO state enum to string for debugging
|
||||
* @param QSO state enum
|
||||
* @return String representation of the enum value
|
||||
*
|
||||
* This function is used to convert QSO state enum to readable
|
||||
* string for debugging purposes
|
||||
*/
|
||||
std::string stateToString(State state);
|
||||
|
||||
/**
|
||||
* @brief Called internally to set new QSO state
|
||||
*
|
||||
* @param New state to set
|
||||
*/
|
||||
void setState(State newState);
|
||||
|
||||
/**
|
||||
* @brief Emitted when FRN list is fully populated
|
||||
*
|
||||
* @param vector of strings populated from FRN
|
||||
*/
|
||||
sigc::signal<void, const FrnList& > frnListReceived;
|
||||
|
||||
/**
|
||||
* @brief Emitted when FRN client list is fully populated
|
||||
*
|
||||
* @param vector of strings populated from FRN
|
||||
*/
|
||||
sigc::signal<void, const FrnList& > frnClientListReceived;
|
||||
|
||||
/**
|
||||
* @brief Emitted when started receiving voice from client
|
||||
*
|
||||
* @param string with client xml data
|
||||
*/
|
||||
sigc::signal<void, const std::string& > rxVoiceStarted;
|
||||
|
||||
/**
|
||||
* @brief Called when started receiving voice from the client
|
||||
*
|
||||
* @param xml FRN format client descrition
|
||||
*/
|
||||
void onRxVoiceStarted(const std::string &client_description) const;
|
||||
|
||||
/**
|
||||
* @brief Called when completed FRN list is received
|
||||
*
|
||||
* @param xml FRN format client descrition line
|
||||
*/
|
||||
void onFrnListReceived(const FrnList &list) const;
|
||||
|
||||
/**
|
||||
* @brief Called when completed FRN client list is received
|
||||
*
|
||||
* @param xml FRN format client descrition line
|
||||
*/
|
||||
void onFrnClientListReceived(const FrnList &list);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Initiate connection to FRN server when connection was lost
|
||||
*
|
||||
* Starts new connection retry. If maximum retry count is reached sets
|
||||
* QSO state to error.
|
||||
*/
|
||||
void reconnect();
|
||||
|
||||
/**
|
||||
* @brief Starts initial FRN server login process
|
||||
*
|
||||
* Sends connection string with credentials and other required information
|
||||
* to the FRN server, sets proper QSO state.
|
||||
*/
|
||||
void login(void);
|
||||
|
||||
/**
|
||||
* @brief Sends pcm raw frames to FRN server
|
||||
*
|
||||
* @param Pcm buffer with samples
|
||||
* @param Size of buffer
|
||||
*/
|
||||
void sendVoiceData(short *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Sends FRN client request to the server
|
||||
*
|
||||
* @param Request to send out
|
||||
*
|
||||
* This method is used to send FRN control request.
|
||||
*/
|
||||
void sendRequest(Request rq);
|
||||
|
||||
/**
|
||||
* @brief Consumed and handles incoming data when in loging in stage
|
||||
*
|
||||
* @param pointer to received data
|
||||
* @param length of incoming data
|
||||
* @param true if logging in stage 1, else stage 2
|
||||
* @return count of bytes consumed
|
||||
*
|
||||
* Handles incoming data when in logging in stage 1 or 2,
|
||||
* switches to next state if all required data was consumed.
|
||||
*/
|
||||
int handleLogin(unsigned char *data, int len, bool stage_one);
|
||||
|
||||
/**
|
||||
* @brief Consume and handle one incoming FRN command
|
||||
* @param pointer to received data
|
||||
* @param length of received data
|
||||
* @return count of bytes consumed
|
||||
*
|
||||
* Consumes and handles one incoming FRN command and sets
|
||||
* internal QSO state accordingly.
|
||||
*/
|
||||
int handleCommand(unsigned char *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Consume and handle one incoming audio frame
|
||||
*
|
||||
* @param pointer to received data
|
||||
* @param length of received data
|
||||
* @return count of bytes consumed
|
||||
*
|
||||
* Consumes and handles one incoming FRN audio frame. Decodes
|
||||
* from gsm wav49 format and sends to audio sink.
|
||||
*/
|
||||
int handleAudioData(unsigned char *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Consume and handle FRN incoming string lists
|
||||
*
|
||||
* @param pointer to received data
|
||||
* @param length of received data
|
||||
* @return count of bytes consumed
|
||||
*
|
||||
* Method is used to consume and handle FRN incoming list
|
||||
* header
|
||||
*/
|
||||
int handleListHeader(unsigned char *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Consume and handle FRN incoming string lists
|
||||
*
|
||||
* @param pointer to received data
|
||||
* @param length of received data
|
||||
* @return count of bytes consumed
|
||||
*
|
||||
* Method is used to consume and handle FRN incoming lists, for
|
||||
* example list of users, chat messages, etc.
|
||||
*/
|
||||
int handleList(unsigned char *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Called when connection to FRN server is established
|
||||
*
|
||||
* Called when connection to FRN server is established, updates
|
||||
* internal state, starts timeout timer.
|
||||
*/
|
||||
void onConnected(void);
|
||||
|
||||
/**
|
||||
* @brief Called when connection to FRN server is lost
|
||||
*
|
||||
* Called when connection to FRN server is lost, based on
|
||||
* disconnect reason sets the internal QSO state and start
|
||||
* reconnection if needed.
|
||||
*/
|
||||
void onDisconnected(Async::TcpConnection *conn,
|
||||
Async::TcpConnection::DisconnectReason reason);
|
||||
|
||||
/**
|
||||
* @brief Called when new data is received from FRN server
|
||||
*
|
||||
* Called when new data is received from FRN server, calls proper
|
||||
* handler based on the current state.
|
||||
*/
|
||||
int onDataReceived(Async::TcpConnection *con, void *data, int len);
|
||||
|
||||
/**
|
||||
* @brief Called when FRN connection buffer is full
|
||||
*/
|
||||
void onSendBufferFull(bool is_full);
|
||||
|
||||
/**
|
||||
* @brief Called when no pings were received during timeout time
|
||||
*
|
||||
* Initiates reconnection when no pings/data was received from
|
||||
* the FRN serer during timeout time.
|
||||
*/
|
||||
void onConnectTimeout(Async::Timer *timer);
|
||||
|
||||
/**
|
||||
* @brief Called when no voice data was received during timeout time
|
||||
*
|
||||
* Called when no more voice data is received during timeout time,
|
||||
* which is treated as end of voice data receive state.
|
||||
*/
|
||||
void onRxTimeout(Async::Timer *timer);
|
||||
|
||||
/**
|
||||
* @brief Called when wait for tx timer timeouts
|
||||
*
|
||||
* Called when server does not respond withing timeout time
|
||||
* to TX request
|
||||
*/
|
||||
void onTxWaitTimeout(Async::Timer *timer);
|
||||
|
||||
/**
|
||||
* @brief Called when no pings/data were received during timeout time
|
||||
*
|
||||
* Called when no pings and data were received during timeout time
|
||||
*/
|
||||
void onKeepaliveTimeout(Async::Timer *timer);
|
||||
|
||||
/**
|
||||
* @brief Delayed reconnect timer callback
|
||||
*
|
||||
* Called to start delayed reconnection to FRN server
|
||||
*/
|
||||
void onDelayedReconnect(Async::Timer *timer);
|
||||
|
||||
private:
|
||||
static const int CLIENT_INDEX_SIZE = 2;
|
||||
static const int TCP_BUFFER_SIZE = 65536;
|
||||
static const int FRAME_COUNT = 5;
|
||||
static const int PCM_FRAME_SIZE = 160*2; // WAV49 has 2x
|
||||
static const int GSM_FRAME_SIZE = 65; // WAV49 has 65
|
||||
static const int BUFFER_SIZE = FRAME_COUNT*PCM_FRAME_SIZE;
|
||||
static const int FRN_AUDIO_PACKET_SIZE = FRAME_COUNT*GSM_FRAME_SIZE;
|
||||
|
||||
static const int CON_TIMEOUT_TIME = 30000;
|
||||
static const int RX_TIMEOUT_TIME = 1000;
|
||||
static const int KEEPALIVE_TIMEOUT_TIME = 5000;
|
||||
|
||||
static const int MAX_CONNECT_RETRY_CNT = 5;
|
||||
static const int RECONNECT_TIMEOUT_TIME = 2000;
|
||||
static const int RECONNECT_BACKOFF = 5;
|
||||
|
||||
bool init_ok;
|
||||
|
||||
Async::TcpClient * tcp_client;
|
||||
Async::Timer * rx_timeout_timer;
|
||||
Async::Timer * con_timeout_timer;
|
||||
Async::Timer * keepalive_timer;
|
||||
Async::Timer * reconnect_timer;
|
||||
State state;
|
||||
int connect_retry_cnt;
|
||||
short receive_buffer[BUFFER_SIZE];
|
||||
short send_buffer[BUFFER_SIZE];
|
||||
int send_buffer_cnt;
|
||||
gsm gsmh;
|
||||
int lines_to_read;
|
||||
FrnList cur_item_list;
|
||||
FrnList client_list;
|
||||
bool is_receiving_voice;
|
||||
bool is_rf_disabled;
|
||||
int reconnect_timeout_ms;
|
||||
|
||||
bool opt_frn_debug;
|
||||
std::string opt_server;
|
||||
std::string opt_port;
|
||||
std::string opt_version;
|
||||
std::string opt_email_address;
|
||||
std::string opt_dyn_password;
|
||||
std::string opt_callsign_and_user;
|
||||
std::string opt_client_type;
|
||||
std::string opt_band_and_channel;
|
||||
std::string opt_description;
|
||||
std::string opt_country;
|
||||
std::string opt_city_city_part;
|
||||
std::string opt_net;
|
||||
};
|
||||
|
||||
|
||||
//} /* namespace */
|
||||
|
||||
#endif /* QSO_FRN_INCLUDED */
|
||||
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
@file Utils.cpp
|
||||
@brief Helpers for qso frn module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
This file contains a class that implementes the things needed for one
|
||||
EchoLink Qso.
|
||||
|
||||
\verbatim
|
||||
A module (plugin) for the multi purpose tranciever frontend system.
|
||||
Copyright (C) 2004-2014 Tobias Blomberg / SM0SVX
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
namespace FrnUtils
|
||||
{
|
||||
|
||||
std::istream& safeGetline(std::istream& is, std::string& t)
|
||||
{
|
||||
t.clear();
|
||||
std::istream::sentry se(is, true);
|
||||
std::streambuf* sb = is.rdbuf();
|
||||
|
||||
for(;;) {
|
||||
int c = sb->sbumpc();
|
||||
switch (c) {
|
||||
case '\n':
|
||||
if(sb->sgetc() == '\r')
|
||||
sb->sbumpc();
|
||||
return is;
|
||||
case '\r':
|
||||
if(sb->sgetc() == '\n')
|
||||
{
|
||||
sb->sbumpc();
|
||||
return is;
|
||||
}
|
||||
t += (char)c;
|
||||
break;
|
||||
case EOF:
|
||||
if(t.empty())
|
||||
is.setstate(std::ios::eofbit);
|
||||
return is;
|
||||
default:
|
||||
t += (char)c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool hasLine(std::istringstream& is)
|
||||
{
|
||||
return (is.str().find('\n') != std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
bool hasWinNewline(std::istringstream& is)
|
||||
{
|
||||
return !(is.str().find("\r\n") == std::string::npos &&
|
||||
is.str().find("\n\r") == std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
} // namspace FrnUtils
|
||||
|
||||
/*
|
||||
* This file has not been truncated
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
@file Utils.h
|
||||
@brief Helpers for QSO FRN module
|
||||
@author sh123
|
||||
@date 2014-12-30
|
||||
|
||||
This file contains a class that implementes the things needed for one
|
||||
Frn Qso.
|
||||
|
||||
\verbatim
|
||||
A module (plugin) for the multi purpose tranciever frontend system.
|
||||
Copyright (C) 2004-2014 Tobias Blomberg / SM0SVX
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
\endverbatim
|
||||
*/
|
||||
|
||||
namespace FrnUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Checks if there is a line in string stream
|
||||
*
|
||||
* This functions checks if string stream has a line, returns true
|
||||
* when line has been found.
|
||||
*/
|
||||
bool hasLine(std::istringstream& is);
|
||||
|
||||
/**
|
||||
* @brief Checks if string stream has windows style newline
|
||||
*
|
||||
* This function returns true if windows style newline has been found
|
||||
* in given stream.
|
||||
*/
|
||||
bool hasWinNewline(std::istringstream& is);
|
||||
|
||||
/**
|
||||
* @brief Returns line from string stream halding also windows newlines
|
||||
*
|
||||
* This function is similar to std::getline, but takes care of windows
|
||||
* style new lines.
|
||||
*/
|
||||
std::istream& safeGetline(std::istream& is, std::string& t);
|
||||
|
||||
} // namespace FrnUtils
|
||||
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
SYSMAN_SERVER=sysman.freeradionetwork.eu
|
||||
#SYSMAN_SERVER=localhost
|
||||
REG_PORT=10025
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "Usage: $0 <FRN module config file>"
|
||||
exit 1
|
||||
fi
|
||||
CFG_FILE="$1"
|
||||
|
||||
# Read the FRN module config file
|
||||
if [[ ! -r "$CFG_FILE" ]]; then
|
||||
echo "*** ERROR: Could not read the FRN configuration file: $CFG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
source <(grep -v "^\[" < $CFG_FILE)
|
||||
|
||||
# Check syntax of configuration variables
|
||||
if ! [[ "$CALLSIGN_AND_USER" =~ ^.+,\ .+$ ]]; then
|
||||
echo "*** ERROR: CALLSIGN_AND_USER not set or malformed." \
|
||||
"Should be \"Callsign, Name\""
|
||||
exit 1
|
||||
fi
|
||||
if ! [[ "$EMAIL_ADDRESS" =~ ^.+@.+$ ]]; then
|
||||
echo "*** ERROR: EMAIL_ADDRESS not set or malformed." \
|
||||
"Should be \"user@example.org\""
|
||||
exit 1
|
||||
fi
|
||||
if ! [[ "$BAND_AND_CHANNEL" =~ ^PC\ Only$ || \
|
||||
"$BAND_AND_CHANNEL" =~ ^Crosslink$ ||
|
||||
"$BAND_AND_CHANNEL" =~ ^[0-9]+\.[0-9]+(AM|FM|DIG)(\ (CTC[0-9.]+|DSC[0-9]+|NONE))?$ ]]; then
|
||||
echo "*** ERROR: BAND_AND_CHANNEL not set or malformed." \
|
||||
"Should be \"PC Only\", \"Crosslink\" or ..."
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$COUNTRY" ]]; then
|
||||
echo "*** ERROR: COUNTRY not set."
|
||||
exit 1
|
||||
fi
|
||||
if ! [[ "$CITY_CITY_PART" =~ ^.+\ -\ .+$ ]]; then
|
||||
echo "*** ERROR: CITY_CITY_PART not set or malformed." \
|
||||
"Should be \"city - city_part\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--- About to register using the following information ---"
|
||||
echo "Callsign and user: ${CALLSIGN_AND_USER}"
|
||||
echo "E-mail address: ${EMAIL_ADDRESS}"
|
||||
echo "Band and channel: ${BAND_AND_CHANNEL}"
|
||||
echo "Description: ${DESCRIPTION}"
|
||||
echo "Country: ${COUNTRY}"
|
||||
echo "City and city part: ${CITY_CITY_PART}"
|
||||
echo
|
||||
|
||||
ANS=""
|
||||
until [[ "$ANS" =~ ^[yYnN] ]]; do
|
||||
read -p "OK [Y/N]? " ANS
|
||||
done
|
||||
|
||||
if [[ "$ANS" =~ ^[yY] ]]; then
|
||||
REG_CMD="IG:<ON>${CALLSIGN_AND_USER}</ON><EA>${EMAIL_ADDRESS}</EA><BC>${BAND_AND_CHANNEL}</BC><DS>${DESCRIPTION}</DS><NN>${COUNTRY}</NN><CT>${CITY_CITY_PART}</CT>"
|
||||
|
||||
echo "Registering..."
|
||||
if ! exec 100<>/dev/tcp/${SYSMAN_SERVER}/${REG_PORT}; then
|
||||
echo "*** ERROR: Could not connect to system manager"
|
||||
exit 1
|
||||
fi
|
||||
echo "$REG_CMD" >&100
|
||||
if ! read -u 100 -t 30 ANS; then
|
||||
echo "*** ERROR: Could not read answer from system manager"
|
||||
exit 1
|
||||
fi
|
||||
# Remove trailing whitespace
|
||||
ANS=${ANS%%*([[:space:]])}
|
||||
echo -e "==$ANS=="
|
||||
case "$ANS" in
|
||||
OK)
|
||||
echo "Registration succeeded. An email will be sent to the email" \
|
||||
"address you specified."
|
||||
;;
|
||||
NU)
|
||||
echo "*** ERROR: Registration failed. A user with the chosen username" \
|
||||
"already exist."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "*** ERROR: Registration failed with error code \"$ANS\""
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "Aborted!"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
#ifndef MULTIRATE_FILTER_COEFF_INCLUDED
|
||||
#define MULTIRATE_FILTER_COEFF_INCLUDED
|
||||
|
||||
|
||||
/*
|
||||
First stage 48kHz <-> 16kHz (3.5kHz cut-off)
|
||||
This is an intermediate filter meant to be used to downsample to 8kHz.
|
||||
|
||||
Parks-McClellan FIR Filter Design
|
||||
|
||||
Filter type: Low pass
|
||||
Passband: 0 - 0.07291666666666666667 (0 - 3500Hz)
|
||||
Order: 29
|
||||
Passband ripple: 0.1 dB
|
||||
Transition band: 0.09375 (4500Hz)
|
||||
Stopband attenuation: 60.0 dB
|
||||
*/
|
||||
static const int coeff_48_16_int_taps = 30;
|
||||
static const float coeff_48_16_int[coeff_48_16_int_taps] =
|
||||
{
|
||||
-0.001104533022845565,
|
||||
1.4483111628894497E-4,
|
||||
0.0030143616079341333,
|
||||
0.007290576776838937,
|
||||
0.010111003515779919,
|
||||
0.007406824406566465,
|
||||
-0.0033299650331323396,
|
||||
-0.019837606041858764,
|
||||
-0.03369491630668587,
|
||||
-0.03261321520115128,
|
||||
-0.006227597046237875,
|
||||
0.0472474773894006,
|
||||
0.11741132225100549,
|
||||
0.18394793387595304,
|
||||
0.22449383849677723,
|
||||
0.22449383849677723,
|
||||
0.18394793387595304,
|
||||
0.11741132225100549,
|
||||
0.0472474773894006,
|
||||
-0.006227597046237875,
|
||||
-0.03261321520115128,
|
||||
-0.03369491630668587,
|
||||
-0.019837606041858764,
|
||||
-0.0033299650331323396,
|
||||
0.007406824406566465,
|
||||
0.010111003515779919,
|
||||
0.007290576776838937,
|
||||
0.0030143616079341333,
|
||||
1.4483111628894497E-4,
|
||||
-0.001104533022845565
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
48kHz <-> 16kHz (5.5kHz cut-off)
|
||||
|
||||
Parks-McClellan FIR Filter Design
|
||||
|
||||
Filter type: Low pass
|
||||
Passband: 0 - 0.1145833333333333333 (0 - 5500Hz)
|
||||
Order: 49
|
||||
Passband ripple: 0.1 dB
|
||||
Transition band: 0.05208333333333333333 (2500Hz)
|
||||
Stopband attenuation: 60.0 dB
|
||||
*/
|
||||
static const int coeff_48_16_taps = 50;
|
||||
static const float coeff_48_16[coeff_48_16_taps] =
|
||||
{
|
||||
-0.0006552324784575,
|
||||
-0.0023665474931056,
|
||||
-0.0046009521986267,
|
||||
-0.0065673940075750,
|
||||
-0.0063452223170932,
|
||||
-0.0030442928485507,
|
||||
0.0027216740916904,
|
||||
0.0079365191173948,
|
||||
0.0088820372171036,
|
||||
0.0034577679862077,
|
||||
-0.0063356171066514,
|
||||
-0.0145569576678951,
|
||||
-0.0143873806232840,
|
||||
-0.0031353455170217,
|
||||
0.0143500967202013,
|
||||
0.0267723137455069,
|
||||
0.0227432656734411,
|
||||
-0.0007785303731755,
|
||||
-0.0333072891420923,
|
||||
-0.0533991698157678,
|
||||
-0.0390764894652067,
|
||||
0.0189267202445683,
|
||||
0.1088868590088443,
|
||||
0.2005613197280159,
|
||||
0.2583048205906900,
|
||||
0.2583048205906900,
|
||||
0.2005613197280159,
|
||||
0.1088868590088443,
|
||||
0.0189267202445683,
|
||||
-0.0390764894652067,
|
||||
-0.0533991698157678,
|
||||
-0.0333072891420923,
|
||||
-0.0007785303731755,
|
||||
0.0227432656734411,
|
||||
0.0267723137455069,
|
||||
0.0143500967202013,
|
||||
-0.0031353455170217,
|
||||
-0.0143873806232840,
|
||||
-0.0145569576678951,
|
||||
-0.0063356171066514,
|
||||
0.0034577679862077,
|
||||
0.0088820372171036,
|
||||
0.0079365191173948,
|
||||
0.0027216740916904,
|
||||
-0.0030442928485507,
|
||||
-0.0063452223170932,
|
||||
-0.0065673940075750,
|
||||
-0.0046009521986267,
|
||||
-0.0023665474931056,
|
||||
-0.0006552324784575
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
8kHz <-> 16kHz
|
||||
|
||||
Parks-McClellan FIR Filter Design
|
||||
|
||||
Filter type: Low pass
|
||||
Passband: 0 - 0.21875 (0 - 3500Hz)
|
||||
Order: 89
|
||||
Passband ripple: 0.1 dB
|
||||
Transition band: 0.03125 (500Hz)
|
||||
Stopband attenuation: 62.0 dB
|
||||
*/
|
||||
static const int coeff_16_8_taps = 90;
|
||||
static const float coeff_16_8[coeff_16_8_taps] =
|
||||
{
|
||||
4.4954770039301524E-4,
|
||||
-8.268172996066966E-4,
|
||||
-0.002123078315145856,
|
||||
-0.0015479438021244402,
|
||||
7.273225897575334E-4,
|
||||
0.0013974534015721682,
|
||||
-7.334976988828609E-4,
|
||||
-0.0019468497129111343,
|
||||
4.1355600739715313E-4,
|
||||
0.002536269673526767,
|
||||
1.5022005765340837E-4,
|
||||
-0.003101672879509627,
|
||||
-9.95458834752388E-4,
|
||||
0.00354467345212626,
|
||||
0.0021278523715996304,
|
||||
-0.0037661500010028543,
|
||||
-0.00353539274926452,
|
||||
0.0036538076631845626,
|
||||
0.005173997894832533,
|
||||
-0.003092155201519595,
|
||||
-0.006964869006639621,
|
||||
0.001972228534636602,
|
||||
0.008799395727660558,
|
||||
-1.908879053321082E-4,
|
||||
-0.01053574038718076,
|
||||
-0.0023470042371114453,
|
||||
0.011994344679012392,
|
||||
0.005724529332766167,
|
||||
-0.012958939230749365,
|
||||
-0.010021252057195512,
|
||||
0.013170597031930194,
|
||||
0.015338845914920506,
|
||||
-0.012300860896401845,
|
||||
-0.021850249720503187,
|
||||
0.009887401534293974,
|
||||
0.029911674274011077,
|
||||
-0.0051694230705885726,
|
||||
-0.04035692286061595,
|
||||
-0.0034027067537959477,
|
||||
0.05542257393205645,
|
||||
0.01998932901259646,
|
||||
-0.08281607098012608,
|
||||
-0.0619525333134873,
|
||||
0.17225790685629527,
|
||||
0.42471952920395545,
|
||||
0.42471952920395545,
|
||||
0.17225790685629527,
|
||||
-0.0619525333134873,
|
||||
-0.08281607098012608,
|
||||
0.01998932901259646,
|
||||
0.05542257393205645,
|
||||
-0.0034027067537959477,
|
||||
-0.04035692286061595,
|
||||
-0.0051694230705885726,
|
||||
0.029911674274011077,
|
||||
0.009887401534293974,
|
||||
-0.021850249720503187,
|
||||
-0.012300860896401845,
|
||||
0.015338845914920506,
|
||||
0.013170597031930194,
|
||||
-0.010021252057195512,
|
||||
-0.012958939230749365,
|
||||
0.005724529332766167,
|
||||
0.011994344679012392,
|
||||
-0.0023470042371114453,
|
||||
-0.01053574038718076,
|
||||
-1.908879053321082E-4,
|
||||
0.008799395727660558,
|
||||
0.001972228534636602,
|
||||
-0.006964869006639621,
|
||||
-0.003092155201519595,
|
||||
0.005173997894832533,
|
||||
0.0036538076631845626,
|
||||
-0.00353539274926452,
|
||||
-0.0037661500010028543,
|
||||
0.0021278523715996304,
|
||||
0.00354467345212626,
|
||||
-9.95458834752388E-4,
|
||||
-0.003101672879509627,
|
||||
1.5022005765340837E-4,
|
||||
0.002536269673526767,
|
||||
4.1355600739715313E-4,
|
||||
-0.0019468497129111343,
|
||||
-7.334976988828609E-4,
|
||||
0.0013974534015721682,
|
||||
7.273225897575334E-4,
|
||||
-0.0015479438021244402,
|
||||
-0.002123078315145856,
|
||||
-8.268172996066966E-4,
|
||||
4.4954770039301524E-4
|
||||
};
|
||||
|
||||
|
||||
#endif /* MULTIRATE_FILTER_COEFF_INCLUDED */
|
||||
|
|
@ -18,6 +18,7 @@ MODULE_TCL_VOICE_MAIL=1.0.0
|
|||
MODULE_SELCALLENC=1.0.0
|
||||
MODULE_DTMF_REPEATER=1.0.1
|
||||
MODULE_METARINFO=1.0.1
|
||||
MODULE_FRN=1.0.0
|
||||
|
||||
# Version for the RemoteTrx application
|
||||
REMOTE_TRX=1.2.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue