2 Commits

12 changed files with 887 additions and 98 deletions

View File

@@ -214,12 +214,12 @@ void UBitxDSP::txUSBIn() {
/**********************************************************************/
// RX filter settings
const int minRxFilterLo = MIN_RX_FILTER_LO;
const int maxRxFilterHi = MAX_RX_FILTER_HI;
const int minRxFilterWidth = MIN_RX_FILTER_WIDTH;
const int maxRxFilterWidth = MAX_RX_FILTER_WIDTH;
const int minRxFilterCenter = MIN_RX_FILTER_CENTER;
const int maxRxFilterCenter = MAX_RX_FILTER_CENTER;
const float minRxFilterLo = MIN_RX_FILTER_LO;
const float maxRxFilterHi = MAX_RX_FILTER_HI;
const float minRxFilterWidth = MIN_RX_FILTER_WIDTH;
const float maxRxFilterWidth = MAX_RX_FILTER_WIDTH;
const float minRxFilterCenter = MIN_RX_FILTER_CENTER;
const float maxRxFilterCenter = MAX_RX_FILTER_CENTER;
/*!
* @brief Bypass the RX audio filter.
@@ -238,7 +238,7 @@ void UBitxDSP::updateRxFilter() {
rxFilter.begin(coefficients, NUM_COEFFICIENTS);
}
void UBitxDSP::setRxFilter(int lo, int hi) {
void UBitxDSP::setRxFilter(float lo, float hi) {
if (hi < lo + minRxFilterWidth) {
hi = lo + minRxFilterWidth;
}
@@ -256,7 +256,7 @@ void UBitxDSP::setRxFilter(int lo, int hi) {
updateRxFilter();
}
void UBitxDSP::setRxFilterLo(int lo) {
void UBitxDSP::setRxFilterLo(float lo) {
if (lo > state.rxFilterHi - minRxFilterWidth) {
lo = state.rxFilterHi - minRxFilterWidth;
}
@@ -267,7 +267,7 @@ void UBitxDSP::setRxFilterLo(int lo) {
updateRxFilter();
}
void UBitxDSP::setRxFilterHi(int hi) {
void UBitxDSP::setRxFilterHi(float hi) {
if (hi < state.rxFilterLo + minRxFilterWidth) {
hi = state.rxFilterLo + minRxFilterWidth;
}
@@ -278,27 +278,27 @@ void UBitxDSP::setRxFilterHi(int hi) {
updateRxFilter();
}
void UBitxDSP::setRxFilterWidth(int width) {
void UBitxDSP::setRxFilterWidth(float width) {
if (width < minRxFilterWidth) {
width = minRxFilterWidth;
} else if (width > maxRxFilterWidth) {
width = maxRxFilterWidth;
}
int center = (state.rxFilterHi + state.rxFilterLo) / 2;
int lo = center - (width / 2);
int hi = center + (width / 2);
float center = (state.rxFilterHi + state.rxFilterLo) / 2;
float lo = center - (width / 2);
float hi = center + (width / 2);
setRxFilter(lo, hi);
}
void UBitxDSP::setRxFilterCenter(int center) {
void UBitxDSP::setRxFilterCenter(float center) {
if (center < minRxFilterCenter) {
center = minRxFilterCenter;
} else if (center > maxRxFilterCenter) {
center = maxRxFilterCenter;
}
int width = state.rxFilterHi - state.rxFilterLo;
int lo = center - (width / 2);
int hi = center + (width / 2);
float width = state.rxFilterHi - state.rxFilterLo;
float lo = center - (width / 2);
float hi = center + (width / 2);
setRxFilter(lo, hi);
}

View File

@@ -9,12 +9,12 @@
#include <dynamicFilters.h>
#include "Debug.h"
#define MIN_RX_FILTER_LO 0
#define MAX_RX_FILTER_HI 5000
#define MIN_RX_FILTER_WIDTH 0
#define MAX_RX_FILTER_WIDTH 5000
#define MIN_RX_FILTER_CENTER 0
#define MAX_RX_FILTER_CENTER 5000
#define MIN_RX_FILTER_LO 0.0
#define MAX_RX_FILTER_HI 5000.0
#define MIN_RX_FILTER_WIDTH 0.0
#define MAX_RX_FILTER_WIDTH 5000.0
#define MIN_RX_FILTER_CENTER 0.0
#define MAX_RX_FILTER_CENTER 5000.0
#define DSP_MILLIS_PER_UPDATE 100
@@ -41,8 +41,8 @@ struct DSPState {
// RX audio output settings
// RX filter settings
int rxFilterLo = 300;
int rxFilterHi = 3000;
float rxFilterLo = 300.0;
float rxFilterHi = 3000.0;
// TX audio input settings
float txInLvl[4] = {0.5, 0.5, 0.0, 0.0};
@@ -86,11 +86,11 @@ class UBitxDSP {
// RX filter settings
void bypassRxFilter();
void setRxFilter(int lo, int hi);
void setRxFilterLo(int lo);
void setRxFilterHi(int hi);
void setRxFilterWidth(int width);
void setRxFilterCenter(int center);
void setRxFilter(float lo, float hi);
void setRxFilterLo(float lo);
void setRxFilterHi(float hi);
void setRxFilterWidth(float width);
void setRxFilterCenter(float center);
/*!
* @brief Get the current low frequency bound of the RX band pass filter.

View File

@@ -61,7 +61,7 @@ speed(wpm), symWeight(weight)
// Calculate the length of dot, dash and silence
void UBitxKeyer::calcRatio()
{
float w = (1 + symWeight) / (symWeight -1);
float w = (1.0 + symWeight) / (symWeight - 1.0);
spaceLen = (1200 / speed);
dotLen = spaceLen * (w - 1);
dashLen = (1 + w) * spaceLen;
@@ -73,6 +73,12 @@ void UBitxKeyer::setWPM(int wpm)
calcRatio();
}
void UBitxKeyer::setWeight(float weight)
{
symWeight = weight;
calcRatio();
}
//======================================================================
// Latch paddle press
//======================================================================
@@ -107,11 +113,10 @@ bool UBitxKeyer::doPaddles()
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
updatePaddleLatch();
keyerState = CHK_DIT;
// letting this fall through // return true;
} else {
return false;
return true;
}
// break;
return false;
//break;
case CHK_DIT: // See if the dit paddle was pressed
if (keyerControl & DIT_L) {
@@ -119,59 +124,57 @@ bool UBitxKeyer::doPaddles()
ktimer = dotLen;
keyerState = KEYED_PREP;
return true;
} else { // fall through
keyerState = CHK_DAH;
}
// fall through
keyerState = CHK_DAH;
case CHK_DAH: // See if dah paddle was pressed
if (keyerControl & DAH_L) {
ktimer = dashLen;
keyerState = KEYED_PREP;
// letting this fall through // return true;
return true;
} else {
keyerState = IDLE;
return false;
}
// break;
//break;
case KEYED_PREP: // Assert key down, start timing
// state shared for dit or dah
keyDown = true;
ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
keyerState = KEYED; // next state
// letting this fall through // return true;
// break;
case KEYED_PREP: // Assert key down, start timing
// state shared for dit or dah
keyDown = true;
ktimer += millis(); // set ktimer to interval end time
keyerControl &= ~(DIT_L + DAH_L); // clear both paddle latch bits
keyerState = KEYED; // next state
return true;
//break;
case KEYED: // Wait for timer to expire
if (millis() > ktimer) { // are we at end of key down ?
keyDown = false;
ktimer = millis() + spaceLen; // inter-element time
keyerState = INTER_ELEMENT; // next state
// letting this fall through // return true;
} else if (keyMode == IAMBICB) { // Iambic B Mode ?
updatePaddleLatch(); // yes, early paddle latch in Iambic B mode
} else {
return true;
}
// break;
case KEYED: // Wait for timer to expire
if (millis() > ktimer) { // are we at end of key down ?
keyDown = false;
ktimer = millis() + spaceLen; // inter-element time
keyerState = INTER_ELEMENT; // next state
return true;
} else if (keyMode == IAMBICB) { // Iambic B Mode ?
updatePaddleLatch(); // yes, early paddle latch in Iambic B mode
}
return true;
// break;
case INTER_ELEMENT: // Insert time between dits/dahs
updatePaddleLatch(); // latch paddle state
if (millis() > ktimer) { // are we at end of inter-space ?
if (keyerControl & DIT_PROC) { // was it a dit or dah ?
keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
keyerState = CHK_DAH; // dit done, check for dah
return true;
} else {
keyerControl &= ~(DAH_L); // clear dah latch
keyerState = IDLE; // go idle
return false;
}
} else {
updatePaddleLatch(); // latch paddle state
if (millis() > ktimer) { // are we at end of inter-space ?
if (keyerControl & DIT_PROC) { // was it a dit or dah ?
keyerControl &= ~(DIT_L + DIT_PROC); // clear two bits
keyerState = CHK_DAH; // dit done, check for dah
return true;
} else {
keyerControl &= ~(DAH_L); // clear dah latch
keyerState = IDLE; // go idle
return false;
}
// break;
}
return true;
//break;
}
return false; // resolve compiler warning; do we ever get here?

View File

@@ -44,10 +44,12 @@ public:
//void cw_pin(int pin);
//void ptt_pin(int pin);
void setWPM(int wpm);
inline int getWPM() { return speed; }
void setWeight(float weight);
inline float getWeight() { return symWeight; }
inline void setMode(int mode) { keyMode = mode; }
inline int getMode() { return keyMode; }
inline bool isDown() { return keyDown; }
// void setWeight();
bool doPaddles();

View File

@@ -3,15 +3,56 @@
#include "RigState.h"
#define DEFAULT_SSB_LO_CUT 300.0
#define DEFAULT_SSB_HI_CUT 3000.0
#define DEFAULT_CW_WIDTH 500.0
#define DEFAULT_LOW_USB false
#define DEFAULT_LOW_CWU true
#define DEFAULT_HIGH_USB true
#define DEFAULT_HIGH_CWU true
enum HamBand {
BAND_80M = 0,
BAND_60M,
BAND_40M,
BAND_30M,
BAND_20M,
BAND_17M,
BAND_15M,
BAND_12M,
BAND_10M,
NUM_BANDS
};
struct ModeConfig {
bool isUpper;
float dspLo;
float dspHi;
};
struct BandConfig {
ModeConfig cw;
ModeConfig ssb;
};
struct RigConfig {
//bool isData = false;
bool useUSBInput = true; // whether or not to use the USB input for data
};
class UBitxRig {
public:
UBitxRig();
inline void begin() {}
inline void update() {}
inline unsigned getFreqA() const { return radState.getFreqA(); }
inline unsigned getFreqB() const { return radState.getFreqB(); }
inline int getRIT() const { return radState.getRIT(); }
inline int getXIT() const { return radState.getXIT(); }
inline bool isVFOA() const { return radState.isVFOA(); }
inline bool isVFOB() const { return radState.isVFOB(); }
inline bool isSplit() const { return radState.isSplit(); }
@@ -22,12 +63,20 @@ class UBitxRig {
inline bool isModeCWR() const { return radState.isModeCWR(); }
inline bool isModeUSB() const { return radState.isModeUSB(); }
inline bool isModeLSB() const { return radState.isModeLSB(); }
inline float getCWSidetone() const { return static_cast<float>(radState.getSidetone()); }
inline bool isUSBInput() const { return conf.useUSBInput; }
inline bool isLineInput() const { return !conf.useUSBInput; }
inline bool isAI() const { return autoInfo; }
inline void setFreqA(unsigned freq) { catState.setFreqA(freq); }
inline void setFreqB(unsigned freq) { catState.setFreqB(freq); }
inline void setRIT(int freq) { catState.setRIT(freq); }
inline void setXIT(int freq) { catState.setXIT(freq); }
inline void setVFOA() { catState.setVFOA(); }
inline void setVFOB() { catState.setVFOB(); }
inline void setSplitOn() { catState.setSplitOn(); }
@@ -36,10 +85,15 @@ class UBitxRig {
inline void setRITOff() { catState.setRITOff(); }
inline void setXITOn() { catState.setXITOn(); }
inline void setXITOff() { catState.setXITOff(); }
inline void setCW() { catState.setCW(); }
inline void setCWR() { catState.setCWR(); }
inline void setUSB() { catState.setUSB(); }
inline void setLSB() { catState.setLSB(); }
inline void setModeCW() { catState.setModeCW(); }
inline void setModeCWR() { catState.setModeCWR(); }
inline void setModeUSB() { catState.setModeUSB(); }
inline void setModeLSB() { catState.setModeLSB(); }
inline void setCWSidetone(float f) { catState.setSidetone(static_cast<uint16_t>(f)); }
inline void setUSBInput() { conf.useUSBInput = true; }
inline void setLineInput() { conf.useUSBInput = false; }
inline void aiOn() { autoInfo = true; }
inline void aiOff() { autoInfo = false; }
@@ -58,10 +112,13 @@ class UBitxRig {
//void setBand();
//void getBand();
private:
private:
RigConfig conf;
UBitxRigState catState;
UBitxRigState radState;
bool autoInfo = false;
BandConfig band[NUM_BANDS];
};
extern UBitxRig Rig;

View File

@@ -56,7 +56,7 @@ void UBitxRigState::begin() {
#include "ubitx.h"
#include "ubitx_eemap.h"
extern unsigned long frequency, ritRxFrequency, ritTxFrequency;
extern unsigned long frequency, ritRxFrequency, ritTxFrequency, sideTone;
extern unsigned long vfoA;
extern unsigned long vfoB;
extern char cwMode;
@@ -169,8 +169,10 @@ void UBitxRigState::writeDirty() {
// XIT - TODO
}
// VFO A/B selection
// Various flags
if (isDirty(FLAGS_WORD)) {
// VFO A/B selection
char prev = vfoActive;
vfoActive = isVFOA() ? VFO_A : VFO_B;
if (vfoActive != prev) {
@@ -214,6 +216,13 @@ void UBitxRigState::writeDirty() {
setFrequency(frequency);
}
}
// Keyer information
if (isDirty(KEYER_WORD)) {
// Sidetone frequency
sideTone = static_cast<unsigned long>(getSidetone());
}
}
/*!
@@ -284,17 +293,22 @@ void UBitxRigState::readDirty() {
char curr = (cwMode << 1) | isUSB;
if (curr != prev) {
if (cwMode == 2) {
setCW();
setModeCW();
} else if (cwMode == 1) {
setCWR();
setModeCWR();
} else {
if (isUSB) {
setUSB();
setModeUSB();
} else {
setLSB();
setModeLSB();
}
}
}
// Sidetone
if (getSidetone() != static_cast<uint16_t>(sideTone)) {
setSidetone(static_cast<uint16_t>(sideTone));
}
}
/***********************************************************************
@@ -332,6 +346,16 @@ void UBitxRigState::receive_RIGINF(int numBytes) {
}
}
//--------------------------------------------------------------------
// Do anything that needs to happen when something is updated by the
// Raduino... this would be due to e.g. changes through the menu.
// Current (as of 2/19/2021) things that DON'T need to have any
// updates: frequency (those just get requested by CAT as needed).
// Current things that do need to get updated: sidetone (used to
// update CW filter values).
//--------------------------------------------------------------------
processDirty();
IFDEBUG( serialHexState("Rcvd") );
IFDEBUG( serialPrettyState("Rcvd") );
}
@@ -372,6 +396,13 @@ void UBitxRigState::send_RIGINF() {
//--------------------------------------------------------------------
}
/*!
* @brief Perform required actions based on any dirty bits set.
*/
void UBitxRigState::processDirty() {
}
#endif
#ifdef DEBUG

View File

@@ -15,6 +15,9 @@
#define UBITX_USB_FLAG 0x00000020
#define UBITX_TX_FLAG 0x00000040
#define UBITX_SIDETONE_MASK 0x000007FF
#define UBITX_KEYER_MODE_MASK 0x00003800
#ifdef TEENSYDUINO
#define DISABLEINTS(CMD) do { noInterrupts(); CMD; interrupts(); } while (0)
#else
@@ -27,6 +30,7 @@ enum RigStateWord {
VFOB_WORD,
OFFSETS_WORD,
FLAGS_WORD,
KEYER_WORD,
NUM_WORDS
};
@@ -255,25 +259,25 @@ struct UBitxRigState {
return result;
}
inline void setUSB(bool mark = true) {
inline void setModeUSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setLSB(bool mark = true) {
inline void setModeLSB(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] &= ~UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setCW(bool mark = true) {
inline void setModeCW(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] |= UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
}
inline void setCWR(bool mark = true) {
inline void setModeCWR(bool mark = true) {
DISABLEINTS( data[FLAGS_WORD] &= ~UBITX_USB_FLAG;
data[FLAGS_WORD] |= UBITX_CW_FLAG;
if (mark) setDirty(FLAGS_WORD) );
@@ -309,6 +313,18 @@ struct UBitxRigState {
return result;
}
inline void setSidetone(uint16_t f, bool mark = true) {
DISABLEINTS( data[KEYER_WORD] &= ~UBITX_SIDETONE_MASK;
data[KEYER_WORD] |= (uint32_t(f) & UBITX_SIDETONE_MASK);
if (mark) setDirty(KEYER_WORD) );
}
inline uint16_t getSidetone() {
uint32_t result;
DISABLEINTS( result = data[KEYER_WORD] & UBITX_SIDETONE_MASK );
return uint16_t(result);
}
#ifdef DEBUG
void serialHexState(const char* label);
void serialPrettyState(const char* label);
@@ -319,6 +335,9 @@ struct UBitxRigState {
// RigState, not in the TeensyDSP (Teensy) case.
void writeDirty(); // write fields FROM RigState TO Raduino
void readDirty(); // read variables FROM Raduino TO RigState
#else
// These methods are only defined (currently) in the TeensyDSP case.
void processDirty();
#endif
};

View File

@@ -5,7 +5,8 @@
#include <Arduino.h>
#include "TR.h"
UBitxTR TR(DSP);
UBitxTR _tr(DSP);
UBitxTR& TR = _tr;
void UBitxTR::update(bool cw, bool extKey) {
updateKey();

View File

@@ -19,6 +19,12 @@ const int uBitxTRPttPin = UBITX_TR_PTT_PIN;
const int uBitxTRVoxPin = UBITX_TR_VOX_PIN;
const int uBitxTRKeyPin = UBITX_TR_KEY_PIN;
struct TxSource {
MIC_SOURCE = 0,
LINE_SOURCE,
USB_SOURCE,
};
class UBitxTR {
public:
UBitxTR(UBitxDSP& d, int out = uBitxTROutPin, int p = uBitxTRPttPin, int v = uBitxTRVoxPin, int k = uBitxTRKeyPin):
@@ -63,9 +69,10 @@ class UBitxTR {
inline bool catActivated() { return (L_catActive != catActive) && L_catActive; }
inline bool catDeactivated() { return (L_catActive != catActive) && catActive; }
inline void catTX() {
inline void catTX(TxSource src) {
L_catActive = catActive;
catActive = true;
txSource = src;
}
inline void catRX() {
@@ -73,10 +80,11 @@ class UBitxTR {
catActive = false;
}
//======================================================================
//====================================================================
inline bool transmitting() { return isTX; }
inline bool receiving() { return !isTX; }
inline TxSource source() const { return txSource; }
/*!
* @brief Check if any of the PTT's have been pressed or released
@@ -145,6 +153,7 @@ class UBitxTR {
bool L_keyDown = false;
bool catActive = false;
bool L_catActive = false;
TxSource txSource = MIC_SOURCE;
};
extern UBitxTR TR;

View File

@@ -41,8 +41,6 @@ TS590Command::TS590Command(const char* pre) {
}
}
TS590Command::~TS590Command() {}
/*!
* @brief Determine whether this is a Read command or not. by
* default, if it's a 2-letter command, it's a Read.
@@ -139,8 +137,18 @@ void TS590Command::setDSP(UBitxDSP* d) {
theDSP = d;
}
/*!
* @brief Set the T/R that will be used to process commands.
* @param t
* Pointer to the UBitxTR object.
*/
void TS590Command::setDSP(UBitxTR* t) {
theTR = t;
}
UBitxRig* TS590Command::theRig = &Rig;
UBitxDSP* TS590Command::theDSP = &DSP;
UBitxTR* TR590Command::theTR = &TR;
TS590Error TS590Command::theError = NoError;
/**********************************************************************/
@@ -238,19 +246,19 @@ void TS590_MD::handleCommand(const char* cmd) {
break;
case '1': // LSB
rig()->setLSB();
rig()->setModeLSB();
break;
case '2': // USB
rig()->setUSB();
rig()->setModeUSB();
break;
case '3': // CW
rig()->setCW();
rig()->setModeCW();
break;
case '7': // CW-R
rig()->setCWR();
rig()->setModeCWR();
break;
default:
@@ -319,6 +327,123 @@ void TS590_SL::sendResponse(const char* cmd) {
/**********************************************************************/
void TS590_TX::handleCommand(const char* cmd) {
if (strlen(cmd) == 3) {
switch (cmd[2]) {
case '0':
tr.catTX(MIC_SOURCE);
break;
case '1':
tr.catTX(rig.isUSBInput() ? USB_INPUT : LINE_INPUT);
break;
case '2':
// TODO: Need to implement w/ Teensy Audio Tool.
//tr.catTX();
break;
default:
setSyntaxError();
}
} else if (strlen(cmd) == 2) {
tr.catTX(MIC_SOURCE);
} else {
setSyntaxError();
}
}
void TS590_TX::sendResponse(const char* cmd) {
char src;
switch (tr.source()) {
case MIC_SOURCE:
src = '0';
break;
case LINE_SOURCE:
case USB_SOURCE:
src = '1';
break;
}
ts590SeondCommand("TX%1c", src);
}
/**********************************************************************/
/*!
* @brief Create a new CAT EX command. It should be initialized with
* a 3-character P1 parameter (command number).
* @param pre
* A 3-character command prefix. If more than 3 characters
* are supplied, only the first two will be used. If less
* than three are supplied, then the command will be
* initialized with a null prefix.
*/
TS590EXCommand::TS590EXCommand(const char* P1):
TS590Command("EX") {
if (strlen(P1) >= 3) {
strncpy(myMenu, P1, 3);
}
}
/*!
* @brief Determine whether this is a Read command or not. by
* default, if it's a 2-letter command, it's a Read.
* @return True if a Read command; false otherwise.
*/
bool TS590EXCommand::isReadCommand(const char* cmd) const {
if (strlen(cmd) == 9) {
return true;
} else {
return false;
}
}
void TS590EXCommand::sendResponse(const char* cmd) {
ts590sendCommand("%2c%3c0000%s", prefix(), menu(), getReturnValue());
}
/**********************************************************************/
void TS590_EX034::handleCommand(const char* cmd) {
if (strlen(cmd) == 10 || strlen(cmd) == 11) {
index = (uint8_t)atol(&cmd[9]);
if (index < 15) {
rig().setCWSidetone(300.0 + (float)(index * 50));
} else {
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_EX034::sendResponse(const char* cmd) {
ts590SendCommand("EX0340000%02d", index % 15);
}
/**********************************************************************/
void TS590_EX063::handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
if (cmd[9] == '0') {
rig().setLineInput();
} else if (cmd[9] == '1') {
rig().setUSBInput();
} else {
setSyntaxError();
}
} else {
setSyntaxError();
}
}
void TS590_EX063::sendResponse(const char* cmd) {
ts590SendCommand("EX0630000%1c", rig().isUSBInput() ? '1' : '0');
}
/**********************************************************************/
TS590_FA cmdFA;
TS590_FB cmdFB;
TS590_FR cmdFR;
@@ -393,8 +518,27 @@ int compareCATCommands(const void* a, const void* b) {
return cmp;
}
int compareCATEXCommands(const void* a, const void* b) {
TS590Command const *B = *(TS590Command const **)b;
int cmp = strncmp((char*)a, (char*)B->prefix(), 5);
#ifdef DEBUG
Serial.print("Comparison: ");
Serial.print((char*)a);
Serial.print(" ? ");
Serial.print((char*)B->prefix());
Serial.print(" --> ");
Serial.println(cmp);
#endif
return cmp;
}
void UBitxTS590::processCommand() {
TS590Command** cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
TS590Command** cmd;
if (strncmp(buf, "EX", 2) == 0) {
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATEXCommands);
} else {
cmd = (TS590Command**)bsearch(buf, commands, numCommands, sizeof(TS590Command*), compareCATCommands);
}
if (cmd == NULL) {
ts590SyntaxError();
} else {

View File

@@ -4,9 +4,13 @@
#include <Arduino.h>
#include "DSP.h"
#include "Rig.h"
#include "TR.h"
/**********************************************************************/
// uncomment to use TS-590SG / comment to use TS-590S
#define USE_TS590SG
#define TS590_COMMAND_MAX_LENGTH 50 // including terminator (which will get converted to null)
const int ts590CommandMaxLength = TS590_COMMAND_MAX_LENGTH;
@@ -62,6 +66,11 @@ class TS590Command {
*/
inline UBitxDSP* dsp() const { return theDSP; }
/*!
* @brief Return the T/R that this command will be used to control.
*/
inline UBitxDSP* tr() const { return theTR; }
/*!
* @brief Handle the provided Set command. If the Set command
* results in an error, then set the appropriate flag with
@@ -88,6 +97,7 @@ class TS590Command {
static void setProcessError();
static void setRig(UBitxRig* r);
static void setDSP(UBitxDSP* d);
static void setTR(UBitxTR* t);
private:
char myPrefix[3] = "\0\0";
@@ -192,6 +202,133 @@ class TS590_SL : public TS590Command {
unsigned index;
};
/*!
* @brief CAT command to start transmitting.
*/
class TS590_TX : public TS590Command {
public:
TS590_TX(): TS590Command("TX") {}
virtual void handleCommand(const char* cmd);
virtual void sendResponse(const char* cmd);
};
/**********************************************************************/
class TS590EXCommand : public TS590Command {
public:
TS590EXCommand(const char *P1);
virtual ~TS590EXCommand() = 0;
inline const char* menu() const { return &myMenu[0]; }
virtual const char* getReturnValue() const = 0;
virtual void sendResponse(const char* cmd);
private:
char myMenu[4] = "\0\0\0";
};
/**********************************************************************/
/*!
* @brief CAT command for setting the sidetone pitch/frequency.
*/
class TS590_EX034 : public TS590EXCommand {
public:
TS590_EX034(): TS590EXCommand("034") {}
virtual void handleCommand(const char* cmd);
virtual const char* getReturnValue();
private:
uint8_t index;
};
/*!
* @brief CAT command for selecting the data input line.
*/
class TS590_EX063 : public TS590EXCommand {
public:
TS590_EX063(): TS590EXCommand("063") {}
virtual void handleCommand(const char* cmd);
virtual const char* getReturnValue();
};
/**********************************************************************/
/*!
* @brief CAT command for setting USB/Line audio input levels.
*/
template<bool USB, bool SG>
class TS590_EXDataAudioInLevel : public TS590EXCommand {
public:
TS590_EXDataAudioInLevel(): TS590EXCommand(USB ? (SG ? "071" : "064") : (SG ? "073" : "066")) {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
uint8_t val = (cmd[9] - 48) % 10;
if (USB) {
// set USB input level
} else {
// set Line input level
}
} else {
setSyntaxError();
}
}
virtual const char* getReturnValue() {
static char str[2] = "\0";
// get input level - decimal 0 to 9 ... str[1] = ...
return str;
}
};
#ifdef USE_TS590SG
typedef TS590_EXDataAudioInLevel<true, true> TS590_EX071;
typedef TS590_EXDataAudioInLevel<false, true> TS590_EX073;
#else
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX064;
typedef TS590_EXDataAudioInLevel<true, false> TS590_EX066;
#endif
/**********************************************************************/
/*!
* @brief CAT command for setting USB/Line audio output levels.
*/
template<bool USB, bool SG>
class TS590_EXDataAudioOutLevel : public TS590EXCommand {
public:
TS590_EXDataAudioOutLevel(): TS590EXCommand(USB ? (SG ? "072" : "065") : (SG ? "074" : "067")) {}
virtual void handleCommand(const char* cmd) {
if (strlen(cmd) == 10) {
uint8_t val = (cmd[9] - 48) % 10;
if (USB) {
// set USB output level
} else {
// set Line output level
}
} else {
setSyntaxError();
}
}
virtual const char* getReturnValue() {
static char str[2] = "\0";
// get output level - decimal 0 to 9 ... str[1] = ...
return str;
}
};
#ifdef USE_TS590SG
typedef TS590_EXDataAudioOutLevel<true, true> TS590_EX072;
typedef TS590_EXDataAudioOutLevel<false, true> TS590_EX074;
#else
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX065;
typedef TS590_EXDataAudioOutLevel<true, false> TS590_EX067;
#endif
/**********************************************************************/
class UBitxTS590 {

386
TeensyDSP/temp.h Normal file
View File

@@ -0,0 +1,386 @@
const int rxLoCutSSB[] = { 0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
const int rxHiCutSSB[] = {1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3400, 4000, 5000};
const int rxDataWidth[] = { 50, 80, 100, 150, 200, 250, 300, 400, 500, 600, 1000, 1500, 2000, 2500};
#ifdef USE_TS590SG
const int rxDataShift[] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1750, 1800, 1900, 2000, 2100, 2210};
#else
const int rxDataShift[] = {1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2100, 2210};
#endif
const int txLowCutFilter[] = { 10, 100, 200, 300, 400, 500};
const int txHighCutFilter[] = {2500, 2600, 2700, 2800, 2900, 3000};
const int timeOutMinutes[] = {3, 5, 10, 20, 30};
#define MAX_MENU_TITLE_LEN 13
#define MAX_MENU_OPTION_LEN 13
typedef void (*toggleFunc)(UBitxRig&);
typedef bool (*boolStatus)(UBitxRig&);
#define KEYER_MIN_SPEED 4
#define KEYER_MAX_SPEED 60
/**********************************************************************/
/*!
* @brief Abstract base class for a switch/toggle (on/off, A/B, etc.)
* option. Derived classes must provide set() and get()
* methods for controlling the state of the switch.
*/
struct ConfigSwitch {
virtual ~ConfigSwitch() = 0;
virtual void set(bool on) = 0;
virtual bool get() const = 0;
};
/*!
* @brief Abstract base class for an option that supports a linear
* sequence of integer values. There can be a minimum and a
* maximum value, but otherwise, all integers between those
* should be acceptable. The base class takes care of checking
* min/max bounds, so the derived class must provide an get()
* and onSet() methods. In addition, derived classes can
* optionally override the onTooHigh() and onTooLow(), which
* by default clamp any out-of-band inputs to the min/max.
*/
struct ConfigInteger {
ConfigInteger(int min, int max)
: myMin(min), myMax(max) {}
virtual ~ConfigInteger() = 0;
inline void set(int val) {
if (val > myMax) val = onTooHigh(val);
if (val < myMin) val = onTooLow(val);
onSet(val);
}
virtual int get() const = 0;
virtual void onSet() = 0;
virtual int onTooHigh(int val) const { return myMax; }
virtual int onTooLow(int val) const { return myMin; }
int myMin, myMax;
};
/*!
* @brief Abstract base class for an option that supports a set
* of integer values stored in an array. The derived
* class must provide get() and onSet() methods. It can
* optionally provide an onTooHigh() method, which by
* default will clamp indices to the highest allowable.
*/
struct ConfigArray {
ConfigArray(int len, int* data)
: myLen(len), myData(data) {}
virtual ~ConfigArray() = 0;
inline void set(int i) {
if (i < 0) i = 0;
if (i > myLen - 1) i = onTooHigh(i);
onSet(myData[i]);
}
virtual int get() const = 0;
virtual void onSet(int val) = 0;
virtual int onTooHigh(int i) const { return myLen - 1; }
inline int getData(int i) { return myData[i]; }
int myLen;
int *myData;
};
/**********************************************************************/
/*!
* @brief Option class to set the configuration of the RX DSP filter.
*/
template<bool isHIGH>
struct ConfigRXFilter : public ConfigArray {
ConfigFilter(UBitxDSP& d, int default)
: ConfigArray(0, NULL), dsp(d), useData(false) {
if (isHIGH) {
current[0] = 10; // SSB - hi cut
current[1] = 05; // Data - center (shift)
data[0] = rxHiCutSSB;
data[1] = rxDataShift;
length[0] = sizeof(rxHiCutSSB)/sizeof(rxHiCutSSB[0]);
length[1] = sizeof(rxDataShift)/sizeof(rxDataShift[0]);
} else {
current[0] = 04; // SSB - lo cut
current[1] = 12; // Data - width
data[0] = rxLoCutSSB;
data[1] = rxDataWidth;
length[0] = sizeof(rxLoCutSSB)/sizeof(rxLoCutSSB[0]);
length[1] = sizeof(rxDataWidth)/sizeof(rxDataWidth[0]);
}
}
// TODO - A TON MORE TO DO HERE TO MAKE IT CONSISTENT WITH CONSTRUCTION
inline void setSSB() {
useData = false;
}
inline void setData() { useData = true; }
virtual void get() { return current; }
virtual void onSet(int i) {
current = i;
float value = static_cast<float>(getData(i));
if (isHIGH) {
if (useCENTER) {
dsp.setRxFilterCenter(value);
} else {
dsp.setRxFilterHi(value);
}
} else {
if (useCENTER) {
dsp.setRxFilterWidth(value);
} else {
dsp.setRxFilterLo(static_cast<float>(getData(i)));
}
}
}
private:
UBitxDSP& dsp;
int* data[2];
int length[2];
int current[2];
int mode; // 0 = SSB, 1 = Data
};
typedef ConfigRXFilter<false, false> ConfigRxLoCutSSB;
typedef ConfigRXFilter<true, false> ConfigRxHiCutSSB;
typedef ConfigRXFilter<false, true > ConfigRxLoCutData;
typedef ConfigRXFilter<true, true > ConfigRxHiCutData;
struct DSPConfigurator {
DSPConfigurator(UBitxDSP& d)
: dsp(d) {}
ConfigRxLoCutSSB ssbRxLoCut;
ConfigRxHiCutSSB ssbRxHiCut;
ConfigRxLoCutData dataRxLoCut;
ConfigRxHiCutData dataRxHiCut;
private:
UBitxDSP& dsp;
} dspConfigurator(DSP);
/**********************************************************************/
/**********************************************************************/
/*!
* @brief A configuration class for the Keyer. Since the Keyer has
* its own state that is not 100% compatible with the way way
* the Rig will interact with it (primarily in terms of CAT
* commands), this class provides the glue. (This is mostly
* used because, while the configuration objects are generally
* all separate, there is some state that needs to be
* communicated between e.g. the iambic mode configuration and
* the bug mode configuration.)
*/
struct KeyerConfigurator {
KeyerConfigurator(UBitxKeyer& k): keyer(k) { iambicAB = keyer.getMode(); }
/*!
* @brief An inner class that supports switching between Iambic A & B.
*/
struct IambicMode : public ConfigSwitch {
IambicMode(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
virtual void set(bool on) {
config.iambicAB = on;
if (config.keyer.getMode() != STRAIGHT) {
config.keyer.setMode(config.iambicAB ? IAMBICB : IAMBICA);
}
}
virtual bool get() const { return config.iambicAB; }
private: KeyerConfigurator& config;
} iambicMode;
/*!
* @brief An inner class that supports switching bug mode on/off.
*/
struct BugMode : public ConfigSwitch {
BugMode(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
virtual void set(bool on) {
config.isBug = on;
if (config.isBug && (config.keyer.getMode() != STRAIGHT)) {
config.keyer.setMode(STRAIGHT);
} else if (!config.isBug && (config.keyer.getMode() == STRAIGHT)) {
config.keyer.setMode(config.iambicAB ? IAMBICB : IAMBICA);
}
}
virtual bool get() const { return config.isBug; }
private: KeyerConfigurator& config;
} bugMode;
/*!
* @brief An inner class that supports switching the left and right
* paddles. TODO: This currently does nothing.
*/
struct PaddleSwap : public ConfigSwitch {
PaddleSwap(KeyerConfigurator& c): ConfigSwitch(), config(c) {}
virtual void set(bool on) { ; }
virtual bool get() const { return false(); }
private: KeyerConfigurator& config;
} paddleSwap;
/*!
* @brief An inner class that supports changing the keyer speed.
*/
struct KeyerSpeed : public ConfigInteger {
KeyerSpeed(KeyerConfigurator& c): ConfigInteger(KEYER_MIN_SPEED, KEYER_MAX_SPEED), config(c) {}
virtual void onSet(int val) { config.keyer.setWPM(val); }
virtual int get { return config.keyer.getWPM(); }
private: KeyerConfigurator& config;
} keyerSpeed;
private:
Keyer& keyer;
bool iambicAB;
bool isBug;
} keyerConfigurator(Keyer);
/**********************************************************************/
/**********************************************************************/
class MenuItem {
public:
MenuItem(int idno, const char* title, int numOpt, int defOpt, ): myID(idno), myTitle(title), numOptions(numOpt), selected(defOpt) {}
virtual ~MenuItem() = 0;
inline void id() { return myID; }
inline void writeTitle(char* outbuf, int maxlen) { strncpy(outbuf, myTitle, maxlen); }
inline char* writeOption(char* outbuf, int o = -1, int maxlen = MAX_MENU_OPTION_LEN) {
if (o == -1) {
setOptionText(selected);
} else if ((o >= 0) && (o < numOptions)) {
setOptionTitle(o);
} else {
memset(myOption, '\0', MAX_MENU_OPTION_LEN);
}
return strncpy(outbuf, myOption, maxlen);
}
inline int getNumOptions() { return numOptions; }
inline int getSelected() { return selected; }
inline int nextOption(bool doUpdate = true) {
selected = (selected + 1) % numOptions;
if (doUpdate) update();
return selected;
}
inline int prevOption(bool doUpdate = true) {
selected = (selected - 1) % numOptions;
if (doUpdate) update();
return selected;
}
inline int gotoOption(int o, bool doUpdate = true) {
selected = o >= numOptions ? numOptions : (o < 0 ? 0 : o);
if (doUpdate) update();
return selected;
}
virtual void update() = 0;
private:
int myID;
char myTitle[MAX_MENU_TITLE_LEN] = {'\0'};
char myOption[MAX_MENU_OPTION_LEN] = {'\0'};
int numOptions;
int selected;
};
class StepItem : public MenuItem {
public:
StepItem(int idno, const char* title, int numOpt, int defOpt): MenuItem(idno, title, numOpt, defOpt) {}
};
static const char off_str[] = "OFF";
static const char on_str[] = "ON";
static const char line_str[] = "LINE";
static const char usb_str[] = "USB";
static const char key_a_str[] = "A";
static const char key_b_str[] = "B";
class ToggleItem : public MenuItem {
public:
ToggleItem(int idno, const char* title, const char* off, const char* on, int defOpt): MenuItem(idno, title, 2, defOpt) {}
};
/**********************************************************************/
struct MenuConfig {
byte sidetoneVolume = 9;
byte ssbLoCut = 0;
byte ssbHiCut = 5;
byte dataLoCut = 0;
byte dataHiCut = 0;
byte keyerAB = 0;
byte sidetonePitch = 6;
};
// CAT COMMANDS - BASIC
// KS - Sets and reads the Keying speed.
IntegerItem ("KS", "KEYER SPEED", 0, 2, keyerConfigurator.keyerSpeed),
ArrayItem ("SH", "RX HI CUT ", 0, 2, dspConfigurator.ssbRxHiCut),
ArrayItem ("SL", "RX LO CUT ", 0, 2, dspConfigurator.ssbRxLoCut),
// CAT COMMANDS - EX MENU
// Sidetone volume
Steps {004, "ST VOL ", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
// SSB/AM Low Cut transmit filter (Hz)
Steps {025, "SSB TX LO ", 6, [](auto x) { return txLowCutFilter[x]; }},
// SSB/AM High Cut transmit filter (Hz)
Steps {026, "SSB TX HI ", 6, [](auto x) { return txHighCutFilter[x]; }},
// SSB-DATA Low Cut transmit filter (Hz)
Steps {027, "DATA TX LO ", 6, [](auto x) { return txLowCutFilter[x]; }},
// SSB-DATA High Cut transmit filter (Hz)
Steps {028, "DATA TX HI ", 6, [](auto x) { return txHighCutFilter[x]; }},
// Electronic keyer operation mode
ToggleItem (032, "KEYER A/B ", "A ", "B ", keyerConfigurator.iambicMode),
// Sidetone/ pitch frequency setting (Hz)
Steps {034, "ST PITCH ", 15, [](auto x) { return 300+(x*50); }},
// Keying weight ratio
StepsSpec {036, "KEYER WEIGHT", 17, ...},
// Bug key function
ToggleItem (038, "KEYER BUG ", "OFF ", "ON ", keyerConfigurator.bugMode),
// Paddle dot/dash replacement setting
ToggleItem (039, "KEYER SWAP ", "OFF ", "ON ", keyerConfigurator.paddleSwap),
// Auto CW TX in SSB mode
//ToggleItem (041, "AUTO CW TX ", "OFF ", "ON ", [&Rig](bool on) { Rig.setKeyerAutoTransmitCW(on); } ),
// Time-out Timer
//StepsSpec {049, "TIMEOUT ", 6, ... },
// Transmit inhibit
ToggleItem (060, "TX INHIBIT ", "OFF ", "ON ", [&Rig](bool on) { Rig.setTransmitInhibit(on); } ),
// DATA moduleation line
ToggleItem (063, "DATA LINE ", "LINE", "USB ", [&Rig](bool usb) { Rig.setDataInputLine(usb); } ),
// USB audio input level
Steps {064, "USB IN LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// USB audio output level
Steps {065, "USB OUT LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// ACC2 terminal AF input level
Steps {066, "LINE IN LVL ", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// ACC2 terminal AF output level
Steps {067, "LINE OUT LVL", [](auto x) { return static_cast<double>(x-48)/9.0; }},
// DATA VOX
ToggleItem (069, "DATA VOX ", "OFF ", "ON ", [&Rig](bool on) { Rig.setDataVoxOn(on); } ),
// DATA VOX delay
Steps {070, "DATA VOX DEL", 20, ...},
// DATA VOX gain for USB audio input
Steps {071, "USB VOX LVL ", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},
// DATA VOX gain for ACC2 terminal input
Steps {072, "LINE VOX LVL", 10, [](auto x) { return static_cast<double>(x-48)/9.0; }},