448 lines
13 KiB
C++
448 lines
13 KiB
C++
/*!
|
|
* @file RigState.cpp
|
|
*
|
|
* @mainpage uBITX V5X Software - RigState
|
|
*
|
|
* @section introsec Introduction
|
|
*
|
|
* TBD
|
|
*
|
|
* @section dependencies Dependencies
|
|
*
|
|
* TBD
|
|
*
|
|
* @section author Author
|
|
*
|
|
* Written by Rob "Scrape" French, KC4UPR
|
|
*
|
|
* @section license License
|
|
*
|
|
* TBD
|
|
*/
|
|
|
|
#include "Debug.h"
|
|
#include "RigState.h"
|
|
|
|
/***********************************************************************
|
|
* COMMON FUNCTIONS
|
|
*
|
|
* The following are all common to RigState objects, whether on the
|
|
* Raduino or on the TeensyDSP.
|
|
**********************************************************************/
|
|
|
|
static uint32_t zeroes[1] = {0}; // used to transmit zeroes
|
|
|
|
/*!
|
|
* @brief Begin using the RigState object. In order to force an
|
|
* initial update (i.e. sending current state to the remote
|
|
* device), all fields are initially marked dirty.
|
|
*/
|
|
void UBitxRigState::begin() {
|
|
setDirty();
|
|
}
|
|
|
|
/***********************************************************************
|
|
* RADUINO FUNCTIONS
|
|
*
|
|
* The following are specific to the Raduino implementation. Note that
|
|
* this depends on the use of the TEENSYDUINO #define, which may result
|
|
* in a fragile implementation for other development environments (e.g.
|
|
* if the normal Arduino IDE is not being used).
|
|
**********************************************************************/
|
|
|
|
#ifndef TEENSYDUINO
|
|
|
|
#include <Wire.h>
|
|
#include "ubitx.h"
|
|
#include "ubitx_eemap.h"
|
|
|
|
extern unsigned long frequency, ritRxFrequency, ritTxFrequency, sideTone;
|
|
extern unsigned long vfoA;
|
|
extern unsigned long vfoB;
|
|
extern char cwMode;
|
|
extern char isUSB;
|
|
extern char vfoActive;
|
|
extern char ritOn;
|
|
extern char splitOn;
|
|
extern char inTx;
|
|
void setFrequency(unsigned long);
|
|
|
|
/*!
|
|
* @brief Send the RigState from the Raduino to the TeensyDSP. The
|
|
* basic process is: (1) read in any updated (dirty) data
|
|
* from the Raduino's state variables; (2) transmit the dirty
|
|
* data to the TeensyDSP; (2a) for clean data, zeroes are
|
|
* transmitted; (3) mark all data as clean.
|
|
*/
|
|
void UBitxRigState::send_RIGINF() {
|
|
readDirty();
|
|
Wire.beginTransmission(I2CMETER_ADDR);
|
|
Wire.write(I2CMETER_RIGINF);
|
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS; i++) {
|
|
if (i == DIRTY_WORD || isDirty(i)) {
|
|
// always send the current dirty bits
|
|
// or, bytes for updated (dirty) fields
|
|
Wire.write((byte*)&data[i], sizeof(uint32_t));
|
|
} else {
|
|
// otherwise, send out zeroes
|
|
Wire.write((byte*)&zeroes, sizeof(uint32_t));
|
|
//----------------------------------------------------------------
|
|
// NOTE: I am sending these zeroed out fields under a possibly
|
|
// mistaken assumption that in doing so, I will be sending a
|
|
// constant voltage on the SDA line most of the time, i.e. no
|
|
// bit changes, and so this will help reduce noise generated by
|
|
// I2C traffic (since most of the time there will be no updates.)
|
|
//----------------------------------------------------------------
|
|
}
|
|
}
|
|
Wire.endTransmission();
|
|
IFDEBUG( serialHexState("Sent") );
|
|
//IFDEBUG( serialPrettyState("Sent") );
|
|
setClean();
|
|
}
|
|
|
|
// delay(1); // 1ms - some delay required between ending transmission and requesting?
|
|
|
|
/*!
|
|
* @brief Receive the RigState from the TeensyDSP. This generally
|
|
* reflects changes due to CAT transmission to the TeensyDSP.
|
|
* @param numBytes
|
|
* Number of bytes received from the TeensyDSP.
|
|
*/
|
|
void UBitxRigState::receive_RIGINF(int numBytes) {
|
|
// Retrieve all of the deltas. Mark any received fields as dirty. It
|
|
// is assumed that send_RIGINF() was called immedaitely before this,
|
|
// so the fields are already clean.
|
|
byte* ptr = (byte*)&data;
|
|
Wire.requestFrom(I2CMETER_ADDR, sizeof(data));
|
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS && Wire.available(); i++) {
|
|
for (size_t j = 0; j < sizeof(uint32_t) && Wire.available(); j++) {
|
|
byte incomingByte = Wire.read();
|
|
if (i == DIRTY_WORD || isDirty(i)) {
|
|
// always overwrite the dirty bits
|
|
// and, update bytes for fields marked dirty
|
|
*ptr = incomingByte;
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
writeDirty();
|
|
IFDEBUG( serialHexState("Rcvd") );
|
|
//IFDEBUG( serialPrettyState("Rcvd") );
|
|
setClean(); // They get marked dirty as req'd during readDirty().
|
|
}
|
|
|
|
/*!
|
|
* @brief Write dirty fields from the RigState out to the Raduino
|
|
* variables.
|
|
*/
|
|
void UBitxRigState::writeDirty() {
|
|
// VFO A frequency
|
|
if (isDirty(VFOA_WORD)) {
|
|
if (vfoActive == VFO_A) {
|
|
setFrequency(getFreqA());
|
|
} else {
|
|
vfoA = getFreqA();
|
|
}
|
|
}
|
|
|
|
// VFO B frequency
|
|
if (isDirty(VFOB_WORD)) {
|
|
if (vfoActive == VFO_B) {
|
|
setFrequency(getFreqB());
|
|
} else {
|
|
vfoB = getFreqB();
|
|
}
|
|
}
|
|
|
|
// RIT and XIT frequencies
|
|
if (isDirty(OFFSETS_WORD)) {
|
|
// RIT
|
|
ritRxFrequency = getRIT() + ritTxFrequency;
|
|
if (ritOn == 1) {
|
|
if (inTx == 0) {
|
|
setFrequency(ritRxFrequency);
|
|
} else {
|
|
setFrequency(ritTxFrequency);
|
|
}
|
|
}
|
|
// XIT - TODO
|
|
}
|
|
|
|
// Various flags
|
|
if (isDirty(FLAGS_WORD)) {
|
|
|
|
// VFO A/B selection
|
|
char prev = vfoActive;
|
|
vfoActive = isVFOA() ? VFO_A : VFO_B;
|
|
if (vfoActive != prev) {
|
|
if (vfoActive == VFO_A) {
|
|
if (vfoA != frequency) {
|
|
setFrequency(vfoA);
|
|
}
|
|
} else if (vfoActive == VFO_B) {
|
|
if (vfoB != frequency) {
|
|
setFrequency(vfoB);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Split on/off
|
|
splitOn = isSplit() ? 1 : 0;
|
|
|
|
// RIT on/off
|
|
prev = ritOn;
|
|
ritOn = isRIT() ? 1 : 0;
|
|
if (ritOn != prev) {
|
|
if ((ritOn == 1) && (inTx == 0)) {
|
|
setFrequency(ritRxFrequency);
|
|
}
|
|
}
|
|
|
|
// XIT on/off
|
|
// TODO
|
|
|
|
// Mode
|
|
prev = (cwMode << 1) | isUSB;
|
|
isUSB = isModeUSB() ? 1 : 0;
|
|
if (isModeCW()) {
|
|
cwMode = 2; // 2 = cwu
|
|
} else if (isModeCWR()) {
|
|
cwMode = 1; // 1 = cwl
|
|
} else {
|
|
cwMode = 0; // 0 = no cw
|
|
}
|
|
if ((cwMode << 1) | isUSB != prev) {
|
|
setFrequency(frequency);
|
|
}
|
|
}
|
|
|
|
// Keyer information
|
|
if (isDirty(KEYER_WORD)) {
|
|
|
|
// Sidetone frequency
|
|
sideTone = static_cast<unsigned long>(getSidetone());
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* @brief Read current Raduino variables into the RigState
|
|
* (if they are changed) and set the appropriate dirty flags.
|
|
* @param r
|
|
* RigState reference to put the values into.
|
|
*/
|
|
void UBitxRigState::readDirty() {
|
|
unsigned long freq;
|
|
short offset;
|
|
|
|
// VFO A frequency
|
|
freq = (vfoActive == VFO_A) ? frequency : vfoA;
|
|
if (getFreqA() != freq) {
|
|
setFreqA(freq);
|
|
}
|
|
|
|
// VFO B frequency
|
|
freq = (vfoActive == VFO_B) ? frequency : vfoB;
|
|
if (getFreqB() != freq) {
|
|
setFreqB(freq);
|
|
}
|
|
|
|
// RIT frequency
|
|
if (inTx) {
|
|
offset = ritRxFrequency - ritTxFrequency;
|
|
} else {
|
|
offset = frequency - ritTxFrequency;
|
|
}
|
|
if (getRIT() != offset) {
|
|
setRIT(offset);
|
|
}
|
|
|
|
// XIT frequency
|
|
offset = 0; // xitRxFrequency - frequency;
|
|
if (getXIT() != offset) {
|
|
setXIT(offset);
|
|
}
|
|
|
|
// VFO A/B selection
|
|
if (isVFOA() && vfoActive == VFO_B) {
|
|
setVFOB();
|
|
} else if (isVFOB() && vfoActive == VFO_A) {
|
|
setVFOA();
|
|
}
|
|
|
|
// Split selection
|
|
if (isSplit() && splitOn == 0) {
|
|
setSplitOff();
|
|
} else if (!isSplit() && splitOn != 0) {
|
|
setSplitOn();
|
|
}
|
|
|
|
// RIT selection
|
|
if (isRIT() && ritOn == 0) {
|
|
setRITOff();
|
|
} else if (!isRIT() && ritOn != 0) {
|
|
setRITOn();
|
|
}
|
|
|
|
// XIT selection
|
|
//setXITOff();
|
|
// TODO
|
|
|
|
// Mode
|
|
char prev = (isModeCW() ? 4 : 0) | (isModeCWR() ? 2 : 0) | (isModeUSB() ? 1 : 0);
|
|
char curr = (cwMode << 1) | isUSB;
|
|
if (curr != prev) {
|
|
if (cwMode == 2) {
|
|
setModeCW();
|
|
} else if (cwMode == 1) {
|
|
setModeCWR();
|
|
} else {
|
|
if (isUSB) {
|
|
setModeUSB();
|
|
} else {
|
|
setModeLSB();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sidetone
|
|
if (getSidetone() != static_cast<uint16_t>(sideTone)) {
|
|
setSidetone(static_cast<uint16_t>(sideTone));
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* TEENSYDSP FUNCTIONS
|
|
*
|
|
* The following are specific to the TeensyDSP implementation. Note
|
|
* that this depends on the use of the TEENSYDUINO #define, which may
|
|
* result in a fragile implementation for other development environments
|
|
* (e.g. if the normal Arduino IDE is not being used).
|
|
**********************************************************************/
|
|
|
|
#else
|
|
|
|
#include <i2c_t3.h>
|
|
|
|
/*!
|
|
* @brief Receive RIGINF data from the Raduino. This method should
|
|
* be called on the TeensyDSP 'radState' (Raduino state)
|
|
* instance, when a RIGINF signal is received via I2C. It
|
|
* receives the incoming data from the Raduino and updates the
|
|
* state.
|
|
*/
|
|
void UBitxRigState::receive_RIGINF(int numBytes) {
|
|
byte* ptr = (byte*)&data;
|
|
setClean(); // we'll get new dirty bits via the I2C message
|
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS && Wire1.available(); i++) {
|
|
for (size_t j = 0; j < sizeof(uint32_t) && Wire1.available(); j++) {
|
|
byte incomingByte = Wire1.read();
|
|
if (i == DIRTY_WORD || isDirty(i)) {
|
|
// always overwrite the dirty bits
|
|
// and, update bytes for fields marked dirty
|
|
*ptr = incomingByte;
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Do anything that needs to happen when something is updated by the
|
|
// Raduino... this would be due to e.g. changes through the menu.
|
|
// Current (as of 2/19/2021) things that DON'T need to have any
|
|
// updates: frequency (those just get requested by CAT as needed).
|
|
// Current things that do need to get updated: sidetone (used to
|
|
// update CW filter values).
|
|
//--------------------------------------------------------------------
|
|
processDirty();
|
|
|
|
IFDEBUG( serialHexState("Rcvd") );
|
|
IFDEBUG( serialPrettyState("Rcvd") );
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/*!
|
|
* @brief Handle a RIGINF signal from the Raduino. This method should
|
|
* be called on the TeensyDSP 'catState' (CAT state)
|
|
* instance, when a RIGINF signal is received via I2C. It
|
|
* sends a response to the Raduino via I2C, using the Wire1
|
|
* interface.
|
|
*/
|
|
void UBitxRigState::send_RIGINF() {
|
|
for (RigStateWord i = DIRTY_WORD; i < NUM_WORDS; i++) {
|
|
if (i == DIRTY_WORD || isDirty(i)) {
|
|
// always send the current dirty bits
|
|
// or, bytes for updated (dirty) fields
|
|
Wire1.write((byte*)&data[i], sizeof(uint32_t));
|
|
} else {
|
|
// otherwise, send out zeroes
|
|
Wire1.write((byte*)&zeroes, sizeof(uint32_t));
|
|
//----------------------------------------------------------------
|
|
// NOTE: I am sending these zeroed out fields under a possibly
|
|
// mistaken assumption that in doing so, I will be sending a
|
|
// constant voltage on the SDA line most of the time, i.e. no
|
|
// bit changes, and so this will help reduce noise generated by
|
|
// I2C traffic (since most of the time there will be no updates.)
|
|
//----------------------------------------------------------------
|
|
}
|
|
}
|
|
IFDEBUG( serialHexState("Sent") );
|
|
IFDEBUG( serialPrettyState("Sent") );
|
|
setClean(); // now that we've sent them, they're clean
|
|
//--------------------------------------------------------------------
|
|
// TODO: Need to look at possibly merging the two states together at
|
|
// this point. The purpose would be to minimize the turnaround time
|
|
// for getting the most recent data to a CAT response.
|
|
//--------------------------------------------------------------------
|
|
}
|
|
|
|
/*!
|
|
* @brief Perform required actions based on any dirty bits set.
|
|
*/
|
|
void UBitxRigState::processDirty() {
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
char debugString[81] = {'\0'};
|
|
|
|
void UBitxRigState::serialHexState(const char* label = "RigState") {
|
|
Serial.print(label);
|
|
sprintf(debugString, ": %#010lx, %#010lx, %#010lx, %#010lx, %#010lx",
|
|
data[DIRTY_WORD], data[VFOA_WORD], data[VFOB_WORD], data[OFFSETS_WORD], data[FLAGS_WORD]);
|
|
Serial.println(debugString);
|
|
}
|
|
|
|
void UBitxRigState::serialPrettyState(const char* label = "RigState") {
|
|
Serial.println(label);
|
|
sprintf(debugString, "VFO A : %011ld %1c / VFO B : %011ld %1c",
|
|
getFreqA(), isDirty(VFOA_WORD) ? 'D' : ' ', getFreqB(), isDirty(VFOB_WORD) ? 'D' : ' ');
|
|
Serial.println(debugString);
|
|
sprintf(debugString, "RIT : %011ld %1c / XIT : %011ld %1c",
|
|
getRIT(), isDirty(OFFSETS_WORD) ? 'D' : ' ', getXIT(), isDirty(OFFSETS_WORD) ? 'D' : ' ');
|
|
Serial.println(debugString);
|
|
sprintf(debugString, "Split? %1c / VFO? %1c / RIT? %1c / XIT? %1c / Mode? %3s",
|
|
isSplit() ? 'Y' : 'N', isVFOA() ? 'A' : 'B', isRIT() ? 'Y' : 'N', isXIT() ? 'Y' : 'N',
|
|
isModeUSB() ? "USB" : (isModeLSB() ? "LSB" : (isModeCW() ? "CW " : (isModeCWR() ? "CWR" : " "))));
|
|
Serial.println(debugString);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/**********************************************************************/
|
|
|
|
#ifndef TEENSYDUINO
|
|
|
|
UBitxRigState _rigState;
|
|
UBitxRigState& rigState = _rigState;
|
|
|
|
#endif
|
|
|
|
/***********************************************************************
|
|
* EOF
|
|
**********************************************************************/
|