Compare commits

...
15 Commits
Author SHA1 Message Date
Jonathan Naylor ee07f76bd5 Split the repeater and link mode audio paths. 2021-09-30 18:16:51 +01:00
Jonathan Naylor d063a1da1e Change the EOT handling on TX. 2021-09-19 14:25:12 +01:00
Jonathan Naylor 6a4dad4c44 Change the LinkMode state machine. 2021-09-06 22:50:40 +01:00
Jonathan Naylor ed6bd66fab Rename SimpleMode to LinkMode. 2021-09-06 21:53:01 +01:00
Jonathan Naylor 1a07a6f0d1 Add more details to the simple FM state machine. 2021-09-05 19:46:01 +01:00
Jonathan Naylor 70bf47f93e Add a bery simple FM mode for internet gatewaying. 2021-09-05 19:06:56 +01:00
Jonathan Naylor a5eaafc7e0 Change a method name. 2021-08-26 17:39:52 +01:00
Jonathan Naylor 2200945857 Change the EOF sync pattern for simplicity. 2021-08-26 16:50:18 +01:00
Jonathan Naylor 63a8e61a45 Update to send and receive the new EOF marker. 2021-08-25 21:11:09 +01:00
Jonathan Naylor 7275f65e44 Reset the transmit levels to be correct. 2021-08-23 21:07:17 +01:00
Jonathan Naylor 2aabdb61ab Remove the last traces of M17 packet mode and fix a buffer overrun bug. 2021-08-08 18:55:09 +01:00
Jonathan Naylor 96c0e5b141 Fix compiler warnings when building for POG style boards. 2021-07-19 21:20:57 +01:00
Jonathan Naylor 9c883cfafd Revert "Restore SerialRB for POG builds."
This reverts commit 8a1ab88c3a.
2021-07-01 22:58:59 +01:00
Jonathan Naylor 8a1ab88c3a Restore SerialRB for POG builds. 2021-07-01 22:28:59 +01:00
Jonathan Naylor f4e1c0b401 Fix a potential issue with reporting the wrong command type in ACK and NAK messages. 2021-06-17 16:55:59 +01:00
11 changed files with 346 additions and 149 deletions
+11 -5
View File
@@ -58,7 +58,7 @@
// Frequencies such as 10.0 Mhz (48000 * 208.333) or 20 Mhz (48000 * 416.666) are not suitable.
//
// For 12 MHz
// #define EXTERNAL_OSC 12000000
#define EXTERNAL_OSC 12000000
// For 12.288 MHz
// #define EXTERNAL_OSC 12288000
// For 14.4 MHz
@@ -66,10 +66,10 @@
// For 19.2 MHz
// #define EXTERNAL_OSC 19200000
// Use a higher baudrate for host communication. Required for FM network !
// #define SERIAL_SPEED 115200 //suitable for most older boards (Arduino, Due STM32F1_POG etc). External FM will NOT work with this !
// #define SERIAL_SPEED 230400 // Only works on newer board M4, M7, Teensy. External FM might work with this
#define SERIAL_SPEED 460800 // Only works on newer board M4, M7, Teensy. External FM should work with this
// Select a baud rate for host communication. The faster speeds are needed for external FM to work.
// #define SERIAL_SPEED 115200 // Suitable for most older boards (Arduino Due, STM32F1_POG, etc). External FM will NOT work with this!
// #define SERIAL_SPEED 230400 // Only works on newer boards like fast M4, M7, Teensy 3.x. External FM might work with this
#define SERIAL_SPEED 460800 // Only works on newer boards like fast M4, M7, Teensy 3.x. External FM should work with this
// Allow the use of the COS line to lockout the modem
// #define USE_COS_AS_LOCKOUT
@@ -129,5 +129,11 @@
// Use the D-Star and YSF LEDs for FM
#define USE_ALTERNATE_FM_LEDS
#if defined(STM32F1_POG)
// Slower boards need to run their serial at 115200 baud
#undef SERIAL_SPEED
#define SERIAL_SPEED 115200
#endif
#endif
+183 -13
View File
@@ -28,13 +28,6 @@ const uint16_t FM_SERIAL_BLOCK_SIZE = 80U;//this is the number of sample pairs t
//three times this value shall never exceed 252
const uint16_t FM_SERIAL_BLOCK_SIZE_BYTES = FM_SERIAL_BLOCK_SIZE * 3U;
/*
* Access Mode values are:
* 0 - Carrier access with COS
* 1 - CTCSS only access without COS
* 2 - CTCSS only access with COS
* 3 - CTCSS only access with COS to start, then carrier access with COS
*/
CFM::CFM() :
m_callsign(),
@@ -63,6 +56,7 @@ m_filterStage2(32768, 0,-32768, 32768, -50339, 19052),
m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460),
m_blanking(),
m_accessMode(1U),
m_linkMode(false),
m_cosInvert(false),
m_noiseSquelch(false),
m_rfAudioBoost(1U),
@@ -72,7 +66,9 @@ m_extEnabled(false),
m_rxLevel(1),
m_inputRFRB(2401U), // 100ms of audio + 1 sample
m_outputRFRB(2400U), // 100ms of audio
m_inputExtRB()
m_inputExtRB(),
m_rfSignal(false),
m_extSignal(false)
{
m_statusTimer.setTimeout(1U, 0U);
m_reverseTimer.setTimeout(0U, 150U);
@@ -81,6 +77,14 @@ m_inputExtRB()
}
void CFM::samples(bool cos, q15_t* samples, uint8_t length)
{
if (m_linkMode)
linkSamples(cos, samples, length);
else
repeaterSamples(cos, samples, length);
}
void CFM::repeaterSamples(bool cos, q15_t* samples, uint8_t length)
{
if (m_cosInvert)
cos = !cos;
@@ -219,6 +223,108 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length)
}
}
void CFM::linkSamples(bool cos, q15_t* samples, uint8_t length)
{
if (m_cosInvert)
cos = !cos;
clock(length);
uint8_t i = 0U;
for (; i < length; i++) {
// ARMv7-M has hardware integer division
q15_t currentRFSample = q15_t((q31_t(samples[i]) << 8) / m_rxLevel);
if (m_noiseSquelch)
cos = m_squelch.process(currentRFSample);
q15_t currentExtSample;
bool inputExt = m_inputExtRB.getSample(currentExtSample);//always consume the external input data so it does not overflow
inputExt = inputExt && m_extEnabled;
switch (m_accessMode) {
case 0U:
if (!inputExt && !cos && m_modemState != STATE_FM)
continue;
else
stateMachine(cos, inputExt);
break;
case 1U: {
bool ctcss = m_ctcssRX.process(currentRFSample);
// Delay the audio by 100ms to better match the CTCSS detector output
m_inputRFRB.put(currentRFSample);
m_inputRFRB.get(currentRFSample);
if (!inputExt && !ctcss && m_modemState != STATE_FM) {
// No CTCSS detected, just carry on
continue;
} else if ((inputExt || ctcss) && m_modemState != STATE_FM) {
// We had CTCSS or external input
stateMachine(ctcss, inputExt);
if (m_state == FS_LISTENING)
continue;
} else {
stateMachine(ctcss, inputExt);
}
}
break;
case 2U: {
bool ctcss = m_ctcssRX.process(currentRFSample);
if (!inputExt && !ctcss && m_modemState != STATE_FM) {
// No CTCSS detected, just carry on
continue;
} else if ((inputExt || (ctcss && cos)) && m_modemState != STATE_FM) {
// We had CTCSS or external input
stateMachine(ctcss && cos, inputExt);
if (m_state == FS_LISTENING)
continue;
} else {
stateMachine(ctcss && cos, inputExt);
}
}
break;
default: {
bool ctcss = m_ctcssRX.process(currentRFSample);
if (!inputExt && !ctcss && m_modemState != STATE_FM) {
// No CTCSS detected, just carry on
continue;
} else if ((inputExt || (ctcss && cos)) && m_modemState != STATE_FM) {
// We had CTCSS or external input
stateMachine(ctcss && cos, inputExt);
if (m_state == FS_LISTENING)
continue;
} else {
stateMachine(cos, inputExt);
}
}
break;
}
if (m_modemState != STATE_FM)
continue;
if (m_rfSignal && m_extEnabled) {
q15_t currentSample = m_blanking.process(currentRFSample);
m_downSampler.addSample(currentSample);
}
if (!m_extSignal)
continue;
q15_t currentSample = currentExtSample * m_extAudioBoost;
currentSample = m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)));
currentSample += m_ctcssTX.getAudio(m_reverseTimer.isRunning());
m_outputRFRB.put(currentSample);
}
}
void CFM::process()
{
uint16_t space = io.getSpace();
@@ -287,6 +393,8 @@ void CFM::reset()
m_squelch.reset();
m_needReverse = false;
m_rfSignal = false;
m_extSignal = false;
}
uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch)
@@ -317,9 +425,10 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_
return m_rfAck.setParams(rfAck, speed, frequency, level, level);
}
uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel)
uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool linkMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel)
{
m_accessMode = accessMode;
m_linkMode = linkMode;
m_cosInvert = cosInvert;
m_noiseSquelch = noiseSquelch;
@@ -356,10 +465,14 @@ uint8_t CFM::setExt(const char* ack, uint8_t audioBoost, uint8_t speed, uint16_t
void CFM::stateMachine(bool validRFSignal, bool validExtSignal)
{
if (m_duplex)
duplexStateMachine(validRFSignal, validExtSignal);
else
simplexStateMachine(validRFSignal, validExtSignal);
if (m_linkMode) {
linkStateMachine(validRFSignal, validExtSignal);
} else {
if (m_duplex)
duplexStateMachine(validRFSignal, validExtSignal);
else
simplexStateMachine(validRFSignal, validExtSignal);
}
}
void CFM::simplexStateMachine(bool validRFSignal, bool validExtSignal)
@@ -1009,6 +1122,63 @@ void CFM::timeoutExtWaitStateSimplex(bool validSignal)
}
}
void CFM::linkStateMachine(bool validRFSignal, bool validExtSignal)
{
if (validRFSignal && !m_rfSignal) {
io.setDecode(true);
io.setADCDetection(true);
if (!m_extSignal) {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
m_rfSignal = true;
}
if (validExtSignal && !m_extSignal) {
if (!m_rfSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
insertSilence(50U);
m_extSignal = true;
}
if (!validRFSignal && m_rfSignal) {
io.setDecode(false);
io.setADCDetection(false);
if (!m_extSignal) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_statusTimer.stop();
}
m_rfSignal = false;
if (m_extEnabled)
serial.writeFMEOT();
}
if (!validExtSignal && m_extSignal) {
if (!m_rfSignal) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_statusTimer.stop();
}
m_needReverse = true;
m_extSignal = false;
}
}
void CFM::sendCallsign()
{
if (m_holdoffTimer.isRunning()) {
+10 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -63,7 +63,7 @@ public:
uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch);
uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level);
uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel);
uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool linkMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel);
uint8_t setExt(const char* ack, uint8_t audioBoost, uint8_t speed, uint16_t frequency, uint8_t level);
uint8_t getSpace() const;
@@ -97,6 +97,7 @@ private:
CFMDirectFormI m_filterStage3;
CFMBlanking m_blanking;
uint8_t m_accessMode;
bool m_linkMode;
bool m_cosInvert;
bool m_noiseSquelch;
q15_t m_rfAudioBoost;
@@ -107,9 +108,14 @@ private:
CRingBuffer<q15_t> m_inputRFRB;
CRingBuffer<q15_t> m_outputRFRB;
CFMUpSampler m_inputExtRB;
bool m_rfSignal;
bool m_extSignal;
void stateMachine(bool validRFSignal, bool validExtSignal);
void repeaterSamples(bool cos, q15_t* samples, uint8_t length);
void linkSamples(bool cos, q15_t* samples, uint8_t length);
void duplexStateMachine(bool validRFSignal, bool validExtSignal);
void listeningStateDuplex(bool validRFSignal, bool validExtSignal);
void kerchunkRFStateDuplex(bool validSignal);
@@ -135,6 +141,8 @@ private:
void timeoutExtStateSimplex(bool validSignal);
void timeoutExtWaitStateSimplex(bool validSignal);
void linkStateMachine(bool validRFSignal, bool validExtSignal);
void clock(uint8_t length);
void sendCallsign();
+2
View File
@@ -746,6 +746,7 @@ void CIO::setMode(MMDVM_STATE state)
case STATE_M17: setM17Int(false); break;
case STATE_POCSAG: setPOCSAGInt(false); break;
case STATE_FM: setFMInt(false); break;
default: break;
}
switch (state) {
@@ -757,6 +758,7 @@ void CIO::setMode(MMDVM_STATE state)
case STATE_M17: setM17Int(true); break;
case STATE_POCSAG: setPOCSAGInt(true); break;
case STATE_FM: setFMInt(true); break;
default: break;
}
#endif
+9 -10
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,18 +27,17 @@ const unsigned int M17_FRAME_LENGTH_SYMBOLS = M17_FRAME_LENGTH_BITS / 2U;
const unsigned int M17_FRAME_LENGTH_SAMPLES = M17_FRAME_LENGTH_SYMBOLS * M17_RADIO_SYMBOL_LENGTH;
const unsigned int M17_SYNC_LENGTH_BITS = 16U;
const unsigned int M17_SYNC_LENGTH_BYTES = M17_SYNC_LENGTH_BITS / 8U;
const unsigned int M17_SYNC_LENGTH_SYMBOLS = M17_SYNC_LENGTH_BITS / 2U;
const unsigned int M17_SYNC_LENGTH_SAMPLES = M17_SYNC_LENGTH_SYMBOLS * M17_RADIO_SYMBOL_LENGTH;
const uint8_t M17_LINK_SETUP_SYNC_BYTES[] = {0x55U, 0xF7U};
const uint8_t M17_STREAM_SYNC_BYTES[] = {0xFFU, 0x5DU};
const uint8_t M17_PACKET_SYNC_BYTES[] = {0x75U, 0xFFU};
const uint8_t M17_SYNC_BYTES_LENGTH = 2U;
const uint8_t M17_EOF_SYNC_BYTES[] = {0x55U, 0x5DU};
const uint16_t M17_LINK_SETUP_SYNC_BITS = 0x55F7U;
const uint16_t M17_STREAM_SYNC_BITS = 0xFF5DU;
const uint16_t M17_PACKET_SYNC_BITS = 0x75FFU;
const uint16_t M17_EOF_SYNC_BITS = 0x555DU;
// 5 5 F 7
// 01 01 01 01 11 11 01 11
@@ -56,13 +55,13 @@ const int8_t M17_STREAM_SYNC_SYMBOLS_VALUES[] = {-3, -3, -3, -3, +3, +3, -3, +3}
const uint8_t M17_STREAM_SYNC_SYMBOLS = 0x0DU;
// 7 5 F F
// 01 11 01 01 11 11 11 11
// +3 -3 +3 +3 -3 -3 -3 -3
// 5 5 5 D
// 01 01 01 01 01 01 11 01
// +3 +3 +3 +3 +3 +3 -3 +3
const int8_t M17_PACKET_SYNC_SYMBOLS_VALUES[] = {+3, -3, +3, +3, -3, -3, -3, -3};
const int8_t M17_EOF_SYNC_SYMBOLS_VALUES[] = {+3, +3, +3, +3, +3, +3, -3, +3};
const uint8_t M17_PACKET_SYNC_SYMBOLS = 0xB0U;
const uint8_t M17_EOF_SYNC_SYMBOLS = 0xFDU;
#endif
+38 -51
View File
@@ -105,7 +105,6 @@ void CM17RX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
switch (m_state) {
case M17RXS_LINK_SETUP:
case M17RXS_STREAM:
case M17RXS_PACKET:
processData(sample);
break;
default:
@@ -127,9 +126,8 @@ void CM17RX::processNone(q15_t sample)
{
bool ret1 = correlateSync(M17_LINK_SETUP_SYNC_SYMBOLS, M17_LINK_SETUP_SYNC_SYMBOLS_VALUES, M17_LINK_SETUP_SYNC_BYTES, MAX_SYNC_SYMBOL_START_ERRS, MAX_SYNC_BIT_START_ERRS);
bool ret2 = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_START_ERRS, MAX_SYNC_BIT_START_ERRS);
// bool ret3 = correlateSync(M17_PACKET_SYNC_SYMBOLS, M17_PACKET_SYNC_SYMBOLS_VALUES, M17_PACKET_SYNC_BYTES, MAX_SYNC_SYMBOL_START_ERRS, MAX_SYNC_BIT_START_ERRS);
if (ret1 || ret2 /* || ret3 */ ) {
if (ret1 || ret2) {
// On the first sync, start the countdown to the state change
if (m_countdown == 0U) {
m_rssiAccum = 0U;
@@ -144,7 +142,6 @@ void CM17RX::processNone(q15_t sample)
if (ret1) m_nextState = M17RXS_LINK_SETUP;
if (ret2) m_nextState = M17RXS_STREAM;
// if (ret3) m_nextState = M17RXS_PACKET;
}
}
@@ -168,28 +165,42 @@ void CM17RX::processNone(q15_t sample)
void CM17RX::processData(q15_t sample)
{
bool eof = false;
if (m_minSyncPtr < m_maxSyncPtr) {
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr) {
bool ret1 = correlateSync(M17_LINK_SETUP_SYNC_SYMBOLS, M17_LINK_SETUP_SYNC_SYMBOLS_VALUES, M17_LINK_SETUP_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
bool ret2 = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
// bool ret3 = correlateSync(M17_PACKET_SYNC_SYMBOLS, M17_PACKET_SYNC_SYMBOLS_VALUES, M17_PACKET_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
bool ret = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
if (ret1) m_state = M17RXS_LINK_SETUP;
if (ret2) m_state = M17RXS_STREAM;
// if (ret3) m_state = M17RXS_PACKET;
eof = correlateSync(M17_EOF_SYNC_SYMBOLS, M17_EOF_SYNC_SYMBOLS_VALUES, M17_EOF_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
if (ret) m_state = M17RXS_STREAM;
}
} else {
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr) {
bool ret1 = correlateSync(M17_LINK_SETUP_SYNC_SYMBOLS, M17_LINK_SETUP_SYNC_SYMBOLS_VALUES, M17_LINK_SETUP_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
bool ret2 = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
// bool ret3 = correlateSync(M17_PACKET_SYNC_SYMBOLS, M17_PACKET_SYNC_SYMBOLS_VALUES, M17_PACKET_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
bool ret = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
if (ret1) m_state = M17RXS_LINK_SETUP;
if (ret2) m_state = M17RXS_STREAM;
// if (ret3) m_state = M17RXS_PACKET;
eof = correlateSync(M17_EOF_SYNC_SYMBOLS, M17_EOF_SYNC_SYMBOLS_VALUES, M17_EOF_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
if (ret) m_state = M17RXS_STREAM;
}
}
if (eof) {
DEBUG4("M17RX: eof sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
io.setDecode(false);
io.setADCDetection(false);
serial.writeM17EOT();
m_state = M17RXS_NONE;
m_endPtr = NOENDPTR;
m_averagePtr = NOAVEPTR;
m_countdown = 0U;
m_nextState = M17RXS_NONE;
m_maxCorr = 0;
}
if (m_dataPtr == m_endPtr) {
// Only update the centre and threshold if they are from a good sync
if (m_lostCount == MAX_SYNC_FRAMES) {
@@ -211,9 +222,8 @@ void CM17RX::processData(q15_t sample)
case M17RXS_STREAM:
DEBUG4("M17RX: stream sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
break;
case M17RXS_PACKET:
DEBUG4("M17RX: packet sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
break;
default:
break;
}
uint8_t frame[M17_FRAME_LENGTH_BYTES + 3U];
@@ -236,8 +246,7 @@ void CM17RX::processData(q15_t sample)
m_nextState = M17RXS_NONE;
m_maxCorr = 0;
} else {
frame[0U] = 0x00U;
frame[0U] |= m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
switch (m_state) {
case M17RXS_LINK_SETUP:
@@ -246,9 +255,8 @@ void CM17RX::processData(q15_t sample)
case M17RXS_STREAM:
writeRSSIStream(frame);
break;
case M17RXS_PACKET:
writeRSSIPacket(frame);
break;
default:
break;
}
m_maxCorr = 0;
@@ -308,11 +316,11 @@ bool CM17RX::correlateSync(uint8_t syncSymbols, const int8_t* syncSymbolValues,
if (startPtr >= M17_FRAME_LENGTH_SAMPLES)
startPtr -= M17_FRAME_LENGTH_SAMPLES;
uint8_t sync[M17_SYNC_BYTES_LENGTH];
uint8_t sync[M17_SYNC_LENGTH_BYTES];
samplesToBits(startPtr, M17_SYNC_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal);
uint8_t errs = 0U;
for (uint8_t i = 0U; i < M17_SYNC_BYTES_LENGTH; i++)
for (uint8_t i = 0U; i < M17_SYNC_LENGTH_BYTES; i++)
errs += countBits8(sync[i] ^ syncBytes[i]);
if (errs <= maxBitErrs) {
@@ -437,8 +445,8 @@ void CM17RX::writeRSSILinkSetup(uint8_t* data)
if (m_rssiCount > 0U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
data[121U] = (rssi >> 8) & 0xFFU;
data[122U] = (rssi >> 0) & 0xFFU;
data[49U] = (rssi >> 8) & 0xFFU;
data[50U] = (rssi >> 0) & 0xFFU;
serial.writeM17LinkSetup(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
@@ -458,8 +466,8 @@ void CM17RX::writeRSSIStream(uint8_t* data)
if (m_rssiCount > 0U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
data[121U] = (rssi >> 8) & 0xFFU;
data[122U] = (rssi >> 0) & 0xFFU;
data[49U] = (rssi >> 8) & 0xFFU;
data[50U] = (rssi >> 0) & 0xFFU;
serial.writeM17Stream(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
@@ -473,26 +481,5 @@ void CM17RX::writeRSSIStream(uint8_t* data)
m_rssiCount = 0U;
}
void CM17RX::writeRSSIPacket(uint8_t* data)
{
#if defined(SEND_RSSI_DATA)
if (m_rssiCount > 0U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
data[121U] = (rssi >> 8) & 0xFFU;
data[122U] = (rssi >> 0) & 0xFFU;
serial.writeM17Packet(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
serial.writeM17Packet(data, M17_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeM17Packet(data, M17_FRAME_LENGTH_BYTES + 1U);
#endif
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
#endif
+1 -3
View File
@@ -28,8 +28,7 @@
enum M17RX_STATE {
M17RXS_NONE,
M17RXS_LINK_SETUP,
M17RXS_STREAM,
M17RXS_PACKET
M17RXS_STREAM
};
class CM17RX {
@@ -70,7 +69,6 @@ private:
void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
void writeRSSILinkSetup(uint8_t* data);
void writeRSSIStream(uint8_t* data);
void writeRSSIPacket(uint8_t* data);
};
#endif
+5 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2018,2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
@@ -33,10 +33,10 @@ static q15_t RRC_0_5_FILTER[] = {0, 0, 0, 0, -290, -174, 142, 432, 438, 90, -387
438, 432, 142, -174, -290}; // numTaps = 45, L = 5
const uint16_t RRC_0_5_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
const q15_t M17_LEVELA = 1683;
const q15_t M17_LEVELB = 561;
const q15_t M17_LEVELC = -561;
const q15_t M17_LEVELD = -1683;
const q15_t M17_LEVELA = 1481;
const q15_t M17_LEVELB = 494;
const q15_t M17_LEVELC = -494;
const q15_t M17_LEVELD = -1481;
const uint8_t M17_START_SYNC = 0x77U;
const uint8_t M17_END_SYNC = 0xFFU;
+83 -56
View File
@@ -67,6 +67,7 @@ const uint8_t MMDVM_M17_LINK_SETUP = 0x45U;
const uint8_t MMDVM_M17_STREAM = 0x46U;
const uint8_t MMDVM_M17_PACKET = 0x47U;
const uint8_t MMDVM_M17_LOST = 0x48U;
const uint8_t MMDVM_M17_EOT = 0x49U;
const uint8_t MMDVM_POCSAG_DATA = 0x50U;
@@ -146,26 +147,26 @@ m_i2CData()
{
}
void CSerialPort::sendACK()
void CSerialPort::sendACK(uint8_t type)
{
uint8_t reply[4U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 4U;
reply[2U] = MMDVM_ACK;
reply[3U] = m_buffer[2U];
reply[3U] = type;
writeInt(1U, reply, 4);
}
void CSerialPort::sendNAK(uint8_t err)
void CSerialPort::sendNAK(uint8_t type, uint8_t err)
{
uint8_t reply[5U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 5U;
reply[2U] = MMDVM_NAK;
reply[3U] = m_buffer[2U];
reply[3U] = type;
reply[4U] = err;
writeInt(1U, reply, 5);
@@ -374,22 +375,41 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
bool txInvert = (data[0U] & 0x02U) == 0x02U;
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
#if defined(MODE_YSF)
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
#endif
bool useCOSAsLockout = (data[0U] & 0x20U) == 0x20U;
bool simplex = (data[0U] & 0x80U) == 0x80U;
m_debug = (data[0U] & 0x10U) == 0x10U;
#if defined(MODE_DSTAR)
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
#endif
#if defined(MODE_DMR)
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
#endif
#if defined(MODE_YSF)
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
#endif
#if defined(MODE_P25)
bool p25Enable = (data[1U] & 0x08U) == 0x08U;
#endif
#if defined(MODE_NXDN)
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
#endif
#if defined(MODE_FM)
bool fmEnable = (data[1U] & 0x20U) == 0x20U;
#endif
#if defined(MODE_M17)
bool m17Enable = (data[1U] & 0x40U) == 0x40U;
#endif
#if defined(MODE_POCSAG)
bool pocsagEnable = (data[2U] & 0x01U) == 0x01U;
#endif
#if defined(MODE_AX25)
bool ax25Enable = (data[2U] & 0x02U) == 0x02U;
#endif
uint8_t txDelay = data[3U];
if (txDelay > 50U)
@@ -480,17 +500,28 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
uint8_t fmTXLevel = data[16U];
uint8_t ax25TXLevel = data[17U];
#if defined(MODE_YSF)
uint8_t ysfTXHang = data[20U];
#endif
#if defined(MODE_P25)
uint8_t p25TXHang = data[21U];
#endif
#if defined(MODE_NXDN)
uint8_t nxdnTXHang = data[22U];
#endif
#if defined(MODE_M17)
uint8_t m17TXHang = data[23U];
#endif
#if defined(MODE_DMR)
uint8_t colorCode = data[26U];
if (colorCode > 15U)
return 4U;
uint8_t dmrDelay = data[27U];
#endif
#if defined(MODE_AX25)
int8_t ax25RXTwist = int8_t(data[28U]) - 128;
if (ax25RXTwist < -4 || ax25RXTwist > 10)
return 4U;
@@ -498,6 +529,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
uint8_t ax25TXDelay = data[29U];
uint8_t ax25SlotTime = data[30U];
uint8_t ax25PPersist = data[31U];
#endif
setMode(modemState);
@@ -620,6 +652,7 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint16_t length)
uint8_t hangTime = data[7U];
uint8_t accessMode = data[8U] & 0x0FU;
bool linkMode = (data[8U] & 0x20U) == 0x20U;
bool noiseSquelch = (data[8U] & 0x40U) == 0x40U;
bool cosInvert = (data[8U] & 0x80U) == 0x80U;
@@ -630,7 +663,7 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint16_t length)
uint8_t squelchHighThreshold = data[12U];
uint8_t squelchLowThreshold = data[13U];
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, cosInvert, noiseSquelch, squelchHighThreshold, squelchLowThreshold, rfAudioBoost, maxDev, rxLevel);
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, linkMode, cosInvert, noiseSquelch, squelchHighThreshold, squelchLowThreshold, rfAudioBoost, maxDev, rxLevel);
}
uint8_t CSerialPort::setFMParams4(const uint8_t* data, uint16_t length)
@@ -939,7 +972,7 @@ void CSerialPort::process()
}
writeSerialData(buffer, serialAvail - m_lastSerialAvail);
m_lastSerialAvailCount = 0U;
} else if (serialAvail > 0U && serialAvail == m_lastSerialAvail) {
} else if (serialAvail > 0 && serialAvail == m_lastSerialAvail) {
m_lastSerialAvailCount++;
} else {
m_lastSerialAvail = serialAvail;
@@ -980,61 +1013,61 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
case MMDVM_SET_CONFIG:
err = setConfig(buffer, length);
if (err == 0U)
sendACK();
sendACK(type);
else
sendNAK(err);
sendNAK(type, err);
break;
case MMDVM_SET_MODE:
err = setMode(buffer, length);
if (err == 0U)
sendACK();
sendACK(type);
else
sendNAK(err);
sendNAK(type, err);
break;
case MMDVM_SET_FREQ:
sendACK();
sendACK(type);
break;
#if defined(MODE_FM)
case MMDVM_FM_PARAMS1:
err = setFMParams1(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 1", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_FM_PARAMS2:
err = setFMParams2(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 2", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_FM_PARAMS3:
err = setFMParams3(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 3", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_FM_PARAMS4:
err = setFMParams4(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 4", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#else
@@ -1042,7 +1075,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
case MMDVM_FM_PARAMS2:
case MMDVM_FM_PARAMS3:
case MMDVM_FM_PARAMS4:
sendACK();
sendACK(type);
break;
#endif
@@ -1072,10 +1105,10 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = calPOCSAG.write(buffer, length);
#endif
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid calibration data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1085,7 +1118,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = cwIdTX.write(buffer, length);
if (err != 0U) {
DEBUG2("Invalid CW Id data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1100,7 +1133,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star header", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1114,7 +1147,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1128,7 +1161,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star EOT", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1146,7 +1179,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DMR);
} else {
DEBUG2("Received invalid DMR data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1164,7 +1197,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DMR);
} else {
DEBUG2("Received invalid DMR data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1185,7 +1218,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
}
if (err != 0U) {
DEBUG2("Received invalid DMR start", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1194,7 +1227,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = dmrTX.writeShortLC(buffer, length);
if (err != 0U) {
DEBUG2("Received invalid DMR Short LC", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1203,7 +1236,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = dmrTX.writeAbort(buffer, length);
if (err != 0U) {
DEBUG2("Received invalid DMR Abort", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1219,7 +1252,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_YSF);
} else {
DEBUG2("Received invalid System Fusion data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1235,7 +1268,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_P25);
} else {
DEBUG2("Received invalid P25 header", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1249,7 +1282,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_P25);
} else {
DEBUG2("Received invalid P25 LDU", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1265,7 +1298,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_NXDN);
} else {
DEBUG2("Received invalid NXDN data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1281,7 +1314,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 link setup data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1295,11 +1328,11 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 stream data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_M17_PACKET:
case MMDVM_M17_EOT:
if (m_m17Enable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_M17)
err = m17TX.writeData(buffer, length);
@@ -1308,8 +1341,8 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
if (m_modemState == STATE_IDLE)
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 packet data", err);
sendNAK(err);
DEBUG2("Received invalid M17 EOT", err);
sendNAK(type, err);
}
break;
#endif
@@ -1325,7 +1358,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_POCSAG);
} else {
DEBUG2("Received invalid POCSAG data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1341,7 +1374,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_FM);
} else {
DEBUG2("Received invalid FM data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1354,7 +1387,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
}
if (err != 0U) {
DEBUG2("Received invalid AX.25 data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1382,7 +1415,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
default:
// Handle this, send a NAK back
sendNAK(1U);
sendNAK(type, 1U);
break;
}
@@ -1709,7 +1742,7 @@ void CSerialPort::writeM17Stream(const uint8_t* data, uint8_t length)
writeInt(1U, reply, count);
}
void CSerialPort::writeM17Packet(const uint8_t* data, uint8_t length)
void CSerialPort::writeM17EOT()
{
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
@@ -1717,19 +1750,13 @@ void CSerialPort::writeM17Packet(const uint8_t* data, uint8_t length)
if (!m_m17Enable)
return;
uint8_t reply[130U];
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_M17_PACKET;
reply[1U] = 3U;
reply[2U] = MMDVM_M17_EOT;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
writeInt(1U, reply, count);
writeInt(1U, reply, 3);
}
void CSerialPort::writeM17Lost()
+3 -3
View File
@@ -67,8 +67,8 @@ public:
#if defined(MODE_M17)
void writeM17LinkSetup(const uint8_t* data, uint8_t length);
void writeM17Stream(const uint8_t* data, uint8_t length);
void writeM17Packet(const uint8_t* data, uint8_t length);
void writeM17Lost();
void writeM17EOT();
#endif
#if defined(MODE_AX25)
@@ -109,8 +109,8 @@ private:
uint16_t m_lastSerialAvailCount;
CRingBuffer<uint8_t> m_i2CData;
void sendACK();
void sendNAK(uint8_t err);
void sendACK(uint8_t type);
void sendNAK(uint8_t type, uint8_t err);
void getStatus();
void getVersion();
uint8_t setConfig(const uint8_t* data, uint16_t length);
+1 -1
View File
@@ -19,7 +19,7 @@
#if !defined(VERSION_H)
#define VERSION_H
#define VERSION "20210508"
#define VERSION "20210930"
#endif