diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 150f491..af33140 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -87,7 +87,8 @@ include_directories( external scan server - sigproc + sigproc + util external/PiScan-protobuf external/loguru external/tinyxml diff --git a/src/core/PiScan.h b/src/core/PiScan.h index 7adb21d..44f1a2c 100644 --- a/src/core/PiScan.h +++ b/src/core/PiScan.h @@ -29,10 +29,11 @@ ScannerContext getScannerContext(); void setTunerGain(float gain); void setDemodSquelch(float level); DemodContext getDemodContext(); -void audioMute(bool mute = true); +void squelchBreak(bool mute = true); long long getTunerSampleRate(); /* server functions */ void scannerContextUpdate(ScannerContext ctx); void demodContextUpdate(DemodContext ctx); +void signalLevelUpdate(int level); } diff --git a/src/core/ScannerSM.cpp b/src/core/ScannerSM.cpp index 0b3f92f..097819e 100644 --- a/src/core/ScannerSM.cpp +++ b/src/core/ScannerSM.cpp @@ -170,7 +170,7 @@ void ScannerSM::ST_Hold(EventData* data){ if(currentState != lastState || indexHold) _broadcastContextUpdate(); - DLOG_F(6, "Ext hold: %i", _externalHold.load()); + DLOG_F(8, "Ext hold: %i", _externalHold.load()); /* start receive if signal active */ if (_currentEntry->hasSignal()) { @@ -320,7 +320,7 @@ void ScannerSM::_broadcastContextUpdate() { } void ScannerSM::_enableAudioOut(bool en){ - app::audioMute(en); + app::squelchBreak(en); } void ScannerSM::giveMessage(shared_ptr message) { diff --git a/src/core/messages.h b/src/core/messages.h index 1c9c41a..2e2c553 100644 --- a/src/core/messages.h +++ b/src/core/messages.h @@ -114,6 +114,7 @@ struct ServerMessage : public Message { ~ServerMessage() {}; enum { CONTEXT_UPDATE, + SIGNAL_LEVEL, NOTIFY_ALL_CLIENTS, NOTIFY_USERS, diff --git a/src/piScan_backend.cpp b/src/piScan_backend.cpp index 6c5fc6c..bc333af 100644 --- a/src/piScan_backend.cpp +++ b/src/piScan_backend.cpp @@ -371,8 +371,8 @@ DemodContext app::getDemodContext(){ return DemodContext(demod.getTunerGain(), demod.getSquelch()); } -void app::audioMute(bool mute){ - demod.audioMute(mute); +void app::squelchBreak(bool mute){ + demod.squelchBreak(mute); } long long app::getTunerSampleRate() { @@ -397,6 +397,10 @@ void app::demodContextUpdate(DemodContext ctx){ connectionManager.giveMessage(make_shared(DEMOD, ServerMessage::CONTEXT_UPDATE, new DemodContext(ctx))); } +void app::signalLevelUpdate(int level){ + connectionManager.giveMessage(make_shared(DEMOD, ServerMessage::SIGNAL_LEVEL, new int(level))); +} + } using namespace piscan; diff --git a/src/server/DebugServer.cpp b/src/server/DebugServer.cpp index 12a6e81..438b82f 100644 --- a/src/server/DebugServer.cpp +++ b/src/server/DebugServer.cpp @@ -201,6 +201,10 @@ void DebugConsole::handleSystemInfo(const SystemInfo info){ } } +void DebugConsole::handleSignalLevel(const int level){ + std::cerr << "\r" << "Signal: " << level << "\t\t\t\t"; +} + void DebugServer::start(){ //this->_connection(new DebugConsole()); this->_host.requestConnection(_connection); diff --git a/src/server/DebugServer.h b/src/server/DebugServer.h index 7fb0246..3a30058 100644 --- a/src/server/DebugServer.h +++ b/src/server/DebugServer.h @@ -32,6 +32,7 @@ public: void contextUpdate(const DemodContext context); void handleSystemMessage(const GeneralMessage message); void handleSystemInfo(const SystemInfo info); + void handleSignalLevel(const int level); const std::string identifier() { return "Debug Console"; diff --git a/src/server/ServerManager.cpp b/src/server/ServerManager.cpp index 3102751..9dfc34e 100644 --- a/src/server/ServerManager.cpp +++ b/src/server/ServerManager.cpp @@ -225,6 +225,7 @@ int ServerManager::giveRequest(void* request){ void ServerManager::_handleMessage(std::shared_ptr message){ assert(message->destination == SERVER_MAN); auto msg = std::dynamic_pointer_cast(message); + int* level = nullptr; switch (msg->type) { case ServerMessage::CONTEXT_UPDATE: switch(message->source){ @@ -247,6 +248,12 @@ void ServerManager::_handleMessage(std::shared_ptr message){ disconnectClients(); _run = false; break; + case ServerMessage::SIGNAL_LEVEL: + DLOG_F(7, "Broadcast siglevel update"); + level = reinterpret_cast(msg->pData); + _broadcastSignalLevelUpdate(*level); + delete level; + break; default: break; } @@ -327,3 +334,12 @@ std::shared_ptr ServerManager::_makeContextRequest(ClientRequest* rq){ } } +void ServerManager::_broadcastSignalLevelUpdate(int level) { + for (size_t i = 0; i < MAX_CONNECTIONS; i++) { + if (_connections[i] != nullptr) { + Connection* con = _connections[i].get(); + con->handleSignalLevel(level); + } + } +} + diff --git a/src/server/ServerManager.h b/src/server/ServerManager.h index c5811ce..6088c28 100644 --- a/src/server/ServerManager.h +++ b/src/server/ServerManager.h @@ -76,6 +76,8 @@ private: void _broadcastGeneralMessage(unsigned char group, GeneralMessage& message); std::shared_ptr _makeContextRequest(ClientRequest* rq); + void _broadcastSignalLevelUpdate(int level); + }; } #endif /* SERVER_SERVERMANAGER_H_ */ diff --git a/src/server/SocketServer.cpp b/src/server/SocketServer.cpp index 124ed6a..e864ba0 100644 --- a/src/server/SocketServer.cpp +++ b/src/server/SocketServer.cpp @@ -134,6 +134,11 @@ void SocketConnection::handleSystemInfo(const SystemInfo info){ delete ctx; } +void SocketConnection::handleSignalLevel(const int level){ + //TODO + (void) level; +} + void SocketConnection::_startRead() { _socket.async_read_some(boost::asio::buffer(_readBuffer, READ_BUFFER_LENGTH), boost::bind(&SocketConnection::_handleRead, shared_from_this(), diff --git a/src/server/SocketServer.h b/src/server/SocketServer.h index e869e76..32e9ec5 100644 --- a/src/server/SocketServer.h +++ b/src/server/SocketServer.h @@ -41,6 +41,7 @@ public: void contextUpdate(const DemodContext context); void handleSystemMessage(const GeneralMessage message); void handleSystemInfo(const SystemInfo info); + void handleSignalLevel(const int level); tcp::socket& socket() { return _socket; }; diff --git a/src/server/connection.h b/src/server/connection.h index 0bcd2b4..4ec06a1 100644 --- a/src/server/connection.h +++ b/src/server/connection.h @@ -58,6 +58,7 @@ public: virtual void contextUpdate(const DemodContext context) = 0; virtual void handleSystemMessage(const GeneralMessage message) = 0; virtual void handleSystemInfo(const SystemInfo info) = 0; + virtual void handleSignalLevel(const int level) = 0; virtual const std::string identifier() = 0; diff --git a/src/sigproc/Demodulator.cpp b/src/sigproc/Demodulator.cpp index b264c89..5ca51ba 100644 --- a/src/sigproc/Demodulator.cpp +++ b/src/sigproc/Demodulator.cpp @@ -6,6 +6,7 @@ */ #include +#include #include "PiScan.h" #include "Demodulator.h" @@ -14,6 +15,7 @@ #define DEFAULT_SDR_SAMPLE_RATE 2048000 #define INIT_FREQUENCY 100000000 #define NUM_RATES_DEFAULT 4 +#define SIGLEVEL_REFRESH_INTERVAL 250 // milliseconds using namespace piscan; @@ -145,6 +147,14 @@ void Demodulator::start(){ //setModem(NFM); _demodMgr.setActiveDemodulator(_demods[NFM], false); + //create signal level refresh timer + std::function func([this](){ + int level = getSignalStrength(); + LOG_F(7, "Signal strength %i", level); + app::signalLevelUpdate(level); + }); + _sigLevelRefresher.create(SIGLEVEL_REFRESH_INTERVAL, func); + //auto message = std::make_shared(DEMOD, ControllerMessage::NOTIFY_READY); //_centralQueue.giveMessage(message); LOG_F(1, "Demodulator started"); @@ -334,7 +344,10 @@ float Demodulator::getSquelch(){ return _squelchLevel; } -void Demodulator::audioMute(bool mute){ +void Demodulator::squelchBreak(bool mute){ + //mute = !mute; + mute ? _sigLevelRefresher.start() : _sigLevelRefresher.stop(); + _demodMgr.getCurrentModem()->setMuted(!mute); } diff --git a/src/sigproc/Demodulator.h b/src/sigproc/Demodulator.h index d37493b..d05e9ab 100644 --- a/src/sigproc/Demodulator.h +++ b/src/sigproc/Demodulator.h @@ -16,6 +16,7 @@ #include "SDRDeviceInfo.h" #include "Configuration.h" #include "synchronize.h" +#include "IntervalTimer.h" namespace piscan { @@ -65,7 +66,7 @@ public: void setTunerGain(float gain); float getTunerGain(); float getSquelch(); - void audioMute(bool mute); + void squelchBreak(bool mute); long long getTunerSampleRate(); private: @@ -80,7 +81,7 @@ private: std::map _demods; - + IntervalTimer _sigLevelRefresher; void _handleMessage(std::shared_ptr message); void _handleRequest(ClientRequest& request); diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 8c49db4..d635a15 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -2,6 +2,7 @@ project(piScan_backend_tests) add_executable(piScan_backend_tests EXCLUDE_FROM_ALL test_ScannerStateMachine.cpp) add_executable(testclient EXCLUDE_FROM_ALL testclient.cpp) +add_executable(test_utils EXCLUDE_FROM_ALL test_utils.cpp) option(DETACH_KERNEL_DRIVER "Detach kernel driver if loaded" OFF) if (DETACH_KERNEL_DRIVER) @@ -12,3 +13,4 @@ else (DETACH_KERNEL_DRIVER) endif (DETACH_KERNEL_DRIVER) target_link_libraries(testclient server external pthread boost_system protobuf dl) +target_link_libraries(test_utils pthread) diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp new file mode 100644 index 0000000..84dc675 --- /dev/null +++ b/src/tests/test_utils.cpp @@ -0,0 +1,76 @@ +/* + * test_utils.cpp + * + * Created on: Feb 12, 2020 + * Author: ezra + */ + +#include +#include +#include + +#include "IntervalTimer.h" + +using piscan::IntervalTimer; +using std::cout; +using std::chrono::milliseconds; + +int main(int argc, char** argv){ + (void) argc; + (void) argv; + + cout << "testing IntervalTimer" << std::endl; + size_t runCount = 0; + auto func = std::function([&runCount](){ + runCount++; + cout << "\ttimer running\n"; + }); + + IntervalTimer it; + cout << "create 10ms timer\n"; + it.create(10, func); + cout << "start timer\n"; + it.start(); + cout << "waiting 50ms\n"; + std::this_thread::sleep_for(milliseconds(50)); + cout << "stopping timer\n"; + it.stop(); + cout << "timer executed " << runCount << " times\n"; + + IntervalTimer& it2 = *(new IntervalTimer()); + cout << "recreating timer, 15ms\n"; + runCount = 0; + it2.create(15, func); + cout << "waiting 50ms before starting\n"; + std::this_thread::sleep_for(milliseconds(50)); + cout << "start timer\n"; + it2.start(); + cout << "waiting 50ms\n"; + std::this_thread::sleep_for(milliseconds(50)); + cout << "destroy while timer is running\n"; + delete &it2; + cout << "timer destroyed\n"; + + IntervalTimer it3; + cout << "recreating timer, 20ms\n"; + runCount = 0; + it3.create(20, func); + cout << "start timer, 2 runs\n"; + it3.start(); + cout << "wait 50ms\n"; + std::this_thread::sleep_for(milliseconds(50)); + cout << "stop timer\n"; + it3.stop(); + cout << "ran " << runCount << " times. wait 50ms\n"; + std::this_thread::sleep_for(milliseconds(50)); + cout << "restart timer\n"; + it3.start(); + cout << "wait 50ms\n"; + std::this_thread::sleep_for(milliseconds(50)); + cout << "stop timer\n"; + it3.stop(); + cout << "total " << runCount << " runs\n"; + + return 0; +} + diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/util/IntervalTimer.h b/src/util/IntervalTimer.h new file mode 100644 index 0000000..e377ff8 --- /dev/null +++ b/src/util/IntervalTimer.h @@ -0,0 +1,133 @@ +/* + * IntervalTimer.h + * + * Created on: Feb 12, 2020 + * Author: ezra + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace piscan { + +using std::thread; +using std::mutex; +using std::condition_variable; +using std::atomic_bool; +using std::function; +using std::unique_lock; +using std::chrono::milliseconds; +using std::chrono::high_resolution_clock; +using std::chrono::duration_cast; + +class IntervalTimer { +public: + IntervalTimer(): _runTimer(false), _execute(false) {}; + ~IntervalTimer(){ + if(_execute.load()) + destroy(); + }; + + inline void create(long long interval, function func){ + _execute = true; + _runTimer = false; + _ready = false; + + _timerThread = thread([this, interval, func](){ + while(_execute){ + { + unique_lock lock(_mtx); + while(!_runTimer.load() && _execute.load()){ + _cv.wait(lock); + } + lock.unlock(); + + if(!_execute.load()) break; + } + + auto periodStart = high_resolution_clock::now(); + + func(); + + { + milliseconds remainingSleep(interval); + bool sleep = true; + + unique_lock lock(_sleepMtx); + + while(sleep){ + if(_sleepTime.wait_for(lock, remainingSleep, [this]{ return _stateChanged.load();})){ + //std::cout << "\t\ttimer state changed\n"; + _stateChanged = false; + break; + } + // should account for spurious wakeup + else{ + //std::cout << "\t\tstate change false\n"; + auto elapsed = high_resolution_clock::now() - periodStart; + remainingSleep -= duration_cast(elapsed); + //std::cout << "\t\t" << remainingSleep.count() << "\n"; + if(remainingSleep.count() <= 0){ + sleep = false; + //std::cout << "\t\ttimed out\n"; + } + } + } + //std::cout << "\t\twoken up\n"; + } + } + }); + _timerThread.detach(); + + } + + inline void start(){ + unique_lock lock(_mtx); + _runTimer = true; + _stateChanged = false; + _cv.notify_all(); + //unique_lock lock2(_sleepMtx); + //_sleepTime.notify_all(); + + } + + inline void stop(){ + //unique_lock lock(_mtx); + _runTimer = false; + _stateChanged = true; + unique_lock lock2(_sleepMtx); + _sleepTime.notify_all(); + } + +private: + inline void destroy(){ + //std::cout << "\tdestroy timer\n"; + _execute = false; + unique_lock lock(_mtx); + _cv.notify_all(); + stop(); + } + +private: + thread _timerThread; + atomic_bool _runTimer; + atomic_bool _execute; + atomic_bool _stateChanged{false}; + + mutex _mtx; + condition_variable _cv; + mutex _sleepMtx; + condition_variable _sleepTime; + bool _ready = false; +}; + +} /* namespace piscan */ + diff --git a/src/core/synchronize.h b/src/util/synchronize.h similarity index 100% rename from src/core/synchronize.h rename to src/util/synchronize.h diff --git a/src/core/threadname.h b/src/util/threadname.h similarity index 100% rename from src/core/threadname.h rename to src/util/threadname.h