Merge remote-tracking branch 'ubitx-iop/master'. This incorporates the
IOP (Teensy 3.2) code.
This commit is contained in:
commit
649784d285
472
iopcomm/iopcomm.cpp
Normal file
472
iopcomm/iopcomm.cpp
Normal file
@ -0,0 +1,472 @@
|
||||
//======================================================================
|
||||
// iopcomm.cpp
|
||||
//======================================================================
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "iopcomm.h"
|
||||
|
||||
#if defined(TEENSYDUINO)
|
||||
// Compiling for the Teensy, so assuming this is IOP code.
|
||||
#define MYSERIAL Serial1
|
||||
#else
|
||||
// Not compiling for the Teensy, so assuming this is Raduino code.
|
||||
#define MYSERIAL Serial
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendCATMessage(const uint8_t* buf)
|
||||
{
|
||||
MYSERIAL.write(prefixAndLengthToByte(CAT_PREFIX, 5));
|
||||
for (int i = 0; i < 5; i++) {
|
||||
MYSERIAL.write(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendIOPMessage(IOPMessage const& msg)
|
||||
{
|
||||
MYSERIAL.write(prefixAndLengthToByte(IOP_PREFIX, msg.len + 1));
|
||||
MYSERIAL.write(msg.id);
|
||||
for (int i = 0; i < msg.len; i++) {
|
||||
MYSERIAL.write(msg.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void recvIOPMessage(IOPMessage& msg, const uint8_t* buf, int len)
|
||||
{
|
||||
msg.id = buf[0];
|
||||
msg.len = len - 1;
|
||||
for (int i = 1; i < len; i++) {
|
||||
msg.data[i-1] = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendIOPModeCommand(rig_mode mode)
|
||||
{
|
||||
IOPMessage m;
|
||||
m.id = IOP_MODE_COMMAND;
|
||||
m.len = 1;
|
||||
m.data[0] = static_cast<uint8_t>(mode);
|
||||
sendIOPMessage(m);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendIOPStartTxCommand()
|
||||
{
|
||||
IOPMessage m;
|
||||
m.id = IOP_START_TX_COMMAND;
|
||||
m.len = 0;
|
||||
sendIOPMessage(m);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendIOPStopTxCommand()
|
||||
{
|
||||
IOPMessage m;
|
||||
m.id = IOP_STOP_TX_COMMAND;
|
||||
m.len = 0;
|
||||
sendIOPMessage(m);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendIOPModeRequest()
|
||||
{
|
||||
IOPMessage m;
|
||||
m.id = IOP_MODE_REQUEST;
|
||||
m.len = 4; // NOTE: LEN = 4 for padding only... temporary
|
||||
sendIOPMessage(m);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void sendIOPDebugMessage(const char* text)
|
||||
{
|
||||
IOPMessage m;
|
||||
m.id = IOP_DEBUG_MSG;
|
||||
m.len = strlen(text);
|
||||
if (m.len > IOP_MESSAGE_MAX_LEN) {
|
||||
m.len = IOP_MESSAGE_MAX_LEN;
|
||||
}
|
||||
strncpy(m.data, text, m.len);
|
||||
sendIOPMessage(m);
|
||||
};
|
||||
|
||||
////======================================================================
|
||||
//// SSB STATUS MESSAGE
|
||||
////======================================================================
|
||||
|
||||
//void sendIOPSSBStatus(SSBConfig const &c)
|
||||
//{
|
||||
//IOPMessage m;
|
||||
//m.id = IOP_SSB_STATUS_MSG;
|
||||
//m.len = 4;
|
||||
//m.data[0] = 'S'; // current mode; redundant w/ Raduino mode, but maybe useful for debugging
|
||||
//m.data[1] = '-'; // placeholder for transmit filter/compressor
|
||||
//m.data[2] = RX_FILTER_LETTER[c.filter];
|
||||
//m.data[3] = '\0';
|
||||
//sendIOPMessage(m);
|
||||
//}
|
||||
|
||||
////======================================================================
|
||||
//// DGT STATUS MESSAGE
|
||||
////======================================================================
|
||||
|
||||
//void sendIOPDGTStatus(DGTConfig const &c)
|
||||
//{
|
||||
//IOPMessage m;
|
||||
//m.id = IOP_DGT_STATUS_MSG;
|
||||
//m.len = 4;
|
||||
//m.data[0] = 'D'; // current mode; redundant w/ Raduino mode, but maybe useful for debugging
|
||||
//m.data[1] = '-'; // placeholder for future digital submodes?
|
||||
//m.data[2] = RX_FILTER_LETTER[c.filter];
|
||||
//m.data[3] = '\0';
|
||||
//sendIOPMessage(m);
|
||||
//}
|
||||
|
||||
////======================================================================
|
||||
//// CW STATUS MESSAGE
|
||||
////======================================================================
|
||||
|
||||
//void sendIOPCWStatus(CWConfig const &c)
|
||||
//{
|
||||
//IOPMessage m;
|
||||
//m.id = IOP_CW_STATUS_MSG;
|
||||
//m.len = 4;
|
||||
//m.data[0] = 'C'; // current mode; redundant w/ Raduino mode, but maybe useful for debugging
|
||||
//m.data[1] = KEYER_MODE_LETTER[c.mode];
|
||||
//m.data[2] = RX_FILTER_LETTER[c.filter];
|
||||
//m.data[3] = '\0';
|
||||
//sendIOPMessage(m);
|
||||
//}
|
||||
|
||||
////======================================================================
|
||||
//// TEST STATUS MESSAGE
|
||||
////======================================================================
|
||||
|
||||
//void sendIOPTestStatus()
|
||||
//{
|
||||
//IOPMessage m;
|
||||
//m.id = IOP_TEST_STATUS_MSG;
|
||||
//m.len = 4;
|
||||
//m.data[0] = ' ';
|
||||
//m.data[1] = 'T';
|
||||
//m.data[2] = 'T';
|
||||
//m.data[3] = '\0';
|
||||
//sendIOPMessage(m);
|
||||
//}
|
||||
|
||||
//======================================================================
|
||||
// MENU DISPLAY MESSAGE
|
||||
//======================================================================
|
||||
|
||||
void sendIOPMenuDisplay(const char* text)
|
||||
{
|
||||
IOPMessage m;
|
||||
int l = strlen(text);
|
||||
m.id = IOP_MENU_DISPLAY_MSG;
|
||||
m.len = 16;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i < l) {
|
||||
m.data[i] = text[i];
|
||||
} else {
|
||||
m.data[i] = ' ';
|
||||
}
|
||||
}
|
||||
m.data[16] = '\0';
|
||||
sendIOPMessage(m);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// MENU INACTIVE MESSAGE
|
||||
//======================================================================
|
||||
|
||||
void sendIOPMenuInactive()
|
||||
{
|
||||
IOPMessage m;
|
||||
m.id = IOP_MENU_INACTIVE_MSG;
|
||||
m.len = 4; // NOTE: LEN = 4 for padding only... temporary
|
||||
sendIOPMessage(m);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
/*
|
||||
enum SerialState {
|
||||
NORMAL = 0,
|
||||
CAT_MODE,
|
||||
IOP_MODE,
|
||||
EEPROM_READ,
|
||||
EEPROM_WRITE,
|
||||
};
|
||||
|
||||
Translator::Translator(RigMode& m, CWConfig& c, Stream& s = SERIAL):
|
||||
mode(m), cwConfig(c), serial(s)
|
||||
{
|
||||
for (int i = 0; i < NUM_PREFIX_IDS; i++) {
|
||||
prefixCb[i] = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_MESSAGE_IDS; i++) {
|
||||
messageCb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::sendACK()
|
||||
{
|
||||
serial.write(prefixAndLengthToByte(ACK, 0));
|
||||
}
|
||||
|
||||
void Translator::sendCATMessage(byte b, bool continued=false)
|
||||
{
|
||||
if (!continued) { // we're starting a CAT transmission sequence
|
||||
|
||||
} else { // we're continuing a CAT transmission sequence
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::sendIOPMessage(IOPMessage const& m)
|
||||
{
|
||||
serial.write(prefixAndLengthToByte(IOP_PREFIX, m.len+1));
|
||||
serial.write(m.id);
|
||||
for (int i = 0; i < m.len; i++) {
|
||||
serial.write(m.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Translator::receive()
|
||||
{
|
||||
static SerialState state;
|
||||
|
||||
static PrefixID readPrefix = 0;
|
||||
static uint8_t readLength = 0;
|
||||
|
||||
static IOPMessage iopMsg;
|
||||
|
||||
static uint8_t cmdLength = 0;
|
||||
static byte cmdBuffer[16];
|
||||
static char cmdString[17]; // added a char for null termination when required
|
||||
|
||||
static uint16_t eepromStartIndex;
|
||||
static uint16_t eepromReadLength;
|
||||
static int eepromMagicFlag = 0;
|
||||
|
||||
int incomingByte;
|
||||
|
||||
for (int i = 0; i < serial.available(); i++) {
|
||||
incomingByte = serial.read();
|
||||
|
||||
switch(state) {
|
||||
case NORMAL:
|
||||
if (incomingByte == ACK) {
|
||||
prefixCb[ACK](NULL);
|
||||
} else {
|
||||
readPrefix = byteToPrefix(incomingbyte);
|
||||
readLength = byteToLength(incomingbyte);
|
||||
if (readLength > 0) {
|
||||
switch(readPrefix) {
|
||||
case CAT_PREFIX:
|
||||
case RAD_EEPROM_WRITE_PREFIX:
|
||||
state = CAT_MODE;
|
||||
break;
|
||||
|
||||
case IOP_PREFIX:
|
||||
state = IOP_MODE;
|
||||
iopMsg.len = readLength - 1;
|
||||
cmdLength = 0;
|
||||
break;
|
||||
|
||||
case RAD_EEPROM_READ_PREFIX:
|
||||
state = EEPROM_READ;
|
||||
readLength = 5;
|
||||
eepromMagicFlag = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CAT_MODE:
|
||||
// In CAT mode, we just pass thru the remaining bytes to the PC.
|
||||
if
|
||||
USBSERIAL.write(incomingByte);
|
||||
readLength--;
|
||||
if (readLength == 0) {
|
||||
state = NORMAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOP_MODE:
|
||||
cmdBuffer[cmdLength] = incomingByte;
|
||||
cmdLength++;
|
||||
readLength--;
|
||||
if (readLength == 0) {
|
||||
processIOPCommand(cmdBuffer, cmdLength);
|
||||
state = NORMAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case EEPROM_READ:
|
||||
readLength--;
|
||||
switch(readLength) {
|
||||
case 4:
|
||||
eepromStartIndex = incomingByte;
|
||||
if (incomingByte == 0x16) {
|
||||
magicFlag++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
eepromStartIndex += (256 * incomingByte);
|
||||
if (incomingByte == 0xe8) {
|
||||
magicFlag++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
eepromReadLength = incomingByte;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
eepromReadLength += (256 * incomingByte);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
USBSERIAL.write(incomingByte);
|
||||
if (magicFlag == 2) {
|
||||
readLength = 126 + 2;
|
||||
} else {
|
||||
readLength = eepromReadLength + 2;
|
||||
}
|
||||
state = CAT_MODE;
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EEPROM_WRITE:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen...
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::registerPrefixCb(PrefixID id, void (*func)(void*))
|
||||
{
|
||||
if (id >= 0 && id < NUM_PREFIX_IDS) {
|
||||
prefixCb[id] = func;
|
||||
}
|
||||
|
||||
void Translator::registerMessageCb(MessageID id, void (*func)(void*))
|
||||
{
|
||||
if (id >= 0 && id < NUM_MESSAGE_IDS) {
|
||||
messageCb[id] = func;
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::pack_ModeCommand(IOPMessage& m, RigMode r)
|
||||
{
|
||||
m.id = IOP_MODE_COMMAND;
|
||||
m.len = 1;
|
||||
m.data[0] = r;
|
||||
}
|
||||
|
||||
void Translator::unpack_ModeCommand(IOPMessage const& m)
|
||||
{
|
||||
*mode = RigMode(m.data[0]);
|
||||
}
|
||||
|
||||
void Translator::pack_CWConfig(IOPMessage &m, CWConfig const& c)
|
||||
{
|
||||
m.id = IOP_CW_CONFIG_MSG;
|
||||
m.len = 6;
|
||||
// mode
|
||||
m.data[0] = byte(c.mode);
|
||||
// flags
|
||||
m.data[1] = NO_FLAGS;
|
||||
m.data[1] |= (c.reversed ? REVERSED : 0);
|
||||
// parameters
|
||||
m.data[2] = byte(c.wpm);
|
||||
m.data[3] = byte(c.weight * 10.0);
|
||||
m.data[4] = byte(c.sidetone >> 8);
|
||||
m.data[5] = byte(c.sidetone | 0x0F);
|
||||
}
|
||||
|
||||
void Translator::unpack_CWConfig(IOPMessage const &m)
|
||||
{
|
||||
//if (m.id != IOP_CW_CONFIG_MSG || m.len != 6) {
|
||||
// // do some error thing...
|
||||
//}
|
||||
//mode
|
||||
cwConfig->mode = KeyMode(m.data[0]);
|
||||
// flags
|
||||
cwConfig->reversed = bool(m.data[1] & REVERSED);
|
||||
// parameters
|
||||
cwConfig->wpm = uint8_t(m.data[2]);
|
||||
cwConfig->weight = float(m.data[3]) / 10.0;
|
||||
cwConfig->sidetone = (m.data[4] << 8) | m.data[5];
|
||||
}
|
||||
|
||||
void Translator::pack_ModeRequest(IOPMessage& m)
|
||||
{
|
||||
m.id = IOP_MODE_REQUEST;
|
||||
m.len = 0;
|
||||
}
|
||||
|
||||
void Translator::unpack_ModeRequest(IOPMessage const& m)
|
||||
{
|
||||
}
|
||||
|
||||
void Translator::unpack(IOPMessage const& m)
|
||||
{
|
||||
switch (m.id) {
|
||||
case IOP_MODE_COMMAND:
|
||||
unpack_ModeCommand(m);
|
||||
messageCb[m.id](mode);
|
||||
break;
|
||||
|
||||
case IOP_START_TX_COMMAND:
|
||||
break;
|
||||
|
||||
case IOP_STOP_TX_COMMAND:
|
||||
break;
|
||||
|
||||
case IOP_CW_CONFIG_MSG:
|
||||
unpack_CWConfig(m);
|
||||
messageCb[m.id](cwConfig);
|
||||
break;
|
||||
|
||||
case IOP_MODE_REQUEST:
|
||||
unpack_ModeRequest(m);
|
||||
messageCb[m.id](NULL);
|
||||
break;
|
||||
|
||||
case IOP_CW_STATUS_MSG:
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
263
iopcomm/iopcomm.h
Normal file
263
iopcomm/iopcomm.h
Normal file
@ -0,0 +1,263 @@
|
||||
//======================================================================
|
||||
// iopcomm.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __iopcomm_h__
|
||||
#define __iopcomm_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Message prefixes, starting with the ACK message (0). Message format
|
||||
* consists of 1 or more bytes, where:
|
||||
* b[0] - leftmost 3 bits == prefix
|
||||
* - rightmost 5 bits == length of data (0-31)
|
||||
* b[1]...b[31] (if present) - data bits
|
||||
*/
|
||||
|
||||
enum PrefixID {
|
||||
ACK = 0,
|
||||
CAT_PREFIX,
|
||||
IOP_PREFIX,
|
||||
RAD_EEPROM_READ_PREFIX,
|
||||
RAD_EEPROM_WRITE_PREFIX,
|
||||
IOP_EEPROM_READ_PREFIX,
|
||||
IOP_EEPROM_WRITE_PREFIX,
|
||||
NUM_PREFIX_IDS
|
||||
};
|
||||
|
||||
/* Masks for the leftmost 3 bits (prefix) and rightmost 5 bits (length)
|
||||
* of a message.
|
||||
*/
|
||||
|
||||
#define PREFIX_MASK 0xE0
|
||||
#define LENGTH_MASK 0x1F
|
||||
|
||||
/* Convert a prefix and length to a byte, by shifting the prefix left
|
||||
* 5 bits and OR-ing with the 5 bits of length.
|
||||
*/
|
||||
|
||||
inline uint8_t prefixAndLengthToByte(PrefixID id, uint8_t len)
|
||||
{
|
||||
return (uint8_t(id) << 5) | (len & LENGTH_MASK);
|
||||
}
|
||||
|
||||
/* Extract the prefix (leftmost 3 bits) from a byte.
|
||||
*/
|
||||
|
||||
inline PrefixID byteToPrefix(uint8_t b)
|
||||
{
|
||||
return PrefixID(b >> 5);
|
||||
}
|
||||
|
||||
/* Extract the length (rightmost 5 bits) from a byte.
|
||||
*/
|
||||
|
||||
inline uint8_t byteToLength(byte b)
|
||||
{
|
||||
return uint8_t(b & LENGTH_MASK);
|
||||
}
|
||||
|
||||
/* Message IDs/types, for IOP messages (messages between the Raduino and
|
||||
* the IOP, not including CAT messages to be passed through).
|
||||
*/
|
||||
|
||||
enum MessageID {
|
||||
// Commands
|
||||
IOP_MODE_COMMAND = 0,
|
||||
IOP_START_TX_COMMAND,
|
||||
IOP_STOP_TX_COMMAND,
|
||||
IOP_CW_CONFIG_MSG,
|
||||
IOP_DEBUG_MSG,
|
||||
|
||||
// Requests
|
||||
IOP_MODE_REQUEST,
|
||||
IOP_SSB_STATUS_MSG,
|
||||
IOP_DGT_STATUS_MSG,
|
||||
IOP_CW_STATUS_MSG,
|
||||
IOP_TEST_STATUS_MSG,
|
||||
IOP_MENU_DISPLAY_MSG,
|
||||
IOP_MENU_INACTIVE_MSG,
|
||||
|
||||
// add any new elements here
|
||||
NUM_MESSAGE_IDS
|
||||
};
|
||||
|
||||
/* Max length of an IOP message payload (data), based on the following
|
||||
* message format:
|
||||
*
|
||||
* byte 0: prefix (3 bits) + length (5 bits)
|
||||
* byte 1: message ID (type)
|
||||
* bytes 2-31: data bytes
|
||||
*/
|
||||
|
||||
#define IOP_MESSAGE_MAX_LEN 30
|
||||
|
||||
/* Rig modes. Currently just defined as what the IOP cares about, but
|
||||
* maybe these need to be expanded to encompass all of the rig modes.
|
||||
* (e.g. USB, LSB, etc.)
|
||||
*/
|
||||
|
||||
enum struct rig_mode {
|
||||
ssb = 0,
|
||||
cw,
|
||||
digi,
|
||||
// add new items here
|
||||
count
|
||||
};
|
||||
|
||||
/* Keyer modes.
|
||||
*/
|
||||
|
||||
enum struct keyer_mode {
|
||||
straight = 0,
|
||||
iambic_a,
|
||||
iambic_b,
|
||||
//ultimatic,
|
||||
//bug,
|
||||
// add any new items here
|
||||
count
|
||||
};
|
||||
|
||||
enum struct rx_filter {
|
||||
wide = 0,
|
||||
medium,
|
||||
narrow,
|
||||
count
|
||||
};
|
||||
|
||||
enum struct digi_submode {
|
||||
rtty = 0,
|
||||
psk31,
|
||||
user_defined,
|
||||
count
|
||||
};
|
||||
|
||||
//const unsigned char RIG_MODE_LETTER[num_rig_modes] = {'s', 'S', 'c', 'C', 'd', 'D', 't', 'T'};
|
||||
//const unsigned char KEYER_MODE_LETTER[num_keyer_modes] = {'S', 'A', 'B'};
|
||||
//const unsigned char RX_FILTER_LETTER[num_rx_filters] = {'W', 'M', 'N'};
|
||||
|
||||
const uint8_t NO_FLAGS = 0;
|
||||
const uint8_t REVERSED = 1;
|
||||
|
||||
struct IOPMessage {
|
||||
IOPMessage() { memset(data, 0, IOP_MESSAGE_MAX_LEN); }
|
||||
uint8_t id;
|
||||
uint8_t len;
|
||||
uint8_t data[IOP_MESSAGE_MAX_LEN];
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// IConfig
|
||||
//
|
||||
// Interface to a configuration object.
|
||||
//======================================================================
|
||||
|
||||
struct bpf_config {
|
||||
float lo_freq;
|
||||
float hi_freq;
|
||||
float gain;
|
||||
};
|
||||
|
||||
struct mode_config {
|
||||
mode_config(bool usb, rx_filter f, const bpf_config *fc) : is_usb(usb), filter(f) {
|
||||
for (int i = 0; i < static_cast<int>(rx_filter::count); i++) {
|
||||
filter_cfg[i] = fc[i];
|
||||
}
|
||||
}
|
||||
bool is_usb;
|
||||
rx_filter filter;
|
||||
bpf_config filter_cfg[static_cast<size_t>(rx_filter::count)];
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// SSB CONFIGURATION
|
||||
//======================================================================
|
||||
|
||||
const bpf_config ssb_filter_config[] =
|
||||
{bpf_config{ 300.0, 3100.0, 1.0},
|
||||
bpf_config{ 500.0, 2900.0, 1.0},
|
||||
bpf_config{ 700.0, 2500.0, 1.0}};
|
||||
|
||||
struct comp_config {
|
||||
bool enabled = false;
|
||||
float threshold = 0.1;
|
||||
float ratio = 20.0;
|
||||
float gain = 2.0;
|
||||
};
|
||||
|
||||
struct ssb_config : public mode_config {
|
||||
ssb_config() : mode_config(true, rx_filter::wide, ssb_filter_config) {}
|
||||
comp_config comp;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// DIGI CONFIGURATION
|
||||
//======================================================================
|
||||
|
||||
const bpf_config digi_filter_config[] =
|
||||
{bpf_config{ 300.0, 3100.0, 1.0},
|
||||
bpf_config{ 500.0, 2900.0, 1.0},
|
||||
bpf_config{1250.0, 1750.0, 1.0}};
|
||||
|
||||
struct digi_config : public mode_config {
|
||||
digi_config() : mode_config(true, rx_filter::wide, digi_filter_config) {}
|
||||
digi_submode submode = digi_submode::user_defined;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// CW CONFIGURATION
|
||||
//======================================================================
|
||||
|
||||
const bpf_config cw_filter_config[] =
|
||||
{bpf_config{ 300.0, 1300.0, 1.0},
|
||||
bpf_config{ 450.0, 950.0, 1.0},
|
||||
bpf_config{ 575.0, 825.0, 1.0}};
|
||||
|
||||
struct cw_config : public mode_config {
|
||||
cw_config() : mode_config(true, rx_filter::wide, cw_filter_config) {}
|
||||
keyer_mode mode = keyer_mode::iambic_a;
|
||||
// flags
|
||||
bool reversed = false;
|
||||
// parameters
|
||||
uint8_t wpm = 15;
|
||||
float weight = 3.0;
|
||||
uint16_t sidetone = 700;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// TT CONFIGURATION
|
||||
//======================================================================
|
||||
/*
|
||||
struct TTConfig : public IConfig {
|
||||
public:
|
||||
TTConfig(RxFilter f): filter(f) {}
|
||||
// parameters
|
||||
RxFilter filter = RX_FILTER_MEDIUM;
|
||||
};
|
||||
*/
|
||||
//======================================================================
|
||||
// FUNCTION PROTOTYPES
|
||||
//======================================================================
|
||||
|
||||
void sendCATMessage(const uint8_t*);
|
||||
void sendIOPMessage(IOPMessage const&);
|
||||
void recvIOPMessage(IOPMessage&, const uint8_t*, int);
|
||||
|
||||
void sendIOPModeCommand(rig_mode);
|
||||
void sendIOPStartTxCommand();
|
||||
void sendIOPStopTxCommand();
|
||||
void sendIOPDebugMessage(const char*);
|
||||
|
||||
void sendIOPModeRequest();
|
||||
//void sendIOPSSBStatus(SSBConfig const&);
|
||||
//void sendIOPDGTStatus(DGTConfig const&);
|
||||
//void sendIOPCWStatus(CWConfig const&);
|
||||
//void sendIOPTestStatus();
|
||||
void sendIOPMenuDisplay(const char*);
|
||||
void sendIOPMenuInactive();
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
293
ubitx_iop/RigMode.h
Normal file
293
ubitx_iop/RigMode.h
Normal file
@ -0,0 +1,293 @@
|
||||
//======================================================================
|
||||
// RigMode.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __RigMode_h__
|
||||
#define __RigMode_h__
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
//======================================================================
|
||||
// basic_mode
|
||||
//
|
||||
// A basic rig mode. It can perform actions on_entry() and on_exit(),
|
||||
// as well as on_tx() and on_rx(). These actions are defined in an
|
||||
// appropriate subclass. In addition, the basic mode support an audio
|
||||
// audio band pass filter with wide, medium, and narrow modes.
|
||||
//======================================================================
|
||||
|
||||
class basic_mode {
|
||||
public:
|
||||
|
||||
basic_mode(mode_config& c, RigAudio& a, bp_filter& f) :
|
||||
config_(c), audio_(a), filter_(f), active_(false), transmitting_(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~basic_mode() {}
|
||||
|
||||
inline mode_config& config() { return config_; }
|
||||
|
||||
inline RigAudio& audio() { return audio_; }
|
||||
|
||||
inline bp_filter& filter() { return filter_; }
|
||||
|
||||
// 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 on_entry() = 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 on_exit() = 0;
|
||||
|
||||
// Called when transmitting.
|
||||
virtual void on_tx() = 0;
|
||||
|
||||
// Called when receiving.
|
||||
virtual void on_rx() = 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;
|
||||
on_entry();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
on_exit();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_active() { return active_; }
|
||||
inline bool is_inactive() { 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;
|
||||
on_tx();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
on_rx();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_tx() const { return transmitting_; }
|
||||
inline bool is_rx() const { return !transmitting_; }
|
||||
|
||||
inline void set_rx_filter(rx_filter f) {
|
||||
filter_.disable();
|
||||
config_.filter = f;
|
||||
filter_.init(config_.filter_cfg[static_cast<int>(config_.filter)]);
|
||||
filter_.enable();
|
||||
#if defined(DEBUG)
|
||||
switch(config_.filter) {
|
||||
case rx_filter::wide:
|
||||
USBDEBUG("selected wide filter");
|
||||
break;
|
||||
|
||||
case rx_filter::medium:
|
||||
USBDEBUG("selected medium filter");
|
||||
break;
|
||||
|
||||
case rx_filter::narrow:
|
||||
USBDEBUG("selected narrow filter");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
mode_config& config_;
|
||||
RigAudio& audio_;
|
||||
bp_filter& filter_;
|
||||
bool active_;
|
||||
bool transmitting_;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// ssb_mode
|
||||
//======================================================================
|
||||
|
||||
class ssb_mode : public basic_mode
|
||||
{
|
||||
public:
|
||||
|
||||
ssb_mode(ssb_config& c, RigAudio& a, bp_filter& f, speech_comp& s, bool default_mic=true) :
|
||||
basic_mode(c, a, f), use_mic_(default_mic), comp_(s) {}
|
||||
|
||||
virtual void on_entry()
|
||||
{
|
||||
set_rx_filter(config().filter);
|
||||
if (comp_.is_enabled()) {
|
||||
enable_comp();
|
||||
}
|
||||
audio().unmuteRx();
|
||||
audio().muteAllTx();
|
||||
USBDEBUG("SSB mode entered");
|
||||
}
|
||||
|
||||
virtual void on_exit() {
|
||||
audio().muteAllTx();
|
||||
audio().muteRx();
|
||||
disable_comp();
|
||||
USBDEBUG("SSB mode exited");
|
||||
}
|
||||
|
||||
virtual void on_tx()
|
||||
{
|
||||
audio().muteRx();
|
||||
if (use_mic_) {
|
||||
audio().unmuteMicIn();
|
||||
} else {
|
||||
audio().unmuteLineIn();
|
||||
}
|
||||
USBDEBUG("SSB mode transmitting");
|
||||
}
|
||||
|
||||
virtual void on_rx()
|
||||
{
|
||||
if (use_mic_) {
|
||||
audio().muteMicIn();
|
||||
} else {
|
||||
audio().muteLineIn();
|
||||
}
|
||||
audio().unmuteRx();
|
||||
USBDEBUG("SSB mode receiving");
|
||||
}
|
||||
|
||||
void set_mic_in()
|
||||
{
|
||||
if (is_rx()) {
|
||||
// can't switch inputs while already transmitting
|
||||
use_mic_ = true;
|
||||
}
|
||||
USBDEBUG("SSB mode - Mic In set");
|
||||
}
|
||||
|
||||
void set_line_in()
|
||||
{
|
||||
if (is_rx()) {
|
||||
// can't switch inputs while already transmitting
|
||||
use_mic_ = false;
|
||||
}
|
||||
USBDEBUG("SSB mode - Line In set");
|
||||
}
|
||||
|
||||
inline void enable_comp() { comp_.enable(); }
|
||||
inline void disable_comp() { comp_.disable(); }
|
||||
|
||||
private:
|
||||
|
||||
bool use_mic_;
|
||||
speech_comp& comp_;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// digi_mode
|
||||
//======================================================================
|
||||
|
||||
class digi_mode : public basic_mode
|
||||
{
|
||||
public:
|
||||
|
||||
digi_mode(digi_config& c, RigAudio& a, bp_filter& f) :
|
||||
basic_mode(c, a, f) {}
|
||||
|
||||
virtual void on_entry()
|
||||
{
|
||||
set_rx_filter(config().filter);
|
||||
audio().unmuteRx();
|
||||
audio().muteAllTx();
|
||||
USBDEBUG("Digi mode entered");
|
||||
}
|
||||
|
||||
virtual void on_exit() {
|
||||
audio().muteAllTx();
|
||||
audio().muteRx();
|
||||
USBDEBUG("Digi mode exited");
|
||||
}
|
||||
|
||||
virtual void on_tx()
|
||||
{
|
||||
audio().muteRx();
|
||||
audio().unmuteUSBIn();
|
||||
USBDEBUG("Digi mode transmitting");
|
||||
}
|
||||
|
||||
virtual void on_rx()
|
||||
{
|
||||
audio().muteUSBIn();
|
||||
audio().unmuteRx();
|
||||
USBDEBUG("Digi mode receiving");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//======================================================================
|
||||
// cw_mode
|
||||
//======================================================================
|
||||
|
||||
class cw_mode : public basic_mode
|
||||
{
|
||||
public:
|
||||
|
||||
cw_mode(cw_config& c, RigAudio& a, bp_filter& f):
|
||||
basic_mode(c, a, f) {}
|
||||
|
||||
virtual void on_entry()
|
||||
{
|
||||
set_rx_filter(config().filter);
|
||||
audio().unmuteRx();
|
||||
audio().muteAllTx();
|
||||
USBDEBUG("CW mode entered");
|
||||
}
|
||||
|
||||
virtual void on_exit() {
|
||||
audio().muteAllTx();
|
||||
audio().muteRx();
|
||||
USBDEBUG("CW mode exited");
|
||||
}
|
||||
|
||||
virtual void on_tx()
|
||||
{
|
||||
// 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 on_rx()
|
||||
{
|
||||
USBDEBUG("CW mode receiving");
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
165
ubitx_iop/TxSwitch.h
Normal file
165
ubitx_iop/TxSwitch.h
Normal file
@ -0,0 +1,165 @@
|
||||
//======================================================================
|
||||
// TxSwitch.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __TxSwitch_h__
|
||||
#define __TxSwitch_h__
|
||||
|
||||
#include <Bounce2.h>
|
||||
#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(basic_mode* m) = 0;
|
||||
|
||||
// Called before stopping tranmit; if false, transmit continues.
|
||||
virtual bool onRelease(basic_mode* m) = 0;
|
||||
|
||||
void press(basic_mode* 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(basic_mode* 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(basic_mode* m) {
|
||||
// If another transmission is already occuring, abort... CAT can't
|
||||
// interrupt transmissions already ongoing.
|
||||
if (m->is_rx()) {
|
||||
USBDEBUG("CAT PTT pressed");
|
||||
_transmitting = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool onRelease(basic_mode* 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
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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(is_mic), _ssb_mode(false), _bounce() {
|
||||
_bounce.attach(pin, INPUT_PULLUP);
|
||||
_bounce.interval(msec);
|
||||
}
|
||||
|
||||
inline void setSSBMode(bool flag) { _ssb_mode = flag; }
|
||||
|
||||
virtual bool onPress(basic_mode* m) {
|
||||
if (m->is_rx()) {
|
||||
if (_ssb_mode) {
|
||||
if (_is_mic) {
|
||||
USBDEBUG("Mic PTT pressed");
|
||||
((ssb_mode*)m)->set_mic_in();
|
||||
} else {
|
||||
USBDEBUG("Line PTT pressed");
|
||||
((ssb_mode*)m)->set_line_in();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool onRelease(basic_mode* m) {
|
||||
if (m->is_tx()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void update(basic_mode* 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
|
||||
//======================================================================
|
121
ubitx_iop/audio.h
Normal file
121
ubitx_iop/audio.h
Normal file
@ -0,0 +1,121 @@
|
||||
//======================================================================
|
||||
// audio.h
|
||||
//
|
||||
// NOTE: Let's change the name of this file to RigAudio.h.
|
||||
//======================================================================
|
||||
|
||||
#ifndef __iop_audio_h__
|
||||
#define __iop_audio_h__
|
||||
|
||||
#include <Audio.h>
|
||||
#include <dynamicFilters.h>
|
||||
#include <effect_compressor_fb.h>
|
||||
#include "config.h"
|
||||
|
||||
class RigAudio
|
||||
{
|
||||
public:
|
||||
RigAudio(AudioConfig& c): _config(c) {}
|
||||
|
||||
void init() const;
|
||||
|
||||
void muteRx() const;
|
||||
void unmuteRx() const;
|
||||
|
||||
void muteAllTx() const;
|
||||
|
||||
void muteMicIn() const;
|
||||
void unmuteMicIn() const;
|
||||
|
||||
void muteLineIn() const;
|
||||
void unmuteLineIn() const;
|
||||
|
||||
void muteUSBIn() const;
|
||||
void unmuteUSBIn() const;
|
||||
|
||||
void muteTTIn() const;
|
||||
void unmuteTTIn() const;
|
||||
|
||||
void muteSpkrOut() const;
|
||||
void unmuteSpkrOut() const;
|
||||
|
||||
void muteLineOut() const;
|
||||
void unmuteLineOut() const;
|
||||
|
||||
void muteUSBOut() const;
|
||||
void unmuteUSBOut() const;
|
||||
|
||||
private:
|
||||
AudioConfig _config;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
class bp_filter {
|
||||
|
||||
public:
|
||||
//bp_filter(double f1, double f2, bool use_center, short int window=-1, short int coeff=-1);
|
||||
bp_filter();
|
||||
bp_filter(AudioFilterFIR& f, AudioAmplifier& a);
|
||||
void init(const bpf_config& cfg);
|
||||
//void init(AudioFilterFIR* filter=NULL, short* coefficients=NULL);
|
||||
void set_band(double f1, double f2);
|
||||
void set_freq_lo(double f);
|
||||
void set_freq_hi(double f);
|
||||
void set_center_and_width(double c, double w);
|
||||
void set_center(double c);
|
||||
void set_width(double w);
|
||||
void set_gain(double g);
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
private:
|
||||
|
||||
double freq_lo;
|
||||
double freq_hi;
|
||||
short int window;
|
||||
short int coeff;
|
||||
float recovery; // recovery amplifier value
|
||||
|
||||
AudioFilterFIR& filter; // = &filterRX;
|
||||
AudioAmplifier& amp;
|
||||
short coefficients[NUM_COEFFICIENTS];
|
||||
bool setup_complete;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
class speech_comp
|
||||
{
|
||||
public:
|
||||
speech_comp(comp_config* cfg);
|
||||
// speech_comp(AudioEffectCompressor&, AudioAmplifier&, AudioAnalyzeRMS&);
|
||||
void update();
|
||||
void enable();
|
||||
void disable();
|
||||
inline bool is_enabled() const { return config_->enabled; }
|
||||
|
||||
private:
|
||||
comp_config* config_;
|
||||
AudioEffectCompressor& comp_;
|
||||
AudioAmplifier& amp_;
|
||||
AudioAnalyzeRMS& rms_;
|
||||
float env_ = 1.0;
|
||||
float alpha_ = 0.8;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
/*
|
||||
void audioInit();
|
||||
void audioSelectTxInput(TxInput);
|
||||
void audioTransmit();
|
||||
void audioReceive();
|
||||
void audioCalibrate(AudioConfig *, char, char, char, float, bool);
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
370
ubitx_iop/audio.ino
Normal file
370
ubitx_iop/audio.ino
Normal file
@ -0,0 +1,370 @@
|
||||
//======================================================================
|
||||
// audio.ino
|
||||
//
|
||||
// NOTE: Let's change the name of this file to Rigconfig.cc. Will need
|
||||
// to ensure that "Arduino-isms" are resolved if it's converted to .cc
|
||||
// from .ino, however.
|
||||
//======================================================================
|
||||
|
||||
#include <dynamicFilters.h>
|
||||
#include <effect_compressor_fb.h>
|
||||
#include "audio.h"
|
||||
|
||||
//short firActive[NUM_COEFFICIENTS];
|
||||
|
||||
#define RX_RIG_IN 0
|
||||
#define RX_USB_IN 1
|
||||
#define RX_ST_IN 2 // sidetone
|
||||
|
||||
#define TX_MIC_IN 0
|
||||
#define TX_LINE_IN 0
|
||||
#define TX_USB_IN 1
|
||||
#define TX_TEST_IN 2
|
||||
|
||||
//extern RigConfig rigConfig;
|
||||
|
||||
/*
|
||||
#define DEFAULT_RX_INPUT RIG
|
||||
#define DEFAULT_RX_OUTPUT SPKR
|
||||
#define DEFAULT_TX_INPUT MIC
|
||||
#define DEFAULT_TX_OUTPUT RIG
|
||||
*/
|
||||
|
||||
#include <Audio.h>
|
||||
#include <Wire.h>
|
||||
#include <SPI.h>
|
||||
#include <SD.h>
|
||||
#include <SerialFlash.h>
|
||||
|
||||
// GUItool: begin automatically generated code
|
||||
AudioInputI2S inLine; //xy=134,131
|
||||
AudioInputUSB inUSB; //xy=134,303
|
||||
AudioSynthWaveformSine sideTone; //xy=136,214
|
||||
AudioSynthWaveformSine sine2; //xy=323,482
|
||||
AudioSynthWaveformSine sine1; //xy=324,428
|
||||
AudioAnalyzeRMS rmsRX; //xy=362,20
|
||||
AudioAnalyzePeak peakRX; //xy=362,65
|
||||
AudioEffectCompressor compTX; //xy=383,256
|
||||
AudioAnalyzeRMS compRMS; //xy=388,224
|
||||
AudioMixer4 mixRX; //xy=444,163
|
||||
AudioAmplifier compAmp; //xy=514,257
|
||||
AudioFilterFIR filterRX; //xy=577,161
|
||||
AudioMixer4 mixTX; //xy=652,321
|
||||
AudioAmplifier filterAmp; //xy=700,160
|
||||
AudioAnalyzeRMS rmsTX; //xy=841,449
|
||||
AudioAnalyzePeak peakTX; //xy=843,393
|
||||
AudioAmplifier calRxUSB; //xy=873,172
|
||||
AudioAmplifier calRxSpkr; //xy=877,62
|
||||
AudioAmplifier calRxLine; //xy=880,119
|
||||
AudioAmplifier calTxLine; //xy=892,255
|
||||
AudioAmplifier calTxUSB; //xy=893,312
|
||||
AudioOutputAnalog outSpkr; //xy=1053,62
|
||||
AudioOutputUSB outUSB; //xy=1066,307
|
||||
AudioOutputI2S outLine; //xy=1070,192
|
||||
AudioConnection patchCord1(inLine, 0, rmsRX, 0);
|
||||
AudioConnection patchCord2(inLine, 0, peakRX, 0);
|
||||
AudioConnection patchCord3(inLine, 0, mixRX, 0);
|
||||
AudioConnection patchCord4(inLine, 1, compTX, 0);
|
||||
AudioConnection patchCord5(inLine, 1, compRMS, 0);
|
||||
AudioConnection patchCord6(inUSB, 0, mixRX, 1);
|
||||
AudioConnection patchCord7(inUSB, 1, mixTX, 1);
|
||||
AudioConnection patchCord8(sideTone, 0, mixRX, 2);
|
||||
AudioConnection patchCord9(sine2, 0, mixTX, 3);
|
||||
AudioConnection patchCord10(sine1, 0, mixTX, 2);
|
||||
AudioConnection patchCord11(compTX, compAmp);
|
||||
AudioConnection patchCord12(mixRX, filterRX);
|
||||
AudioConnection patchCord13(compAmp, 0, mixTX, 0);
|
||||
AudioConnection patchCord14(filterRX, filterAmp);
|
||||
AudioConnection patchCord15(mixTX, calTxLine);
|
||||
AudioConnection patchCord16(mixTX, calTxUSB);
|
||||
AudioConnection patchCord17(mixTX, peakTX);
|
||||
AudioConnection patchCord18(mixTX, rmsTX);
|
||||
AudioConnection patchCord19(filterAmp, calRxSpkr);
|
||||
AudioConnection patchCord20(filterAmp, calRxLine);
|
||||
AudioConnection patchCord21(filterAmp, calRxUSB);
|
||||
AudioConnection patchCord22(calRxUSB, 0, outUSB, 0);
|
||||
AudioConnection patchCord23(calRxSpkr, outSpkr);
|
||||
AudioConnection patchCord24(calRxLine, 0, outLine, 0);
|
||||
AudioConnection patchCord25(calTxLine, 0, outLine, 1);
|
||||
AudioConnection patchCord26(calTxUSB, 0, outUSB, 1);
|
||||
AudioControlSGTL5000 audioCtrl; //xy=648,517
|
||||
// GUItool: end automatically generated code
|
||||
|
||||
void RigAudio::init() const {
|
||||
USBDEBUG("audio initialization started");
|
||||
|
||||
audioCtrl.enable();
|
||||
audioCtrl.adcHighPassFilterEnable(); // default
|
||||
audioCtrl.dacVolume(1.0); // default
|
||||
audioCtrl.dacVolumeRampDisable(); // not default; just trying to cull out culprits for my high freq hiss
|
||||
audioCtrl.audioProcessorDisable();
|
||||
audioCtrl.autoVolumeDisable();
|
||||
audioCtrl.surroundSoundDisable();
|
||||
audioCtrl.enhanceBassDisable();
|
||||
audioCtrl.eqSelect(FLAT_FREQUENCY); // eventually, use this to smooth out the normal uBITX passband
|
||||
|
||||
audioCtrl.muteHeadphone(); // not using the headphone output
|
||||
audioCtrl.volume(0.0); // not using the headphone output
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN); // required for RX audio
|
||||
audioCtrl.unmuteLineout(); // required for RX audio
|
||||
|
||||
audioCtrl.lineInLevel(_config.rxRigInLevel, _config.txLineInLevel); // NOTE: need to see if this persists through input changes (see mic gain...)
|
||||
audioCtrl.lineOutLevel(_config.rxLineOutLevel, _config.txRigOutLevel); // NOTE: need to see if this persists through input changes (see mic gain...)
|
||||
audioCtrl.micGain(_config.txMicInGain); // superfluous, as I have to do this anytime I switch to mic for some reason
|
||||
|
||||
// configure line input
|
||||
audioCtrl.lineInLevel(_config.rxRigInLevel, _config.txLineInLevel);
|
||||
|
||||
// configure line output
|
||||
calTxLine.gain(_config.txRigOutCal);
|
||||
audioCtrl.lineOutLevel(_config.rxLineOutLevel, _config.txRigOutLevel);
|
||||
|
||||
// configure "receive" of USB audio input (debug only)
|
||||
if (_config.rxUSBInEnable) {
|
||||
mixRX.gain(RX_USB_IN, _config.rxUSBInVol * _config.rxUSBInCal);
|
||||
} else {
|
||||
mixRX.gain(RX_USB_IN, 0.0);
|
||||
}
|
||||
|
||||
// configure USB audio output of transmit audio (useful for debug)
|
||||
if (_config.txUSBOutEnable) {
|
||||
calTxUSB.gain(_config.txUSBOutCal);
|
||||
} else {
|
||||
calTxUSB.gain(0.0);
|
||||
}
|
||||
|
||||
// setup the two-tone generator
|
||||
sine1.frequency(700);
|
||||
sine2.frequency(1900);
|
||||
sine1.amplitude(0);
|
||||
sine2.amplitude(0);
|
||||
|
||||
//audioFilter(firActive, NUM_COEFFICIENTS, ID_BANDPASS, W_HAMMING, 300.0, 3100.0); // 2.8 kHz filter
|
||||
//filterRX.begin(firActive, NUM_COEFFICIENTS);
|
||||
filterAmp.gain(1.0);
|
||||
|
||||
// for now, just pass through the compressor
|
||||
compTX.disable();
|
||||
compAmp.gain(1.0);
|
||||
|
||||
// Hardware should be all setup... now we're going to mute everything
|
||||
// and let the modes take care of enabling/disabling what they should.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mixRX.gain(i, 0.0);
|
||||
mixTX.gain(i, 0.0);
|
||||
}
|
||||
|
||||
audioEqualizer();
|
||||
|
||||
USBDEBUG("audio initialization completed");
|
||||
}
|
||||
|
||||
void RigAudio::muteRx() const {
|
||||
mixRX.gain(RX_RIG_IN, 0.0);
|
||||
USBDEBUG("RX audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteRx() const {
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
||||
mixRX.gain(RX_RIG_IN, _config.rxRigInVol * _config.rxRigInCal);
|
||||
USBDEBUG("RX audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteAllTx() const {
|
||||
muteMicIn();
|
||||
muteLineIn();
|
||||
muteUSBIn();
|
||||
muteTTIn();
|
||||
USBDEBUG("all TX audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::muteMicIn() const {
|
||||
mixTX.gain(TX_LINE_IN, 0.0);
|
||||
USBDEBUG("Mic In audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteMicIn() const {
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_MIC);
|
||||
audioCtrl.micGain(_config.txMicInGain);
|
||||
mixTX.gain(TX_LINE_IN, _config.txMicInVol * _config.txMicInCal);
|
||||
USBDEBUG("Mic In audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteLineIn() const {
|
||||
mixTX.gain(TX_LINE_IN, 0.0);
|
||||
USBDEBUG("Line In audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteLineIn() const {
|
||||
audioCtrl.inputSelect(AUDIO_INPUT_LINEIN);
|
||||
mixTX.gain(TX_LINE_IN, _config.txLineInVol * _config.txLineInCal);
|
||||
USBDEBUG("Line In audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteUSBIn() const {
|
||||
mixTX.gain(TX_USB_IN, 0.0);
|
||||
USBDEBUG("USB In audio muted");
|
||||
}
|
||||
void RigAudio::unmuteUSBIn() const {
|
||||
mixTX.gain(TX_USB_IN, _config.txUSBInVol * _config.txUSBInCal);
|
||||
USBDEBUG("USB In audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteTTIn() const {
|
||||
mixTX.gain(TX_TEST_IN, 0.0);
|
||||
mixTX.gain(TX_TEST_IN + 1, 0.0);
|
||||
sine1.amplitude(0.0);
|
||||
sine2.amplitude(0.0);
|
||||
USBDEBUG("Two Tone audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteTTIn() const {
|
||||
sine1.amplitude(0.5);
|
||||
sine2.amplitude(0.5);
|
||||
mixTX.gain(TX_TEST_IN, _config.txSine1Vol);
|
||||
mixTX.gain(TX_TEST_IN + 1, _config.txSine2Vol);
|
||||
USBDEBUG("Two Tone audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteSpkrOut() const {
|
||||
calRxSpkr.gain(0.0);
|
||||
USBDEBUG("Speaker Out audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteSpkrOut() const {
|
||||
calRxSpkr.gain(_config.rxSpkrOutCal);
|
||||
USBDEBUG("Speaker Out audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteLineOut() const {
|
||||
calRxLine.gain(0.0);
|
||||
USBDEBUG("Line Out audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteLineOut() const {
|
||||
calRxLine.gain(_config.rxLineOutCal);
|
||||
USBDEBUG("Line Out audio unmuted");
|
||||
}
|
||||
|
||||
void RigAudio::muteUSBOut() const {
|
||||
calRxUSB.gain(0.0);
|
||||
USBDEBUG("USB Out audio muted");
|
||||
}
|
||||
|
||||
void RigAudio::unmuteUSBOut() const {
|
||||
calRxUSB.gain(_config.rxUSBOutCal);
|
||||
USBDEBUG("USB Out audio unmuted");
|
||||
}
|
||||
|
||||
/*
|
||||
RxInput audioRxInput;
|
||||
RxOutput audioRxOutput;
|
||||
TxInput audioTxInput;
|
||||
TxOutput audioTxOutput;
|
||||
*/
|
||||
|
||||
//======================================================================
|
||||
// bp_filter
|
||||
//
|
||||
// Band Pass Filter methods.
|
||||
//======================================================================
|
||||
|
||||
bp_filter::bp_filter(): filter(filterRX), amp(filterAmp) {}
|
||||
|
||||
bp_filter::bp_filter(AudioFilterFIR& f, AudioAmplifier& a): filter(f), amp(a) {}
|
||||
|
||||
void bp_filter::init(const bpf_config& cfg) {
|
||||
set_band(cfg.lo_freq, cfg.hi_freq);
|
||||
set_gain(cfg.gain);
|
||||
}
|
||||
|
||||
void bp_filter::set_band(double f1, double f2) {
|
||||
freq_lo = f1;
|
||||
freq_hi = f2;
|
||||
}
|
||||
|
||||
void bp_filter::set_freq_lo(double f) { freq_lo = f; }
|
||||
|
||||
void bp_filter::set_freq_hi(double f) { freq_hi = f; }
|
||||
|
||||
void bp_filter::set_center_and_width(double c, double w) {
|
||||
freq_lo = c - (0.5 * w);
|
||||
freq_hi = c + (0.5 * w);
|
||||
}
|
||||
|
||||
void bp_filter::set_center(double c) {
|
||||
double w = freq_hi - freq_lo;
|
||||
set_center_and_width(c, w);
|
||||
}
|
||||
|
||||
void bp_filter::set_width(double w) {
|
||||
double c = (freq_lo + freq_hi) * 0.5;
|
||||
set_center_and_width(c, w);
|
||||
}
|
||||
|
||||
void bp_filter::set_gain(double g) { recovery = g; }
|
||||
|
||||
void bp_filter::enable() {
|
||||
audioFilter(coefficients, NUM_COEFFICIENTS, ID_BANDPASS, W_HAMMING, freq_lo, freq_hi);
|
||||
filter.begin(coefficients, NUM_COEFFICIENTS);
|
||||
amp.gain(recovery);
|
||||
}
|
||||
|
||||
void bp_filter::disable() {
|
||||
filter.begin(FIR_PASSTHRU, NUM_COEFFICIENTS);
|
||||
amp.gain(1.0);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
speech_comp::speech_comp(comp_config* cfg) :
|
||||
config_(cfg), comp_(compTX), amp_(compAmp), rms_(compRMS) {}
|
||||
|
||||
void speech_comp::enable()
|
||||
{
|
||||
config_->enabled = true;
|
||||
comp_.begin(1, config_->threshold, config_->ratio); // Need to make configurable
|
||||
amp_.gain(config_->gain);
|
||||
}
|
||||
|
||||
void speech_comp::disable()
|
||||
{
|
||||
config_->enabled = false;
|
||||
comp_.disable();
|
||||
amp_.gain(1.0);
|
||||
}
|
||||
|
||||
// Speech compressor code based on post by 'hyperdyne': https://forum.pjrc.com/threads/36245-Changing-Pitch-of-Voice
|
||||
|
||||
void speech_comp::update()
|
||||
{
|
||||
float rms_cur;
|
||||
if (config_->enabled && rms_.available()) {
|
||||
rms_cur = rms_.read();
|
||||
env_ = rms_cur + (alpha_ * (env_ - rms_cur));
|
||||
comp_.update_pwr(env_);
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
int eqFilter1[5];
|
||||
|
||||
void audioEqualizer()
|
||||
{
|
||||
audioCtrl.audioPreProcessorEnable();
|
||||
audioCtrl.eqSelect(PARAMETRIC_EQUALIZER);
|
||||
// calcBiquad(FilterType,FrequencyC,dBgain,Q,QuantizationUnit,SampleRate,int*);
|
||||
calcBiquad(FILTER_PARAEQ, 2700, 6, 0.707, 524288, 44100, eqFilter1);
|
||||
// calcBiquad(FILTER_HIPASS, 100, 0, 0.707, 524288, 44100, hpFilter);
|
||||
audioCtrl.eqFilter(0, eqFilter1);
|
||||
// audioCtrl.eqFilter(1, hpFilter);
|
||||
// audioCtrl.eqFilter(2, lpFilter);
|
||||
// audioCtrl.eqFilter(3, hpFilter);
|
||||
// audioCtrl.eqFilter(4, lpFilter);
|
||||
// audioCtrl.eqFilter(5, hpFilter);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
27
ubitx_iop/cat.h
Normal file
27
ubitx_iop/cat.h
Normal file
@ -0,0 +1,27 @@
|
||||
//======================================================================
|
||||
// cat.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __iop_cat_h__
|
||||
#define __iop_cat_h__
|
||||
|
||||
//#define ACK 0
|
||||
//#define CAT_PREFIX 0xC0
|
||||
//#define IOP_PREFIX 0xD0
|
||||
//#define EEPROM_READ_PREFIX 0xE0
|
||||
//#define EEPROM_WRITE_PREFIX 0xF0
|
||||
|
||||
#include <iopcomm.h>
|
||||
#include "config.h"
|
||||
|
||||
#define USBSERIAL Serial
|
||||
#define HWSERIAL Serial1
|
||||
|
||||
void initCAT(long, int);
|
||||
void serviceCAT();
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
354
ubitx_iop/cat.ino
Normal file
354
ubitx_iop/cat.ino
Normal file
@ -0,0 +1,354 @@
|
||||
//======================================================================
|
||||
// cat.ino
|
||||
//======================================================================
|
||||
|
||||
#include "cat.h"
|
||||
#include "TxSwitch.h"
|
||||
|
||||
// make these messages static inside a function
|
||||
IOPMessage inBuf; // input message buffer
|
||||
IOPMessage outBuf; // output message buffer
|
||||
|
||||
extern CATSwitch catPTT;
|
||||
extern basic_rig rig;
|
||||
|
||||
int received_mode = 0;
|
||||
|
||||
//======================================================================
|
||||
// CAT from PC-to-IOP
|
||||
//
|
||||
// The general concept for IOP use of CAT is for the IOP to pass thru
|
||||
// all incoming CAT data (from the PC) to the Raduino.
|
||||
//
|
||||
// This might change in the future, if we want to grab CAT data straight
|
||||
// from the PC. That might apply to things like specific audio filter
|
||||
// settings or something, but since the Raduino modes are an important
|
||||
// part of the mix, I think the commands really need to come from the
|
||||
// Raduino... and besides, what if a PC is not connected?
|
||||
//
|
||||
//
|
||||
// For data coming from the Raduino, the IOP does have to do a minimal
|
||||
// processing to extra any Raduino-to-IOP commands.
|
||||
//======================================================================
|
||||
|
||||
void initCAT(long baud, int portConfig)
|
||||
{
|
||||
// CAT with PC via USB
|
||||
USBSERIAL.begin(baud);
|
||||
USBSERIAL.flush();
|
||||
USBDEBUG("opened USB serial port (to PC)");
|
||||
|
||||
// Comm (including CAT passthru) with Raduino via UART
|
||||
HWSERIAL.begin(baud, portConfig);
|
||||
HWSERIAL.flush();
|
||||
USBDEBUG("opened H/W serial port (to Raduino)");
|
||||
|
||||
//sendIOPModeRequest(); // Raduino doesn't support this yet
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void processIOPCommand(IOPMessage const& m)
|
||||
{
|
||||
switch(inBuf.id) {
|
||||
case IOP_MODE_COMMAND:
|
||||
received_mode = 2;
|
||||
USBDEBUG("IOP_MODE_COMMAND received (from Raduino)");
|
||||
if (m.len < 1) {
|
||||
return;
|
||||
} else {
|
||||
rig.set_rig_mode(static_cast<rig_mode>(m.data[0]));
|
||||
#if defined(DEBUG)
|
||||
switch(rig.get_rig_mode()) {
|
||||
case rig_mode::cw:
|
||||
USBDEBUG("new mode - CW");
|
||||
break;
|
||||
case rig_mode::ssb:
|
||||
USBDEBUG("new mode - SSB");
|
||||
break;
|
||||
case rig_mode::digi:
|
||||
USBDEBUG("new mode - DIG");
|
||||
break;
|
||||
default:
|
||||
char errormessage[32];
|
||||
sprintf(errormessage, "unknown mode command - %3d", rig.get_rig_mode());
|
||||
USBDEBUG("mode command not recognized");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case IOP_START_TX_COMMAND:
|
||||
catPTT.press(rig.mode());
|
||||
break;
|
||||
|
||||
case IOP_STOP_TX_COMMAND:
|
||||
catPTT.release(rig.mode());
|
||||
break;
|
||||
|
||||
case IOP_CW_CONFIG_MSG:
|
||||
break;
|
||||
|
||||
case IOP_DEBUG_MSG:
|
||||
USBDEBUG("IOP_DEBUG_MSG");
|
||||
USBDEBUG((const char*)m.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void processCalCommand(const char* buf)
|
||||
{
|
||||
int count;
|
||||
char cmd;
|
||||
char subcmd;
|
||||
char parm;
|
||||
float value;
|
||||
|
||||
count = sscanf(buf, "%1c %1c %1c %f", &cmd, &subcmd, &parm, &value);
|
||||
|
||||
if (count < 3) {
|
||||
USBSERIAL.println("Calibration: invalid command");
|
||||
} else {
|
||||
switch(cmd) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
case 't':
|
||||
case 'T':
|
||||
//audioCalibrate(&rigConfig.audio, cmd, subcmd, parm, value, (count == 4));
|
||||
break;
|
||||
|
||||
default:
|
||||
USBSERIAL.println("Calibration: invalid command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
enum SerialState {
|
||||
NORMAL = 0,
|
||||
CAT_MODE,
|
||||
IOP_MODE,
|
||||
EEPROM_READ,
|
||||
EEPROM_WRITE,
|
||||
} serialState = NORMAL;
|
||||
|
||||
PrefixID readPrefix;
|
||||
uint8_t readLength;
|
||||
|
||||
int cmdLength = 0;
|
||||
byte cmdBuffer[16];
|
||||
char cmdString[17]; // added a char for null termination when required
|
||||
|
||||
uint16_t eepromStartIndex;
|
||||
uint16_t eepromReadLength;
|
||||
int magicFlag = 0;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
uint8_t usbCatLength = 0;
|
||||
byte usbCatBuffer[5];
|
||||
elapsedMillis usbCatTimer;
|
||||
|
||||
#define CAT_RECEIVE_TIMEOUT 500
|
||||
|
||||
void serviceCAT()
|
||||
{
|
||||
uint8_t incomingByte;
|
||||
|
||||
// First, if we've never received or requested a mode command from the Raduino, send a request.
|
||||
if (received_mode == 0) {
|
||||
USBDEBUG("requesting mode");
|
||||
sendIOPModeRequest();
|
||||
received_mode = 1;
|
||||
}
|
||||
|
||||
if ((usbCatLength > 0) && (usbCatTimer > CAT_RECEIVE_TIMEOUT)) {
|
||||
// timeout... clear the buffer and start over
|
||||
usbCatLength = 0;
|
||||
usbCatTimer = 0;
|
||||
}
|
||||
|
||||
// read from the USB serial, pass through to UART serial
|
||||
for (int i = 0; i < USBSERIAL.available(); i++) {
|
||||
incomingByte = USBSERIAL.read();
|
||||
usbCatTimer = 0;
|
||||
|
||||
#if not defined(FACTORY_CALIBRATION)
|
||||
usbCatBuffer[usbCatLength++] = incomingByte;
|
||||
if (usbCatLength == 5) {
|
||||
sendCATMessage(usbCatBuffer);
|
||||
usbCatLength = 0;
|
||||
}
|
||||
|
||||
// NOTE: This code does NOT handle any interrupts in COMMS from the PC...
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(FACTORY_CALIBRATION)
|
||||
// unless we're in factory calibration mode, in which case we're going
|
||||
// to process calibration commands...
|
||||
switch(incomingByte) {
|
||||
case ';':
|
||||
cmdString[cmdLength] = '\0';
|
||||
if (cmdLength > 0) {
|
||||
processCalCommand(cmdString);
|
||||
cmdLength = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
case '\r':
|
||||
cmdString[0] = '\0';
|
||||
cmdLength = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmdString[cmdLength++] = char(incomingByte);
|
||||
if (cmdLength == 16) {
|
||||
cmdString[cmdLength] = '\0';
|
||||
processCalCommand(cmdString);
|
||||
cmdLength = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Don't pass CAT commands through if in DEBUG mode.
|
||||
#if not defined(DEBUG)
|
||||
//HWSERIAL.write(incomingByte);
|
||||
#endif
|
||||
}
|
||||
|
||||
// read from the UART serial, see what we need to do with it
|
||||
for (int i = 0; i < HWSERIAL.available(); i++) {
|
||||
incomingByte = HWSERIAL.read();
|
||||
#if defined(DEBUG)
|
||||
char ibBuff[20];
|
||||
itoa(serialState, ibBuff, 10);
|
||||
USBSERIAL.print("IOP: serialState = ");
|
||||
USBSERIAL.print(ibBuff);
|
||||
itoa(incomingByte, ibBuff, 10);
|
||||
USBSERIAL.print("; incomingByte = ");
|
||||
USBSERIAL.println(ibBuff);
|
||||
#endif
|
||||
|
||||
switch(serialState) {
|
||||
case NORMAL:
|
||||
if (incomingByte == ACK) {
|
||||
USBSERIAL.write(incomingByte);
|
||||
} else {
|
||||
readPrefix = byteToPrefix(incomingByte);
|
||||
readLength = byteToLength(incomingByte);
|
||||
#if defined(DEBUG)
|
||||
itoa(readPrefix, ibBuff, 10);
|
||||
USBSERIAL.print("IOP: readPrefix = ");
|
||||
USBSERIAL.print(ibBuff);
|
||||
itoa(readLength, ibBuff, 10);
|
||||
USBSERIAL.print("; readLength = ");
|
||||
USBSERIAL.println(ibBuff);
|
||||
#endif
|
||||
if (readLength > 0) {
|
||||
USBDEBUG("readLength > 0");
|
||||
switch(readPrefix) {
|
||||
case CAT_PREFIX:
|
||||
case RAD_EEPROM_WRITE_PREFIX:
|
||||
serialState = CAT_MODE;
|
||||
break;
|
||||
|
||||
case IOP_PREFIX:
|
||||
USBDEBUG("entering IOP_MODE");
|
||||
serialState = IOP_MODE;
|
||||
cmdLength = 0;
|
||||
break;
|
||||
|
||||
case RAD_EEPROM_READ_PREFIX:
|
||||
serialState = EEPROM_READ;
|
||||
readLength = 5;
|
||||
magicFlag = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CAT_MODE:
|
||||
// In CAT mode, we just pass thru the remaining bytes to the PC.
|
||||
USBSERIAL.write(incomingByte);
|
||||
readLength--;
|
||||
if (readLength == 0) {
|
||||
serialState = NORMAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case IOP_MODE:
|
||||
cmdBuffer[cmdLength] = incomingByte;
|
||||
cmdLength++;
|
||||
readLength--;
|
||||
if (readLength == 0) {
|
||||
recvIOPMessage(inBuf, cmdBuffer, cmdLength);
|
||||
processIOPCommand(inBuf);
|
||||
serialState = NORMAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case EEPROM_READ:
|
||||
readLength--;
|
||||
switch(readLength) {
|
||||
case 4:
|
||||
eepromStartIndex = incomingByte;
|
||||
if (incomingByte == 0x16) {
|
||||
magicFlag++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
eepromStartIndex += (256 * incomingByte);
|
||||
if (incomingByte == 0xe8) {
|
||||
magicFlag++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
eepromReadLength = incomingByte;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
eepromReadLength += (256 * incomingByte);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
USBSERIAL.write(incomingByte);
|
||||
if (magicFlag == 2) {
|
||||
readLength = 126 + 2;
|
||||
} else {
|
||||
readLength = eepromReadLength + 2;
|
||||
}
|
||||
serialState = CAT_MODE;
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EEPROM_WRITE:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen...
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
137
ubitx_iop/config.h
Normal file
137
ubitx_iop/config.h
Normal file
@ -0,0 +1,137 @@
|
||||
//======================================================================
|
||||
// config.h
|
||||
//
|
||||
// NOTE: Let's change the name of this file to RigConfig.h.
|
||||
//======================================================================
|
||||
|
||||
#ifndef __iop_config_h__
|
||||
#define __iop_config_h__
|
||||
|
||||
#include <iopcomm.h>
|
||||
//#include "rig.h"
|
||||
|
||||
#define KEYER_LEFT_PADDLE_PIN 16
|
||||
#define KEYER_RIGHT_PADDLE_PIN 17
|
||||
|
||||
// Uncomment to use the "factory" calibration mode. This is intended to
|
||||
// allow calibration of IOP settings, separately from the uBITX/Raduino.
|
||||
// There will be no pass-thru of any CAT.
|
||||
//#define FACTORY_CALIBRATION
|
||||
|
||||
//======================================================================
|
||||
// AudioConfig
|
||||
//======================================================================
|
||||
|
||||
class AudioConfig {
|
||||
public:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// RECEIVE PARAMETERS
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// rig-in parameters (RX)
|
||||
uint8_t rxRigInLevel = 8; //5;
|
||||
float rxRigInVol = 1.0; //0.7;
|
||||
float rxRigInCal = 8.0; //1.0;
|
||||
// USB-in parameters (RX) - debug/monitor use only
|
||||
bool rxUSBInEnable = false;
|
||||
float rxUSBInVol = 1.0;
|
||||
float rxUSBInCal = 1.0;
|
||||
// speaker-out (DAC) parameters (RX)
|
||||
float rxSpkrOutCal = 1.0;
|
||||
// line-out parameters (RX)
|
||||
uint8_t rxLineOutLevel = 29;
|
||||
float rxLineOutCal = 1.0;
|
||||
// USB-out parameters (RX)
|
||||
float rxUSBOutCal = 1.0;
|
||||
// sidetone parameters (really a TX thing, but "RX" side of audio)
|
||||
float sideToneVol = 1.0;
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// TRANSMIT PARAMETERS
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// NOTE: Default rig-out parameters are based on trying to hit a
|
||||
// 25mVrms output to the rig. This is based on some discussion at the
|
||||
// following URL:
|
||||
//
|
||||
// https://groups.io/g/BITX20/message/58951
|
||||
//
|
||||
// This may or may not be totally applicable. I believe it was for
|
||||
// IMD/spurs on a pre-V5 version of the uBITX. So it may be OBE for
|
||||
// my V5. It also alludes to modifications to the transmit audio
|
||||
// chain, which may be something I need to look into (i.e. increasing
|
||||
// the gain on the initial mic pre-amp).
|
||||
//
|
||||
// Specifically, for the rig-out parameters:
|
||||
// line out level 31 = 1.16 V p-p
|
||||
// = 0.58 V peak
|
||||
// = 0.41 V rms = 410 mV rms
|
||||
// so output calibration needs to reduce that to 17.5 mV rms (70%
|
||||
// of 25 mV rms)
|
||||
// txMicInCal = 0.043 ... seems pretty low...
|
||||
//
|
||||
// Likewise, default line-in (TX) parameters assume consumer line
|
||||
// level input, but with the default audio adaptor line-in level
|
||||
// setting (5 = 1.33V p-p).
|
||||
// line in level 5 = 1.33 V p-p
|
||||
// signal in = 0.894 V p-p
|
||||
// = 0.447 V peak
|
||||
// = 0.316 V rms = 316 mV rms
|
||||
// so input calibration needs to convert that to the 410 mV noted
|
||||
// above (equivalent for the microphone), so that when that signal
|
||||
// gets to the rig-out, it gets treated the same as the mic input.
|
||||
// txLineInCal = 1.30
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// microphone-in parameters (TX)
|
||||
uint8_t txMicInGain = 12;
|
||||
float txMicInVol = 1.0; //0.7;
|
||||
float txMicInCal = 1.0;
|
||||
// line-in parameters (TX)
|
||||
uint8_t txLineInLevel = 5;
|
||||
float txLineInVol = 1.0; //0.7;
|
||||
float txLineInCal = 1.30;
|
||||
// USB-in parameters (TX)
|
||||
float txUSBInVol = 1.0; //0.7;
|
||||
float txUSBInCal = 1.0;
|
||||
// two-tone parameters (TX)
|
||||
float txSine1Vol = 1.0;
|
||||
float txSine2Vol = 1.0;
|
||||
// rig-out parameters (TX) - default settings are based on hitting 70% of 25mVrms w/ mic
|
||||
uint8_t txRigOutLevel = 31;
|
||||
float txRigOutCal = 0.1; // 0.061;
|
||||
// USB-out parameters (TX)- debug/monitor use only
|
||||
bool txUSBOutEnable = true;
|
||||
float txUSBOutCal = 1.0;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// RigConfig
|
||||
// Used to store configuration parameters during runtime, as well as to
|
||||
// save them to EEPROM.
|
||||
//======================================================================
|
||||
|
||||
class RigConfig {
|
||||
public:
|
||||
RigConfig() {}
|
||||
// audio configuration
|
||||
AudioConfig audio;
|
||||
|
||||
// mode configuration
|
||||
ssb_config ssb;
|
||||
digi_config digi;
|
||||
cw_config cw;
|
||||
|
||||
// General rig configuration entries.
|
||||
rig_mode mode = rig_mode::ssb;
|
||||
};
|
||||
|
||||
extern RigConfig rigConfig;
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
9
ubitx_iop/config.ino
Normal file
9
ubitx_iop/config.ino
Normal file
@ -0,0 +1,9 @@
|
||||
//======================================================================
|
||||
// config.ino
|
||||
//======================================================================
|
||||
|
||||
#include "config.h"
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
14
ubitx_iop/eeprom.h
Normal file
14
ubitx_iop/eeprom.h
Normal file
@ -0,0 +1,14 @@
|
||||
//======================================================================
|
||||
// eeprom.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __iop_eeprom_h__
|
||||
#define __iop_eeprom_h__
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
9
ubitx_iop/eeprom.ino
Normal file
9
ubitx_iop/eeprom.ino
Normal file
@ -0,0 +1,9 @@
|
||||
//======================================================================
|
||||
// eeprom.ino
|
||||
//======================================================================
|
||||
|
||||
#include "eeprom.h"
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
81
ubitx_iop/keyer.h
Normal file
81
ubitx_iop/keyer.h
Normal file
@ -0,0 +1,81 @@
|
||||
//**********************************************************************
|
||||
//
|
||||
// Keyer, a part of nanoIO
|
||||
//
|
||||
// nanoIO paddle keyer (c) 2018, David Freese, W1HKJ
|
||||
//
|
||||
// based on code from Iambic Keyer Code Keyer Sketch
|
||||
// Copyright (c) 2009 Steven T. Elliott
|
||||
//
|
||||
// nanoIO 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// nanoIO 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 fldigi. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
//Revisions:
|
||||
//
|
||||
//1.0.0: Initial release
|
||||
//
|
||||
//**********************************************************************
|
||||
|
||||
|
||||
#ifndef __iop_keyer_h__
|
||||
#define __iop_keyer_h__
|
||||
|
||||
//#include "Arduino.h"
|
||||
|
||||
//#include "config.h"
|
||||
|
||||
#define IAMBICA 0
|
||||
#define IAMBICB 1
|
||||
#define STRAIGHT 2
|
||||
|
||||
class Keyer
|
||||
{
|
||||
private:
|
||||
//int cw_pin_;
|
||||
//int ptt_pin_;
|
||||
bool key_down;
|
||||
|
||||
long ktimer;
|
||||
|
||||
int _speed;
|
||||
int _dashlen; // Length of dash
|
||||
int _dotlen; // Length of dot
|
||||
int _space_len; // Length of space
|
||||
float _weight;
|
||||
|
||||
char keyerControl;
|
||||
char keyerState;
|
||||
int key_mode;
|
||||
|
||||
void calc_ratio();
|
||||
void update_PaddleLatch();
|
||||
|
||||
public:
|
||||
Keyer(int wpm, float _weight);
|
||||
//void cw_pin(int pin);
|
||||
//void ptt_pin(int pin);
|
||||
void wpm(int spd);
|
||||
void set_mode(int md);
|
||||
int get_mode() { return key_mode; }
|
||||
bool is_down() { return key_down; }
|
||||
// void set__weight();
|
||||
|
||||
bool do_paddles();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
214
ubitx_iop/keyer.ino
Normal file
214
ubitx_iop/keyer.ino
Normal file
@ -0,0 +1,214 @@
|
||||
//======================================================================
|
||||
//
|
||||
// nanoIO paddle keyer (c) 2018, David Freese, W1HKJ
|
||||
//
|
||||
// based on code from Iambic Keyer Code Keyer Sketch
|
||||
// Copyright (c) 2009 Steven T. Elliott
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details:
|
||||
//
|
||||
// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
||||
// Boston, MA 02111-1307 USA
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
//#include "Arduino.h"
|
||||
//#include "TimerOne.h"
|
||||
#include "config.h"
|
||||
#include "keyer.h"
|
||||
|
||||
const uint8_t LP_in = KEYER_LEFT_PADDLE_PIN;
|
||||
const uint8_t RP_in = KEYER_RIGHT_PADDLE_PIN;
|
||||
|
||||
//#define ST_Freq 600 // Set the Sidetone Frequency to 600 Hz
|
||||
|
||||
//======================================================================
|
||||
// keyerControl bit definitions
|
||||
//
|
||||
#define DIT_L 0x01 // Dit latch
|
||||
#define DAH_L 0x02 // Dah latch
|
||||
#define DIT_PROC 0x04 // Dit is being processed
|
||||
#define PDLSWAP 0x08 // 0 for normal, 1 for swap
|
||||
//======================================================================
|
||||
//
|
||||
// State Machine Defines
|
||||
|
||||
enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };
|
||||
|
||||
Keyer::Keyer(int wpm, float weight)
|
||||
{
|
||||
//ptt_pin_ = PTT_PIN;
|
||||
//cw_pin_ = CW_PIN;
|
||||
// Setup outputs
|
||||
pinMode(LP_in, INPUT_PULLUP); // sets Left Paddle digital pin as input
|
||||
pinMode(RP_in, INPUT_PULLUP); // sets Right Paddle digital pin as input
|
||||
|
||||
// pinMode(ST_Pin, OUTPUT); // Sets the Sidetone digital pin as output
|
||||
|
||||
// digitalWrite(LP_in, HIGH); // Enable pullup resistor on Left Paddle Input Pin
|
||||
// digitalWrite(RP_in, HIGH); // Enable pullup resistor on Right Paddle Input Pin
|
||||
|
||||
keyerState = IDLE;
|
||||
keyerControl = 0;
|
||||
key_mode = IAMBICA;
|
||||
key_down = false;
|
||||
_weight = weight;
|
||||
|
||||
_speed = wpm;
|
||||
calc_ratio();
|
||||
}
|
||||
|
||||
// Calculate the length of dot, dash and silence
|
||||
void Keyer::calc_ratio()
|
||||
{
|
||||
float w = (1 + _weight) / (_weight -1);
|
||||
_space_len = (1200 / _speed);
|
||||
_dotlen = _space_len * (w - 1);
|
||||
_dashlen = (1 + w) * _space_len;
|
||||
}
|
||||
|
||||
//void Keyer::cw_pin(int pin)
|
||||
//{
|
||||
// ptt_pin_ = pin;
|
||||
//}
|
||||
|
||||
//void Keyer::ptt_pin(int pin)
|
||||
//{
|
||||
// cw_pin_ = pin;
|
||||
//}
|
||||
|
||||
void Keyer::set_mode(int md)
|
||||
{
|
||||
key_mode = md;
|
||||
}
|
||||
|
||||
void Keyer::wpm(int wpm)
|
||||
{
|
||||
_speed = wpm;
|
||||
calc_ratio();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Latch paddle press
|
||||
//======================================================================
|
||||
|
||||
void Keyer::update_PaddleLatch()
|
||||
{
|
||||
if (digitalRead(LP_in) == LOW) {
|
||||
keyerControl |= DIT_L;
|
||||
}
|
||||
if (digitalRead(RP_in) == LOW) {
|
||||
keyerControl |= DAH_L;
|
||||
}
|
||||
}
|
||||
|
||||
bool Keyer::do_paddles()
|
||||
{
|
||||
if (key_mode == STRAIGHT) { // Straight Key
|
||||
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW)) {
|
||||
// Key from either paddle
|
||||
// digitalWrite(ptt_pin_, HIGH);
|
||||
// digitalWrite(cw_pin_, HIGH);
|
||||
// tone(ST_Pin, 600);
|
||||
key_down = true;
|
||||
return true;
|
||||
} else {
|
||||
// digitalWrite(ptt_pin_, LOW);
|
||||
// digitalWrite(cw_pin_, LOW);
|
||||
// noTone(ST_Pin);
|
||||
key_down = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// keyerControl contains processing flags and keyer mode bits
|
||||
// Supports Iambic A and B
|
||||
// State machine based, uses calls to millis() for timing.
|
||||
switch (keyerState) {
|
||||
case IDLE: // Wait for direct or latched paddle press
|
||||
if ((digitalRead(LP_in) == LOW) || (digitalRead(RP_in) == LOW) || (keyerControl & 0x03)) {
|
||||
update_PaddleLatch();
|
||||
keyerState = CHK_DIT;
|
||||
// letting this fall through // return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// break;
|
||||
case CHK_DIT: // See if the dit paddle was pressed
|
||||
if (keyerControl & DIT_L) {
|
||||
keyerControl |= DIT_PROC;
|
||||
ktimer = _dotlen;
|
||||
keyerState = KEYED_PREP;
|
||||
return true;
|
||||
} else { // 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;
|
||||
} else {
|
||||
keyerState = IDLE;
|
||||
return false;
|
||||
}
|
||||
// break;
|
||||
case KEYED_PREP: // Assert key down, start timing
|
||||
// state shared for dit or dah
|
||||
// digitalWrite(ptt_pin_, HIGH); // Enable PTT
|
||||
// tone(ST_Pin, ST_Freq); // Turn the Sidetone on
|
||||
// digitalWrite(cw_pin_, HIGH); // Key the CW line
|
||||
key_down = 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: // Wait for timer to expire
|
||||
if (millis() > ktimer) { // are we at end of key down ?
|
||||
// digitalWrite(ptt_pin_, LOW); // Disable PTT
|
||||
// noTone(ST_Pin); // Turn the Sidetone off
|
||||
// digitalWrite(cw_pin_, LOW); // Unkey the CW line
|
||||
key_down = false;
|
||||
ktimer = millis() + _space_len; // inter-element time
|
||||
keyerState = INTER_ELEMENT; // next state
|
||||
// letting this fall through // return true;
|
||||
} else if (key_mode == IAMBICB) { // Iambic B Mode ?
|
||||
update_PaddleLatch(); // yes, early paddle latch in Iambic B mode
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
// break;
|
||||
|
||||
case INTER_ELEMENT: // Insert time between dits/dahs
|
||||
update_PaddleLatch(); // 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 {
|
||||
return true;
|
||||
}
|
||||
// break;
|
||||
}
|
||||
|
||||
return false; // resolve compiler warning; do we ever get here?
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
299
ubitx_iop/menu.h
Normal file
299
ubitx_iop/menu.h
Normal file
@ -0,0 +1,299 @@
|
||||
//======================================================================
|
||||
// menu.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __menu_h__
|
||||
#define __menu_h__
|
||||
|
||||
//#include "etl_profile.h"
|
||||
//#include <ArduinoSTL.h>
|
||||
#include "etl/array.h"
|
||||
#include "etl/cstring.h"
|
||||
#include "etl/delegate.h"
|
||||
//#include <vector>
|
||||
#include <initializer_list>
|
||||
#include "rig.h"
|
||||
|
||||
// 16 characters on display
|
||||
const int MAX_TEXT_LEN = 16;
|
||||
const int max_text_len = 16;
|
||||
const char MENU_SELECTED_CHAR = '>';
|
||||
const char menu_selection_char = '>';
|
||||
|
||||
const char blankLine[17] = " ";
|
||||
|
||||
typedef etl::string<max_text_len> Menu_string;
|
||||
|
||||
//======================================================================
|
||||
// Menu_item
|
||||
//======================================================================
|
||||
|
||||
class Menu_item {
|
||||
public:
|
||||
Menu_item(): parent_link(nullptr) {}
|
||||
virtual ~Menu_item() {}
|
||||
|
||||
virtual const Menu_string& title() const = 0;
|
||||
virtual void get_title(Menu_string& outstr) const = 0;
|
||||
virtual void get_text(Menu_string& outstr) const = 0;
|
||||
|
||||
virtual Menu_item* select() = 0;
|
||||
virtual Menu_item* altSelect() = 0;
|
||||
virtual Menu_item* exit() = 0;
|
||||
virtual Menu_item* prev() = 0;
|
||||
virtual Menu_item* next() = 0;
|
||||
|
||||
void set_parent(Menu_item* item) {
|
||||
parent_link = item;
|
||||
}
|
||||
|
||||
const Menu_item* parent() const {
|
||||
return parent_link;
|
||||
}
|
||||
|
||||
private:
|
||||
Menu_item* parent_link;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// List_menu
|
||||
//======================================================================
|
||||
|
||||
const int MAX_LISTMENU_TITLE_LEN = 15;
|
||||
|
||||
//const int max_size_of_list_menu = 20; // arbitrary...
|
||||
|
||||
template <const size_t SIZE>
|
||||
class List_menu : public Menu_item {
|
||||
public:
|
||||
List_menu(const char* title, etl::array<const Menu_item*, SIZE> items):
|
||||
Menu_item(), list_title(title), list_items(items), index(0) {
|
||||
for (auto element : list_items) {
|
||||
element->set_parent(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual const Menu_string& title() const {
|
||||
return list_title;
|
||||
}
|
||||
|
||||
virtual void get_title(Menu_string& outstr) const {
|
||||
outstr.assign(list_title);
|
||||
}
|
||||
|
||||
virtual void get_text(Menu_string& outstr) const {
|
||||
list_items[index]->get_text(outstr);
|
||||
}
|
||||
|
||||
virtual Menu_item* select()
|
||||
{
|
||||
return list_items[index];
|
||||
}
|
||||
|
||||
virtual Menu_item* altSelect()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* exit()
|
||||
{
|
||||
return parent();
|
||||
}
|
||||
|
||||
virtual Menu_item* prev()
|
||||
{
|
||||
if (--index < 0) {
|
||||
index += list_items.size();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* next()
|
||||
{
|
||||
index = ++index % list_items.size();
|
||||
return this;
|
||||
}
|
||||
|
||||
private:
|
||||
Menu_string list_title;
|
||||
etl::array<const Menu_item*, SIZE> list_items;
|
||||
int index;
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Config_parm
|
||||
//======================================================================
|
||||
|
||||
typedef etl::delegate<void(void)> Update_func;
|
||||
|
||||
template<typename T>
|
||||
class Config_parm : public Menu_item {
|
||||
public:
|
||||
Config_parm(const char* title, T& parm, T min, T max, T step, Update_func f):
|
||||
Menu_item(), parm_title(title), parameter(parm), min_val(min), max_val(max), step_val(step), on_update(f) {}
|
||||
|
||||
virtual const Menu_string& title() const {
|
||||
return parm_title;
|
||||
}
|
||||
|
||||
virtual void get_title(Menu_string& outstr) const {
|
||||
outstr.assign(parm_title);
|
||||
}
|
||||
|
||||
virtual void get_text(Menu_string& outstr) const = 0;
|
||||
|
||||
virtual Menu_item* select() {
|
||||
on_update();
|
||||
return parent();
|
||||
}
|
||||
|
||||
virtual Menu_item* altSelect() {
|
||||
on_update();
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* exit() {
|
||||
return parent();
|
||||
}
|
||||
|
||||
virtual Menu_item* prev() {
|
||||
parameter -= step_val;
|
||||
if (parameter < min_val) {
|
||||
parameter = min_val;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* next() {
|
||||
parameter += step_val;
|
||||
if (parameter > max_val) {
|
||||
parameter = max_val;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const T& value() const {
|
||||
return parameter;
|
||||
}
|
||||
|
||||
private:
|
||||
Menu_string parm_title;
|
||||
T& parameter;
|
||||
T min_val;
|
||||
T max_val;
|
||||
T step_val;
|
||||
Update_func on_update;
|
||||
};
|
||||
|
||||
class Parm_uint8 : public Config_parm<uint8_t> {
|
||||
public:
|
||||
Parm_uint8(const char* title, uint8_t& parm, uint8_t min, uint8_t max, uint8_t step, Update_func f):
|
||||
Config_parm<uint8_t>(title, parm, min, max, step, f) {}
|
||||
|
||||
virtual void get_text(Menu_string& outstr) const {
|
||||
snprintf(outstr.data(), max_text_len+1, "%-*.*s %3d", max_text_len-4, max_text_len-4, title().data(), value());
|
||||
}
|
||||
};
|
||||
|
||||
class Parm_float : public Config_parm<float> {
|
||||
public:
|
||||
Parm_float(const char* title, float& parm, float min, float max, float step, Update_func f):
|
||||
Config_parm<float>(title, parm, min, max, step, f) {}
|
||||
|
||||
virtual void get_text(Menu_string& outstr) const {
|
||||
snprintf(outstr.data(), max_text_len+1, "%-*.*s %5.2f", max_text_len-6, max_text_len-6, title().data(), value());
|
||||
}
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
|
||||
const char modeID[] = {'s', 'S', 'c', 'C', 'd', 'D', 't', 'T'};
|
||||
const char* const filterID[static_cast<int>(rig_mode::count)][static_cast<int>(rx_filter::count)] = {
|
||||
{"2.8", "2.4", "1.8"}, // SSB
|
||||
{"1.0", "500", "250"}, // CW
|
||||
{"2.8", "2.4", "500"}, // DIG
|
||||
};
|
||||
|
||||
//======================================================================
|
||||
// Top_menu
|
||||
//======================================================================
|
||||
|
||||
|
||||
class Main_menu : public Menu_item {
|
||||
public:
|
||||
Main_menu(basic_rig& rig): menu_title("Main Menu"), rig_(rig), adjust_tx(false) {}
|
||||
|
||||
virtual const Menu_string& title() const {
|
||||
return menu_title;
|
||||
}
|
||||
|
||||
virtual void get_title(Menu_string& outstr) const {
|
||||
outstr.assign(menu_title);
|
||||
}
|
||||
|
||||
virtual void get_text(Menu_string& outstr) const {
|
||||
char text[max_text_len+1];
|
||||
sprintf(text, "%1cR:%3s %1cT:%3s ",
|
||||
adjust_tx ? ' ' : menu_selection_char,
|
||||
filterID[static_cast<size_t>(rig_.get_rig_mode())][static_cast<size_t>(rig_.get_rx_filter())],
|
||||
adjust_tx ? menu_selection_char : ' ',
|
||||
rig_.is_ssb_mode() && rig_.config().ssb.comp.enabled ? "CMP" : " ");
|
||||
outstr.assign(text);
|
||||
}
|
||||
|
||||
virtual Menu_item* select() {
|
||||
adjust_tx = !adjust_tx;
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* altSelect() {
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* exit() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual Menu_item* prev() {
|
||||
if (adjust_tx) {
|
||||
if (rig_.is_ssb_mode()) {
|
||||
rig_.config().ssb.comp.enabled = !rig_.config().ssb.comp.enabled;
|
||||
if (rig_.config().ssb.comp.enabled)
|
||||
rig_.enable_comp();
|
||||
else
|
||||
rig_.disable_comp();
|
||||
}
|
||||
} else {
|
||||
rig_.prev_rx_filter();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual Menu_item* next() {
|
||||
if (adjust_tx) {
|
||||
if (rig_.is_ssb_mode()) {
|
||||
rig_.config().ssb.comp.enabled = !rig_.config().ssb.comp.enabled;
|
||||
if (rig_.config().ssb.comp.enabled)
|
||||
rig_.enable_comp();
|
||||
else
|
||||
rig_.disable_comp();
|
||||
}
|
||||
} else {
|
||||
rig_.next_rx_filter();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private:
|
||||
Menu_string menu_title;
|
||||
basic_rig& rig_;
|
||||
bool adjust_tx;
|
||||
};
|
||||
|
||||
extern const Menu_item* audio_config_menu;
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
95
ubitx_iop/menu.ino
Normal file
95
ubitx_iop/menu.ino
Normal file
@ -0,0 +1,95 @@
|
||||
//======================================================================
|
||||
// menu.ino
|
||||
//======================================================================
|
||||
|
||||
#include <iopcomm.h>
|
||||
#include "config.h"
|
||||
#include "menu.h"
|
||||
|
||||
void do_nothing_func() {}
|
||||
Update_func do_nothing = Update_func::create<do_nothing_func>();
|
||||
|
||||
List_menu<7> audio_config_menu_("Audio Config", {
|
||||
new Parm_uint8 ("RX ADC Lvl", rigConfig.audio.rxRigInLevel, 0, 15, 1, do_nothing),
|
||||
new Parm_uint8 ("RX DAC Lvl", rigConfig.audio.rxLineOutLevel, 13, 31, 1, do_nothing),
|
||||
new Parm_float ("RX Inp Lvl", rigConfig.audio.rxRigInVol, 0.0, 1.0, 0.1, do_nothing),
|
||||
new Parm_float ("RX Inp Cal", rigConfig.audio.rxRigInCal, 0.0, 99.9, 0.1, do_nothing),
|
||||
new Parm_float ("RX Spkr Cal", rigConfig.audio.rxSpkrOutCal, 0.0, 99.9, 0.1, do_nothing),
|
||||
new Parm_float ("RX Line Cal", rigConfig.audio.rxLineOutCal, 0.0, 99.9, 0.1, do_nothing),
|
||||
new Parm_float ("RX USB Cal", rigConfig.audio.rxUSBOutCal, 0.0, 99.9, 0.1, do_nothing),
|
||||
});
|
||||
|
||||
const Menu_item* audio_config_menu = &audio_config_menu_;
|
||||
|
||||
/* commented out til I go back and fix SSBConfig class
|
||||
List_menu<3> ssb_config_menu("SSB Config", {
|
||||
Parm_float("Comp Thresh", rigConfig.lsb.comp_threshold, 0.0, 1.0, 0.1, do_nothing),
|
||||
Parm_float("Comp Ratio", rigConfig.lsb.comp_ratio, 0.0, 50.0, 1.0, do_nothing),
|
||||
Parm_float("Comp Gain", rigConfig.lsb.comp_gain, 0.0, 99.9, 0.1, do_nothing)
|
||||
});
|
||||
*/
|
||||
|
||||
/*
|
||||
ListMenu("Configuration",
|
||||
ListMenu("LSB Mode",
|
||||
ParameterFilter("Filter", c.lsb.filter),
|
||||
ParameterOnOff ("Compressor", c.lsb.comp_enable),
|
||||
ParameterFloat ("Comp Thresh", c.lsb.comp_threshold, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("Comp Ratio", c.lsb.comp_ratio, 0.0, 50.0, 1.0),
|
||||
ParameterFloat ("Comp Gain", c.lsb.comp_gain, 0.0, 99.9, 0.1)),
|
||||
ListMenu("USB Mode",
|
||||
ParameterFilter("Filter", c.usb.filter),
|
||||
ParameterOnOff ("Compressor", c.usb.comp_enable),
|
||||
ParameterFloat ("Comp Thresh", c.usb.comp_threshold, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("Comp Ratio", c.usb.comp_ratio, 0.0, 50.0, 1.0),
|
||||
ParameterFloat ("Comp Gain", c.usb.comp_gain, 0.0, 99.9, 0.1)),
|
||||
ListMenu("CWL Mode",
|
||||
ParameterFilter("Filter", c.cwl.filter),
|
||||
ParameterKeyer ("Keyer Mode", c.cwl.mode),
|
||||
ParameterOnOff ("Keyer Rev?", c.cwl.reversed),
|
||||
ParameterInt ("Keyer WPM", c.cwl.wpm, 0, 60, 1),
|
||||
ParameterFloat ("Keyer Weight", c.cwl.weight, 0.0, 9.9, 0.1),
|
||||
ParameterInt ("ST Freq", c.sidetone, 300, 3000, 10)),
|
||||
ListMenu("CWU Mode",
|
||||
ParameterFilter("Filter", c.cwl.filter),
|
||||
ParameterKeyer ("Keyer Mode", c.cwl.mode),
|
||||
ParameterOnOff ("Keyer Rev?", c.cwl.reversed),
|
||||
ParameterInt ("Keyer WPM", c.cwl.wpm, 0, 60, 1),
|
||||
ParameterFloat ("Keyer Weight", c.cwl.weight, 0.0, 9.9, 0.1),
|
||||
ParameterInt ("ST Freq", c.sidetone, 300, 3000, 10)),
|
||||
ListMenu("DGL Mode",
|
||||
ParameterFilter("Filter", c.dgl.filter)),
|
||||
ListMenu("DGU Mode",
|
||||
ParameterFilter("Filter", c.dgu.filter)),
|
||||
ListMenu("TTL Mode",
|
||||
ParameterFilter("Filter", c.dgl.filter)),
|
||||
ListMenu("TTU Mode",
|
||||
ParameterFilter("Filter", c.dgu.filter)),
|
||||
ListMenu("Audio",
|
||||
ParameterInt ("RX ADC Lvl", c.rxRigInLevel, 0, 15, 1),
|
||||
ParameterInt ("RX DAC Lvl", c.rxLineOutLevel, 13, 31, 1),
|
||||
ParameterFloat ("RX Inp Lvl", c.rxRigInVol, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("RX Inp Cal", c.rxRigInCal, 0.0, 99.9, 0.1),
|
||||
ParameterFloat ("RX Spkr Cal", c.rxSpkrOutCal, 0.0, 99.9, 0.1),
|
||||
ParameterFloat ("RX Line Cal", c.rxLineOutCal, 0.0, 99.9, 0.1),
|
||||
ParameterFloat ("RX USB Cal", c.rxUSBOutCal, 0.0, 99.9, 0.1),
|
||||
ParameterOnOff ("RX USB Dbg", c.rxUSBInEnable),
|
||||
ParameterInt ("TX Mic Gain", c.txMicInGain, 0, 63, 1),
|
||||
ParameterInt ("TX ADC Lvl", c.txLineInLevel, 0, 15, 1),
|
||||
ParameterInt ("TX DAC Lvl", c.txRigOutLevel, 13, 31, 1),
|
||||
ParameterFloat ("TX Out Cal", c.txRigOutCal, 0.0, 99.9, 0.1),
|
||||
ParameterFloat ("TX Mic Lvl", c.txMicInVol, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("TX Mic Cal", c.txMicInCal, 0.0, 99.9, 0.1),
|
||||
ParameterFloat ("TX Line Lvl", c.txLineInVol, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("TX Line Cal", c.txLineInCal, 0.0, 99.9, 0.1),
|
||||
ParameterFloat ("TX USB Lvl", c.txUSBInVol, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("TX USB Cal", c.txUSBInCal, 0.0, 99.9, 0.1),
|
||||
ParameterOnOff ("TX USB Dbg", c.txUSBOutEnable),
|
||||
ParameterFloat ("Tone 1 Lvl", c.txSine1Vol, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("Tone 2 Lvl", c.txSine2Vol, 0.0, 1.0, 0.1),
|
||||
ParameterFloat ("ST Lvl", c.sideToneVol, 0.0, 1.0, 0.1)));
|
||||
*/
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
240
ubitx_iop/rig.h
Normal file
240
ubitx_iop/rig.h
Normal file
@ -0,0 +1,240 @@
|
||||
//======================================================================
|
||||
// rig.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __rig_h__
|
||||
#define __rig_h__
|
||||
|
||||
#include <iopcomm.h>
|
||||
#include "audio.h"
|
||||
#include "RigMode.h"
|
||||
|
||||
//======================================================================
|
||||
// basic_rig
|
||||
//======================================================================
|
||||
|
||||
class basic_rig {
|
||||
public:
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Constructor/destructor.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
basic_rig(RigConfig& c, RigAudio& a) :
|
||||
config_(c), audio_(a),
|
||||
bpf_(),
|
||||
comp_(&config_.ssb.comp),
|
||||
ssb_ (config_.ssb, audio_, bpf_, comp_),
|
||||
cw_ (config_.cw, audio_, bpf_),
|
||||
digi_(config_.digi, audio_, bpf_) { set_rig_mode(config_.mode); }
|
||||
|
||||
void init() {}
|
||||
|
||||
RigConfig& config() { return config_; }
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Mode control.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
inline bool is_ssb_mode() const { return (config_.mode == rig_mode::ssb ); }
|
||||
inline bool is_digi_mode() const { return (config_.mode == rig_mode::digi); }
|
||||
inline bool is_cw_mode() const { return (config_.mode == rig_mode::cw ); }
|
||||
|
||||
// Switch to the specified mode. Returns a pointer to the new mode.
|
||||
basic_mode* set_rig_mode(rig_mode m) {
|
||||
// exit the previous mode
|
||||
current_mode_->exit(); // NOTE: This could currently occur during TX, which is NOT desirable.
|
||||
|
||||
// select the new mode
|
||||
config_.mode = m;
|
||||
|
||||
switch(config_.mode) {
|
||||
case rig_mode::ssb:
|
||||
current_mode_ = &ssb_;
|
||||
break;
|
||||
|
||||
case rig_mode::cw:
|
||||
current_mode_ = &cw_;
|
||||
break;
|
||||
|
||||
case rig_mode::digi:
|
||||
current_mode_ = &digi_;
|
||||
break;
|
||||
}
|
||||
|
||||
//enter the new mode
|
||||
current_mode_->enter();
|
||||
|
||||
// return a pointer to the new mode
|
||||
return current_mode_;
|
||||
}
|
||||
|
||||
// Returns the rig_mode (enum) of the current mode object.
|
||||
inline rig_mode get_rig_mode() const { return config_.mode; }
|
||||
|
||||
|
||||
// Returns a pointer to the current mode object.
|
||||
inline basic_mode* mode() const { return current_mode_; }
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Transmit/Receive (T/R) control.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
inline bool is_tx() const { return mode()->is_tx(); }
|
||||
inline bool is_rx() const { return mode()->is_rx(); }
|
||||
|
||||
// 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.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
inline void enable_comp() {
|
||||
if (is_ssb_mode()) ((ssb_mode*)mode())->enable_comp();
|
||||
}
|
||||
|
||||
inline void disable_comp() {
|
||||
if (is_ssb_mode()) ((ssb_mode*)mode())->disable_comp();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// RX filter control.
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Set the RX filter. This is delegated to the mode.
|
||||
inline void set_rx_filter(rx_filter f) { mode()->set_rx_filter(f); }
|
||||
|
||||
// Returns the rx_filter (enum) of the RX filter currently being used.
|
||||
inline rx_filter get_rx_filter() const { return mode()->config().filter; }
|
||||
|
||||
void next_rx_filter() {
|
||||
switch(mode()->config().filter) {
|
||||
case rx_filter::wide:
|
||||
set_rx_filter(rx_filter::medium);
|
||||
break;
|
||||
|
||||
case rx_filter::medium:
|
||||
set_rx_filter(rx_filter::narrow);
|
||||
break;
|
||||
|
||||
case rx_filter::narrow:
|
||||
set_rx_filter(rx_filter::wide);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void prev_rx_filter() {
|
||||
switch(mode()->config().filter) {
|
||||
case rx_filter::wide:
|
||||
set_rx_filter(rx_filter::narrow);
|
||||
break;
|
||||
|
||||
case rx_filter::medium:
|
||||
set_rx_filter(rx_filter::wide);
|
||||
break;
|
||||
|
||||
case rx_filter::narrow:
|
||||
set_rx_filter(rx_filter::medium);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// 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()
|
||||
{
|
||||
comp_.update(); // It checks if it's enabled on its own.
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
RigConfig& config_;
|
||||
RigAudio& audio_;
|
||||
|
||||
bp_filter bpf_;
|
||||
speech_comp comp_;
|
||||
|
||||
ssb_mode ssb_;
|
||||
cw_mode cw_;
|
||||
digi_mode digi_;
|
||||
|
||||
basic_mode* current_mode_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
75
ubitx_iop/ubitx_iop.h
Normal file
75
ubitx_iop/ubitx_iop.h
Normal file
@ -0,0 +1,75 @@
|
||||
//======================================================================
|
||||
// ubitx_iop.h
|
||||
//======================================================================
|
||||
|
||||
#ifndef __ubitx_iop_h__
|
||||
#define __ubitx_iop_h__
|
||||
|
||||
#include "config.h"
|
||||
#include "audio.h"
|
||||
#include "cat.h"
|
||||
#include "eeprom.h"
|
||||
#include "keyer.h"
|
||||
|
||||
#define PTT_KEY_OUT_PIN 2
|
||||
#define ENCODER_A_PIN 5
|
||||
#define ENCODER_B_PIN 4
|
||||
#define ENCODER_SW_PIN 3
|
||||
|
||||
// comment this out to disable debugging code
|
||||
#define DEBUG
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define USBDEBUG(x) USBSERIAL.print("IOP: "); USBSERIAL.println(x);
|
||||
#else
|
||||
#define USBDEBUG(x)
|
||||
#endif
|
||||
|
||||
//enum RigMode {
|
||||
// MODE_SSB = 0,
|
||||
// MODE_DIGI = 1,
|
||||
// MODE_CW = 2,
|
||||
//};
|
||||
|
||||
enum TxState {
|
||||
TX_OFF = 0,
|
||||
TX_MIC,
|
||||
TX_LINE,
|
||||
TX_CAT,
|
||||
TX_KEYER,
|
||||
};
|
||||
|
||||
//extern RigMode rigMode;
|
||||
|
||||
extern bool keyerKeyDown;
|
||||
|
||||
//======================================================================
|
||||
// Keying functions.
|
||||
//
|
||||
// These are simple functions to assert the key line from the IOP to the
|
||||
// Raduino.
|
||||
//======================================================================
|
||||
|
||||
inline void initKeyLine()
|
||||
{
|
||||
pinMode(PTT_KEY_OUT_PIN, OUTPUT);
|
||||
digitalWrite(PTT_KEY_OUT_PIN, HIGH);
|
||||
}
|
||||
|
||||
inline void setKeyDown()
|
||||
{
|
||||
digitalWrite(PTT_KEY_OUT_PIN, LOW);
|
||||
}
|
||||
|
||||
inline void setKeyUp()
|
||||
{
|
||||
digitalWrite(PTT_KEY_OUT_PIN, HIGH);
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
235
ubitx_iop/ubitx_iop.ino
Normal file
235
ubitx_iop/ubitx_iop.ino
Normal file
@ -0,0 +1,235 @@
|
||||
//======================================================================
|
||||
// ubitx_iop.ino
|
||||
//======================================================================
|
||||
|
||||
#include <Encoder.h>
|
||||
#include <iopcomm.h>
|
||||
#include "audio.h"
|
||||
#include "config.h"
|
||||
#include "ubitx_iop.h"
|
||||
#include "keyer.h"
|
||||
#include "menu.h"
|
||||
#include "rig.h"
|
||||
#include "TxSwitch.h"
|
||||
|
||||
Keyer keyer{15, 3.0}; // NOTE: make configurable
|
||||
|
||||
RigConfig rigConfig;
|
||||
RigAudio rigAudio{rigConfig.audio};
|
||||
basic_rig rig{rigConfig, rigAudio};
|
||||
|
||||
CATSwitch catPTT;
|
||||
//MicSwitch micPTTHelper;
|
||||
GPIOSwitch micPTT(true, MIC_PTT_PIN);
|
||||
//LineSwitch linePTTHelper;
|
||||
GPIOSwitch linePTT(false, LINE_PTT_PIN);
|
||||
|
||||
Encoder knob(ENCODER_A_PIN, ENCODER_B_PIN);
|
||||
long knobPos = 0;
|
||||
|
||||
Bounce btn;
|
||||
elapsedMillis btnMillis;
|
||||
|
||||
Main_menu main_menu(rig);
|
||||
Menu_item* menu_item = &main_menu;
|
||||
|
||||
elapsedMillis frameMillis;
|
||||
unsigned frameTime;
|
||||
unsigned frameCounter;
|
||||
unsigned audioProcUsage;
|
||||
unsigned audioMemUsage;
|
||||
|
||||
//======================================================================
|
||||
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
initCAT(38400, SERIAL_8N1);
|
||||
USBDEBUG("setup started");
|
||||
|
||||
AudioMemory(16); // NOTE: Need to fine tune this. Have had errors due to this being too low.
|
||||
|
||||
initKeyLine();
|
||||
rigAudio.init();
|
||||
|
||||
frameCounter = 0;
|
||||
frameMillis = 0;
|
||||
|
||||
knob.write(0);
|
||||
|
||||
btn.attach(ENCODER_SW_PIN, INPUT_PULLUP);
|
||||
btn.interval(25);
|
||||
|
||||
rig.init();
|
||||
|
||||
USBDEBUG("setup completed");
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void update_io_menu(Menu_item* item, bool is_active)
|
||||
{
|
||||
Menu_string text;
|
||||
|
||||
if (!is_active || (is_active && item == nullptr)) {
|
||||
sendIOPMenuInactive();
|
||||
} else {
|
||||
item->get_text(text);
|
||||
sendIOPMenuDisplay(text.data());
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
void loop()
|
||||
{
|
||||
static bool menu_is_active = false;
|
||||
static bool menu_updated = false;
|
||||
|
||||
static char frame_status[100];
|
||||
static char old_mode_text[17] = " ";
|
||||
static bool paddle_loop = false;
|
||||
// long oldPos = knobPos;
|
||||
|
||||
rig_mode oldRigMode;
|
||||
|
||||
frameCounter++;
|
||||
|
||||
if (rig.is_cw_mode()) {
|
||||
if (keyer.do_paddles()) {
|
||||
|
||||
// Checking for T/R separately from the paddle loop, because it's
|
||||
// possible we're already transmitting (PTT/Key being held), and
|
||||
// we don't want to run the tx() actions if we're already in TX.
|
||||
if (rig.is_rx()) {
|
||||
USBDEBUG("entered TX via paddles");
|
||||
rig.tx();
|
||||
}
|
||||
|
||||
paddle_loop = true;
|
||||
|
||||
if (keyer.is_down()) {
|
||||
setKeyDown();
|
||||
} else {
|
||||
setKeyUp();
|
||||
}
|
||||
|
||||
return; // return early for paddle responsiveness
|
||||
} else {
|
||||
if (paddle_loop) {
|
||||
// If we exit the paddle loop (i.e. keyer completes its keying
|
||||
// sequence), then we'll go back to receive, even if one of the
|
||||
// PTT/Key lines is still held separately. General principle is
|
||||
// that if "something" stops transmitting, then the rig will
|
||||
// stop transmitting.
|
||||
paddle_loop = false;
|
||||
rig.rx();
|
||||
USBDEBUG("exited TX from paddles");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rig.update();
|
||||
|
||||
oldRigMode = rig.get_rig_mode();
|
||||
|
||||
// Update the mic PTT. We need to tell it if we're in SSB mode, so that
|
||||
// it knows if it should switch to the mic input if pressed. We also
|
||||
// need to make it inactive if we're in DGT mode, since only CAT will be
|
||||
// used to start transmitting in that case.
|
||||
micPTT.setSSBMode(rig.is_ssb_mode());
|
||||
micPTT.update(rig.mode(), !rig.is_digi_mode());
|
||||
|
||||
// Update the line PTT. We need to tell it if we're in SSB mode, so that
|
||||
// it knows if it should switch to the line input if pressed. We also
|
||||
// need to make it inactive if we're in DGT mode, since only CAT will be
|
||||
// used to start transmitting in that case.
|
||||
linePTT.setSSBMode(rig.is_ssb_mode());
|
||||
linePTT.update(rig.mode(), !rig.is_digi_mode());
|
||||
|
||||
serviceCAT();
|
||||
|
||||
menu_updated = false;
|
||||
btn.update();
|
||||
knobPos = knob.read();
|
||||
|
||||
if (btn.fell()) {
|
||||
btnMillis = 0;
|
||||
USBDEBUG("button pressed");
|
||||
// cancel out any knob rotation that occurred during in conjunction with press/release
|
||||
knob.write(0);
|
||||
|
||||
} else if (btn.rose()) {
|
||||
menu_updated = true;
|
||||
if (btnMillis > 1000) {
|
||||
// long press - exit
|
||||
menu_item = menu_item->exit();
|
||||
USBDEBUG("button released - long (exit)");
|
||||
} else if (btnMillis > 500) {
|
||||
// medium press - altSelect
|
||||
if (menu_is_active) {
|
||||
menu_item = menu_item->altSelect();
|
||||
} else {
|
||||
menu_item = audio_config_menu;
|
||||
menu_is_active = true;
|
||||
}
|
||||
USBDEBUG("button released - medium (altSelect)");
|
||||
} else {
|
||||
// short press - select
|
||||
if (menu_is_active) {
|
||||
menu_item = menu_item->select();
|
||||
} else {
|
||||
menu_item = &main_menu;
|
||||
menu_is_active = true;
|
||||
}
|
||||
USBDEBUG("button released - short (select)");
|
||||
}
|
||||
// cancel out any knob rotation that occurred during in conjunction with press/release
|
||||
knob.write(0);
|
||||
|
||||
} else if (knobPos > 3 && menu_is_active) {
|
||||
// left
|
||||
menu_item = menu_item->prev();
|
||||
knob.write(0);
|
||||
menu_updated = true;
|
||||
USBDEBUG("knob turned left");
|
||||
|
||||
} else if (knobPos < -3 && menu_is_active) {
|
||||
// right
|
||||
menu_item = menu_item->next();
|
||||
knob.write(0);
|
||||
menu_updated = true;
|
||||
USBDEBUG("knob turned right");
|
||||
}
|
||||
|
||||
if (menu_item == nullptr) {
|
||||
menu_item = &main_menu;
|
||||
menu_is_active = false;
|
||||
menu_updated = true;
|
||||
USBDEBUG("null menu - reset to main menu");
|
||||
}
|
||||
|
||||
if (menu_updated) {
|
||||
update_io_menu(menu_item, menu_is_active);
|
||||
USBDEBUG("updated main menu");
|
||||
}
|
||||
|
||||
rig.update();
|
||||
|
||||
if (frameMillis > 5000) {
|
||||
#if defined(DEBUG)
|
||||
frameTime = frameMillis;
|
||||
audioProcUsage = AudioProcessorUsageMax();
|
||||
audioMemUsage = AudioMemoryUsageMax();
|
||||
sprintf(frame_status, "update: %u ms, %u frames, %d %% CPU max, %d %% mem max\n", frameTime, frameCounter, audioProcUsage, audioMemUsage);
|
||||
USBDEBUG(frame_status);
|
||||
#endif
|
||||
frameMillis = 0;
|
||||
frameCounter = 0;
|
||||
}
|
||||
|
||||
//audioUpdate(); // was used to update the speech compressor
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// EOF
|
||||
//======================================================================
|
Loading…
x
Reference in New Issue
Block a user