From 322909c6f2916b3742b9384fd29b4feed0b6cda5 Mon Sep 17 00:00:00 2001 From: Rob French Date: Mon, 25 May 2020 23:11:36 -0500 Subject: [PATCH] Added missing files to commit. --- ubitx_iop/RigMode.h | 307 ++++++++++++++++++++++++++++++++++++ ubitx_iop/TxSwitch.h | 249 +++++++++++++++++++++++++++++ ubitx_iop/rig.h | 264 +++++++++++++++++++++++++++++++ ubitx_iop/tx_audio_proc.h | 46 ++++++ ubitx_iop/tx_audio_proc.ino | 51 ++++++ 5 files changed, 917 insertions(+) create mode 100644 ubitx_iop/RigMode.h create mode 100644 ubitx_iop/TxSwitch.h create mode 100644 ubitx_iop/rig.h create mode 100644 ubitx_iop/tx_audio_proc.h create mode 100644 ubitx_iop/tx_audio_proc.ino diff --git a/ubitx_iop/RigMode.h b/ubitx_iop/RigMode.h new file mode 100644 index 0000000..fcb4291 --- /dev/null +++ b/ubitx_iop/RigMode.h @@ -0,0 +1,307 @@ +//====================================================================== +// RigMode.h +//====================================================================== + +#ifndef __RigMode_h__ +#define __RigMode_h__ + +// Exceptions not active (for Arduino, I assume?). +//enum IModeExceptions { +// MODE_ALREADY_ACTIVE = -1, +// MODE_STILL_TRANSMITTING = -2, +// MODE_ALREADY_TRANSMITTING = -3, +// MODE_NOT_TRANSMITTING = -4, +//}; + +//====================================================================== +// IMode +// +// Interface to a rig mode (SSB, CW, etc.). +//====================================================================== + +class IMode { + public: + + IMode(IConfig& c, RigAudio& a): + _config(c), _audio(a), _active(false), _transmitting(false) + { + } + + virtual ~IMode() {} + + inline IConfig& config() { return _config; } + + inline RigAudio& audio() { return _audio; } + + // Called upon mode entry. Should assume that the rig's state is + // "clean", i.e. that it still needs to enable anything it needs for + // use in the mode. + virtual void onEntry() = 0; + + // Called upon mode exit. Should clean everything up that it used, + // and not make assumptions about which mode is being entered next. + virtual void onExit() = 0; + + // Called when transmitting. + virtual void onTx() = 0; + + // Called when receiving. + virtual void onRx() = 0; + + inline void enter() { + if (_active) { + // Do nothing. Exceptions not active (for Arduino, I assume?). + //throw MODE_ALREADY_ACTIVE; + USBDEBUG("ERROR - tried to enter mode, but it's already active"); + } else { + _active = true; + onEntry(); + } + } + + inline void exit() { + if (_transmitting) { + // Do nothing. Exceptions not active (for Arduino, I assume?). + //throw MODE_STILL_TRANSMITTING; + USBDEBUG("ERROR - tried to exit mode, but it's transmitting"); + } else { + _active = false; + onExit(); + } + } + + inline bool isActive() { return _active; } + inline bool isInactive() { return !_active; } + + inline void tx() { + if (_transmitting) { + // Do nothing. Exceptions not active (for Arduino, I assume?). + //throw MODE_ALREADY_TRANSMITTING; + USBDEBUG("ERROR - tried to start transmitting, but mode already is transmitting"); + } else { + _transmitting = true; + onTx(); + } + } + + inline void rx() { + if (!_transmitting) { + // Do nothing. Exceptions not active (for Arduino, I assume?). + //throw MODE_NOT_TRANSMITTING; + USBDEBUG("ERROR - tried to start receiving, but mode already is receiving"); + } else { + _transmitting = false; + onRx(); + } + } + + inline bool isTx() const { return _transmitting; } + inline bool isRx() const { return !_transmitting; } + + private: + + IConfig& _config; + RigAudio& _audio; + bool _active; + bool _transmitting; +}; + +//====================================================================== +// SSBMode +//====================================================================== + +class SSBMode : public IMode +{ + public: + + SSBMode(IConfig& c, RigAudio& a, bool default_mic=true): + IMode(c, a), _use_mic(default_mic) + { + } + + virtual void onEntry() + { + audio().unmuteRx(); + audio().muteAllTx(); + USBDEBUG("SSB mode entered"); + } + + virtual void onExit() { + audio().muteAllTx(); + audio().muteRx(); + USBDEBUG("SSB mode exited"); + } + + virtual void onTx() + { + audio().muteRx(); + if (_use_mic) { + audio().unmuteMicIn(); + } else { + audio().unmuteLineIn(); + } + USBDEBUG("SSB mode transmitting"); + } + + virtual void onRx() + { + if (_use_mic) { + audio().muteMicIn(); + } else { + audio().muteLineIn(); + } + audio().unmuteRx(); + USBDEBUG("SSB mode receiving"); + } + + void setMicIn() + { + if (isRx()) { + // can't switch inputs while already transmitting + _use_mic = true; + } + USBDEBUG("SSB mode - Mic In set"); + } + + void setLineIn() + { + if (isRx()) { + // can't switch inputs while already transmitting + _use_mic = false; + } + USBDEBUG("SSB mode - Line In set"); + } + + private: + + bool _use_mic; +}; + +//====================================================================== +// DGTMode +//====================================================================== + +class DGTMode : public IMode +{ + public: + + DGTMode(IConfig& c, RigAudio& a): + IMode(c, a) + { + } + + virtual void onEntry() + { + audio().unmuteRx(); + audio().muteAllTx(); + USBDEBUG("DGT mode entered"); + } + + virtual void onExit() { + audio().muteAllTx(); + audio().muteRx(); + USBDEBUG("DGT mode exited"); + } + + virtual void onTx() + { + audio().muteRx(); + audio().unmuteUSBIn(); + USBDEBUG("DGT mode transmitting"); + } + + virtual void onRx() + { + audio().muteUSBIn(); + audio().unmuteRx(); + USBDEBUG("DGT mode receiving"); + } +}; + + +//====================================================================== +// CWMode +//====================================================================== + +class CWMode : public IMode +{ + public: + + CWMode(IConfig& c, RigAudio& a): + IMode(c, a) + { + } + + virtual void onEntry() + { + audio().unmuteRx(); + audio().muteAllTx(); + USBDEBUG("CW mode entered"); + } + + virtual void onExit() { + audio().muteAllTx(); + audio().muteRx(); + USBDEBUG("CW mode exited"); + } + + virtual void onTx() + { + // Currently not muting Rx, since the uBITX produces it's own + // sidetone... but I'm probably going to replace that with a S/W- + // generated sidetone. + USBDEBUG("CW mode transmitting"); + } + + virtual void onRx() + { + USBDEBUG("CW mode receiving"); + } +}; + +//====================================================================== +// TTMode +//====================================================================== + +class TTMode : public IMode +{ + public: + + TTMode(IConfig& c, RigAudio& a): + IMode(c, a) + { + } + + virtual void onEntry() + { + audio().unmuteRx(); + audio().muteAllTx(); + USBDEBUG("Test (Two-Tone) mode entered"); + } + + virtual void onExit() { + audio().muteAllTx(); + audio().muteRx(); + USBDEBUG("Test (Two-Tone) mode exited"); + } + + virtual void onTx() + { + audio().muteRx(); + audio().unmuteTTIn(); + USBDEBUG("Test (Two-Tone) mode transmitting"); + } + + virtual void onRx() + { + audio().muteTTIn(); + audio().unmuteRx(); + USBDEBUG("Test (Two-Tone) mode receiving"); + } +}; + +#endif + +//====================================================================== +// EOF +//====================================================================== diff --git a/ubitx_iop/TxSwitch.h b/ubitx_iop/TxSwitch.h new file mode 100644 index 0000000..e13db89 --- /dev/null +++ b/ubitx_iop/TxSwitch.h @@ -0,0 +1,249 @@ +//====================================================================== +// TxSwitch.h +//====================================================================== + +#ifndef __TxSwitch_h__ +#define __TxSwitch_h__ + +#include +#define BOUNCE_WITH_PROMPT_DETECTION + +#include "ubitx_iop.h" +#include "rig.h" + +#define MIC_PTT_PIN 21 +#define LINE_PTT_PIN 20 + +//---------------------------------------------------------------------- +// ITxSwitch +// +// Interface for transmit (PTT, Key) switches. onPress() is called +// before transmission begins, and should return true if transmission +// should start. onRelease() is called before transmission ends, and +// should return true if transmission should in fact end. +//---------------------------------------------------------------------- + +class ITxSwitch +{ + public: + virtual ~ITxSwitch() {} + + // Called before beginning transmit; if false, transmit aborts before starting. + virtual bool onPress(IMode* m) = 0; + + // Called before stopping tranmit; if false, transmit continues. + virtual bool onRelease(IMode* m) = 0; + + void press(IMode* m, bool output_enable=true) { + if (onPress(m)) { + USBDEBUG("PTT pressed"); + m->tx(); + if (output_enable) { + setKeyDown(); // NOTE: could still make this more configurable... + } + } + } + + void release(IMode* m, bool output_enable=true) { + if (onRelease(m)) { + USBDEBUG("PTT released"); + if (output_enable) { + setKeyUp(); // NOTE: could still make this more configurable... + } + m->rx(); + } + } +}; + +//---------------------------------------------------------------------- +// CATSwitch +// +// Implementation of the ITxSwitch interface for the CAT control. In +// general, CAT cannot override any existing transmission, and cannot +// terminate an existing transmission. +//---------------------------------------------------------------------- + +class CATSwitch : public ITxSwitch +{ + public: + CATSwitch(): _transmitting(false) {} + + virtual bool onPress(IMode* m) { + // If another transmission is already occuring, abort... CAT can't + // interrupt transmissions already ongoing. + if (m->isRx()) { + USBDEBUG("CAT PTT pressed"); + _transmitting = true; + return true; + } else { + return false; + } + } + + virtual bool onRelease(IMode* m) { + // If CAT transmission is not occurring, abort... CAT can't stop + // transmissions initiated by other sources. We don't check if + // the mode is already transmitting, because it could be + // transmitting because of CAT. + if (_transmitting) { + USBDEBUG("CAT PTT released"); + _transmitting = false; + return true; + } else { + return false; + } + } + + private: + bool _transmitting; // CAT-specific transmission +}; + +/* +//---------------------------------------------------------------------- +// MicSwitch +// +// Implementation of the ITxSwitch interface for the Mic (front panel) +// switch. Features: +// - In SSB, it will automatically select the Mic (front panel) input. +// - If already transmitting, press will have no effect. +// - If already transmitting (any mode, source), release will stop +// the transmission... a failsafe. +//---------------------------------------------------------------------- + +class MicSwitch : public ITxSwitch +{ + public: + MicSwitch(): _ssb_mode(false) {} + + inline void setSSBMode(bool flag) { _ssb_mode = flag; } + + virtual bool onPress(IMode* m) { + if (m->isRx()) { + if (_ssb_mode) { + ((SSBMode*)m)->setMicIn(); + } + return true; + } else { + return false; + } + } + + virtual bool onRelease(IMode* m) { + if (m->isTx()) { + return true; + } else { + return false; + } + } + + private: + bool _ssb_mode; +}; + +//---------------------------------------------------------------------- +// LineSwitch +// +// Implementation of the ITxSwitch interface for the Line (rear panel) +// switch. Features: +// - In SSB, it will automatically select the Line (rear panel) input. +// - If already transmitting, press will have no effect. +// - If already transmitting (any mode, source), release will stop +// the transmission... a failsafe. +//---------------------------------------------------------------------- + +class LineSwitch : public ITxSwitch +{ + public: + LineSwitch(): _ssb_mode(false) {} + + inline void setSSBMode(bool flag) { _ssb_mode = flag; } + + virtual bool onPress(IMode* m) { + if (m->isRx()) { + if (_ssb_mode) { + ((SSBMode*)m)->setLineIn(); + } + return true; + } else { + return false; + } + } + + virtual bool onRelease(IMode* m) { + if (m->isTx()) { + return true; + } else { + return false; + } + } + + private: + bool _ssb_mode; +}; +*/ + +//---------------------------------------------------------------------- +// GPIOSwitch +// +// Class used to implement the physical transmit switches (i.e. the +// Mic and Line input PTTs which are connected to GPIO pins). Takes +// an object implementing the ITxSwitch interface, as well as info for +// setting up a debounced pin. +//---------------------------------------------------------------------- + +class GPIOSwitch : public ITxSwitch +{ + public: + GPIOSwitch(bool is_mic, int pin, int msec=25): _is_mic(false), _ssb_mode(false), _bounce() { + _bounce.attach(pin, INPUT_PULLUP); + _bounce.interval(msec); + } + + inline void setSSBMode(bool flag) { _ssb_mode = flag; } + + virtual bool onPress(IMode* m) { + if (m->isRx()) { + if (_ssb_mode) { + if (_is_mic) { + USBDEBUG("Mic PTT pressed"); + ((SSBMode*)m)->setMicIn(); + } else { + USBDEBUG("Line PTT pressed"); + ((SSBMode*)m)->setLineIn(); + } + } + return true; + } else { + return false; + } + } + + virtual bool onRelease(IMode* m) { + if (m->isTx()) { + return true; + } else { + return false; + } + } + + void update(IMode* m, bool output_enable=true) { + _bounce.update(); + + if (_bounce.fell()) { + press(m, output_enable); + } else if (_bounce.rose()) { + release(m, output_enable); + } + } + + private: + bool _is_mic; + bool _ssb_mode; + Bounce _bounce; +}; + +#endif + +//====================================================================== +// EOF +//====================================================================== diff --git a/ubitx_iop/rig.h b/ubitx_iop/rig.h new file mode 100644 index 0000000..305757c --- /dev/null +++ b/ubitx_iop/rig.h @@ -0,0 +1,264 @@ +//====================================================================== +// rig.h +//====================================================================== + +#ifndef __rig_h__ +#define __rig_h__ + +#include +#include "audio.h" +#include "RigMode.h" + +//====================================================================== +// Rig class +//====================================================================== + +class Rig { + public: + + //-------------------------------------------------------------------- + // Constructor/destructor. + //-------------------------------------------------------------------- + + Rig(RigConfig& c, RigAudio& a): _config(c), _audio(a) + { + _modes = new IMode*[NUM_RIG_MODES]; + _modesLen = NUM_RIG_MODES; + _modesIndex = c.mode; + _modes[RIG_MODE_LSB] = new SSBMode(c.lsb, a); + _modes[RIG_MODE_USB] = new SSBMode(c.usb, a); + _modes[RIG_MODE_CWL] = new CWMode(c.cwl, a); + _modes[RIG_MODE_CWU] = new CWMode(c.cwu, a); + _modes[RIG_MODE_DGL] = new DGTMode(c.dgl, a); + _modes[RIG_MODE_DGU] = new DGTMode(c.dgu, a); + _modes[RIG_MODE_TTL] = new TTMode(c.ttl, a); + _modes[RIG_MODE_TTU] = new TTMode(c.ttu, a); +/* + _rxFilters = new (IFilter*)[NUM_RX_FILTERS]; + _rxFiltersLen = NUM_RX_FILTERS; + _rxFiltersIndex = 0; // need to get default filter from current mode... + _rxFilters[RX_FILTER_WIDE] = mode()->filterWide(); + _rxFilters[RX_FILTER_MEDIUM] = mode()->filterMedium(); + _rxFilters[RX_FILTER_NARROW] = mode()->filterNarrow(); +*/ + } + + ~Rig() + { + for (int i = 0; i < _modesLen; i++) { + delete _modes[i]; + } + delete _modes; + } + + //-------------------------------------------------------------------- + // Mode control. + //-------------------------------------------------------------------- + + inline bool isSSBMode() const { return (_modesIndex == RIG_MODE_USB || _modesIndex == RIG_MODE_LSB); } + inline bool isUSBMode() const { return (_modesIndex == RIG_MODE_USB); } + inline bool isLSBMode() const { return (_modesIndex == RIG_MODE_LSB); } + inline bool isDGTMode() const { return (_modesIndex == RIG_MODE_DGU || _modesIndex == RIG_MODE_DGL); } + inline bool isDGUMode() const { return (_modesIndex == RIG_MODE_DGU); } + inline bool isDGLMode() const { return (_modesIndex == RIG_MODE_DGL); } + inline bool isCWMode() const { return (_modesIndex == RIG_MODE_CWU || _modesIndex == RIG_MODE_CWL); } + inline bool isCWUMode() const { return (_modesIndex == RIG_MODE_CWU); } + inline bool isCWLMode() const { return (_modesIndex == RIG_MODE_CWL); } + inline bool isTTMode() const { return (_modesIndex == RIG_MODE_TTU || _modesIndex == RIG_MODE_TTL); } + inline bool isTTUMode() const { return (_modesIndex == RIG_MODE_TTU); } + inline bool isTTLMode() const { return (_modesIndex == RIG_MODE_TTL); } + + // Returns a pointer to the current mode. + inline IMode* mode() const { + return _modes[_modesIndex]; + } + + inline RigMode modeNum() const { + return RigMode(_modesIndex); + } + +/* // Returns a pointer to the specified mode. + inline IMode* mode(RigMode m) const { + return _modes[m]; + }*/ + + // Switch to the mode specified by the provided index. Returns a + // pointer to the new mode. + IMode* switchMode(RigMode i) { + // exit the previous mode + mode()->exit(); // NOTE: This could currently occur during TX, which is NOT desirable. + + // select the new mode + _modesIndex = i % _modesLen; // do I need the modulo? + + //enter the new mode + mode()->enter(); + + // return a pointer to the new mode + return mode(); + } + + // Advance to the next (or previous) mode. Returns a pointer to the + // new mode. + IMode* switchMode(bool prev=false) { + + // exit the previous mode + mode()->exit(); + + // select the new mode + if (prev) { + _modesIndex--; + } else { + _modesIndex++; + } + _modesIndex %= _modesLen; + + // enter the new mode + mode()->enter(); + + // return a pointer to the new mode + return mode(); + } + + //-------------------------------------------------------------------- + // Transmit/Receive (T/R) control. + //-------------------------------------------------------------------- + + inline bool isTx() const { return mode()->isTx(); } + inline bool isRx() const { return mode()->isRx(); } + + // Enter the transmit state. This is delegated to the mode. + inline void tx() { mode()->tx(); } + + // Enter the receive state. This is delegated to the mode. + inline void rx() { mode()->rx(); } + + //-------------------------------------------------------------------- + // Transmit processor control. + //-------------------------------------------------------------------- + + void switchProcessor(bool prev=false) { + + } + + //-------------------------------------------------------------------- + // RX filter control. + //-------------------------------------------------------------------- +/* + // Returns a pointer to the current RX filter. + IFilter* rxFilter() { + return _rxFilters[_rxFiltersIndex]; + } + + IFilter* switchRxFilter(bool prev=false) { + if (prev) { + _rxFiltersIndex--; + } else { + _rxFiltersIndex++; + } + _rxFiltersIndex %= _rxFiltersLen; + return _rxFiltersList[_rxFiltersIndex]; + } + + IFilter* switchRxFilterWide() { + } + + IFilter* switchFilterMedium() { + + } + + IFilter* switchFilterNarrow() { + + } +*/ + // Audio output control. + + inline void muteSpkrOut() const { + _audio.muteSpkrOut(); + } + + inline void unmuteSpkrOut() const { + _audio.unmuteSpkrOut(); + } + + inline void muteLineOut() const { + _audio.muteLineOut(); + } + + inline void unmuteLineOut() const { + _audio.unmuteLineOut(); + } + + inline void muteUSBOut() const { + _audio.muteUSBOut(); + } + + inline void unmuteUSBOut() const { + _audio.unmuteUSBOut(); + } + + // Audio input control. + + inline void muteAllTx() const { + _audio.muteAllTx(); + } + + inline void muteMicIn() const { + _audio.muteMicIn(); + } + + inline void unmuteMicIn() const { + _audio.unmuteMicIn(); + } + + inline void muteLineIn() const { + _audio.muteLineIn(); + } + + inline void unmuteLineIn() const { + _audio.unmuteLineIn(); + } + + inline void muteUSBIn() const { + _audio.muteUSBIn(); + } + + inline void unmuteUSBIn() const { + _audio.unmuteUSBOut(); + } + + inline void muteTTIn() const { + _audio.muteTTIn(); + } + + inline void unmuteTTIn() const { + _audio.unmuteTTIn(); + } + + // Update the rig state. This should be called once each time through + // the main loop. + void update() + { + } + + private: + + RigConfig& _config; + RigAudio& _audio; + + IMode** _modes; + uint8_t _modesLen; + uint8_t _modesIndex; + +/* + IFilter** _rxFilters; + uint8_t _rxFiltersLen; + uint8_t _rxFiltersIndex; +*/ +}; + +#endif + +//====================================================================== +// EOF +//====================================================================== diff --git a/ubitx_iop/tx_audio_proc.h b/ubitx_iop/tx_audio_proc.h new file mode 100644 index 0000000..9df9834 --- /dev/null +++ b/ubitx_iop/tx_audio_proc.h @@ -0,0 +1,46 @@ +//====================================================================== +// tx_audio_proc.h +// +// Classes/functions for processing transmit audio. +//====================================================================== + +#ifndef __tx_audio_proc_h__ +#define __tx_audio_proc_h__ + +#include +#include + +class IAudioProcessor +{ + public: + virtual ~IAudioProcessor() {} + virtual bool isEnabled() const = 0; + virtual void enable() = 0; + virtual void disable() = 0; + virtual void update() = 0; +}; + +class SpeechCompressor : public IAudioProcessor +{ + public: + SpeechCompressor(AudioEffectCompressor&, AudioAmplifier&, AudioAnalyzeRMS&); + //void config(AudioEffectCompressor*, AudioAnalyzeRMS*); + virtual bool isEnabled() const; + virtual void enable(); + virtual void disable(); + virtual void update(); + + private: + AudioEffectCompressor& _compress; + AudioAmplifier& _amp; + AudioAnalyzeRMS& _rms; + float _env = 0.0; + float _alpha = 0.8; + bool _enabled = false; +}; + +#endif + +//====================================================================== +// EOF +//====================================================================== diff --git a/ubitx_iop/tx_audio_proc.ino b/ubitx_iop/tx_audio_proc.ino new file mode 100644 index 0000000..72b519a --- /dev/null +++ b/ubitx_iop/tx_audio_proc.ino @@ -0,0 +1,51 @@ +//====================================================================== +// tx_audio_proc.ino +// +// Classes/functions for processing transmit audio. +//====================================================================== + +#include "tx_audio_proc.h" + +SpeechCompressor::SpeechCompressor(AudioEffectCompressor& comp, AudioAmplifier& amp, AudioAnalyzeRMS& rms): +_compress(comp), _amp(amp), _rms(rms) +{ +} + +//void SpeechCompressor::config(AudioEffectCompressor* comp, AudioAnalyzeRMS* rms) +//{ +// _compress = comp; +// _rms = rms; +//} + +bool SpeechCompressor::isEnabled() const +{ + return _enabled; +} + +void SpeechCompressor::enable() +{ + _enabled = true; + _compress.begin(1, 0.5, 4); // Need to make configurable +} + +void SpeechCompressor::disable() +{ + _enabled = false; + _compress.disable(); +} + +// Speech compressor code based on post by 'hyperdyne': https://forum.pjrc.com/threads/36245-Changing-Pitch-of-Voice + +void SpeechCompressor::update() +{ + float rms_cur; + if (_enabled && _rms.available()) { + rms_cur = _rms.read(); + _env = rms_cur + (_alpha * (_env - rms_cur)); + _compress.update_pwr(_env); + } +} + +//====================================================================== +// EOF +//======================================================================