mirror of
https://github.com/g4klx/MMDVM.git
synced 2026-10-15 16:24:11 +02:00
Compare commits
99
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07e18ffcfb | ||
|
|
a4af04fae2 | ||
|
|
0092d6d9c3 | ||
|
|
78d79d4124 | ||
|
|
76d2058075 | ||
|
|
dcd8142c51 | ||
|
|
88c530d9e6 | ||
|
|
61d5dd6eb3 | ||
|
|
9eb6bc1a60 | ||
|
|
2098146af8 | ||
|
|
ef32988827 | ||
|
|
ce05dec2e6 | ||
|
|
895bed1f70 | ||
|
|
dbea23f171 | ||
|
|
ead9747f95 | ||
|
|
5fe665a6d3 | ||
|
|
3515b16222 | ||
|
|
a5c162a75e | ||
|
|
b5a56487b1 | ||
|
|
d151c100db | ||
|
|
56ced0d939 | ||
|
|
b6cb23d876 | ||
|
|
20a0a875f3 | ||
|
|
d6c1bea80a | ||
|
|
6d4ec5d8a1 | ||
|
|
474dce0141 | ||
|
|
5adbe6d0d5 | ||
|
|
28adbef81b | ||
|
|
c86d53d9d4 | ||
|
|
eb33bdde5a | ||
|
|
0a89469ffc | ||
|
|
bdd4f395ad | ||
|
|
4ae2999e95 | ||
|
|
077bb3b366 | ||
|
|
b0ca829cf6 | ||
|
|
c11ff6bf20 | ||
|
|
e1b99f6ee4 | ||
|
|
4c792ff668 | ||
|
|
260dca9664 | ||
|
|
91a15a5782 | ||
|
|
03f15d8689 | ||
|
|
86bf20b06b | ||
|
|
41ffe6d2eb | ||
|
|
41946ff0aa | ||
|
|
0ca06e2219 | ||
|
|
59ed3f07c8 | ||
|
|
e20630a425 | ||
|
|
ec8a2bc9ff | ||
|
|
b21c1f21a4 | ||
|
|
01b3dcc689 | ||
|
|
23f27cf496 | ||
|
|
9410e724b8 | ||
|
|
6f7b95a15c | ||
|
|
d5c2fe9280 | ||
|
|
450005d611 | ||
|
|
97a72f5b7f | ||
|
|
ce6d03ae13 | ||
|
|
23f7b6a750 | ||
|
|
36a4404c5c | ||
|
|
66241f2f1b | ||
|
|
e5aab0f165 | ||
|
|
14a4dc2bf9 | ||
|
|
42408d5a9a | ||
|
|
55cb3945df | ||
|
|
a4a95ba5d0 | ||
|
|
8a1ac05df2 | ||
|
|
78ae7f3b33 | ||
|
|
c783d6d101 | ||
|
|
3ea95ce53d | ||
|
|
16d978cea2 | ||
|
|
e43c992249 | ||
|
|
e78fc5b701 | ||
|
|
eebfc56013 | ||
|
|
1d4bd4e409 | ||
|
|
9f28d2f69a | ||
|
|
9c01a6328a | ||
|
|
46528127dc | ||
|
|
b34d3c3dbd | ||
|
|
2bc99deec2 | ||
|
|
a6b6219c6d | ||
|
|
caaf090b10 | ||
|
|
01b7641bd1 | ||
|
|
b42ddbbf42 | ||
|
|
f32b8dc4e8 | ||
|
|
9e6a4ca6df | ||
|
|
91e5d588b0 | ||
|
|
555c308749 | ||
|
|
7b88e883f9 | ||
|
|
e7b52023c6 | ||
|
|
e7a40343b1 | ||
|
|
dfc262bbcd | ||
|
|
615c705044 | ||
|
|
c59c32b350 | ||
|
|
d69df42de4 | ||
|
|
99dafe6327 | ||
|
|
5da3c9352d | ||
|
|
4f49f65a1c | ||
|
|
9b5bbee265 | ||
|
|
b5ca5e3839 |
@@ -1,6 +1,5 @@
|
||||
*.o
|
||||
obj/
|
||||
bin/
|
||||
STM32F4XX_Lib/
|
||||
GitVersion.h
|
||||
build/
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "STM32F4XX_Lib"]
|
||||
path = STM32F4XX_Lib
|
||||
url = https://github.com/juribeparada/STM32F4XX_Lib.git
|
||||
[submodule "STM32F7XX_Lib"]
|
||||
path = STM32F7XX_Lib
|
||||
url = https://github.com/juribeparada/STM32F7XX_Lib.git
|
||||
+5
-5
@@ -22,12 +22,12 @@
|
||||
#include "CWIdTX.h"
|
||||
|
||||
q15_t TONE[] = {
|
||||
0, 261, 518, 765, 1000, 1218, 1414, 1587, 1732, 1848, 1932, 1983, 2000, 1983, 1932, 1848, 1732, 1587, 1414, 1218, 1000, 765, 518, 261, 0, -261, -518, -765, -1000, -1218, -1414,
|
||||
-1587, -1732, -1848, -1932, -1983, -2000, -1983, -1932, -1848, -1732, -1587, -1414, -1218, -1000, -765, -518, -261};
|
||||
q15_t SILENCE[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
0, 518, 1000, 1414, 1732, 1932, 2000, 1932, 1732, 1414, 1000, 518, 0, -518, -1000, -1414, -1732, -1932, -2000, -1932, -1732, -1414, -1000, -518};
|
||||
|
||||
const uint8_t CYCLE_LENGTH = 48U;
|
||||
q15_t SILENCE[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
const uint8_t CYCLE_LENGTH = 24U;
|
||||
|
||||
const uint8_t DOT_LENGTH = 50U;
|
||||
|
||||
|
||||
+73
-4
@@ -21,25 +21,37 @@
|
||||
#include "Globals.h"
|
||||
#include "CalDMR.h"
|
||||
|
||||
// Voice LC Header, CC: 1, srcID: 1, dstID: TG9
|
||||
// Voice LC BS Header, CC: 1, srcID: 1, dstID: TG9
|
||||
const uint8_t VH_1K[] = {0x00U,
|
||||
0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U,
|
||||
0x60U, 0x84U, 0x6DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xDEU, 0x30U, 0x30U,
|
||||
0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU};
|
||||
|
||||
// Voice Term with LC, CC: 1, srcID: 1, dstID: TG9
|
||||
// Voice Term BS with LC, CC: 1, srcID: 1, dstID: TG9
|
||||
const uint8_t VT_1K[] = {0x00U,
|
||||
0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U,
|
||||
0xC0U, 0x84U, 0xADU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD9U, 0x65U, 0x24U,
|
||||
0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU};
|
||||
|
||||
// Voice LC MS Header, CC: 1, srcID: 1, dstID: TG9
|
||||
const uint8_t VH_DMO1K[] = {0x00U,
|
||||
0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U,
|
||||
0x60U, 0x84U, 0x6DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x7EU, 0x30U, 0x30U,
|
||||
0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU};
|
||||
|
||||
// Voice Term MS with LC, CC: 1, srcID: 1, dstID: TG9
|
||||
const uint8_t VT_DMO1K[] = {0x00U,
|
||||
0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U,
|
||||
0xC0U, 0x84U, 0xADU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x79U, 0x65U, 0x24U,
|
||||
0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU};
|
||||
|
||||
// Voice coding data + FEC, 1031 Hz Test Pattern
|
||||
const uint8_t VOICE_1K[] = {0x00U,
|
||||
0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU, 0xCEU, 0xA8U,
|
||||
0xFEU, 0x83U, 0xA0U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x0CU, 0xC4U, 0x58U,
|
||||
0x20U, 0x0AU, 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU};
|
||||
|
||||
// Embedded LC: CC: 1, srcID: 1, dstID: TG9
|
||||
// Embedded LC BS: CC: 1, srcID: 1, dstID: TG9
|
||||
const uint8_t SYNCEMB_1K[6][7] = {
|
||||
{0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U}, // BS VOICE SYNC (audio seq 0)
|
||||
{0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1)
|
||||
@@ -48,6 +60,15 @@ const uint8_t SYNCEMB_1K[6][7] = {
|
||||
{0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4)
|
||||
{0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5)
|
||||
|
||||
// Embedded LC MS: CC: 1, srcID: 1, dstID: TG9
|
||||
const uint8_t SYNCEMB_DMO1K[6][7] = {
|
||||
{0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U}, // MS VOICE SYNC (audio seq 0)
|
||||
{0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1)
|
||||
{0x01U, 0x70U, 0x00U, 0x90U, 0x00U, 0x07U, 0x40U}, // EMB + Embedded LC2 (audio seq 2)
|
||||
{0x01U, 0x70U, 0x00U, 0x31U, 0x40U, 0x07U, 0x40U}, // EMB + Embedded LC3 (audio seq 3)
|
||||
{0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4)
|
||||
{0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5)
|
||||
|
||||
// Short LC:
|
||||
// TS1: dstID: 0, ACTIVITY_NONE
|
||||
// TS2: dstID: TG9, ACTIVITY_VOICE
|
||||
@@ -78,6 +99,9 @@ void CCalDMR::process()
|
||||
case STATE_DMRCAL1K:
|
||||
dmr1kcal();
|
||||
break;
|
||||
case STATE_DMRDMO1K:
|
||||
dmrdmo1k();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -94,6 +118,17 @@ void CCalDMR::createData1k(uint8_t n)
|
||||
m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U;
|
||||
}
|
||||
|
||||
void CCalDMR::createDataDMO1k(uint8_t n)
|
||||
{
|
||||
for(uint8_t i = 0; i < 5U; i++)
|
||||
m_dmr1k[i + 15U] = SYNCEMB_DMO1K[n][i + 1U];
|
||||
|
||||
m_dmr1k[14U] &= 0xF0U;
|
||||
m_dmr1k[20U] &= 0x0FU;
|
||||
m_dmr1k[14U] |= SYNCEMB_DMO1K[n][0] & 0x0FU;
|
||||
m_dmr1k[20U] |= SYNCEMB_DMO1K[n][6] & 0xF0U;
|
||||
}
|
||||
|
||||
void CCalDMR::dmr1kcal()
|
||||
{
|
||||
dmrTX.process();
|
||||
@@ -139,6 +174,40 @@ void CCalDMR::dmr1kcal()
|
||||
}
|
||||
}
|
||||
|
||||
void CCalDMR::dmrdmo1k()
|
||||
{
|
||||
dmrDMOTX.process();
|
||||
|
||||
uint16_t space = dmrDMOTX.getSpace();
|
||||
if (space < 1U)
|
||||
return;
|
||||
|
||||
switch (m_state) {
|
||||
case DMRCAL1K_VH:
|
||||
dmrDMOTX.writeData(VH_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
m_state = DMRCAL1K_VOICE;
|
||||
break;
|
||||
case DMRCAL1K_VOICE:
|
||||
createDataDMO1k(m_audioSeq);
|
||||
dmrDMOTX.writeData(m_dmr1k, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
if(m_audioSeq == 5U) {
|
||||
m_audioSeq = 0U;
|
||||
if(!m_transmit)
|
||||
m_state = DMRCAL1K_VT;
|
||||
} else
|
||||
m_audioSeq++;
|
||||
break;
|
||||
case DMRCAL1K_VT:
|
||||
dmrDMOTX.writeData(VT_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
m_state = DMRCAL1K_IDLE;
|
||||
break;
|
||||
default:
|
||||
m_state = DMRCAL1K_IDLE;
|
||||
m_audioSeq = 0U;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CCalDMR::write(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (length != 1U)
|
||||
@@ -146,7 +215,7 @@ uint8_t CCalDMR::write(const uint8_t* data, uint8_t length)
|
||||
|
||||
m_transmit = data[0U] == 1U;
|
||||
|
||||
if(m_transmit && m_state == DMRCAL1K_IDLE && m_modemState == STATE_DMRCAL1K)
|
||||
if(m_transmit && m_state == DMRCAL1K_IDLE && (m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K))
|
||||
m_state = DMRCAL1K_VH;
|
||||
|
||||
return 0U;
|
||||
|
||||
@@ -37,7 +37,9 @@ public:
|
||||
|
||||
void process();
|
||||
void dmr1kcal();
|
||||
void dmrdmo1k();
|
||||
void createData1k(uint8_t n);
|
||||
void createDataDMO1k(uint8_t n);
|
||||
|
||||
uint8_t write(const uint8_t* data, uint8_t length);
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
const uint32_t PLLMAX = 0x10000U;
|
||||
const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH;
|
||||
const uint32_t INC = PLLINC / 32U;
|
||||
|
||||
// D-Star bit order version of 0x55 0x2D 0x16
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2018 by Andy Uribe CA6JAU
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "CalNXDN.h"
|
||||
|
||||
// NXDN 1031 Hz Test Pattern, RAN: 1, Unit ID: 1, Dst Group ID: 1, Outbound Direction
|
||||
const uint8_t NXDN_CAL1K[4][49] = {
|
||||
{0x00U,
|
||||
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x7CU, 0xFAU, 0x0AU, 0x6EU, 0x8AU, 0x23U, 0x56U, 0xE8U,
|
||||
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
|
||||
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
|
||||
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
|
||||
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U},
|
||||
|
||||
{0x00U,
|
||||
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x7CU, 0x6DU, 0xBBU, 0x0EU, 0xB3U, 0xA4U, 0x26U, 0xA8U,
|
||||
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
|
||||
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
|
||||
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
|
||||
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U},
|
||||
|
||||
{0x00U,
|
||||
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x76U, 0x3AU, 0x1BU, 0x4AU, 0x81U, 0xA8U, 0xE2U, 0x80U,
|
||||
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
|
||||
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
|
||||
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
|
||||
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U},
|
||||
|
||||
{0x00U,
|
||||
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x74U, 0x28U, 0x83U, 0x02U, 0xB0U, 0x2DU, 0x07U, 0xE2U,
|
||||
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
|
||||
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
|
||||
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
|
||||
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U}};
|
||||
|
||||
CCalNXDN::CCalNXDN() :
|
||||
m_transmit(false),
|
||||
m_state(NXDNCAL1K_IDLE),
|
||||
m_audioSeq(0U)
|
||||
{
|
||||
}
|
||||
|
||||
void CCalNXDN::process()
|
||||
{
|
||||
nxdnTX.process();
|
||||
|
||||
uint16_t space = nxdnTX.getSpace();
|
||||
if (space < 1U)
|
||||
return;
|
||||
|
||||
switch (m_state) {
|
||||
case NXDNCAL1K_TX:
|
||||
nxdnTX.writeData(NXDN_CAL1K[m_audioSeq], NXDN_FRAME_LENGTH_BYTES + 1U);
|
||||
m_audioSeq = (m_audioSeq + 1U) % 4U;
|
||||
if(!m_transmit)
|
||||
m_state = NXDNCAL1K_IDLE;
|
||||
break;
|
||||
default:
|
||||
m_state = NXDNCAL1K_IDLE;
|
||||
m_audioSeq = 0U;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CCalNXDN::write(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (length != 1U)
|
||||
return 4U;
|
||||
|
||||
m_transmit = data[0U] == 1U;
|
||||
|
||||
if(m_transmit && m_state == NXDNCAL1K_IDLE)
|
||||
m_state = NXDNCAL1K_TX;
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2018 by Andy Uribe CA6JAU
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(CALNXDN_H)
|
||||
#define CALNXDN_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
enum NXDNCAL1K {
|
||||
NXDNCAL1K_IDLE,
|
||||
NXDNCAL1K_TX
|
||||
};
|
||||
|
||||
class CCalNXDN {
|
||||
public:
|
||||
CCalNXDN();
|
||||
|
||||
void process();
|
||||
|
||||
uint8_t write(const uint8_t* data, uint8_t length);
|
||||
|
||||
private:
|
||||
bool m_transmit;
|
||||
NXDNCAL1K m_state;
|
||||
uint8_t m_audioSeq;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
@@ -39,7 +39,7 @@
|
||||
// #define USE_COS_AS_LOCKOUT
|
||||
|
||||
// Use pins to output the current mode
|
||||
// #define ARDUINO_MODE_PINS
|
||||
// #define MODE_PINS
|
||||
|
||||
// For the original Arduino Due pin layout
|
||||
// #define ARDUINO_DUE_PAPA
|
||||
@@ -66,4 +66,18 @@
|
||||
// Use the modem as a serial repeater for Nextion displays
|
||||
// #define SERIAL_REPEATER
|
||||
|
||||
// To reduce CPU load, you can remove the DC blocker by commenting out the next line
|
||||
#define USE_DCBLOCKER
|
||||
|
||||
// Constant Service LED once repeater is running
|
||||
// Do not use if employing an external hardware watchdog
|
||||
// #define CONSTANT_SRV_LED
|
||||
|
||||
// Use the YSF and P25 LEDs for NXDN
|
||||
// #define USE_ALTERNATE_NXDN_LEDS
|
||||
|
||||
// Use the D-Star and DMR LEDs for POCSAG
|
||||
// #define USE_ALTERNATE_POCSAG_LEDS
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
#include "Config.h"
|
||||
#include "DMRDefines.h"
|
||||
|
||||
const uint16_t DMO_BUFFER_LENGTH_SAMPLES = 2880U; // 60ms at 48 kHz
|
||||
const uint16_t DMO_BUFFER_LENGTH_SAMPLES = 1440U; // 60ms at 24 kHz
|
||||
|
||||
enum DMORX_STATE {
|
||||
DMORXS_NONE,
|
||||
|
||||
+15
-14
@@ -22,16 +22,10 @@
|
||||
#include "Globals.h"
|
||||
#include "DMRSlotType.h"
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179,
|
||||
-1548, -1769, -1795, -1597, -1172, -544, 237, 1092, 1927, 2637,
|
||||
3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739,
|
||||
-6442, -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822,
|
||||
19484, 23810, 27520, 30367, 32156, 32767, 32156, 30367, 27520,
|
||||
23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735,
|
||||
-6483, -6442, -5739, -4544, -3043, -1431, 116, 1447, 2454,
|
||||
3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, -1597,
|
||||
-1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DMR_LEVELA = 1362;
|
||||
@@ -44,6 +38,13 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
// PR FILL pattern
|
||||
const uint8_t PR_FILL[] =
|
||||
{0x63U, 0xEAU, 0x00U, 0x76U, 0x6CU, 0x76U, 0xC4U, 0x52U, 0xC8U, 0x78U,
|
||||
0x09U, 0x2DU, 0xB8U, 0x79U, 0x27U, 0x57U, 0x9BU, 0x31U, 0xBCU, 0x3EU,
|
||||
0xEAU, 0x45U, 0xC3U, 0x30U, 0x49U, 0x17U, 0x93U, 0xAEU, 0x8BU, 0x6DU,
|
||||
0xA4U, 0xA5U, 0xADU, 0xA2U, 0xF1U, 0x35U, 0xB5U, 0x3CU, 0x1EU};
|
||||
|
||||
const uint8_t DMR_SYNC = 0x5FU;
|
||||
|
||||
CDMRDMOTX::CDMRDMOTX() :
|
||||
@@ -72,11 +73,11 @@ void CDMRDMOTX::process()
|
||||
|
||||
m_poLen = m_txDelay;
|
||||
} else {
|
||||
for (unsigned int i = 0U; i < 72U; i++)
|
||||
m_poBuffer[i] = DMR_SYNC;
|
||||
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
||||
m_poBuffer[i + 39U] = m_fifo.get();
|
||||
m_poBuffer[i] = m_fifo.get();
|
||||
|
||||
for (unsigned int i = 0U; i < 39U; i++)
|
||||
m_poBuffer[i + DMR_FRAME_LENGTH_BYTES] = PR_FILL[i];
|
||||
|
||||
m_poLen = 72U;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2016 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
|
||||
@@ -19,7 +19,7 @@
|
||||
#if !defined(DMRDEFINES_H)
|
||||
#define DMRDEFINES_H
|
||||
|
||||
const unsigned int DMR_RADIO_SYMBOL_LENGTH = 10U; // At 48 kHz sample rate
|
||||
const unsigned int DMR_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||
|
||||
const unsigned int DMR_FRAME_LENGTH_BYTES = 33U;
|
||||
const unsigned int DMR_FRAME_LENGTH_BITS = DMR_FRAME_LENGTH_BYTES * 8U;
|
||||
|
||||
+3
-3
@@ -22,8 +22,8 @@
|
||||
#include "DMRSlotType.h"
|
||||
#include "Utils.h"
|
||||
|
||||
const uint16_t SCAN_START = 790U;
|
||||
const uint16_t SCAN_END = 920U;
|
||||
const uint16_t SCAN_START = 400U;
|
||||
const uint16_t SCAN_END = 490U;
|
||||
|
||||
const q15_t SCALING_FACTOR = 19505; // Q15(0.60)
|
||||
|
||||
@@ -97,7 +97,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
return m_state != DMRRXS_NONE;
|
||||
|
||||
// Ensure that the buffer doesn't overflow
|
||||
if (m_dataPtr > m_endPtr || m_dataPtr >= 1900U)
|
||||
if (m_dataPtr > m_endPtr || m_dataPtr >= 900U)
|
||||
return m_state != DMRRXS_NONE;
|
||||
|
||||
m_buffer[m_dataPtr] = sample;
|
||||
|
||||
+2
-2
@@ -44,7 +44,7 @@ public:
|
||||
private:
|
||||
bool m_slot;
|
||||
uint32_t m_bitBuffer[DMR_RADIO_SYMBOL_LENGTH];
|
||||
q15_t m_buffer[1900U];
|
||||
q15_t m_buffer[900U];
|
||||
uint16_t m_bitPtr;
|
||||
uint16_t m_dataPtr;
|
||||
uint16_t m_syncPtr;
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
DMRRX_STATE m_state;
|
||||
uint8_t m_n;
|
||||
uint8_t m_type;
|
||||
uint16_t m_rssi[1900U];
|
||||
uint16_t m_rssi[900U];
|
||||
|
||||
void correlateSync(bool first);
|
||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
|
||||
@@ -22,16 +22,10 @@
|
||||
#include "Globals.h"
|
||||
#include "DMRSlotType.h"
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179,
|
||||
-1548, -1769, -1795, -1597, -1172, -544, 237, 1092, 1927, 2637,
|
||||
3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739,
|
||||
-6442, -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822,
|
||||
19484, 23810, 27520, 30367, 32156, 32767, 32156, 30367, 27520,
|
||||
23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735,
|
||||
-6483, -6442, -5739, -4544, -3043, -1431, 116, 1447, 2454,
|
||||
3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, -1597,
|
||||
-1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DMR_LEVELA = 1362;
|
||||
@@ -60,6 +54,7 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint32_t STARTUP_COUNT = 20U;
|
||||
const uint32_t ABORT_COUNT = 6U;
|
||||
|
||||
CDMRTX::CDMRTX() :
|
||||
m_fifo(),
|
||||
@@ -75,6 +70,7 @@ m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_frameCount(0U),
|
||||
m_abortCount(),
|
||||
m_abort()
|
||||
{
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
@@ -89,6 +85,9 @@ m_abort()
|
||||
|
||||
m_abort[0U] = false;
|
||||
m_abort[1U] = false;
|
||||
|
||||
m_abortCount[0U] = 0U;
|
||||
m_abortCount[1U] = 0U;
|
||||
}
|
||||
|
||||
void CDMRTX::process()
|
||||
@@ -216,10 +215,12 @@ uint8_t CDMRTX::writeAbort(const uint8_t* data, uint8_t length)
|
||||
|
||||
switch (data[0U]) {
|
||||
case 1U:
|
||||
m_abortCount[0U] = 0U;
|
||||
m_abort[0U] = true;
|
||||
return 0U;
|
||||
|
||||
case 2U:
|
||||
m_abortCount[1U] = 0U;
|
||||
m_abort[1U] = true;
|
||||
return 0U;
|
||||
|
||||
@@ -233,6 +234,8 @@ void CDMRTX::setStart(bool start)
|
||||
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
|
||||
|
||||
m_frameCount = 0U;
|
||||
m_abortCount[0U] = 0U;
|
||||
m_abortCount[1U] = 0U;
|
||||
|
||||
m_abort[0U] = false;
|
||||
m_abort[1U] = false;
|
||||
@@ -288,7 +291,7 @@ uint8_t CDMRTX::getSpace2() const
|
||||
|
||||
void CDMRTX::createData(uint8_t slotIndex)
|
||||
{
|
||||
if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) {
|
||||
if (m_fifo[slotIndex].getData() >= DMR_FRAME_LENGTH_BYTES && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) {
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
||||
m_poBuffer[i] = m_fifo[slotIndex].get();
|
||||
m_markBuffer[i] = MARK_NONE;
|
||||
@@ -341,6 +344,8 @@ void CDMRTX::createCal()
|
||||
void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
||||
{
|
||||
m_frameCount++;
|
||||
m_abortCount[0U]++;
|
||||
m_abortCount[1U]++;
|
||||
|
||||
if (m_cachPtr >= 12U)
|
||||
m_cachPtr = 0U;
|
||||
|
||||
@@ -72,6 +72,7 @@ private:
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_frameCount;
|
||||
uint32_t m_abortCount[2U];
|
||||
bool m_abort[2U];
|
||||
|
||||
void createData(uint8_t slotIndex);
|
||||
|
||||
+17
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2015,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2015 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
|
||||
@@ -19,50 +19,30 @@
|
||||
#if !defined(DSTARDEFINES_H)
|
||||
#define DSTARDEFINES_H
|
||||
|
||||
const unsigned int DSTAR_RADIO_SYMBOL_LENGTH = 10U; // At 48 kHz sample rate
|
||||
const unsigned int DSTAR_RADIO_BIT_LENGTH = 5U; // At 24 kHz sample rate
|
||||
|
||||
const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U;
|
||||
const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U;
|
||||
const unsigned int DSTAR_HEADER_LENGTH_BITS = DSTAR_HEADER_LENGTH_BYTES * 8U;
|
||||
|
||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_BYTES = 83U;
|
||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_SYMBOLS = 660U;
|
||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_SAMPLES = DSTAR_FEC_SECTION_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_BYTES = 83U;
|
||||
const unsigned int DSTAR_FEC_SECTION_LENGTH_BITS = 660U;
|
||||
|
||||
const unsigned int DSTAR_DATA_LENGTH_BYTES = 12U;
|
||||
const unsigned int DSTAR_DATA_LENGTH_SYMBOLS = DSTAR_DATA_LENGTH_BYTES * 8U;
|
||||
const unsigned int DSTAR_DATA_LENGTH_SAMPLES = DSTAR_DATA_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
const unsigned int DSTAR_DATA_LENGTH_BYTES = 12U;
|
||||
const unsigned int DSTAR_DATA_LENGTH_BITS = DSTAR_DATA_LENGTH_BYTES * 8U;
|
||||
|
||||
const unsigned int DSTAR_END_SYNC_LENGTH_BYTES = 6U;
|
||||
const unsigned int DSTAR_END_SYNC_LENGTH_BITS = DSTAR_END_SYNC_LENGTH_BYTES * 8U;
|
||||
const uint8_t DSTAR_EOT_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
const unsigned int DSTAR_EOT_LENGTH_BYTES = 6U;
|
||||
const unsigned int DSTAR_EOT_LENGTH_BITS = DSTAR_EOT_LENGTH_BYTES * 8U;
|
||||
|
||||
const unsigned int DSTAR_FRAME_SYNC_LENGTH_BYTES = 3U;
|
||||
const unsigned int DSTAR_FRAME_SYNC_LENGTH_SYMBOLS = DSTAR_FRAME_SYNC_LENGTH_BYTES * 8U;
|
||||
const unsigned int DSTAR_FRAME_SYNC_LENGTH_SAMPLES = DSTAR_FRAME_SYNC_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
const uint8_t DSTAR_DATA_SYNC_LENGTH_BYTES = 3U;
|
||||
const uint8_t DSTAR_DATA_SYNC_LENGTH_BITS = DSTAR_DATA_SYNC_LENGTH_BYTES * 8U;
|
||||
|
||||
const unsigned int DSTAR_DATA_SYNC_LENGTH_BYTES = 3U;
|
||||
const unsigned int DSTAR_DATA_SYNC_LENGTH_SYMBOLS = DSTAR_DATA_SYNC_LENGTH_BYTES * 8U;
|
||||
const unsigned int DSTAR_DATA_SYNC_LENGTH_SAMPLES = DSTAR_DATA_SYNC_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
const uint8_t DSTAR_DATA_SYNC_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16};
|
||||
|
||||
const uint8_t DSTAR_DATA_SYNC_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16};
|
||||
const uint8_t DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
|
||||
const uint8_t DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
|
||||
|
||||
// D-Star bit order version of 0x55 0x6E 0x0A
|
||||
const uint32_t DSTAR_FRAME_SYNC_DATA = 0x00557650U;
|
||||
const uint32_t DSTAR_FRAME_SYNC_MASK = 0x00FFFFFFU;
|
||||
const bool DSTAR_FRAME_SYNC_SYMBOLS[] = {false, true, false, true, false, true, false, true, false, true, true, true, false, true, true, false, false, true, false, true, false, false, false, false};
|
||||
|
||||
// D-Star bit order version of 0x55 0x2D 0x16
|
||||
const uint32_t DSTAR_DATA_SYNC_DATA = 0x00AAB468U;
|
||||
const uint32_t DSTAR_DATA_SYNC_MASK = 0x00FFFFFFU;
|
||||
const bool DSTAR_DATA_SYNC_SYMBOLS[] = {true, false, true, false, true, false, true, false, true, false, true, true, false, true, false, false, false, true, true, false, true, false, false, false};
|
||||
|
||||
// D-Star bit order version of 0x55 0x55 0xC8 0x7A
|
||||
const uint32_t DSTAR_END_SYNC_DATA = 0xAAAA135EU;
|
||||
const uint32_t DSTAR_END_SYNC_MASK = 0xFFFFFFFFU;
|
||||
const uint8_t DSTAR_END_SYNC_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const uint8_t DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U;
|
||||
const uint8_t DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U;
|
||||
|
||||
const uint8_t DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U};
|
||||
const uint8_t DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+136
-237
@@ -22,12 +22,18 @@
|
||||
#include "DStarRX.h"
|
||||
#include "Utils.h"
|
||||
|
||||
const unsigned int MAX_FRAMES = 150U;
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
const uint32_t PLLMAX = 0x10000U;
|
||||
const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH;
|
||||
const uint32_t INC = PLLINC / 32U;
|
||||
|
||||
const unsigned int MAX_SYNC_BITS = 100U * DSTAR_DATA_LENGTH_BITS;
|
||||
|
||||
// D-Star bit order version of 0x55 0x55 0x6E 0x0A
|
||||
const uint32_t FRAME_SYNC_DATA = 0x00557650U;
|
||||
const uint32_t FRAME_SYNC_MASK = 0x00FFFFFFU;
|
||||
const uint8_t FRAME_SYNC_ERRS = 1U;
|
||||
const uint8_t FRAME_SYNC_ERRS = 2U;
|
||||
|
||||
// D-Star bit order version of 0x55 0x2D 0x16
|
||||
const uint32_t DATA_SYNC_DATA = 0x00AAB468U;
|
||||
@@ -231,24 +237,14 @@ const uint16_t CCITT_TABLE[] = {
|
||||
0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U,
|
||||
0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U};
|
||||
|
||||
const uint16_t NOENDPTR = 9999U;
|
||||
|
||||
CDStarRX::CDStarRX() :
|
||||
m_pll(0U),
|
||||
m_prev(false),
|
||||
m_rxState(DSRXS_NONE),
|
||||
m_bitBuffer(),
|
||||
m_headerBuffer(),
|
||||
m_dataBuffer(),
|
||||
m_bitPtr(0U),
|
||||
m_headerPtr(0U),
|
||||
m_dataPtr(0U),
|
||||
m_startPtr(NOENDPTR),
|
||||
m_syncPtr(NOENDPTR),
|
||||
m_minSyncPtr(NOENDPTR),
|
||||
m_maxSyncPtr(NOENDPTR),
|
||||
m_maxFrameCorr(0),
|
||||
m_maxDataCorr(0),
|
||||
m_frameCount(0U),
|
||||
m_countdown(0U),
|
||||
m_patternBuffer(0x00U),
|
||||
m_rxBuffer(),
|
||||
m_rxBufferBits(0U),
|
||||
m_dataBits(0U),
|
||||
m_mar(0U),
|
||||
m_pathMetric(),
|
||||
m_pathMemory0(),
|
||||
@@ -263,20 +259,14 @@ m_rssiCount(0U)
|
||||
|
||||
void CDStarRX::reset()
|
||||
{
|
||||
m_rxState = DSRXS_NONE;
|
||||
m_headerPtr = 0U;
|
||||
m_dataPtr = 0U;
|
||||
m_bitPtr = 0U;
|
||||
m_maxFrameCorr = 0;
|
||||
m_maxDataCorr = 0;
|
||||
m_startPtr = NOENDPTR;
|
||||
m_syncPtr = NOENDPTR;
|
||||
m_minSyncPtr = NOENDPTR;
|
||||
m_maxSyncPtr = NOENDPTR;
|
||||
m_frameCount = 0U;
|
||||
m_countdown = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
m_pll = 0U;
|
||||
m_prev = false;
|
||||
m_rxState = DSRXS_NONE;
|
||||
m_patternBuffer = 0x00U;
|
||||
m_rxBufferBits = 0U;
|
||||
m_dataBits = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CDStarRX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
||||
@@ -285,117 +275,125 @@ void CDStarRX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t lengt
|
||||
m_rssiAccum += rssi[i];
|
||||
m_rssiCount++;
|
||||
|
||||
q15_t sample = samples[i];
|
||||
bool bit = samples[i] < 0;
|
||||
|
||||
m_bitBuffer[m_bitPtr] <<= 1;
|
||||
if (sample < 0)
|
||||
m_bitBuffer[m_bitPtr] |= 0x01U;
|
||||
|
||||
m_dataBuffer[m_dataPtr] = sample;
|
||||
|
||||
switch (m_rxState) {
|
||||
case DSRXS_HEADER:
|
||||
processHeader(sample);
|
||||
break;
|
||||
case DSRXS_DATA:
|
||||
processData();
|
||||
break;
|
||||
default:
|
||||
processNone(sample);
|
||||
break;
|
||||
if (bit != m_prev) {
|
||||
if (m_pll < (PLLMAX / 2U))
|
||||
m_pll += INC;
|
||||
else
|
||||
m_pll -= INC;
|
||||
}
|
||||
|
||||
m_dataPtr++;
|
||||
if (m_dataPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
m_dataPtr = 0U;
|
||||
m_prev = bit;
|
||||
|
||||
m_bitPtr++;
|
||||
if (m_bitPtr >= DSTAR_RADIO_SYMBOL_LENGTH)
|
||||
m_bitPtr = 0U;
|
||||
m_pll += PLLINC;
|
||||
|
||||
if (m_pll >= PLLMAX) {
|
||||
m_pll -= PLLMAX;
|
||||
|
||||
switch (m_rxState) {
|
||||
case DSRXS_NONE:
|
||||
processNone(bit);
|
||||
break;
|
||||
case DSRXS_HEADER:
|
||||
processHeader(bit);
|
||||
break;
|
||||
case DSRXS_DATA:
|
||||
processData(bit);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarRX::processNone(q15_t sample)
|
||||
void CDStarRX::processNone(bool bit)
|
||||
{
|
||||
m_patternBuffer <<= 1;
|
||||
if (bit)
|
||||
m_patternBuffer |= 0x01U;
|
||||
|
||||
// Fuzzy matching of the frame sync sequence
|
||||
bool ret = correlateFrameSync();
|
||||
if (ret) {
|
||||
m_countdown = 5U;
|
||||
|
||||
m_headerBuffer[m_headerPtr] = sample;
|
||||
m_headerPtr++;
|
||||
if (countBits32((m_patternBuffer & FRAME_SYNC_MASK) ^ FRAME_SYNC_DATA) <= FRAME_SYNC_ERRS) {
|
||||
DEBUG1("DStarRX: found frame sync in None");
|
||||
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_FEC_SECTION_LENGTH_BYTES);
|
||||
m_rxBufferBits = 0U;
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
m_rxState = DSRXS_HEADER;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
ret = correlateDataSync();
|
||||
if (ret) {
|
||||
// Exact matching of the data sync bit sequence
|
||||
if (countBits32((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA) == 0U) {
|
||||
DEBUG1("DStarRX: found data sync in None");
|
||||
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
m_rxState = DSRXS_DATA;
|
||||
// Suppress RSSI on the dummy sync message
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
::memcpy(m_rxBuffer, DSTAR_DATA_SYNC_BYTES, DSTAR_DATA_LENGTH_BYTES);
|
||||
writeRSSIData(m_rxBuffer);
|
||||
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
m_rxBufferBits = 0U;
|
||||
|
||||
m_dataBits = MAX_SYNC_BITS;
|
||||
m_rxState = DSRXS_DATA;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarRX::processHeader(q15_t sample)
|
||||
void CDStarRX::processHeader(bool bit)
|
||||
{
|
||||
if (m_countdown > 0U) {
|
||||
correlateFrameSync();
|
||||
m_countdown--;
|
||||
}
|
||||
m_patternBuffer <<= 1;
|
||||
if (bit)
|
||||
m_patternBuffer |= 0x01U;
|
||||
|
||||
m_headerBuffer[m_headerPtr] = sample;
|
||||
m_headerPtr++;
|
||||
WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit);
|
||||
m_rxBufferBits++;
|
||||
|
||||
// A full FEC header
|
||||
if (m_headerPtr == (DSTAR_FEC_SECTION_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH)) {
|
||||
uint8_t buffer[DSTAR_FEC_SECTION_LENGTH_BYTES];
|
||||
samplesToBits(m_headerBuffer, DSTAR_RADIO_SYMBOL_LENGTH, DSTAR_FEC_SECTION_LENGTH_SYMBOLS, buffer, DSTAR_FEC_SECTION_LENGTH_SAMPLES);
|
||||
|
||||
// Process the scrambling, interleaving and FEC, then return true if the chcksum was correct
|
||||
uint8_t header[DSTAR_HEADER_LENGTH_BYTES];
|
||||
bool ok = rxHeader(buffer, header);
|
||||
if (!ok) {
|
||||
// The checksum failed, return to looking for syncs
|
||||
m_rxState = DSRXS_NONE;
|
||||
m_maxFrameCorr = 0;
|
||||
m_maxDataCorr = 0;
|
||||
} else {
|
||||
if (m_rxBufferBits == DSTAR_FEC_SECTION_LENGTH_BITS) {
|
||||
// Process the scrambling, interleaving and FEC, then return if the chcksum was correct
|
||||
unsigned char header[DSTAR_HEADER_LENGTH_BYTES];
|
||||
bool ok = rxHeader(m_rxBuffer, header);
|
||||
if (ok) {
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
writeRSSIHeader(header);
|
||||
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
m_rxBufferBits = 0U;
|
||||
|
||||
m_rxState = DSRXS_DATA;
|
||||
m_dataBits = MAX_SYNC_BITS;
|
||||
} else {
|
||||
// The checksum failed, return to looking for syncs
|
||||
m_rxState = DSRXS_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Ready to start the first data section
|
||||
if (m_headerPtr == (DSTAR_FEC_SECTION_LENGTH_SAMPLES + 2U * DSTAR_RADIO_SYMBOL_LENGTH)) {
|
||||
m_frameCount = 0U;
|
||||
m_dataPtr = 0U;
|
||||
|
||||
m_startPtr = 952U;
|
||||
m_syncPtr = 942U;
|
||||
m_maxSyncPtr = 944U;
|
||||
m_minSyncPtr = 940U;
|
||||
|
||||
DEBUG5("DStarRX: calc start/sync/max/min", m_startPtr, m_syncPtr, m_maxSyncPtr, m_minSyncPtr);
|
||||
|
||||
m_rxState = DSRXS_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarRX::processData()
|
||||
void CDStarRX::processData(bool bit)
|
||||
{
|
||||
m_patternBuffer <<= 1;
|
||||
if (bit)
|
||||
m_patternBuffer |= 0x01U;
|
||||
|
||||
WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit);
|
||||
m_rxBufferBits++;
|
||||
|
||||
// Fuzzy matching of the end frame sequences
|
||||
if (countBits32((m_bitBuffer[m_bitPtr] & DSTAR_END_SYNC_MASK) ^ DSTAR_END_SYNC_DATA) <= END_SYNC_ERRS) {
|
||||
if (countBits32((m_patternBuffer & END_SYNC_MASK) ^ END_SYNC_DATA) <= END_SYNC_ERRS) {
|
||||
DEBUG1("DStarRX: Found end sync in Data");
|
||||
|
||||
io.setDecode(false);
|
||||
@@ -403,24 +401,35 @@ void CDStarRX::processData()
|
||||
|
||||
serial.writeDStarEOT();
|
||||
|
||||
m_maxFrameCorr = 0;
|
||||
m_maxDataCorr = 0;
|
||||
|
||||
m_rxState = DSRXS_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (m_minSyncPtr < m_maxSyncPtr) {
|
||||
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
|
||||
correlateDataSync();
|
||||
} else {
|
||||
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
|
||||
correlateDataSync();
|
||||
bool syncSeen = false;
|
||||
if (m_rxBufferBits >= (DSTAR_DATA_LENGTH_BITS - 3U)) {
|
||||
if (countBits32((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA) <= DATA_SYNC_ERRS) {
|
||||
m_rxBufferBits = DSTAR_DATA_LENGTH_BITS;
|
||||
m_dataBits = MAX_SYNC_BITS;
|
||||
syncSeen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
||||
if (m_frameCount >= MAX_FRAMES) {
|
||||
// Check to see if the sync is arriving late
|
||||
if (m_rxBufferBits == DSTAR_DATA_LENGTH_BITS && !syncSeen) {
|
||||
for (uint8_t i = 1U; i <= 3U; i++) {
|
||||
uint32_t syncMask = DATA_SYNC_MASK >> i;
|
||||
uint32_t syncData = DATA_SYNC_DATA >> i;
|
||||
if (countBits32((m_patternBuffer & syncMask) ^ syncData) <= DATA_SYNC_ERRS) {
|
||||
m_rxBufferBits -= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_dataBits--;
|
||||
|
||||
if (m_dataBits == 0U) {
|
||||
DEBUG1("DStarRX: data sync timed out, lost lock");
|
||||
|
||||
io.setDecode(false);
|
||||
@@ -428,35 +437,24 @@ void CDStarRX::processData()
|
||||
|
||||
serial.writeDStarLost();
|
||||
|
||||
m_maxFrameCorr = 0;
|
||||
m_maxDataCorr = 0;
|
||||
|
||||
m_rxState = DSRXS_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a data frame to the host if the required number of bits have been received
|
||||
if (m_dataPtr == m_maxSyncPtr) {
|
||||
uint8_t buffer[DSTAR_DATA_LENGTH_BYTES + 2U];
|
||||
samplesToBits(m_dataBuffer, m_startPtr, DSTAR_DATA_LENGTH_SYMBOLS, buffer, DSTAR_DATA_LENGTH_SAMPLES);
|
||||
|
||||
if ((m_frameCount % 21U) == 0U) {
|
||||
if (m_frameCount == 0U) {
|
||||
buffer[9U] = DSTAR_DATA_SYNC_BYTES[9U];
|
||||
buffer[10U] = DSTAR_DATA_SYNC_BYTES[10U];
|
||||
buffer[11U] = DSTAR_DATA_SYNC_BYTES[11U];
|
||||
DEBUG5("DStarRX: found start/sync/max/min", m_startPtr, m_syncPtr, m_maxSyncPtr, m_minSyncPtr);
|
||||
}
|
||||
|
||||
writeRSSIData(buffer);
|
||||
// Send a data frame to the host if the required number of bits have been received, or if a data sync has been seen
|
||||
if (m_rxBufferBits == DSTAR_DATA_LENGTH_BITS) {
|
||||
if (syncSeen) {
|
||||
m_rxBuffer[9U] = DSTAR_DATA_SYNC_BYTES[9U];
|
||||
m_rxBuffer[10U] = DSTAR_DATA_SYNC_BYTES[10U];
|
||||
m_rxBuffer[11U] = DSTAR_DATA_SYNC_BYTES[11U];
|
||||
writeRSSIData(m_rxBuffer);
|
||||
} else {
|
||||
serial.writeDStarData(buffer, DSTAR_DATA_LENGTH_BYTES);
|
||||
}
|
||||
serial.writeDStarData(m_rxBuffer, DSTAR_DATA_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
m_frameCount++;
|
||||
|
||||
m_maxFrameCorr = 0;
|
||||
m_maxDataCorr = 0;
|
||||
// Start the next frame
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
m_rxBufferBits = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,105 +500,6 @@ void CDStarRX::writeRSSIData(unsigned char* data)
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
bool CDStarRX::correlateFrameSync()
|
||||
{
|
||||
if (countBits32((m_bitBuffer[m_bitPtr] & DSTAR_FRAME_SYNC_MASK) ^ DSTAR_FRAME_SYNC_DATA) <= FRAME_SYNC_ERRS) {
|
||||
uint16_t ptr = m_dataPtr + DSTAR_DATA_LENGTH_SAMPLES - DSTAR_FRAME_SYNC_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
|
||||
q31_t corr = 0;
|
||||
|
||||
for (uint8_t i = 0U; i < DSTAR_FRAME_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_dataBuffer[ptr];
|
||||
|
||||
if (DSTAR_FRAME_SYNC_SYMBOLS[i])
|
||||
corr -= val;
|
||||
else
|
||||
corr += val;
|
||||
|
||||
ptr += DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
if (corr > m_maxFrameCorr) {
|
||||
m_maxFrameCorr = corr;
|
||||
m_headerPtr = 0U;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDStarRX::correlateDataSync()
|
||||
{
|
||||
uint8_t maxErrs = 0U;
|
||||
if (m_rxState == DSRXS_DATA)
|
||||
maxErrs = DATA_SYNC_ERRS;
|
||||
|
||||
if (countBits32((m_bitBuffer[m_bitPtr] & DSTAR_DATA_SYNC_MASK) ^ DSTAR_DATA_SYNC_DATA) <= maxErrs) {
|
||||
uint16_t ptr = m_dataPtr + DSTAR_DATA_LENGTH_SAMPLES - DSTAR_DATA_SYNC_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
|
||||
q31_t corr = 0;
|
||||
|
||||
for (uint8_t i = 0U; i < DSTAR_DATA_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_dataBuffer[ptr];
|
||||
|
||||
if (DSTAR_DATA_SYNC_SYMBOLS[i])
|
||||
corr -= val;
|
||||
else
|
||||
corr += val;
|
||||
|
||||
ptr += DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
ptr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
if (corr > m_maxDataCorr) {
|
||||
m_maxDataCorr = corr;
|
||||
m_frameCount = 0U;
|
||||
|
||||
m_syncPtr = m_dataPtr;
|
||||
|
||||
m_startPtr = m_dataPtr + DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
if (m_startPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
m_startPtr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_syncPtr + 1U;
|
||||
if (m_maxSyncPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
|
||||
m_minSyncPtr = m_syncPtr + DSTAR_DATA_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= DSTAR_DATA_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= DSTAR_DATA_LENGTH_SAMPLES;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDStarRX::samplesToBits(const q15_t* inBuffer, uint16_t start, uint16_t count, uint8_t* outBuffer, uint16_t limit)
|
||||
{
|
||||
for (uint16_t i = 0U; i < count; i++) {
|
||||
q15_t sample = inBuffer[start];
|
||||
|
||||
if (sample < 0)
|
||||
WRITE_BIT2(outBuffer, i, true);
|
||||
else
|
||||
WRITE_BIT2(outBuffer, i, false);
|
||||
|
||||
start += DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= limit)
|
||||
start -= limit;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDStarRX::rxHeader(uint8_t* in, uint8_t* out)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -37,21 +37,13 @@ public:
|
||||
void reset();
|
||||
|
||||
private:
|
||||
uint32_t m_pll;
|
||||
bool m_prev;
|
||||
DSRX_STATE m_rxState;
|
||||
uint32_t m_bitBuffer[DSTAR_RADIO_SYMBOL_LENGTH];
|
||||
q15_t m_headerBuffer[DSTAR_FEC_SECTION_LENGTH_SAMPLES + 2U * DSTAR_RADIO_SYMBOL_LENGTH];
|
||||
q15_t m_dataBuffer[DSTAR_DATA_LENGTH_SAMPLES];
|
||||
uint16_t m_bitPtr;
|
||||
uint16_t m_headerPtr;
|
||||
uint16_t m_dataPtr;
|
||||
uint16_t m_startPtr;
|
||||
uint16_t m_syncPtr;
|
||||
uint16_t m_minSyncPtr;
|
||||
uint16_t m_maxSyncPtr;
|
||||
q31_t m_maxFrameCorr;
|
||||
q31_t m_maxDataCorr;
|
||||
uint16_t m_frameCount;
|
||||
uint8_t m_countdown;
|
||||
uint32_t m_patternBuffer;
|
||||
uint8_t m_rxBuffer[100U];
|
||||
unsigned int m_rxBufferBits;
|
||||
unsigned int m_dataBits;
|
||||
unsigned int m_mar;
|
||||
int m_pathMetric[4U];
|
||||
unsigned int m_pathMemory0[42U];
|
||||
@@ -62,12 +54,9 @@ private:
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
|
||||
void processNone(q15_t sample);
|
||||
void processHeader(q15_t sample);
|
||||
void processData();
|
||||
bool correlateFrameSync();
|
||||
bool correlateDataSync();
|
||||
void samplesToBits(const q15_t* inBuffer, uint16_t start, uint16_t count, uint8_t* outBuffer, uint16_t limit);
|
||||
void processNone(bool bit);
|
||||
void processHeader(bool bit);
|
||||
void processData(bool bit);
|
||||
void writeRSSIHeader(unsigned char* header);
|
||||
void writeRSSIData(unsigned char* data);
|
||||
bool rxHeader(uint8_t* in, uint8_t* out);
|
||||
|
||||
+10
-12
@@ -27,10 +27,8 @@ const uint8_t BIT_SYNC = 0xAAU;
|
||||
|
||||
const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U};
|
||||
|
||||
// Generated using gaussfir(0.35, 1, 10) in MATLAB
|
||||
static q15_t GAUSSIAN_0_35_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1001, 1942, 3514, 5930,
|
||||
9333, 13699, 18751, 23938, 28499, 31644, 32767, 31644,
|
||||
28499, 23938, 18751, 13699, 9333, 5930, 3514, 1942, 1001}; // numTaps = 30, L = 10
|
||||
// Generated using gaussfir(0.35, 1, 5) in MATLAB
|
||||
static q15_t GAUSSIAN_0_35_FILTER[] = {0, 0, 0, 0, 1001, 3514, 9333, 18751, 28499, 32767, 28499, 18751, 9333, 3514, 1001}; // numTaps = 15, L = 5
|
||||
const uint16_t GAUSSIAN_0_35_FILTER_PHASE_LEN = 3U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DSTAR_LEVEL0 = -841;
|
||||
@@ -198,9 +196,9 @@ m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(60U) // 100ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 15U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 20U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.L = DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.L = DSTAR_RADIO_BIT_LENGTH;
|
||||
m_modFilter.phaseLength = GAUSSIAN_0_35_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = GAUSSIAN_0_35_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
@@ -254,8 +252,8 @@ void CDStarTX::process()
|
||||
m_buffer.get();
|
||||
|
||||
for (uint8_t j = 0U; j < 3U; j++) {
|
||||
for (uint8_t i = 0U; i < DSTAR_END_SYNC_LENGTH_BYTES; i++)
|
||||
m_poBuffer[m_poLen++] = DSTAR_END_SYNC_BYTES[i];
|
||||
for (uint8_t i = 0U; i < DSTAR_EOT_LENGTH_BYTES; i++)
|
||||
m_poBuffer[m_poLen++] = DSTAR_EOT_BYTES[i];
|
||||
}
|
||||
|
||||
m_poPtr = 0U;
|
||||
@@ -264,11 +262,11 @@ void CDStarTX::process()
|
||||
if (m_poLen > 0U) {
|
||||
uint16_t space = io.getSpace();
|
||||
|
||||
while (space > (8U * DSTAR_RADIO_SYMBOL_LENGTH)) {
|
||||
while (space > (8U * DSTAR_RADIO_BIT_LENGTH)) {
|
||||
uint8_t c = m_poBuffer[m_poPtr++];
|
||||
writeByte(c);
|
||||
|
||||
space -= 8U * DSTAR_RADIO_SYMBOL_LENGTH;
|
||||
space -= 8U * DSTAR_RADIO_BIT_LENGTH;
|
||||
|
||||
if (m_poPtr >= m_poLen) {
|
||||
m_poPtr = 0U;
|
||||
@@ -414,7 +412,7 @@ void CDStarTX::txHeader(const uint8_t* in, uint8_t* out) const
|
||||
void CDStarTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[8U];
|
||||
q15_t outBuffer[DSTAR_RADIO_SYMBOL_LENGTH * 8U];
|
||||
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U];
|
||||
|
||||
uint8_t mask = 0x01U;
|
||||
|
||||
@@ -429,7 +427,7 @@ void CDStarTX::writeByte(uint8_t c)
|
||||
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 8U);
|
||||
|
||||
io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_SYMBOL_LENGTH * 8U);
|
||||
io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_BIT_LENGTH * 8U);
|
||||
}
|
||||
|
||||
void CDStarTX::setTXDelay(uint8_t delay)
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[15U]; // blockSize + phaseLength - 1, 8 + 3 - 1 plus some spare
|
||||
q15_t m_modState[20U]; // blockSize + phaseLength - 1, 8 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[600U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
@@ -48,8 +48,12 @@ enum MMDVM_STATE {
|
||||
STATE_DMR = 2,
|
||||
STATE_YSF = 3,
|
||||
STATE_P25 = 4,
|
||||
STATE_NXDN = 5,
|
||||
STATE_POCSAG = 6,
|
||||
|
||||
// Dummy states start at 90
|
||||
STATE_NXDNCAL1K = 91,
|
||||
STATE_DMRDMO1K = 92,
|
||||
STATE_P25CAL1K = 93,
|
||||
STATE_DMRCAL1K = 94,
|
||||
STATE_LFCAL = 95,
|
||||
@@ -71,10 +75,14 @@ enum MMDVM_STATE {
|
||||
#include "YSFTX.h"
|
||||
#include "P25RX.h"
|
||||
#include "P25TX.h"
|
||||
#include "NXDNRX.h"
|
||||
#include "NXDNTX.h"
|
||||
#include "POCSAGTX.h"
|
||||
#include "CalDStarRX.h"
|
||||
#include "CalDStarTX.h"
|
||||
#include "CalDMR.h"
|
||||
#include "CalP25.h"
|
||||
#include "CalNXDN.h"
|
||||
#include "CalRSSI.h"
|
||||
#include "CWIdTX.h"
|
||||
#include "Debug.h"
|
||||
@@ -86,8 +94,8 @@ const uint8_t MARK_NONE = 0x00U;
|
||||
|
||||
const uint16_t RX_BLOCK_SIZE = 2U;
|
||||
|
||||
const uint16_t TX_RINGBUFFER_SIZE = 1000U;
|
||||
const uint16_t RX_RINGBUFFER_SIZE = 1200U;
|
||||
const uint16_t TX_RINGBUFFER_SIZE = 500U;
|
||||
const uint16_t RX_RINGBUFFER_SIZE = 600U;
|
||||
|
||||
extern MMDVM_STATE m_modemState;
|
||||
|
||||
@@ -95,6 +103,8 @@ extern bool m_dstarEnable;
|
||||
extern bool m_dmrEnable;
|
||||
extern bool m_ysfEnable;
|
||||
extern bool m_p25Enable;
|
||||
extern bool m_nxdnEnable;
|
||||
extern bool m_pocsagEnable;
|
||||
|
||||
extern bool m_duplex;
|
||||
|
||||
@@ -120,10 +130,16 @@ extern CYSFTX ysfTX;
|
||||
extern CP25RX p25RX;
|
||||
extern CP25TX p25TX;
|
||||
|
||||
extern CNXDNRX nxdnRX;
|
||||
extern CNXDNTX nxdnTX;
|
||||
|
||||
extern CPOCSAGTX pocsagTX;
|
||||
|
||||
extern CCalDStarRX calDStarRX;
|
||||
extern CCalDStarTX calDStarTX;
|
||||
extern CCalDMR calDMR;
|
||||
extern CCalP25 calP25;
|
||||
extern CCalNXDN calNXDN;
|
||||
extern CCalRSSI calRSSI;
|
||||
|
||||
extern CCWIdTX cwIdTX;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
@@ -23,12 +23,34 @@
|
||||
#include "IO.h"
|
||||
|
||||
// Generated using [b, a] = butter(1, 0.001) in MATLAB
|
||||
static q31_t DC_FILTER[] = {1685306, 0, 1685306, 0, 2144113034, 0}; // {b0, 0, b1, b2, -a1, -a2}
|
||||
static q31_t DC_FILTER[] = {3367972, 0, 3367972, 0, 2140747704, 0}; // {b0, 0, b1, b2, -a1, -a2}
|
||||
const uint32_t DC_FILTER_STAGES = 1U; // One Biquad stage
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t RRC_0_2_FILTER_LEN = 42U;
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB
|
||||
static q15_t NXDN_0_2_FILTER[] = {284, 198, 73, -78, -240, -393, -517, -590, -599, -533, -391, -181, 79, 364, 643, 880, 1041, 1097, 1026, 819,
|
||||
483, 39, -477, -1016, -1516, -1915, -2150, -2164, -1914, -1375, -545, 557, 1886, 3376, 4946, 6502, 7946, 9184,
|
||||
10134, 10731, 10935, 10731, 10134, 9184, 7946, 6502, 4946, 3376, 1886, 557, -545, -1375, -1914, -2164, -2150,
|
||||
-1915, -1516, -1016, -477, 39, 483, 819, 1026, 1097, 1041, 880, 643, 364, 79, -181, -391, -533, -599, -590,
|
||||
-517, -393, -240, -78, 73, 198, 284, 0};
|
||||
const uint16_t NXDN_0_2_FILTER_LEN = 82U;
|
||||
|
||||
static q15_t NXDN_ISINC_FILTER[] = {790, -1085, -1073, -553, 747, 2341, 3156, 2152, -893, -4915, -7834, -7536, -3102, 4441, 12354, 17394, 17394,
|
||||
12354, 4441, -3102, -7536, -7834, -4915, -893, 2152, 3156, 2341, 747, -553, -1073, -1085, 790};
|
||||
const uint16_t NXDN_ISINC_FILTER_LEN = 32U;
|
||||
|
||||
// Generated using gaussfir(0.5, 4, 5) in MATLAB
|
||||
static q15_t GAUSSIAN_0_5_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
||||
const uint16_t GAUSSIAN_0_5_FILTER_LEN = 12U;
|
||||
|
||||
// One symbol boxcar filter
|
||||
static q15_t BOXCAR_FILTER[] = {6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 0, 0};
|
||||
const uint16_t BOXCAR_FILTER_LEN = 12U;
|
||||
static q15_t BOXCAR_FILTER[] = {12000, 12000, 12000, 12000, 12000, 0};
|
||||
const uint16_t BOXCAR_FILTER_LEN = 6U;
|
||||
|
||||
const uint16_t DC_OFFSET = 2048U;
|
||||
|
||||
@@ -39,8 +61,16 @@ m_txBuffer(TX_RINGBUFFER_SIZE),
|
||||
m_rssiBuffer(RX_RINGBUFFER_SIZE),
|
||||
m_dcFilter(),
|
||||
m_dcState(),
|
||||
m_rrcFilter(),
|
||||
m_gaussianFilter(),
|
||||
m_boxcarFilter(),
|
||||
m_nxdnFilter(),
|
||||
m_nxdnISincFilter(),
|
||||
m_rrcState(),
|
||||
m_gaussianState(),
|
||||
m_boxcarState(),
|
||||
m_nxdnState(),
|
||||
m_nxdnISincState(),
|
||||
m_pttInvert(false),
|
||||
m_rxLevel(128 * 128),
|
||||
m_cwIdTXLevel(128 * 128),
|
||||
@@ -48,6 +78,8 @@ m_dstarTXLevel(128 * 128),
|
||||
m_dmrTXLevel(128 * 128),
|
||||
m_ysfTXLevel(128 * 128),
|
||||
m_p25TXLevel(128 * 128),
|
||||
m_nxdnTXLevel(128 * 128),
|
||||
m_pocsagTXLevel(128 * 128),
|
||||
m_rxDCOffset(DC_OFFSET),
|
||||
m_txDCOffset(DC_OFFSET),
|
||||
m_ledCount(0U),
|
||||
@@ -58,18 +90,38 @@ m_dacOverflow(0U),
|
||||
m_watchdog(0U),
|
||||
m_lockout(false)
|
||||
{
|
||||
::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t));
|
||||
::memset(m_dcState, 0x00U, 4U * sizeof(q31_t));
|
||||
::memset(m_rrcState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t));
|
||||
::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t));
|
||||
::memset(m_nxdnState, 0x00U, 110U * sizeof(q15_t));
|
||||
::memset(m_nxdnISincState, 0x00U, 60U * sizeof(q15_t));
|
||||
::memset(m_dcState, 0x00U, 4U * sizeof(q31_t));
|
||||
|
||||
m_dcFilter.numStages = DC_FILTER_STAGES;
|
||||
m_dcFilter.pState = m_dcState;
|
||||
m_dcFilter.pCoeffs = DC_FILTER;
|
||||
m_dcFilter.postShift = 0;
|
||||
|
||||
m_rrcFilter.numTaps = RRC_0_2_FILTER_LEN;
|
||||
m_rrcFilter.pState = m_rrcState;
|
||||
m_rrcFilter.pCoeffs = RRC_0_2_FILTER;
|
||||
|
||||
m_gaussianFilter.numTaps = GAUSSIAN_0_5_FILTER_LEN;
|
||||
m_gaussianFilter.pState = m_gaussianState;
|
||||
m_gaussianFilter.pCoeffs = GAUSSIAN_0_5_FILTER;
|
||||
|
||||
m_boxcarFilter.numTaps = BOXCAR_FILTER_LEN;
|
||||
m_boxcarFilter.pState = m_boxcarState;
|
||||
m_boxcarFilter.pCoeffs = BOXCAR_FILTER;
|
||||
|
||||
m_nxdnFilter.numTaps = NXDN_0_2_FILTER_LEN;
|
||||
m_nxdnFilter.pState = m_nxdnState;
|
||||
m_nxdnFilter.pCoeffs = NXDN_0_2_FILTER;
|
||||
|
||||
m_nxdnISincFilter.numTaps = NXDN_ISINC_FILTER_LEN;
|
||||
m_nxdnISincFilter.pState = m_nxdnISincState;
|
||||
m_nxdnISincFilter.pCoeffs = NXDN_ISINC_FILTER;
|
||||
|
||||
initInt();
|
||||
|
||||
selfTest();
|
||||
@@ -85,20 +137,24 @@ void CIO::selfTest()
|
||||
// We exclude PTT to avoid trigger the transmitter
|
||||
setLEDInt(ledValue);
|
||||
setCOSInt(ledValue);
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
#if defined(MODE_PINS)
|
||||
setDStarInt(ledValue);
|
||||
setDMRInt(ledValue);
|
||||
setYSFInt(ledValue);
|
||||
setP25Int(ledValue);
|
||||
setNXDNInt(ledValue);
|
||||
setPOCSAGInt(ledValue);
|
||||
#endif
|
||||
delayInt(250);
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
#if defined(MODE_PINS)
|
||||
setDStarInt(true);
|
||||
setDMRInt(false);
|
||||
setYSFInt(false);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -106,6 +162,8 @@ void CIO::selfTest()
|
||||
setDMRInt(true);
|
||||
setYSFInt(false);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -113,6 +171,8 @@ void CIO::selfTest()
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -120,6 +180,44 @@ void CIO::selfTest()
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(true);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
setDStarInt(true);
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(true);
|
||||
setNXDNInt(true);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
setDStarInt(true);
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(true);
|
||||
setNXDNInt(true);
|
||||
setPOCSAGInt(true);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
setDStarInt(true);
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(true);
|
||||
setNXDNInt(true);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
setDStarInt(true);
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(true);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -127,6 +225,8 @@ void CIO::selfTest()
|
||||
setDMRInt(true);
|
||||
setYSFInt(true);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -134,6 +234,8 @@ void CIO::selfTest()
|
||||
setDMRInt(true);
|
||||
setYSFInt(false);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -141,6 +243,8 @@ void CIO::selfTest()
|
||||
setDMRInt(false);
|
||||
setYSFInt(false);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
|
||||
delayInt(250);
|
||||
|
||||
@@ -148,6 +252,8 @@ void CIO::selfTest()
|
||||
setDMRInt(false);
|
||||
setYSFInt(false);
|
||||
setP25Int(false);
|
||||
setNXDNInt(false);
|
||||
setPOCSAGInt(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -168,8 +274,8 @@ void CIO::process()
|
||||
m_ledCount++;
|
||||
if (m_started) {
|
||||
// Two seconds timeout
|
||||
if (m_watchdog >= 96000U) {
|
||||
if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF) {
|
||||
if (m_watchdog >= 48000U) {
|
||||
if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN || m_modemState == STATE_POCSAG) {
|
||||
if (m_modemState == STATE_DMR && m_tx)
|
||||
dmrTX.setStart(false);
|
||||
m_modemState = STATE_IDLE;
|
||||
@@ -179,13 +285,17 @@ void CIO::process()
|
||||
m_watchdog = 0U;
|
||||
}
|
||||
|
||||
if (m_ledCount >= 48000U) {
|
||||
#if defined(CONSTANT_SRV_LED)
|
||||
setLEDInt(true);
|
||||
#else
|
||||
if (m_ledCount >= 24000U) {
|
||||
m_ledCount = 0U;
|
||||
m_ledValue = !m_ledValue;
|
||||
setLEDInt(m_ledValue);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (m_ledCount >= 480000U) {
|
||||
if (m_ledCount >= 240000U) {
|
||||
m_ledCount = 0U;
|
||||
m_ledValue = !m_ledValue;
|
||||
setLEDInt(m_ledValue);
|
||||
@@ -225,65 +335,136 @@ void CIO::process()
|
||||
if (m_lockout)
|
||||
return;
|
||||
|
||||
q15_t vals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, samples, vals, RX_BLOCK_SIZE);
|
||||
#if defined(USE_DCBLOCKER)
|
||||
q31_t q31Samples[RX_BLOCK_SIZE];
|
||||
::arm_q15_to_q31(samples, q31Samples, RX_BLOCK_SIZE);
|
||||
|
||||
q31_t dcValues[RX_BLOCK_SIZE];
|
||||
::arm_biquad_cascade_df1_q31(&m_dcFilter, q31Samples, dcValues, RX_BLOCK_SIZE);
|
||||
|
||||
q31_t dcLevel = 0;
|
||||
q31_t dcValues[RX_BLOCK_SIZE];
|
||||
q31_t q31Vals[RX_BLOCK_SIZE];
|
||||
|
||||
::arm_q15_to_q31(vals, q31Vals, RX_BLOCK_SIZE);
|
||||
::arm_biquad_cascade_df1_q31(&m_dcFilter, q31Vals, dcValues, RX_BLOCK_SIZE);
|
||||
|
||||
for (uint8_t i = 0U; i < RX_BLOCK_SIZE; i++)
|
||||
dcLevel += dcValues[i];
|
||||
dcLevel /= RX_BLOCK_SIZE;
|
||||
|
||||
q15_t offset = q15_t(__SSAT((dcLevel >> 16), 16));;
|
||||
|
||||
q15_t dcVals[RX_BLOCK_SIZE];
|
||||
q15_t dcSamples[RX_BLOCK_SIZE];
|
||||
for (uint8_t i = 0U; i < RX_BLOCK_SIZE; i++)
|
||||
dcVals[i] = vals[i] - offset;
|
||||
dcSamples[i] = samples[i] - offset;
|
||||
#endif
|
||||
|
||||
if (m_modemState == STATE_IDLE) {
|
||||
if (m_dstarEnable)
|
||||
dstarRX.samples(dcVals, rssi, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_p25Enable)
|
||||
p25RX.samples(dcVals, rssi, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_dmrEnable) {
|
||||
if (m_duplex)
|
||||
dmrIdleRX.samples(vals, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrDMORX.samples(vals, rssi, RX_BLOCK_SIZE);
|
||||
if (m_dstarEnable) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, dcSamples, GMSKVals, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_ysfEnable)
|
||||
ysfRX.samples(dcVals, rssi, RX_BLOCK_SIZE);
|
||||
if (m_p25Enable) {
|
||||
q15_t P25Vals[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_nxdnEnable) {
|
||||
q15_t NXDNValsTmp[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_nxdnFilter, samples, NXDNValsTmp, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
q15_t NXDNVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE);
|
||||
|
||||
nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_dmrEnable || m_ysfEnable) {
|
||||
q15_t RRCVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_rrcFilter, samples, RRCVals, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_ysfEnable)
|
||||
ysfRX.samples(RRCVals, rssi, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_dmrEnable) {
|
||||
if (m_duplex)
|
||||
dmrIdleRX.samples(RRCVals, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
} else if (m_modemState == STATE_DSTAR) {
|
||||
if (m_dstarEnable)
|
||||
dstarRX.samples(dcVals, rssi, RX_BLOCK_SIZE);
|
||||
if (m_dstarEnable) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, dcSamples, GMSKVals, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DMR) {
|
||||
if (m_dmrEnable) {
|
||||
q15_t DMRVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_rrcFilter, samples, DMRVals, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_duplex) {
|
||||
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
|
||||
if (m_tx)
|
||||
dmrRX.samples(vals, rssi, control, RX_BLOCK_SIZE);
|
||||
dmrRX.samples(DMRVals, rssi, control, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrIdleRX.samples(vals, RX_BLOCK_SIZE);
|
||||
dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE);
|
||||
} else {
|
||||
dmrDMORX.samples(vals, rssi, RX_BLOCK_SIZE);
|
||||
dmrDMORX.samples(DMRVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
} else if (m_modemState == STATE_YSF) {
|
||||
if (m_ysfEnable)
|
||||
ysfRX.samples(dcVals, rssi, RX_BLOCK_SIZE);
|
||||
if (m_ysfEnable) {
|
||||
q15_t YSFVals[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_rrcFilter, dcSamples, YSFVals, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_rrcFilter, samples, YSFVals, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_P25) {
|
||||
if (m_p25Enable)
|
||||
p25RX.samples(dcVals, rssi, RX_BLOCK_SIZE);
|
||||
if (m_p25Enable) {
|
||||
q15_t P25Vals[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_NXDN) {
|
||||
if (m_nxdnEnable) {
|
||||
q15_t NXDNValsTmp[RX_BLOCK_SIZE];
|
||||
#if defined(USE_DCBLOCKER)
|
||||
::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE);
|
||||
#else
|
||||
::arm_fir_fast_q15(&m_nxdnFilter, samples, NXDNValsTmp, RX_BLOCK_SIZE);
|
||||
#endif
|
||||
q15_t NXDNVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE);
|
||||
|
||||
nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DSTARCAL) {
|
||||
calDStarRX.samples(vals, RX_BLOCK_SIZE);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
calDStarRX.samples(GMSKVals, RX_BLOCK_SIZE);
|
||||
} else if (m_modemState == STATE_RSSICAL) {
|
||||
calRSSI.samples(rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
@@ -318,6 +499,12 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t
|
||||
case STATE_P25:
|
||||
txLevel = m_p25TXLevel;
|
||||
break;
|
||||
case STATE_NXDN:
|
||||
txLevel = m_nxdnTXLevel;
|
||||
break;
|
||||
case STATE_POCSAG:
|
||||
txLevel = m_pocsagTXLevel;
|
||||
break;
|
||||
default:
|
||||
txLevel = m_cwIdTXLevel;
|
||||
break;
|
||||
@@ -359,24 +546,28 @@ void CIO::setADCDetection(bool detect)
|
||||
|
||||
void CIO::setMode()
|
||||
{
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
#if defined(MODE_PINS)
|
||||
setDStarInt(m_modemState == STATE_DSTAR);
|
||||
setDMRInt(m_modemState == STATE_DMR);
|
||||
setYSFInt(m_modemState == STATE_YSF);
|
||||
setP25Int(m_modemState == STATE_P25);
|
||||
setNXDNInt(m_modemState == STATE_NXDN);
|
||||
setPOCSAGInt(m_modemState == STATE_POCSAG);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, int16_t txDCOffset, int16_t rxDCOffset)
|
||||
void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, int16_t txDCOffset, int16_t rxDCOffset)
|
||||
{
|
||||
m_pttInvert = pttInvert;
|
||||
|
||||
m_rxLevel = q15_t(rxLevel * 128);
|
||||
m_cwIdTXLevel = q15_t(cwIdTXLevel * 128);
|
||||
m_dstarTXLevel = q15_t(dstarTXLevel * 128);
|
||||
m_dmrTXLevel = q15_t(dmrTXLevel * 128);
|
||||
m_ysfTXLevel = q15_t(ysfTXLevel * 128);
|
||||
m_p25TXLevel = q15_t(p25TXLevel * 128);
|
||||
m_rxLevel = q15_t(rxLevel * 128);
|
||||
m_cwIdTXLevel = q15_t(cwIdTXLevel * 128);
|
||||
m_dstarTXLevel = q15_t(dstarTXLevel * 128);
|
||||
m_dmrTXLevel = q15_t(dmrTXLevel * 128);
|
||||
m_ysfTXLevel = q15_t(ysfTXLevel * 128);
|
||||
m_p25TXLevel = q15_t(p25TXLevel * 128);
|
||||
m_nxdnTXLevel = q15_t(nxdnTXLevel * 128);
|
||||
m_pocsagTXLevel = q15_t(pocsagTXLevel * 128);
|
||||
|
||||
m_rxDCOffset = DC_OFFSET + rxDCOffset;
|
||||
m_txDCOffset = DC_OFFSET + txDCOffset;
|
||||
@@ -385,10 +576,12 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx
|
||||
m_rxLevel = -m_rxLevel;
|
||||
|
||||
if (txInvert) {
|
||||
m_dstarTXLevel = -m_dstarTXLevel;
|
||||
m_dmrTXLevel = -m_dmrTXLevel;
|
||||
m_ysfTXLevel = -m_ysfTXLevel;
|
||||
m_p25TXLevel = -m_p25TXLevel;
|
||||
m_dstarTXLevel = -m_dstarTXLevel;
|
||||
m_dmrTXLevel = -m_dmrTXLevel;
|
||||
m_ysfTXLevel = -m_ysfTXLevel;
|
||||
m_p25TXLevel = -m_p25TXLevel;
|
||||
m_nxdnTXLevel = -m_nxdnTXLevel;
|
||||
m_pocsagTXLevel = -m_pocsagTXLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
|
||||
void interrupt();
|
||||
|
||||
void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, int16_t txDCOffset, int16_t rxDCOffset);
|
||||
void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, int16_t txDCOffset, int16_t rxDCOffset);
|
||||
|
||||
void getOverflow(bool& adcOverflow, bool& dacOverflow);
|
||||
|
||||
@@ -66,8 +66,16 @@ private:
|
||||
arm_biquad_casd_df1_inst_q31 m_dcFilter;
|
||||
q31_t m_dcState[4];
|
||||
|
||||
arm_fir_instance_q15 m_rrcFilter;
|
||||
arm_fir_instance_q15 m_gaussianFilter;
|
||||
arm_fir_instance_q15 m_boxcarFilter;
|
||||
arm_fir_instance_q15 m_nxdnFilter;
|
||||
arm_fir_instance_q15 m_nxdnISincFilter;
|
||||
q15_t m_rrcState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
|
||||
q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare
|
||||
q15_t m_nxdnState[110U]; // NoTaps + BlockSize - 1, 82 + 20 - 1 plus some spare
|
||||
q15_t m_nxdnISincState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare
|
||||
|
||||
bool m_pttInvert;
|
||||
q15_t m_rxLevel;
|
||||
@@ -76,6 +84,8 @@ private:
|
||||
q15_t m_dmrTXLevel;
|
||||
q15_t m_ysfTXLevel;
|
||||
q15_t m_p25TXLevel;
|
||||
q15_t m_nxdnTXLevel;
|
||||
q15_t m_pocsagTXLevel;
|
||||
|
||||
uint16_t m_rxDCOffset;
|
||||
uint16_t m_txDCOffset;
|
||||
@@ -106,6 +116,8 @@ private:
|
||||
void setDMRInt(bool on);
|
||||
void setYSFInt(bool on);
|
||||
void setP25Int(bool on);
|
||||
void setNXDNInt(bool on);
|
||||
void setPOCSAGInt(bool on);
|
||||
|
||||
void delayInt(unsigned int dly);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
@@ -33,6 +33,8 @@
|
||||
#define PIN_DMR 17
|
||||
#define PIN_YSF 18
|
||||
#define PIN_P25 19
|
||||
#define PIN_NXDN 20
|
||||
#define PIN_POCSAG 4
|
||||
#define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7)
|
||||
#define ADC_ISR_EOC_Chan ADC_ISR_EOC7
|
||||
#define ADC_CDR_Chan 7
|
||||
@@ -46,6 +48,8 @@
|
||||
#define PIN_DMR 8
|
||||
#define PIN_YSF 7
|
||||
#define PIN_P25 6
|
||||
#define PIN_NXDN 5
|
||||
#define PIN_POCSAG 4
|
||||
#define ADC_CHER_Chan (1<<13) // ADC on Due pin A11 - Due AD13 - (1 << 13)
|
||||
#define ADC_ISR_EOC_Chan ADC_ISR_EOC13
|
||||
#define ADC_CDR_Chan 13
|
||||
@@ -61,6 +65,8 @@
|
||||
#define PIN_DMR 8
|
||||
#define PIN_YSF 7
|
||||
#define PIN_P25 6
|
||||
#define PIN_NXDN 5
|
||||
#define PIN_POCSAG 4
|
||||
#define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7)
|
||||
#define ADC_ISR_EOC_Chan ADC_ISR_EOC7
|
||||
#define ADC_CDR_Chan 7
|
||||
@@ -89,12 +95,14 @@ void CIO::initInt()
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
pinMode(PIN_COS, INPUT);
|
||||
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
#if defined(MODE_PINS)
|
||||
// Set up the mode output pins
|
||||
pinMode(PIN_DSTAR, OUTPUT);
|
||||
pinMode(PIN_DMR, OUTPUT);
|
||||
pinMode(PIN_YSF, OUTPUT);
|
||||
pinMode(PIN_P25, OUTPUT);
|
||||
pinMode(PIN_NXDN, OUTPUT);
|
||||
pinMode(PIN_POCSAG, OUTPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -140,11 +148,11 @@ void CIO::startInt()
|
||||
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
|
||||
TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR;
|
||||
#if defined(EXTERNAL_OSC)
|
||||
t->TC_RC = EXTERNAL_OSC / 48000; // Counter resets on RC, so sets period in terms of the external clock
|
||||
t->TC_RA = EXTERNAL_OSC / 96000; // Roughly square wave
|
||||
t->TC_RC = EXTERNAL_OSC / 24000; // Counter resets on RC, so sets period in terms of the external clock
|
||||
t->TC_RA = EXTERNAL_OSC / 48000; // Roughly square wave
|
||||
#else
|
||||
t->TC_RC = 875; // Counter resets on RC, so sets period in terms of 42MHz internal clock
|
||||
t->TC_RA = 438; // Roughly square wave
|
||||
t->TC_RC = 1750; // Counter resets on RC, so sets period in terms of 42MHz internal clock
|
||||
t->TC_RA = 880; // Roughly square wave
|
||||
#endif
|
||||
t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET; // Set clear and set from RA and RC compares
|
||||
t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; // re-enable local clocking and switch to hardware trigger source.
|
||||
@@ -226,9 +234,30 @@ void CIO::setP25Int(bool on)
|
||||
digitalWrite(PIN_P25, on ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void CIO::setNXDNInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_NXDN_LEDS)
|
||||
digitalWrite(PIN_YSF, on ? HIGH : LOW);
|
||||
digitalWrite(PIN_P25, on ? HIGH : LOW);
|
||||
#else
|
||||
digitalWrite(PIN_NXDN, on ? HIGH : LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setPOCSAGInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_POCSAG_LEDS)
|
||||
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
|
||||
digitalWrite(PIN_DMR, on ? HIGH : LOW);
|
||||
#else
|
||||
digitalWrite(PIN_POCSAG, on ? HIGH : LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::delayInt(unsigned int dly)
|
||||
{
|
||||
delay(dly);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
||||
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2017,2018 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
|
||||
@@ -37,6 +37,7 @@ DSTAR PD12 output P1 Pin44
|
||||
DMR PD13 output P1 Pin45
|
||||
YSF PD14 output P1 Pin46
|
||||
P25 PD11 output P1 Pin43
|
||||
NXDN PD10 output P1 Pin42
|
||||
|
||||
RX PA0 analog input P1 Pin12
|
||||
RSSI PA1 analog input P1 Pin11
|
||||
@@ -65,6 +66,10 @@ EXT_CLK PA15 input P2 Pin40
|
||||
#define PORT_P25 GPIOD
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_10
|
||||
#define PORT_NXDN GPIOD
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_12
|
||||
#define PORT_DSTAR GPIOD
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOD
|
||||
@@ -107,6 +112,7 @@ DSTAR PC7 output
|
||||
DMR PC8 output
|
||||
YSF PA8 output
|
||||
P25 PC9 output
|
||||
NXDN PB1 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA7 analog input
|
||||
@@ -135,6 +141,10 @@ EXT_CLK PA15 input
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_1
|
||||
#define PORT_NXDN GPIOB
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_7
|
||||
#define PORT_DSTAR GPIOC
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
||||
@@ -177,6 +187,7 @@ DSTAR PC7 output
|
||||
DMR PC8 output
|
||||
YSF PA8 output
|
||||
P25 PC9 output
|
||||
NXDN PB1 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA7 analog input
|
||||
@@ -205,6 +216,10 @@ EXT_CLK PA15 input
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_1
|
||||
#define PORT_NXDN GPIOB
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_7
|
||||
#define PORT_DSTAR GPIOC
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
||||
@@ -247,6 +262,7 @@ DSTAR PC7 output
|
||||
DMR PC8 output
|
||||
YSF PA8 output
|
||||
P25 PC9 output
|
||||
NXDN PB1 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA7 analog input
|
||||
@@ -275,6 +291,85 @@ EXT_CLK PA15 input
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_1
|
||||
#define PORT_NXDN GPIOB
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_7
|
||||
#define PORT_DSTAR GPIOC
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_DMR GPIO_Pin_8
|
||||
#define PORT_DMR GPIOC
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_YSF GPIO_Pin_8
|
||||
#define PORT_YSF GPIOA
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_EXT_CLK GPIO_Pin_15
|
||||
#define SRC_EXT_CLK GPIO_PinSource15
|
||||
#define PORT_EXT_CLK GPIOA
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PORT_RX GPIOA
|
||||
#define RCC_Per_RX RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_7
|
||||
#define PIN_RSSI_CH ADC_Channel_7
|
||||
#define PORT_RSSI GPIOA
|
||||
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_TX GPIO_Pin_4
|
||||
#define PIN_TX_CH DAC_Channel_1
|
||||
|
||||
#elif defined(STM32F722_F7M)
|
||||
/*
|
||||
Pin definitions for MMDVM-F7M Pi-Hat F0DEI board:
|
||||
|
||||
PTT PB13 output
|
||||
COSLED PB14 output
|
||||
LED PB15 output
|
||||
COS PC0 input
|
||||
|
||||
DSTAR PC7 output
|
||||
DMR PC8 output
|
||||
YSF PA8 output
|
||||
P25 PC9 output
|
||||
NXDN PB1 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA7 analog input
|
||||
TX PA4 analog output
|
||||
|
||||
EXT_CLK PA15 input
|
||||
*/
|
||||
|
||||
#define PIN_COS GPIO_Pin_0
|
||||
#define PORT_COS GPIOC
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_COSLED GPIO_Pin_14
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_LED GPIO_Pin_15
|
||||
#define PORT_LED GPIOB
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_P25 GPIO_Pin_9
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_1
|
||||
#define PORT_NXDN GPIOB
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_7
|
||||
#define PORT_DSTAR GPIOC
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
||||
@@ -319,11 +414,13 @@ DSTAR PB10 output CN10 Pin25
|
||||
DMR PB4 output CN10 Pin27
|
||||
YSF PB5 output CN10 Pin29
|
||||
P25 PB3 output CN10 Pin31
|
||||
NXDN PA10 output CN10 Pin33
|
||||
|
||||
MDSTAR PC4 output CN10 Pin34
|
||||
MDMR PC5 output CN10 Pin6
|
||||
MYSF PC2 output CN7 Pin35
|
||||
MP25 PC3 output CN7 Pin37
|
||||
MNXDN PC6 output CN10 Pin4
|
||||
|
||||
RX PA0 analog input CN7 Pin28
|
||||
RSSI PA1 analog input CN7 Pin30
|
||||
@@ -352,6 +449,10 @@ EXT_CLK PA15 input CN7 Pin17
|
||||
#define PORT_P25 GPIOB
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_10
|
||||
#define PORT_NXDN GPIOA
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_10
|
||||
#define PORT_DSTAR GPIOB
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
|
||||
@@ -369,6 +470,10 @@ EXT_CLK PA15 input CN7 Pin17
|
||||
#define PORT_MP25 GPIOC
|
||||
#define RCC_Per_MP25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_MNXDN GPIO_Pin_6
|
||||
#define PORT_MNXDN GPIOC
|
||||
#define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_MDSTAR GPIO_Pin_4
|
||||
#define PORT_MDSTAR GPIOC
|
||||
#define RCC_Per_MDSTAR RCC_AHB1Periph_GPIOC
|
||||
@@ -412,6 +517,7 @@ DSTAR PA1 output CN8 Pin2
|
||||
DMR PA4 output CN8 Pin3
|
||||
YSF PB0 output CN8 Pin4
|
||||
P25 PC1 output CN8 Pin5
|
||||
NXDN PA3 output CN9 Pin1
|
||||
|
||||
RX PA0 analog input CN8 Pin1
|
||||
RSSI PC0 analog input CN8 Pin6
|
||||
@@ -440,6 +546,10 @@ EXT_CLK PB8 input CN5 Pin10
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_3
|
||||
#define PORT_NXDN GPIOA
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_1
|
||||
#define PORT_DSTAR GPIOA
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOA
|
||||
@@ -486,11 +596,13 @@ DSTAR PB10 output CN12 Pin25
|
||||
DMR PB4 output CN12 Pin27
|
||||
YSF PB5 output CN12 Pin29
|
||||
P25 PB3 output CN12 Pin31
|
||||
NXDN PA10 output CN12 Pin33
|
||||
|
||||
MDSTAR PC4 output CN12 Pin34
|
||||
MDMR PC5 output CN12 Pin6
|
||||
MYSF PC2 output CN11 Pin35
|
||||
MP25 PC3 output CN11 Pin37
|
||||
MNXDN PC6 output CN12 Pin4
|
||||
|
||||
RX PA0 analog input CN11 Pin28
|
||||
RSSI PA1 analog input CN11 Pin30
|
||||
@@ -519,6 +631,10 @@ EXT_CLK PA15 input CN11 Pin17
|
||||
#define PORT_P25 GPIOB
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_10
|
||||
#define PORT_NXDN GPIOA
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_10
|
||||
#define PORT_DSTAR GPIOB
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
|
||||
@@ -536,6 +652,10 @@ EXT_CLK PA15 input CN11 Pin17
|
||||
#define PORT_MP25 GPIOC
|
||||
#define RCC_Per_MP25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_MNXDN GPIO_Pin_6
|
||||
#define PORT_MNXDN GPIOC
|
||||
#define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_MDSTAR GPIO_Pin_4
|
||||
#define PORT_MDSTAR GPIOC
|
||||
#define RCC_Per_MDSTAR RCC_AHB1Periph_GPIOC
|
||||
@@ -566,14 +686,90 @@ EXT_CLK PA15 input CN11 Pin17
|
||||
#define PIN_TX GPIO_Pin_4
|
||||
#define PIN_TX_CH DAC_Channel_1
|
||||
|
||||
#elif defined(STM32F4_DVM)
|
||||
/*
|
||||
Pin definitions for STM32F4 STM32-DVM rev 3 Board:
|
||||
|
||||
COS PB13 input
|
||||
PTT PB12 output
|
||||
COSLED PB4 output
|
||||
LED PB3 output
|
||||
|
||||
P25 PB8 output
|
||||
NXDN PB9 output
|
||||
DSTAR PB6 output
|
||||
DMR PB5 output
|
||||
YSF PB7 output
|
||||
|
||||
RX PB0 analog input
|
||||
RSSI PB1 analog input
|
||||
TX PA4 analog output
|
||||
|
||||
EXT_CLK PA15 input
|
||||
*/
|
||||
|
||||
#define PIN_COS GPIO_Pin_13
|
||||
#define PORT_COS GPIOB
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOB
|
||||
|
||||
|
||||
#define PIN_PTT GPIO_Pin_12
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_COSLED GPIO_Pin_4
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_LED GPIO_Pin_3
|
||||
#define PORT_LED GPIOB
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_P25 GPIO_Pin_8
|
||||
#define PORT_P25 GPIOB
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_NXDN GPIO_Pin_9
|
||||
#define PORT_NXDN GPIOB
|
||||
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_6
|
||||
#define PORT_DSTAR GPIOB
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DMR GPIO_Pin_5
|
||||
#define PORT_DMR GPIOB
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_YSF GPIO_Pin_7
|
||||
#define PORT_YSF GPIOB
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_EXT_CLK GPIO_Pin_15
|
||||
#define SRC_EXT_CLK GPIO_PinSource15
|
||||
#define PORT_EXT_CLK GPIOA
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_8
|
||||
#define PORT_RX GPIOB
|
||||
#define RCC_Per_RX RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_1
|
||||
#define PIN_RSSI_CH ADC_Channel_9
|
||||
#define PORT_RSSI GPIOB
|
||||
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_TX GPIO_Pin_4
|
||||
#define PIN_TX_CH DAC_Channel_1
|
||||
|
||||
#else
|
||||
#error "Either STM32F4_DISCOVERY, STM32F4_PI, STM32F722_PI, STM32F4_F4M, STM32F4_NUCLEO or STM32F7_NUCLEO need to be defined"
|
||||
#error "Either STM32F4_DISCOVERY, STM32F4_PI, STM32F722_PI, STM32F4_F4M, STM32F722_F7M, STM32F4_DVM, STM32F4_NUCLEO or STM32F7_NUCLEO need to be defined"
|
||||
#endif
|
||||
|
||||
const uint16_t DC_OFFSET = 2048U;
|
||||
|
||||
// Sampling frequency
|
||||
#define SAMP_FREQ 48000
|
||||
#define SAMP_FREQ 24000
|
||||
|
||||
extern "C" {
|
||||
void TIM2_IRQHandler() {
|
||||
@@ -616,7 +812,7 @@ void CIO::initInt()
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_Init(PORT_COS, &GPIO_InitStruct);
|
||||
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
#if defined(MODE_PINS)
|
||||
// DSTAR pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_DSTAR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_DSTAR;
|
||||
@@ -640,6 +836,12 @@ void CIO::initInt()
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_P25;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_P25, &GPIO_InitStruct);
|
||||
|
||||
// NXDN pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_NXDN, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_NXDN;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_NXDN, &GPIO_InitStruct);
|
||||
#endif
|
||||
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
@@ -666,6 +868,12 @@ void CIO::initInt()
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_MP25;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_MP25, &GPIO_InitStruct);
|
||||
|
||||
// NXDN mode pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_MNXDN, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_MNXDN;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_MNXDN, &GPIO_InitStruct);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -855,7 +1063,7 @@ void CIO::interrupt()
|
||||
|
||||
bool CIO::getCOSInt()
|
||||
{
|
||||
return GPIO_ReadOutputDataBit(PORT_COS, PIN_COS) == Bit_SET;
|
||||
return GPIO_ReadInputDataBit(PORT_COS, PIN_COS) == Bit_SET;
|
||||
}
|
||||
|
||||
void CIO::setLEDInt(bool on)
|
||||
@@ -905,6 +1113,35 @@ void CIO::setP25Int(bool on)
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setNXDNInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_NXDN_LEDS)
|
||||
GPIO_WriteBit(PORT_YSF, PIN_YSF, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_P25, PIN_P25, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MYSF, PIN_MYSF, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_MP25, PIN_MP25, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
#else
|
||||
GPIO_WriteBit(PORT_NXDN, PIN_NXDN, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MNXDN, PIN_MNXDN, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setPOCSAGInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_POCSAG_LEDS)
|
||||
GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_DMR, PIN_DMR, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MDSTAR, PIN_MDSTAR, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_MDMR, PIN_MDMR, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Simple delay function for STM32
|
||||
// Example from: http://thehackerworkshop.com/?p=1209
|
||||
void CIO::delayInt(unsigned int dly)
|
||||
|
||||
+39
-9
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
||||
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2017 by Wojciech Krutnik N0CALL
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -39,6 +39,8 @@ DSTAR PB7 output
|
||||
DMR PB6 output
|
||||
YSF PB8 output
|
||||
P25 PB9 output
|
||||
NXDN PB10 output
|
||||
POCSAG PB11 output
|
||||
|
||||
RX PB0 analog input (ADC1_8)
|
||||
RSSI PB1 analog input (ADC2_9)
|
||||
@@ -76,6 +78,12 @@ USART1_RXD PA10 input (AF)
|
||||
#define PIN_P25 9
|
||||
#define PORT_P25 GPIOB
|
||||
#define BB_P25 *((bitband_t)BITBAND_PERIPH(&PORT_P25->ODR, PIN_P25))
|
||||
#define PIN_NXDN 10
|
||||
#define PORT_NXDN GPIOB
|
||||
#define BB_NXDN *((bitband_t)BITBAND_PERIPH(&PORT_NXDN->ODR, PIN_NXDN))
|
||||
#define PIN_POCSAG 11
|
||||
#define PORT_POCSAG GPIOB
|
||||
#define BB_POCSAG *((bitband_t)BITBAND_PERIPH(&PORT_POCSAG->ODR, PIN_POCSAG))
|
||||
|
||||
#define PIN_RX 0
|
||||
#define PIN_RX_ADC_CH 8
|
||||
@@ -103,7 +111,7 @@ USART1_RXD PA10 input (AF)
|
||||
const uint16_t DC_OFFSET = 2048U;
|
||||
|
||||
// Sampling frequency
|
||||
#define SAMP_FREQ 48000
|
||||
#define SAMP_FREQ 24000
|
||||
|
||||
extern "C" {
|
||||
void TIM2_IRQHandler() {
|
||||
@@ -205,15 +213,17 @@ static inline void GPIOInit()
|
||||
GPIOD->ODR = 0;
|
||||
|
||||
// configure ports
|
||||
GPIOConfigPin(PORT_PTT, PIN_PTT, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_PTT, PIN_PTT, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_COSLED, PIN_COSLED, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_LED, PIN_LED, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_COS, PIN_COS, GPIO_CRL_CNF0_1);
|
||||
GPIOConfigPin(PORT_LED, PIN_LED, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_COS, PIN_COS, GPIO_CRL_CNF0_1);
|
||||
|
||||
GPIOConfigPin(PORT_DSTAR, PIN_DSTAR, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_DMR, PIN_DMR, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_YSF, PIN_YSF, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_P25, PIN_P25, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_DSTAR, PIN_DSTAR, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_DMR, PIN_DMR, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_YSF, PIN_YSF, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_P25, PIN_P25, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_NXDN, PIN_NXDN, GPIO_CRL_MODE0_1);
|
||||
GPIOConfigPin(PORT_POCSAG, PIN_POCSAG, GPIO_CRL_MODE0_1);
|
||||
|
||||
GPIOConfigPin(PORT_RX, PIN_RX, 0);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
@@ -424,6 +434,26 @@ void CIO::setP25Int(bool on)
|
||||
BB_P25 = !!on;
|
||||
}
|
||||
|
||||
void CIO::setNXDNInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_NXDN_LEDS)
|
||||
BB_YSF = !!on;
|
||||
BB_P25 = !!on;
|
||||
#else
|
||||
BB_NXDN = !!on;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setPOCSAGInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_POCSAG_LEDS)
|
||||
BB_DSTAR = !!on;
|
||||
BB_DMR = !!on;
|
||||
#else
|
||||
BB_POCSAG = !!on;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::delayInt(unsigned int dly)
|
||||
{
|
||||
delay(dly);
|
||||
|
||||
+35
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017,2018 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
|
||||
@@ -34,6 +34,13 @@
|
||||
#define PIN_DMR 10
|
||||
#define PIN_YSF 11
|
||||
#define PIN_P25 12
|
||||
#if defined(__MK20DX256__)
|
||||
#define PIN_NXDN 2
|
||||
#define PIN_POCSAG 3
|
||||
#else
|
||||
#define PIN_NXDN 24
|
||||
#define PIN_POCSAG 25
|
||||
#endif
|
||||
#define PIN_ADC 5 // A0, Pin 14
|
||||
#define PIN_RSSI 4 // Teensy 3.5/3.6, A16, Pin 35. Teensy 3.1/3.2, A17, Pin 28
|
||||
|
||||
@@ -57,12 +64,14 @@ void CIO::initInt()
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
pinMode(PIN_COS, INPUT);
|
||||
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
#if defined(MODE_PINS)
|
||||
// Set up the mode output pins
|
||||
pinMode(PIN_DSTAR, OUTPUT);
|
||||
pinMode(PIN_DMR, OUTPUT);
|
||||
pinMode(PIN_YSF, OUTPUT);
|
||||
pinMode(PIN_P25, OUTPUT);
|
||||
pinMode(PIN_NXDN, OUTPUT);
|
||||
pinMode(PIN_POCSAG, OUTPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -112,7 +121,7 @@ void CIO::startInt()
|
||||
#endif
|
||||
|
||||
#if defined(EXTERNAL_OSC)
|
||||
// Set ADC0 to trigger from the LPTMR at 48 kHz
|
||||
// Set ADC0 to trigger from the LPTMR at 24 kHz
|
||||
SIM_SOPT7 = SIM_SOPT7_ADC0ALTTRGEN | // Enable ADC0 alternate trigger
|
||||
SIM_SOPT7_ADC0TRGSEL(14); // Trigger ADC0 by LPTMR0
|
||||
|
||||
@@ -121,14 +130,14 @@ void CIO::startInt()
|
||||
SIM_SCGC5 |= SIM_SCGC5_LPTIMER; // Enable Low Power Timer Access
|
||||
LPTMR0_CSR = 0; // Disable
|
||||
LPTMR0_PSR = LPTMR_PSR_PBYP; // Bypass prescaler/filter
|
||||
LPTMR0_CMR = (EXTERNAL_OSC / 48000) - 1; // Frequency divided by CMR + 1
|
||||
LPTMR0_CMR = (EXTERNAL_OSC / 24000) - 1; // Frequency divided by CMR + 1
|
||||
LPTMR0_CSR = LPTMR_CSR_TPS(2) | // Pin: 0=CMP0, 1=xtal, 2=pin13
|
||||
LPTMR_CSR_TMS; // Mode Select, 0=timer, 1=counter
|
||||
LPTMR0_CSR |= LPTMR_CSR_TEN; // Enable
|
||||
#else
|
||||
// Setup PDB for ADC0 at 48 kHz
|
||||
// Setup PDB for ADC0 at 24 kHz
|
||||
SIM_SCGC6 |= SIM_SCGC6_PDB; // Enable PDB clock
|
||||
PDB0_MOD = (F_BUS / 48000) - 1; // Timer period - 1
|
||||
PDB0_MOD = (F_BUS / 24000) - 1; // Timer period - 1
|
||||
PDB0_IDLY = 0; // Interrupt delay
|
||||
PDB0_CH0C1 = PDB_CHnC1_TOS | PDB_CHnC1_EN; // Enable pre-trigger for ADC0
|
||||
PDB0_SC = PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | // SW trigger, enable interrupts, continuous mode
|
||||
@@ -210,6 +219,26 @@ void CIO::setP25Int(bool on)
|
||||
digitalWrite(PIN_P25, on ? HIGH : LOW);
|
||||
}
|
||||
|
||||
void CIO::setNXDNInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_NXDN_LEDS)
|
||||
digitalWrite(PIN_YSF, on ? HIGH : LOW);
|
||||
digitalWrite(PIN_P25, on ? HIGH : LOW);
|
||||
#else
|
||||
digitalWrite(PIN_NXDN, on ? HIGH : LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setPOCSAGInt(bool on)
|
||||
{
|
||||
#if defined(USE_ALTERNATE_POCSAG_LEDS)
|
||||
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
|
||||
digitalWrite(PIN_DMR, on ? HIGH : LOW);
|
||||
#else
|
||||
digitalWrite(PIN_POCSAG, on ? HIGH : LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::delayInt(unsigned int dly)
|
||||
{
|
||||
delay(dly);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Mathis Schmieder DB9MAT
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
@@ -26,10 +26,12 @@
|
||||
// Global variables
|
||||
MMDVM_STATE m_modemState = STATE_IDLE;
|
||||
|
||||
bool m_dstarEnable = true;
|
||||
bool m_dmrEnable = true;
|
||||
bool m_ysfEnable = true;
|
||||
bool m_p25Enable = true;
|
||||
bool m_dstarEnable = true;
|
||||
bool m_dmrEnable = true;
|
||||
bool m_ysfEnable = true;
|
||||
bool m_p25Enable = true;
|
||||
bool m_nxdnEnable = true;
|
||||
bool m_pocsagEnable = true;
|
||||
|
||||
bool m_duplex = true;
|
||||
|
||||
@@ -52,10 +54,16 @@ CYSFTX ysfTX;
|
||||
CP25RX p25RX;
|
||||
CP25TX p25TX;
|
||||
|
||||
CNXDNRX nxdnRX;
|
||||
CNXDNTX nxdnTX;
|
||||
|
||||
CPOCSAGTX pocsagTX;
|
||||
|
||||
CCalDStarRX calDStarRX;
|
||||
CCalDStarTX calDStarTX;
|
||||
CCalDMR calDMR;
|
||||
CCalP25 calP25;
|
||||
CCalNXDN calNXDN;
|
||||
CCalRSSI calRSSI;
|
||||
|
||||
CCWIdTX cwIdTX;
|
||||
@@ -91,15 +99,24 @@ void loop()
|
||||
if (m_p25Enable && m_modemState == STATE_P25)
|
||||
p25TX.process();
|
||||
|
||||
if (m_nxdnEnable && m_modemState == STATE_NXDN)
|
||||
nxdnTX.process();
|
||||
|
||||
if (m_pocsagEnable && m_modemState == STATE_POCSAG)
|
||||
pocsagTX.process();
|
||||
|
||||
if (m_modemState == STATE_DSTARCAL)
|
||||
calDStarTX.process();
|
||||
|
||||
if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K)
|
||||
if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K)
|
||||
calDMR.process();
|
||||
|
||||
if (m_modemState == STATE_P25CAL1K)
|
||||
calP25.process();
|
||||
|
||||
if (m_modemState == STATE_NXDNCAL1K)
|
||||
calNXDN.process();
|
||||
|
||||
if (m_modemState == STATE_IDLE)
|
||||
cwIdTX.process();
|
||||
}
|
||||
@@ -113,4 +130,3 @@ int main()
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,10 +23,12 @@
|
||||
// Global variables
|
||||
MMDVM_STATE m_modemState = STATE_IDLE;
|
||||
|
||||
bool m_dstarEnable = true;
|
||||
bool m_dmrEnable = true;
|
||||
bool m_ysfEnable = true;
|
||||
bool m_p25Enable = true;
|
||||
bool m_dstarEnable = true;
|
||||
bool m_dmrEnable = true;
|
||||
bool m_ysfEnable = true;
|
||||
bool m_p25Enable = true;
|
||||
bool m_nxdnEnable = true;
|
||||
bool m_pocsagEnable = true;
|
||||
|
||||
bool m_duplex = true;
|
||||
|
||||
@@ -49,10 +51,16 @@ CYSFTX ysfTX;
|
||||
CP25RX p25RX;
|
||||
CP25TX p25TX;
|
||||
|
||||
CNXDNRX nxdnRX;
|
||||
CNXDNTX nxdnTX;
|
||||
|
||||
CPOCSAGTX pocsagTX;
|
||||
|
||||
CCalDStarRX calDStarRX;
|
||||
CCalDStarTX calDStarTX;
|
||||
CCalDMR calDMR;
|
||||
CCalP25 calP25;
|
||||
CCalNXDN calNXDN;
|
||||
CCalRSSI calRSSI;
|
||||
|
||||
CCWIdTX cwIdTX;
|
||||
@@ -88,15 +96,24 @@ void loop()
|
||||
if (m_p25Enable && m_modemState == STATE_P25)
|
||||
p25TX.process();
|
||||
|
||||
if (m_nxdnEnable && m_modemState == STATE_NXDN)
|
||||
nxdnTX.process();
|
||||
|
||||
if (m_pocsagEnable && m_modemState == STATE_POCSAG)
|
||||
pocsagTX.process();
|
||||
|
||||
if (m_modemState == STATE_DSTARCAL)
|
||||
calDStarTX.process();
|
||||
|
||||
if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K)
|
||||
if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K)
|
||||
calDMR.process();
|
||||
|
||||
if (m_modemState == STATE_P25CAL1K)
|
||||
calP25.process();
|
||||
|
||||
if (m_modemState == STATE_NXDNCAL1K)
|
||||
calNXDN.process();
|
||||
|
||||
if (m_modemState == STATE_IDLE)
|
||||
cwIdTX.process();
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<Define name="STM32F446xx"/>
|
||||
<Define name="HSE_VALUE=12000000"/>
|
||||
<Define name="STM32F4_PI"/>
|
||||
<Define name="ARDUINO_MODE_PINS"/>
|
||||
<Define name="MODE_PINS"/>
|
||||
</DefinedSymbols>
|
||||
</Compile>
|
||||
<Link useDefault="0">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2016,2017 by Andy Uribe CA6JAU
|
||||
# Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
|
||||
# Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -126,6 +126,10 @@ DEFS_NUCLEO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_NUCLEO -D
|
||||
DEFS_NUCLEO_F767=-DUSE_HAL_DRIVER -DSTM32F767xx -DSTM32F7XX -DSTM32F7_NUCLEO -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
# MMDVM-Pi F722 board:
|
||||
DEFS_PI_F722=-DUSE_HAL_DRIVER -DSTM32F722xx -DSTM32F7XX -DSTM32F722_PI -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
# MMDVM-F7M F0DEI board:
|
||||
DEFS_F7M=-DUSE_HAL_DRIVER -DSTM32F722xx -DSTM32F7XX -DSTM32F722_F7M -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
# STM32F4 DVM board:
|
||||
DEFS_DVM=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_DVM -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
|
||||
# Build compiler flags
|
||||
CFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4)
|
||||
@@ -144,7 +148,7 @@ CXXFLAGS=-Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -f
|
||||
LDFLAGS=-Os --specs=nano.specs
|
||||
|
||||
# Build Rules
|
||||
.PHONY: all release dis pi pi_f722 f4m nucleo f767 clean
|
||||
.PHONY: all release dis pi pi_f722 f4m nucleo f767 dvm clean
|
||||
|
||||
# Default target: Nucleo-64 F446RE board
|
||||
all: nucleo
|
||||
@@ -167,6 +171,12 @@ f4m: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_F4M)
|
||||
f4m: LDFLAGS+=$(LDFLAGS_F4)
|
||||
f4m: release_f4
|
||||
|
||||
f7m: GitVersion.h
|
||||
f7m: CFLAGS+=$(CFLAGS_F7) $(DEFS_F7M)
|
||||
f7m: CXXFLAGS+=$(CXXFLAGS_F7) $(DEFS_F7M)
|
||||
f7m: LDFLAGS+=$(LDFLAGS_F722)
|
||||
f7m: release_f7
|
||||
|
||||
nucleo: GitVersion.h
|
||||
nucleo: CFLAGS+=$(CFLAGS_F4) $(DEFS_NUCLEO)
|
||||
nucleo: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_NUCLEO)
|
||||
@@ -185,6 +195,12 @@ f767: CXXFLAGS+=$(CXXFLAGS_F7) $(DEFS_NUCLEO_F767)
|
||||
f767: LDFLAGS+=$(LDFLAGS_F767)
|
||||
f767: release_f7
|
||||
|
||||
dvm: GitVersion.h
|
||||
dvm: CFLAGS+=$(CFLAGS_F4) $(DEFS_DVM)
|
||||
dvm: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_DVM)
|
||||
dvm: LDFLAGS+=$(LDFLAGS_F4)
|
||||
dvm: release_f4
|
||||
|
||||
release_f4: $(BINDIR)
|
||||
release_f4: $(OBJDIR_F4)
|
||||
release_f4: $(BINDIR)/$(BINHEX_F4)
|
||||
@@ -305,6 +321,7 @@ ifneq ($(wildcard /usr/bin/stm32flash),)
|
||||
endif
|
||||
|
||||
deploy-f4m: deploy-pi
|
||||
deploy-dvm: deploy-pi
|
||||
|
||||
deploy-pi-f7:
|
||||
ifneq ($(wildcard /usr/local/bin/stm32flash),)
|
||||
@@ -319,6 +336,8 @@ ifneq ($(wildcard /usr/bin/stm32flash),)
|
||||
/usr/bin/stm32flash -v -w bin/$(BINBIN_F7) -g 0x0 -R -c /dev/ttyAMA0
|
||||
endif
|
||||
|
||||
deploy-f7m: deploy-pi-f7
|
||||
|
||||
# Export the current git version if the index file exists, else 000...
|
||||
GitVersion.h:
|
||||
ifdef SYSTEMROOT
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016,2017,2018 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(NXDNDEFINES_H)
|
||||
#define NXDNDEFINES_H
|
||||
|
||||
const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 10U; // At 24 kHz sample rate
|
||||
|
||||
const unsigned int NXDN_FRAME_LENGTH_BITS = 384U;
|
||||
const unsigned int NXDN_FRAME_LENGTH_BYTES = NXDN_FRAME_LENGTH_BITS / 8U;
|
||||
const unsigned int NXDN_FRAME_LENGTH_SYMBOLS = NXDN_FRAME_LENGTH_BITS / 2U;
|
||||
const unsigned int NXDN_FRAME_LENGTH_SAMPLES = NXDN_FRAME_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int NXDN_FSW_LENGTH_BITS = 20U;
|
||||
const unsigned int NXDN_FSW_LENGTH_SYMBOLS = NXDN_FSW_LENGTH_BITS / 2U;
|
||||
const unsigned int NXDN_FSW_LENGTH_SAMPLES = NXDN_FSW_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const uint8_t NXDN_FSW_BYTES[] = {0xCDU, 0xF5U, 0x90U};
|
||||
const uint8_t NXDN_FSW_BYTES_MASK[] = {0xFFU, 0xFFU, 0xF0U};
|
||||
const uint8_t NXDN_FSW_BYTES_LENGTH = 3U;
|
||||
|
||||
const uint32_t NXDN_FSW_BITS = 0x000CDF59U;
|
||||
const uint32_t NXDN_FSW_BITS_MASK = 0x000FFFFFU;
|
||||
|
||||
// C D F 5 9
|
||||
// 11 00 11 01 11 11 01 01 10 01
|
||||
// -3 +1 -3 +3 -3 -3 +3 +3 -1 +3
|
||||
|
||||
const int8_t NXDN_FSW_SYMBOLS_VALUES[] = {-3, +1, -3, +3, -3, -3, +3, +3, -1, +3};
|
||||
|
||||
const uint16_t NXDN_FSW_SYMBOLS = 0x014DU;
|
||||
const uint16_t NXDN_FSW_SYMBOLS_MASK = 0x03FFU;
|
||||
|
||||
#endif
|
||||
|
||||
+405
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2018 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "NXDNRX.h"
|
||||
#include "Utils.h"
|
||||
|
||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.55)
|
||||
|
||||
const uint8_t MAX_FSW_BIT_START_ERRS = 1U;
|
||||
const uint8_t MAX_FSW_BIT_RUN_ERRS = 3U;
|
||||
|
||||
const uint8_t MAX_FSW_SYMBOLS_ERRS = 2U;
|
||||
|
||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint8_t NOAVEPTR = 99U;
|
||||
|
||||
const uint16_t NOENDPTR = 9999U;
|
||||
|
||||
const unsigned int MAX_FSW_FRAMES = 5U + 1U;
|
||||
|
||||
CNXDNRX::CNXDNRX() :
|
||||
m_state(NXDNRXS_NONE),
|
||||
m_bitBuffer(),
|
||||
m_buffer(),
|
||||
m_bitPtr(0U),
|
||||
m_dataPtr(0U),
|
||||
m_startPtr(NOENDPTR),
|
||||
m_endPtr(NOENDPTR),
|
||||
m_fswPtr(NOENDPTR),
|
||||
m_minFSWPtr(NOENDPTR),
|
||||
m_maxFSWPtr(NOENDPTR),
|
||||
m_maxCorr(0),
|
||||
m_lostCount(0U),
|
||||
m_countdown(0U),
|
||||
m_centre(),
|
||||
m_centreVal(0),
|
||||
m_threshold(),
|
||||
m_thresholdVal(0),
|
||||
m_averagePtr(NOAVEPTR),
|
||||
m_rssiAccum(0U),
|
||||
m_rssiCount(0U)
|
||||
{
|
||||
}
|
||||
|
||||
void CNXDNRX::reset()
|
||||
{
|
||||
m_state = NXDNRXS_NONE;
|
||||
m_dataPtr = 0U;
|
||||
m_bitPtr = 0U;
|
||||
m_maxCorr = 0;
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_startPtr = NOENDPTR;
|
||||
m_endPtr = NOENDPTR;
|
||||
m_fswPtr = NOENDPTR;
|
||||
m_minFSWPtr = NOENDPTR;
|
||||
m_maxFSWPtr = NOENDPTR;
|
||||
m_centreVal = 0;
|
||||
m_thresholdVal = 0;
|
||||
m_lostCount = 0U;
|
||||
m_countdown = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CNXDNRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
|
||||
{
|
||||
for (uint8_t i = 0U; i < length; i++) {
|
||||
q15_t sample = samples[i];
|
||||
|
||||
m_rssiAccum += rssi[i];
|
||||
m_rssiCount++;
|
||||
|
||||
m_bitBuffer[m_bitPtr] <<= 1;
|
||||
if (sample < 0)
|
||||
m_bitBuffer[m_bitPtr] |= 0x01U;
|
||||
|
||||
m_buffer[m_dataPtr] = sample;
|
||||
|
||||
switch (m_state) {
|
||||
case NXDNRXS_DATA:
|
||||
processData(sample);
|
||||
break;
|
||||
default:
|
||||
processNone(sample);
|
||||
break;
|
||||
}
|
||||
|
||||
m_dataPtr++;
|
||||
if (m_dataPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
m_dataPtr = 0U;
|
||||
|
||||
m_bitPtr++;
|
||||
if (m_bitPtr >= NXDN_RADIO_SYMBOL_LENGTH)
|
||||
m_bitPtr = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CNXDNRX::processNone(q15_t sample)
|
||||
{
|
||||
bool ret = correlateFSW();
|
||||
if (ret) {
|
||||
// On the first sync, start the countdown to the state change
|
||||
if (m_countdown == 0U) {
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
m_averagePtr = NOAVEPTR;
|
||||
|
||||
m_countdown = 5U;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_countdown > 0U)
|
||||
m_countdown--;
|
||||
|
||||
if (m_countdown == 1U) {
|
||||
m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxFSWPtr = m_fswPtr + 1U;
|
||||
if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_state = NXDNRXS_DATA;
|
||||
m_countdown = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CNXDNRX::processData(q15_t sample)
|
||||
{
|
||||
if (m_minFSWPtr < m_maxFSWPtr) {
|
||||
if (m_dataPtr >= m_minFSWPtr && m_dataPtr <= m_maxFSWPtr)
|
||||
correlateFSW();
|
||||
} else {
|
||||
if (m_dataPtr >= m_minFSWPtr || m_dataPtr <= m_maxFSWPtr)
|
||||
correlateFSW();
|
||||
}
|
||||
|
||||
if (m_dataPtr == m_endPtr) {
|
||||
// Only update the centre and threshold if they are from a good sync
|
||||
if (m_lostCount == MAX_FSW_FRAMES) {
|
||||
m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxFSWPtr = m_fswPtr + 1U;
|
||||
if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
calculateLevels(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("NXDNRX: sync found pos/centre/threshold", m_fswPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 3U];
|
||||
samplesToBits(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
||||
m_lostCount--;
|
||||
if (m_lostCount == 0U) {
|
||||
DEBUG1("NXDNRX: sync timed out, lost lock");
|
||||
|
||||
io.setDecode(false);
|
||||
io.setADCDetection(false);
|
||||
|
||||
serial.writeNXDNLost();
|
||||
|
||||
m_state = NXDNRXS_NONE;
|
||||
m_endPtr = NOENDPTR;
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_countdown = 0U;
|
||||
m_maxCorr = 0;
|
||||
} else {
|
||||
frame[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U;
|
||||
writeRSSIData(frame);
|
||||
m_maxCorr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CNXDNRX::correlateFSW()
|
||||
{
|
||||
if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_FSW_SYMBOLS_MASK) ^ NXDN_FSW_SYMBOLS) <= MAX_FSW_SYMBOLS_ERRS) {
|
||||
uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
ptr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
q31_t corr = 0;
|
||||
q15_t min = 16000;
|
||||
q15_t max = -16000;
|
||||
|
||||
for (uint8_t i = 0U; i < NXDN_FSW_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_buffer[ptr];
|
||||
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
|
||||
switch (NXDN_FSW_SYMBOLS_VALUES[i]) {
|
||||
case +3:
|
||||
corr -= (val + val + val);
|
||||
break;
|
||||
case +1:
|
||||
corr -= val;
|
||||
break;
|
||||
case -1:
|
||||
corr += val;
|
||||
break;
|
||||
default: // -3
|
||||
corr += (val + val + val);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += NXDN_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
ptr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
if (corr > m_maxCorr) {
|
||||
if (m_averagePtr == NOAVEPTR) {
|
||||
m_centreVal = (max + min) >> 1;
|
||||
|
||||
q31_t v1 = (max - m_centreVal) * SCALING_FACTOR;
|
||||
m_thresholdVal = q15_t(v1 >> 15);
|
||||
}
|
||||
|
||||
uint16_t startPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH;
|
||||
if (startPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
startPtr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
uint8_t sync[NXDN_FSW_BYTES_LENGTH];
|
||||
samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t maxErrs;
|
||||
if (m_state == NXDNRXS_NONE)
|
||||
maxErrs = MAX_FSW_BIT_START_ERRS;
|
||||
else
|
||||
maxErrs = MAX_FSW_BIT_RUN_ERRS;
|
||||
|
||||
uint8_t errs = 0U;
|
||||
for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++)
|
||||
errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]);
|
||||
|
||||
if (errs <= maxErrs) {
|
||||
m_maxCorr = corr;
|
||||
m_lostCount = MAX_FSW_FRAMES;
|
||||
m_fswPtr = m_dataPtr;
|
||||
|
||||
m_startPtr = startPtr;
|
||||
|
||||
m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES - 1U;
|
||||
if (m_endPtr >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
m_endPtr -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CNXDNRX::calculateLevels(uint16_t start, uint16_t count)
|
||||
{
|
||||
q15_t maxPos = -16000;
|
||||
q15_t minPos = 16000;
|
||||
q15_t maxNeg = 16000;
|
||||
q15_t minNeg = -16000;
|
||||
|
||||
for (uint16_t i = 0U; i < count; i++) {
|
||||
q15_t sample = m_buffer[start];
|
||||
|
||||
if (sample > 0) {
|
||||
if (sample > maxPos)
|
||||
maxPos = sample;
|
||||
if (sample < minPos)
|
||||
minPos = sample;
|
||||
} else {
|
||||
if (sample < maxNeg)
|
||||
maxNeg = sample;
|
||||
if (sample > minNeg)
|
||||
minNeg = sample;
|
||||
}
|
||||
|
||||
start += NXDN_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
start -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
q15_t posThresh = (maxPos + minPos) >> 1;
|
||||
q15_t negThresh = (maxNeg + minNeg) >> 1;
|
||||
|
||||
q15_t centre = (posThresh + negThresh) >> 1;
|
||||
|
||||
q15_t threshold = posThresh - centre;
|
||||
|
||||
DEBUG5("NXDNRX: pos/neg/centre/threshold", posThresh, negThresh, centre, threshold);
|
||||
|
||||
if (m_averagePtr == NOAVEPTR) {
|
||||
for (uint8_t i = 0U; i < 16U; i++) {
|
||||
m_centre[i] = centre;
|
||||
m_threshold[i] = threshold;
|
||||
}
|
||||
|
||||
m_averagePtr = 0U;
|
||||
} else {
|
||||
m_centre[m_averagePtr] = centre;
|
||||
m_threshold[m_averagePtr] = threshold;
|
||||
|
||||
m_averagePtr++;
|
||||
if (m_averagePtr >= 16U)
|
||||
m_averagePtr = 0U;
|
||||
}
|
||||
|
||||
m_centreVal = 0;
|
||||
m_thresholdVal = 0;
|
||||
|
||||
for (uint8_t i = 0U; i < 16U; i++) {
|
||||
m_centreVal += m_centre[i];
|
||||
m_thresholdVal += m_threshold[i];
|
||||
}
|
||||
|
||||
m_centreVal >>= 4;
|
||||
m_thresholdVal >>= 4;
|
||||
}
|
||||
|
||||
void CNXDNRX::samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold)
|
||||
{
|
||||
for (uint16_t i = 0U; i < count; i++) {
|
||||
q15_t sample = m_buffer[start] - centre;
|
||||
|
||||
if (sample < -threshold) {
|
||||
WRITE_BIT1(buffer, offset, false);
|
||||
offset++;
|
||||
WRITE_BIT1(buffer, offset, true);
|
||||
offset++;
|
||||
} else if (sample < 0) {
|
||||
WRITE_BIT1(buffer, offset, false);
|
||||
offset++;
|
||||
WRITE_BIT1(buffer, offset, false);
|
||||
offset++;
|
||||
} else if (sample < threshold) {
|
||||
WRITE_BIT1(buffer, offset, true);
|
||||
offset++;
|
||||
WRITE_BIT1(buffer, offset, false);
|
||||
offset++;
|
||||
} else {
|
||||
WRITE_BIT1(buffer, offset, true);
|
||||
offset++;
|
||||
WRITE_BIT1(buffer, offset, true);
|
||||
offset++;
|
||||
}
|
||||
|
||||
start += NXDN_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= NXDN_FRAME_LENGTH_SAMPLES)
|
||||
start -= NXDN_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
void CNXDNRX::writeRSSIData(uint8_t* data)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
if (m_rssiCount > 0U) {
|
||||
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||
|
||||
data[49U] = (rssi >> 8) & 0xFFU;
|
||||
data[50U] = (rssi >> 0) & 0xFFU;
|
||||
|
||||
serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
#else
|
||||
serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(NXDNRX_H)
|
||||
#define NXDNRX_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "NXDNDefines.h"
|
||||
|
||||
enum NXDNRX_STATE {
|
||||
NXDNRXS_NONE,
|
||||
NXDNRXS_DATA
|
||||
};
|
||||
|
||||
class CNXDNRX {
|
||||
public:
|
||||
CNXDNRX();
|
||||
|
||||
void samples(const q15_t* samples, uint16_t* rssi, uint8_t length);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
NXDNRX_STATE m_state;
|
||||
uint16_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH];
|
||||
q15_t m_buffer[NXDN_FRAME_LENGTH_SAMPLES];
|
||||
uint16_t m_bitPtr;
|
||||
uint16_t m_dataPtr;
|
||||
uint16_t m_startPtr;
|
||||
uint16_t m_endPtr;
|
||||
uint16_t m_fswPtr;
|
||||
uint16_t m_minFSWPtr;
|
||||
uint16_t m_maxFSWPtr;
|
||||
q31_t m_maxCorr;
|
||||
uint16_t m_lostCount;
|
||||
uint8_t m_countdown;
|
||||
q15_t m_centre[16U];
|
||||
q15_t m_centreVal;
|
||||
q15_t m_threshold[16U];
|
||||
q15_t m_thresholdVal;
|
||||
uint8_t m_averagePtr;
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
|
||||
void processNone(q15_t sample);
|
||||
void processData(q15_t sample);
|
||||
bool correlateFSW();
|
||||
void calculateLevels(uint16_t start, uint16_t count);
|
||||
void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
void writeRSSIData(uint8_t* data);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2017 by Andy Uribe CA6JAU
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "NXDNTX.h"
|
||||
|
||||
#include "NXDNDefines.h"
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179, -1548, -1769, -1795, -1597, -1172,
|
||||
-544, 237, 1092, 1927, 2637, 3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739, -6442,
|
||||
-6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822, 19484, 23810, 27520, 30367, 32156, 32767,
|
||||
32156, 30367, 27520, 23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735, -6483, -6442,
|
||||
-5739, -4544, -3043, -1431, 116, 1447, 2454, 3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172,
|
||||
-1597, -1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
static q15_t NXDN_SINC_FILTER[] = {572, -1003, -253, 254, 740, 1290, 1902, 2527, 3090, 3517, 3747, 3747, 3517, 3090, 2527, 1902,
|
||||
1290, 740, 254, -253, -1003, 572};
|
||||
const uint16_t NXDN_SINC_FILTER_LEN = 22U;
|
||||
|
||||
const q15_t NXDN_LEVELA = 735;
|
||||
const q15_t NXDN_LEVELB = 245;
|
||||
const q15_t NXDN_LEVELC = -245;
|
||||
const q15_t NXDN_LEVELD = -735;
|
||||
|
||||
const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU};
|
||||
const uint8_t NXDN_SYNC = 0x5FU;
|
||||
|
||||
CNXDNTX::CNXDNTX() :
|
||||
m_buffer(4000U),
|
||||
m_modFilter(),
|
||||
m_sincFilter(),
|
||||
m_modState(),
|
||||
m_sincState(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U) // 200ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
::memset(m_sincState, 0x00U, 70U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.L = NXDN_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = RRC_0_2_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
|
||||
m_sincFilter.numTaps = NXDN_SINC_FILTER_LEN;
|
||||
m_sincFilter.pState = m_sincState;
|
||||
m_sincFilter.pCoeffs = NXDN_SINC_FILTER;
|
||||
}
|
||||
|
||||
void CNXDNTX::process()
|
||||
{
|
||||
if (m_buffer.getData() == 0U && m_poLen == 0U)
|
||||
return;
|
||||
|
||||
if (m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = NXDN_SYNC;
|
||||
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[0U];
|
||||
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[1U];
|
||||
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[2U];
|
||||
} else {
|
||||
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) {
|
||||
uint8_t c = m_buffer.get();
|
||||
m_poBuffer[m_poLen++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
m_poPtr = 0U;
|
||||
}
|
||||
|
||||
if (m_poLen > 0U) {
|
||||
uint16_t space = io.getSpace();
|
||||
|
||||
while (space > (4U * NXDN_RADIO_SYMBOL_LENGTH)) {
|
||||
uint8_t c = m_poBuffer[m_poPtr++];
|
||||
writeByte(c);
|
||||
|
||||
space -= 4U * NXDN_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
if (m_poPtr >= m_poLen) {
|
||||
m_poPtr = 0U;
|
||||
m_poLen = 0U;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CNXDNTX::writeData(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (length != (NXDN_FRAME_LENGTH_BYTES + 1U))
|
||||
return 4U;
|
||||
|
||||
uint16_t space = m_buffer.getSpace();
|
||||
if (space < NXDN_FRAME_LENGTH_BYTES)
|
||||
return 5U;
|
||||
|
||||
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++)
|
||||
m_buffer.put(data[i + 1U]);
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
void CNXDNTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[4U];
|
||||
q15_t intBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U];
|
||||
q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
inBuffer[i] = NXDN_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
inBuffer[i] = NXDN_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
inBuffer[i] = NXDN_LEVELC;
|
||||
break;
|
||||
default:
|
||||
inBuffer[i] = NXDN_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U);
|
||||
|
||||
::arm_fir_fast_q15(&m_sincFilter, intBuffer, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U);
|
||||
|
||||
io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CNXDNTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 300U + uint16_t(delay) * 6U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
uint8_t CNXDNTX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / NXDN_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(NXDNTX_H)
|
||||
#define NXDNTX_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "SerialRB.h"
|
||||
|
||||
class CNXDNTX {
|
||||
public:
|
||||
CNXDNTX();
|
||||
|
||||
uint8_t writeData(const uint8_t* data, uint8_t length);
|
||||
|
||||
void process();
|
||||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
arm_fir_instance_q15 m_sincFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
q15_t m_sincState[70U]; // NoTaps + BlockSize - 1, 22 + 40 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+19
-4
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2018 by Bryan Biedenkapp <[email protected]>
|
||||
*
|
||||
* 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
|
||||
@@ -19,7 +20,7 @@
|
||||
#if !defined(P25DEFINES_H)
|
||||
#define P25DEFINES_H
|
||||
|
||||
const unsigned int P25_RADIO_SYMBOL_LENGTH = 10U; // At 48 kHz sample rate
|
||||
const unsigned int P25_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||
|
||||
const unsigned int P25_HDR_FRAME_LENGTH_BYTES = 99U;
|
||||
const unsigned int P25_HDR_FRAME_LENGTH_BITS = P25_HDR_FRAME_LENGTH_BYTES * 8U;
|
||||
@@ -41,14 +42,20 @@ const unsigned int P25_TERM_FRAME_LENGTH_BITS = P25_TERM_FRAME_LENGTH_BYTES
|
||||
const unsigned int P25_TERM_FRAME_LENGTH_SYMBOLS = P25_TERM_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_TERM_FRAME_LENGTH_SAMPLES = P25_TERM_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_TSDU_FRAME_LENGTH_BYTES = 45U;
|
||||
const unsigned int P25_TSDU_FRAME_LENGTH_BITS = P25_TSDU_FRAME_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_TSDU_FRAME_LENGTH_SYMBOLS = P25_TSDU_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_TSDU_FRAME_LENGTH_SAMPLES = P25_TSDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_SYNC_LENGTH_BYTES = 6U;
|
||||
const unsigned int P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_SYNC_LENGTH_SYMBOLS = P25_SYNC_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_SYNC_LENGTH_SAMPLES = P25_SYNC_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_NID_LENGTH_BITS = 64U;
|
||||
const unsigned int P25_NID_LENGTH_SYMBOLS = 32U;
|
||||
const unsigned int P25_NID_LENGTH_SAMPLESS = P25_NID_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
const unsigned int P25_NID_LENGTH_BYTES = 8U;
|
||||
const unsigned int P25_NID_LENGTH_BITS = P25_NID_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_NID_LENGTH_SYMBOLS = P25_NID_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_NID_LENGTH_SAMPLES = P25_NID_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const uint8_t P25_SYNC_BYTES[] = {0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU};
|
||||
const uint8_t P25_SYNC_BYTES_LENGTH = 6U;
|
||||
@@ -65,5 +72,13 @@ const int8_t P25_SYNC_SYMBOLS_VALUES[] = {+3, +3, +3, +3, +3, -3, +3, +3, -3, -3
|
||||
const uint32_t P25_SYNC_SYMBOLS = 0x00FB30A0U;
|
||||
const uint32_t P25_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
|
||||
|
||||
const uint8_t P25_DUID_HDU = 0x00U; // Header Data Unit
|
||||
const uint8_t P25_DUID_TDU = 0x03U; // Simple Terminator Data Unit
|
||||
const uint8_t P25_DUID_LDU1 = 0x05U; // Logical Link Data Unit 1
|
||||
const uint8_t P25_DUID_TSDU = 0x07U; // Trunking System Data Unit
|
||||
const uint8_t P25_DUID_LDU2 = 0x0AU; // Logical Link Data Unit 2
|
||||
const uint8_t P25_DUID_PDU = 0x0CU; // Packet Data Unit
|
||||
const uint8_t P25_DUID_TDULC = 0x0FU; // Terminator Data Unit with Link Control
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2018 by Bryan Biedenkapp <[email protected]>
|
||||
*
|
||||
* 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
|
||||
@@ -23,6 +24,8 @@
|
||||
|
||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.57)
|
||||
|
||||
const uint8_t CORRELATION_COUNTDOWN = 10U;//5U;
|
||||
|
||||
const uint8_t MAX_SYNC_BIT_START_ERRS = 2U;
|
||||
const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U;
|
||||
|
||||
@@ -60,7 +63,8 @@ m_threshold(),
|
||||
m_thresholdVal(0),
|
||||
m_averagePtr(NOAVEPTR),
|
||||
m_rssiAccum(0U),
|
||||
m_rssiCount(0U)
|
||||
m_rssiCount(0U),
|
||||
m_duid(0U)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -84,6 +88,7 @@ void CP25RX::reset()
|
||||
m_countdown = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
m_duid = 0U;
|
||||
}
|
||||
|
||||
void CP25RX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
|
||||
@@ -113,8 +118,10 @@ void CP25RX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
|
||||
}
|
||||
|
||||
m_dataPtr++;
|
||||
if (m_dataPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
if (m_dataPtr >= P25_LDU_FRAME_LENGTH_SAMPLES) {
|
||||
m_dataPtr = 0U;
|
||||
m_duid = 0U;
|
||||
}
|
||||
|
||||
m_bitPtr++;
|
||||
if (m_bitPtr >= P25_RADIO_SYMBOL_LENGTH)
|
||||
@@ -136,7 +143,7 @@ void CP25RX::processNone(q15_t sample)
|
||||
|
||||
m_averagePtr = NOAVEPTR;
|
||||
|
||||
m_countdown = 5U;
|
||||
m_countdown = CORRELATION_COUNTDOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,16 +176,67 @@ void CP25RX::processHdr(q15_t sample)
|
||||
}
|
||||
|
||||
if (m_dataPtr == m_maxSyncPtr) {
|
||||
if (m_hdrSyncPtr != m_lduSyncPtr) {
|
||||
calculateLevels(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS);
|
||||
uint16_t nidStartPtr = m_hdrStartPtr + P25_SYNC_LENGTH_SAMPLES;
|
||||
if (nidStartPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
nidStartPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
DEBUG4("P25RX: sync found in Hdr pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal);
|
||||
uint8_t nid[2U];
|
||||
samplesToBits(nidStartPtr, (2U * 4U), nid, 0U, m_centreVal, m_thresholdVal);
|
||||
// DEBUG3("P25RX: nid (b0 - b1)", nid[0U], nid[1U]);
|
||||
|
||||
uint8_t frame[P25_HDR_FRAME_LENGTH_BYTES + 1U];
|
||||
samplesToBits(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
m_duid = nid[1U] & 0x0F;
|
||||
|
||||
frame[0U] = 0x01U;
|
||||
serial.writeP25Hdr(frame, P25_HDR_FRAME_LENGTH_BYTES + 1U);
|
||||
switch (m_duid) {
|
||||
case P25_DUID_HDU: {
|
||||
calculateLevels(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("P25RX: sync found in Hdr pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[P25_HDR_FRAME_LENGTH_BYTES + 1U];
|
||||
samplesToBits(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
frame[0U] = 0x01U;
|
||||
serial.writeP25Hdr(frame, P25_HDR_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
break;
|
||||
case P25_DUID_TSDU: {
|
||||
calculateLevels(m_hdrStartPtr, P25_TSDU_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("P25RX: sync found in TSDU pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[P25_TSDU_FRAME_LENGTH_BYTES + 1U];
|
||||
samplesToBits(m_hdrStartPtr, P25_TSDU_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
frame[0U] = 0x01U;
|
||||
serial.writeP25Hdr(frame, P25_TSDU_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
break;
|
||||
case P25_DUID_TDU: {
|
||||
calculateLevels(m_hdrStartPtr, P25_TERM_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("P25RX: sync found in TDU pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[P25_TERM_FRAME_LENGTH_BYTES + 1U];
|
||||
samplesToBits(m_hdrStartPtr, P25_TERM_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
frame[0U] = 0x01U;
|
||||
serial.writeP25Hdr(frame, P25_TERM_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
break;
|
||||
case P25_DUID_TDULC: {
|
||||
calculateLevels(m_hdrStartPtr, P25_TERMLC_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("P25RX: sync found in TDULC pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[P25_TERMLC_FRAME_LENGTH_BYTES + 1U];
|
||||
samplesToBits(m_hdrStartPtr, P25_TERMLC_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
frame[0U] = 0x01U;
|
||||
serial.writeP25Hdr(frame, P25_TERMLC_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_minSyncPtr = m_lduSyncPtr + P25_LDU_FRAME_LENGTH_SAMPLES - 1U;
|
||||
@@ -238,6 +296,7 @@ void CP25RX::processLdu(q15_t sample)
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_countdown = 0U;
|
||||
m_maxCorr = 0;
|
||||
m_duid = 0U;
|
||||
} else {
|
||||
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
||||
writeRSSILdu(frame);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2018 by Bryan Biedenkapp <[email protected]>
|
||||
*
|
||||
* 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
|
||||
@@ -59,6 +60,7 @@ private:
|
||||
uint8_t m_averagePtr;
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
uint8_t m_duid;
|
||||
|
||||
void processNone(q15_t sample);
|
||||
void processHdr(q15_t sample);
|
||||
|
||||
@@ -23,23 +23,17 @@
|
||||
|
||||
#include "P25Defines.h"
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 10, 'normal') in MATLAB
|
||||
static q15_t RC_0_2_FILTER[] = {-444, -897, -1311, -1636, -1825, -1840, -1659, -1278, -712, 0,
|
||||
800, 1613, 2354, 2936, 3277, 3310, 2994, 2315, 1296, 0, -1478,
|
||||
-3011, -4448, -5627, -6386, -6580, -6090, -4839, -2800, 0, 3474,
|
||||
7482, 11835, 16311, 20666, 24651, 28032, 30607, 32219, 32767,
|
||||
32219, 30607, 28032, 24651, 20666, 16311, 11835, 7482, 3474, 0,
|
||||
-2800, -4839, -6090, -6580, -6386, -5627, -4448, -3011, -1478,
|
||||
0, 1296, 2315, 2994, 3310, 3277, 2936, 2354, 1613, 800, 0, -712,
|
||||
-1278, -1659, -1840, -1825, -1636, -1311, -897, -444, 0}; // numTaps = 80, L = 10
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'normal') in MATLAB
|
||||
// numTaps = 40, L = 5
|
||||
static q15_t RC_0_2_FILTER[] = {-897, -1636, -1840, -1278, 0, 1613, 2936, 3310, 2315, 0, -3011, -5627, -6580, -4839,
|
||||
0, 7482, 16311, 24651, 30607, 32767, 30607, 24651, 16311, 7482, 0, -4839, -6580, -5627,
|
||||
-3011, 0, 2315, 3310, 2936, 1613, 0, -1278, -1840, -1636, -897, 0}; // numTaps = 40, L = 5
|
||||
const uint16_t RC_0_2_FILTER_PHASE_LEN = 8U; // phaseLength = numTaps/L
|
||||
|
||||
// Generated in MATLAB using the following commands, and then normalised for unity gain
|
||||
// shape2 = 'Inverse-sinc Lowpass';
|
||||
// d2 = fdesign.interpolator(1, shape2);
|
||||
// h2 = design(d2, 'SystemObject', true);
|
||||
static q15_t LOWPASS_FILTER[] = {1294, -2251, 4312, -8402, 20999, 20999, -8402, 4312, -2251, 1294};
|
||||
const uint16_t LOWPASS_FILTER_LEN = 10U;
|
||||
static q15_t LOWPASS_FILTER[] = {124, -188, -682, 1262, 556, -621, -1912, -911, 2058, 3855, 1234, -4592, -7692, -2799,
|
||||
8556, 18133, 18133, 8556, -2799, -7692, -4592, 1234, 3855, 2058, -911, -1912, -621,
|
||||
556, 1262, -682, -188, 124};
|
||||
const uint16_t LOWPASS_FILTER_LEN = 32U;
|
||||
|
||||
const q15_t P25_LEVELA = 1260;
|
||||
const q15_t P25_LEVELB = 420;
|
||||
@@ -49,7 +43,7 @@ const q15_t P25_LEVELD = -1260;
|
||||
const uint8_t P25_START_SYNC = 0x77U;
|
||||
|
||||
CP25TX::CP25TX() :
|
||||
m_buffer(1500U),
|
||||
m_buffer(4000U),
|
||||
m_modFilter(),
|
||||
m_lpFilter(),
|
||||
m_modState(),
|
||||
@@ -60,7 +54,7 @@ m_poPtr(0U),
|
||||
m_txDelay(240U) // 200ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
::memset(m_lpState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_lpState, 0x00U, 60U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.L = P25_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = RC_0_2_FILTER_PHASE_LEN;
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
arm_fir_instance_q15 m_lpFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare
|
||||
q15_t m_lpState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2018 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "POCSAGTX.h"
|
||||
|
||||
const uint16_t POCSAG_FRAME_LENGTH_BYTES = 17U * sizeof(uint32_t);
|
||||
|
||||
const uint16_t POCSAG_PREAMBLE_LENGTH_BYTES = 18U * sizeof(uint32_t);
|
||||
|
||||
const uint16_t POCSAG_RADIO_SYMBOL_LENGTH = 20U;
|
||||
|
||||
const q15_t POCSAG_LEVEL1[] = { 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700};
|
||||
const q15_t POCSAG_LEVEL0[] = {-1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700, -1700};
|
||||
|
||||
static q15_t SHAPING_FILTER[] = {5461, 5461, 5461, 5461, 5461, 5461};
|
||||
const uint16_t SHAPING_FILTER_LEN = 6U;
|
||||
|
||||
const uint8_t POCSAG_SYNC = 0xAAU;
|
||||
|
||||
CPOCSAGTX::CPOCSAGTX() :
|
||||
m_buffer(4000U),
|
||||
m_modFilter(),
|
||||
m_modState(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(POCSAG_PREAMBLE_LENGTH_BYTES)
|
||||
{
|
||||
::memset(m_modState, 0x00U, 170U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = SHAPING_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.pCoeffs = SHAPING_FILTER;
|
||||
}
|
||||
|
||||
void CPOCSAGTX::process()
|
||||
{
|
||||
if (m_buffer.getData() == 0U && m_poLen == 0U)
|
||||
return;
|
||||
|
||||
if (m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
DEBUG1("MMDVM, sending preamble");
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = POCSAG_SYNC;
|
||||
} else {
|
||||
DEBUG2("MMDVM, sending", m_buffer.get());
|
||||
for (uint8_t i = 0U; i < POCSAG_FRAME_LENGTH_BYTES; i++) {
|
||||
uint8_t c = m_buffer.get();
|
||||
m_poBuffer[m_poLen++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
m_poPtr = 0U;
|
||||
}
|
||||
|
||||
if (m_poLen > 0U) {
|
||||
uint16_t space = io.getSpace();
|
||||
|
||||
while (space > (8U * POCSAG_RADIO_SYMBOL_LENGTH)) {
|
||||
uint8_t c = m_poBuffer[m_poPtr++];
|
||||
writeByte(c);
|
||||
|
||||
space -= 8U * POCSAG_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
if (m_poPtr >= m_poLen) {
|
||||
m_poPtr = 0U;
|
||||
m_poLen = 0U;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CPOCSAGTX::writeData(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (length != (POCSAG_FRAME_LENGTH_BYTES + 1U))
|
||||
return 4U;
|
||||
|
||||
uint16_t space = m_buffer.getSpace();
|
||||
if (space < (POCSAG_FRAME_LENGTH_BYTES + 1U))
|
||||
return 5U;
|
||||
|
||||
DEBUG2("MMDVM, queueing", data[0U]);
|
||||
|
||||
for (uint8_t i = 0U; i < (POCSAG_FRAME_LENGTH_BYTES + 1U); i++)
|
||||
m_buffer.put(data[i]);
|
||||
|
||||
return 0U;
|
||||
}
|
||||
|
||||
void CPOCSAGTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[POCSAG_RADIO_SYMBOL_LENGTH * 8U];
|
||||
q15_t outBuffer[POCSAG_RADIO_SYMBOL_LENGTH * 8U];
|
||||
|
||||
const uint8_t MASK = 0x80U;
|
||||
|
||||
uint8_t n = 0U;
|
||||
for (uint8_t i = 0U; i < 8U; i++, c <<= 1, n += POCSAG_RADIO_SYMBOL_LENGTH) {
|
||||
switch (c & MASK) {
|
||||
case 0x80U:
|
||||
::memcpy(inBuffer + n, POCSAG_LEVEL1, POCSAG_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
break;
|
||||
default:
|
||||
::memcpy(inBuffer + n, POCSAG_LEVEL0, POCSAG_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, POCSAG_RADIO_SYMBOL_LENGTH * 8U);
|
||||
|
||||
io.write(STATE_POCSAG, outBuffer, POCSAG_RADIO_SYMBOL_LENGTH * 8U);
|
||||
}
|
||||
|
||||
void CPOCSAGTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = POCSAG_PREAMBLE_LENGTH_BYTES + (delay * 3U) / 2U;
|
||||
|
||||
if (m_txDelay > 150U)
|
||||
m_txDelay = 150U;
|
||||
}
|
||||
|
||||
uint8_t CPOCSAGTX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / POCSAG_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(POCSAGTX_H)
|
||||
#define POCSAGTX_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "SerialRB.h"
|
||||
|
||||
class CPOCSAGTX {
|
||||
public:
|
||||
CPOCSAGTX();
|
||||
|
||||
uint8_t writeData(const uint8_t* data, uint8_t length);
|
||||
|
||||
void process();
|
||||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[170U]; // NoTaps + BlockSize - 1, 6 + 160 - 1 plus some spare
|
||||
uint8_t m_poBuffer[200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion and P25 modes.
|
||||
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion, P25, NXDN, and POCSAG modes.
|
||||
|
||||
It runs on the Arduino Due, the ST-Micro STM32F407-DISCO and STM32F446-NUCLEO, as well as the Teensy 3.1/3.2/3.5/3.6. What these platforms have in common is the use of an ARM Cortex-M3 or M4 processor with a clock speed greater than 70 MHz, and access to at least one analogue to digital converter and one digital to analogue converter. A Cortex-M7 processor is also probably adequate.
|
||||
It runs on the Arduino Due, the ST-Micro STM32F1xxx, STM32F4xxx and STM32F7xxx processors, as well as the Teensy 3.1/3.2/3.5/3.6. What these platforms have in common is the use of an ARM Cortex-M3 or M4 processor with a clock speed greater than 70 MHz, and access to at least one analogue to digital converter and one digital to analogue converter.
|
||||
|
||||
In order to build this software for the Arduino Due, you will need to edit a file within the Arduino GUI and that is detailed in the BUILD.txt file. The STM32 support is being supplied via the Coocox IDE with ARM GCC. The Teensy support is via the latest beta of Teensyduino.
|
||||
In order to build this software for the Arduino Due, you will need to edit a file within the Arduino GUI and that is detailed in the BUILD.txt file. The STM32 support is being supplied via the Coocox IDE with ARM GCC. The Teensy support uses Teensyduino.
|
||||
|
||||
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.
|
||||
|
||||
@@ -22,7 +22,6 @@ Boston, MA 02110-1301, USA.
|
||||
|
||||
CRSSIRB::CRSSIRB(uint16_t length) :
|
||||
m_length(length),
|
||||
m_rssi(NULL),
|
||||
m_head(0U),
|
||||
m_tail(0U),
|
||||
m_full(false),
|
||||
@@ -101,4 +100,3 @@ bool CRSSIRB::hasOverflowed()
|
||||
|
||||
return overflow;
|
||||
}
|
||||
|
||||
|
||||
Submodule
+1
Submodule STM32F4XX_Lib added at bcd346f530
Submodule
+1
Submodule STM32F7XX_Lib added at 2d5f4236fa
@@ -22,8 +22,6 @@ Boston, MA 02110-1301, USA.
|
||||
|
||||
CSampleRB::CSampleRB(uint16_t length) :
|
||||
m_length(length),
|
||||
m_samples(NULL),
|
||||
m_control(NULL),
|
||||
m_head(0U),
|
||||
m_tail(0U),
|
||||
m_full(false),
|
||||
|
||||
+207
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013,2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2013,2015,2016,2017,2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -60,30 +60,44 @@ const uint8_t MMDVM_P25_HDR = 0x30U;
|
||||
const uint8_t MMDVM_P25_LDU = 0x31U;
|
||||
const uint8_t MMDVM_P25_LOST = 0x32U;
|
||||
|
||||
const uint8_t MMDVM_NXDN_DATA = 0x40U;
|
||||
const uint8_t MMDVM_NXDN_LOST = 0x41U;
|
||||
|
||||
const uint8_t MMDVM_POCSAG_DATA = 0x50U;
|
||||
|
||||
const uint8_t MMDVM_ACK = 0x70U;
|
||||
const uint8_t MMDVM_NAK = 0x7FU;
|
||||
|
||||
const uint8_t MMDVM_SERIAL = 0x80U;
|
||||
|
||||
const uint8_t MMDVM_TRANSPARENT = 0x90U;
|
||||
|
||||
const uint8_t MMDVM_DEBUG1 = 0xF1U;
|
||||
const uint8_t MMDVM_DEBUG2 = 0xF2U;
|
||||
const uint8_t MMDVM_DEBUG3 = 0xF3U;
|
||||
const uint8_t MMDVM_DEBUG4 = 0xF4U;
|
||||
const uint8_t MMDVM_DEBUG5 = 0xF5U;
|
||||
|
||||
|
||||
#if defined(EXTERNAL_OSC)
|
||||
#define DESCRIPTION "MMDVM 20170501 TCXO 48kHz (D-Star/DMR/YSF/P25/RSSI/CW Id)"
|
||||
#if EXTERNAL_OSC == 12000000
|
||||
#define TCXO "12.0000 MHz"
|
||||
#elif EXTERNAL_OSC == 12288000
|
||||
#define TCXO "12.2880 MHz"
|
||||
#elif EXTERNAL_OSC == 14400000
|
||||
#define TCXO "14.4000 MHz"
|
||||
#elif EXTERNAL_OSC == 19200000
|
||||
#define TCXO "19.2000 Mhz"
|
||||
#else
|
||||
#define DESCRIPTION "MMDVM 20170501 48kHz (D-Star/DMR/YSF/P25/RSSI/CW Id)"
|
||||
#define TCXO "NO TCXO"
|
||||
#endif
|
||||
|
||||
#define DESCRIPTION "MMDVM 20180327 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG)"
|
||||
|
||||
#if defined(GITVERSION)
|
||||
#define concat(a, b) a " GitID #" b ""
|
||||
const char HARDWARE[] = concat(DESCRIPTION, GITVERSION);
|
||||
#define concat(a, b, c) a " " b " GitID #" c ""
|
||||
const char HARDWARE[] = concat(DESCRIPTION, TCXO, GITVERSION);
|
||||
#else
|
||||
#define concat(a, b, c) a " (Build: " b " " c ")"
|
||||
const char HARDWARE[] = concat(DESCRIPTION, __TIME__, __DATE__);
|
||||
#define concat(a, b, c, d) a " " b " (Build: " c " " d ")"
|
||||
const char HARDWARE[] = concat(DESCRIPTION, TCXO, __TIME__, __DATE__);
|
||||
#endif
|
||||
|
||||
const uint8_t PROTOCOL_VERSION = 1U;
|
||||
@@ -127,11 +141,11 @@ void CSerialPort::getStatus()
|
||||
{
|
||||
io.resetWatchdog();
|
||||
|
||||
uint8_t reply[15U];
|
||||
uint8_t reply[20U];
|
||||
|
||||
// Send all sorts of interesting internal values
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
reply[1U] = 11U;
|
||||
reply[1U] = 13U;
|
||||
reply[2U] = MMDVM_GET_STATUS;
|
||||
|
||||
reply[3U] = 0x00U;
|
||||
@@ -143,6 +157,10 @@ void CSerialPort::getStatus()
|
||||
reply[3U] |= 0x04U;
|
||||
if (m_p25Enable)
|
||||
reply[3U] |= 0x08U;
|
||||
if (m_nxdnEnable)
|
||||
reply[3U] |= 0x10U;
|
||||
if (m_pocsagEnable)
|
||||
reply[3U] |= 0x20U;
|
||||
|
||||
reply[4U] = uint8_t(m_modemState);
|
||||
|
||||
@@ -197,12 +215,22 @@ void CSerialPort::getStatus()
|
||||
else
|
||||
reply[10U] = 0U;
|
||||
|
||||
writeInt(1U, reply, 11);
|
||||
if (m_nxdnEnable)
|
||||
reply[11U] = nxdnTX.getSpace();
|
||||
else
|
||||
reply[11U] = 0U;
|
||||
|
||||
if (m_pocsagEnable)
|
||||
reply[12U] = pocsagTX.getSpace();
|
||||
else
|
||||
reply[12U] = 0U;
|
||||
|
||||
writeInt(1U, reply, 13);
|
||||
}
|
||||
|
||||
void CSerialPort::getVersion()
|
||||
{
|
||||
uint8_t reply[100U];
|
||||
uint8_t reply[150U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
reply[1U] = 0U;
|
||||
@@ -221,7 +249,7 @@ void CSerialPort::getVersion()
|
||||
|
||||
uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (length < 15U)
|
||||
if (length < 18U)
|
||||
return 4U;
|
||||
|
||||
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
|
||||
@@ -232,10 +260,12 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
|
||||
m_debug = (data[0U] & 0x10U) == 0x10U;
|
||||
|
||||
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
|
||||
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
|
||||
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
|
||||
bool p25Enable = (data[1U] & 0x08U) == 0x08U;
|
||||
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
|
||||
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
|
||||
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
|
||||
bool p25Enable = (data[1U] & 0x08U) == 0x08U;
|
||||
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
|
||||
bool pocsagEnable = (data[1U] & 0x20U) == 0x20U;
|
||||
|
||||
uint8_t txDelay = data[2U];
|
||||
if (txDelay > 50U)
|
||||
@@ -243,7 +273,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
|
||||
MMDVM_STATE modemState = MMDVM_STATE(data[3U]);
|
||||
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K)
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K)
|
||||
return 4U;
|
||||
if (modemState == STATE_DSTAR && !dstarEnable)
|
||||
return 4U;
|
||||
@@ -253,6 +283,10 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
return 4U;
|
||||
if (modemState == STATE_P25 && !p25Enable)
|
||||
return 4U;
|
||||
if (modemState == STATE_NXDN && !nxdnEnable)
|
||||
return 4U;
|
||||
if (modemState == STATE_POCSAG && !pocsagEnable)
|
||||
return 4U;
|
||||
|
||||
uint8_t rxLevel = data[4U];
|
||||
|
||||
@@ -271,18 +305,28 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
int16_t txDCOffset = int16_t(data[13U]) - 128;
|
||||
int16_t rxDCOffset = int16_t(data[14U]) - 128;
|
||||
|
||||
uint8_t nxdnTXLevel = data[15U];
|
||||
|
||||
uint8_t ysfTXHang = data[16U];
|
||||
|
||||
uint8_t pocsagTXLevel = data[17U];
|
||||
|
||||
m_modemState = modemState;
|
||||
|
||||
m_dstarEnable = dstarEnable;
|
||||
m_dmrEnable = dmrEnable;
|
||||
m_ysfEnable = ysfEnable;
|
||||
m_p25Enable = p25Enable;
|
||||
m_duplex = !simplex;
|
||||
m_dstarEnable = dstarEnable;
|
||||
m_dmrEnable = dmrEnable;
|
||||
m_ysfEnable = ysfEnable;
|
||||
m_p25Enable = p25Enable;
|
||||
m_nxdnEnable = nxdnEnable;
|
||||
m_pocsagEnable = pocsagEnable;
|
||||
m_duplex = !simplex;
|
||||
|
||||
dstarTX.setTXDelay(txDelay);
|
||||
ysfTX.setTXDelay(txDelay);
|
||||
p25TX.setTXDelay(txDelay);
|
||||
dmrDMOTX.setTXDelay(txDelay);
|
||||
nxdnTX.setTXDelay(txDelay);
|
||||
pocsagTX.setTXDelay(txDelay);
|
||||
|
||||
dmrTX.setColorCode(colorCode);
|
||||
dmrRX.setColorCode(colorCode);
|
||||
@@ -290,9 +334,9 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
dmrDMORX.setColorCode(colorCode);
|
||||
dmrIdleRX.setColorCode(colorCode);
|
||||
|
||||
ysfTX.setLoDev(ysfLoDev);
|
||||
ysfTX.setParams(ysfLoDev, ysfTXHang);
|
||||
|
||||
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, txDCOffset, rxDCOffset);
|
||||
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, txDCOffset, rxDCOffset);
|
||||
|
||||
io.start();
|
||||
|
||||
@@ -309,7 +353,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
|
||||
if (modemState == m_modemState)
|
||||
return 0U;
|
||||
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K)
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K)
|
||||
return 4U;
|
||||
if (modemState == STATE_DSTAR && !m_dstarEnable)
|
||||
return 4U;
|
||||
@@ -319,6 +363,10 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
|
||||
return 4U;
|
||||
if (modemState == STATE_P25 && !m_p25Enable)
|
||||
return 4U;
|
||||
if (modemState == STATE_NXDN && !m_nxdnEnable)
|
||||
return 4U;
|
||||
if (modemState == STATE_POCSAG && !m_pocsagEnable)
|
||||
return 4U;
|
||||
|
||||
setMode(modemState);
|
||||
|
||||
@@ -333,6 +381,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_DSTAR:
|
||||
@@ -342,6 +391,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dmrRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_YSF:
|
||||
@@ -351,6 +401,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_P25:
|
||||
@@ -360,6 +411,28 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_NXDN:
|
||||
DEBUG1("Mode set to NXDN");
|
||||
dmrIdleRX.reset();
|
||||
dmrDMORX.reset();
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_POCSAG:
|
||||
DEBUG1("Mode set to POCSAG");
|
||||
dmrIdleRX.reset();
|
||||
dmrDMORX.reset();
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_DSTARCAL:
|
||||
@@ -370,6 +443,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_DMRCAL:
|
||||
@@ -380,6 +454,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_RSSICAL:
|
||||
@@ -390,6 +465,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_LFCAL:
|
||||
@@ -400,16 +476,18 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_DMRCAL1K:
|
||||
DEBUG1("Mode set to DMR 1031 Hz Calibrate");
|
||||
DEBUG1("Mode set to DMR BS 1031 Hz Calibrate");
|
||||
dmrIdleRX.reset();
|
||||
dmrDMORX.reset();
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_P25CAL1K:
|
||||
@@ -420,6 +498,29 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_DMRDMO1K:
|
||||
DEBUG1("Mode set to DMR MS 1031 Hz Calibrate");
|
||||
dmrIdleRX.reset();
|
||||
dmrDMORX.reset();
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_NXDNCAL1K:
|
||||
DEBUG1("Mode set to NXDN 1031 Hz Calibrate");
|
||||
dmrIdleRX.reset();
|
||||
dmrDMORX.reset();
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
nxdnRX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
default:
|
||||
@@ -453,7 +554,8 @@ void CSerialPort::process()
|
||||
m_buffer[0U] = c;
|
||||
m_ptr = 1U;
|
||||
m_len = 0U;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
m_ptr = 0U;
|
||||
m_len = 0U;
|
||||
}
|
||||
@@ -502,10 +604,12 @@ void CSerialPort::process()
|
||||
case MMDVM_CAL_DATA:
|
||||
if (m_modemState == STATE_DSTARCAL)
|
||||
err = calDStarTX.write(m_buffer + 3U, m_len - 3U);
|
||||
if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K)
|
||||
if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K)
|
||||
err = calDMR.write(m_buffer + 3U, m_len - 3U);
|
||||
if (m_modemState == STATE_P25CAL1K)
|
||||
err = calP25.write(m_buffer + 3U, m_len - 3U);
|
||||
if (m_modemState == STATE_NXDNCAL1K)
|
||||
err = calNXDN.write(m_buffer + 3U, m_len - 3U);
|
||||
if (err == 0U) {
|
||||
sendACK();
|
||||
} else {
|
||||
@@ -681,6 +785,38 @@ void CSerialPort::process()
|
||||
}
|
||||
break;
|
||||
|
||||
case MMDVM_NXDN_DATA:
|
||||
if (m_nxdnEnable) {
|
||||
if (m_modemState == STATE_IDLE || m_modemState == STATE_NXDN)
|
||||
err = nxdnTX.writeData(m_buffer + 3U, m_len - 3U);
|
||||
}
|
||||
if (err == 0U) {
|
||||
if (m_modemState == STATE_IDLE)
|
||||
setMode(STATE_NXDN);
|
||||
} else {
|
||||
DEBUG2("Received invalid NXDN data", err);
|
||||
sendNAK(err);
|
||||
}
|
||||
break;
|
||||
|
||||
case MMDVM_POCSAG_DATA:
|
||||
if (m_pocsagEnable) {
|
||||
if (m_modemState == STATE_IDLE || m_modemState == STATE_POCSAG)
|
||||
err = pocsagTX.writeData(m_buffer + 3U, m_len - 3U);
|
||||
}
|
||||
if (err == 0U) {
|
||||
if (m_modemState == STATE_IDLE)
|
||||
setMode(STATE_POCSAG);
|
||||
} else {
|
||||
DEBUG2("Received invalid POCSAG data", err);
|
||||
sendNAK(err);
|
||||
}
|
||||
break;
|
||||
|
||||
case MMDVM_TRANSPARENT:
|
||||
// Do nothing on the MMDVM.
|
||||
break;
|
||||
|
||||
#if defined(SERIAL_REPEATER)
|
||||
case MMDVM_SERIAL: {
|
||||
for (uint8_t i = 3U; i < m_len; i++)
|
||||
@@ -701,7 +837,7 @@ void CSerialPort::process()
|
||||
}
|
||||
}
|
||||
|
||||
if (io.getWatchdog() >= 96000U) {
|
||||
if (io.getWatchdog() >= 48000U) {
|
||||
m_ptr = 0U;
|
||||
m_len = 0U;
|
||||
}
|
||||
@@ -948,6 +1084,46 @@ void CSerialPort::writeP25Lost()
|
||||
writeInt(1U, reply, 3);
|
||||
}
|
||||
|
||||
void CSerialPort::writeNXDNData(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE)
|
||||
return;
|
||||
|
||||
if (!m_nxdnEnable)
|
||||
return;
|
||||
|
||||
uint8_t reply[130U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
reply[1U] = 0U;
|
||||
reply[2U] = MMDVM_NXDN_DATA;
|
||||
|
||||
uint8_t count = 3U;
|
||||
for (uint8_t i = 0U; i < length; i++, count++)
|
||||
reply[count] = data[i];
|
||||
|
||||
reply[1U] = count;
|
||||
|
||||
writeInt(1U, reply, count);
|
||||
}
|
||||
|
||||
void CSerialPort::writeNXDNLost()
|
||||
{
|
||||
if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE)
|
||||
return;
|
||||
|
||||
if (!m_nxdnEnable)
|
||||
return;
|
||||
|
||||
uint8_t reply[3U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
reply[1U] = 3U;
|
||||
reply[2U] = MMDVM_NXDN_LOST;
|
||||
|
||||
writeInt(1U, reply, 3);
|
||||
}
|
||||
|
||||
void CSerialPort::writeCalData(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (m_modemState != STATE_DSTARCAL)
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017,2018 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
|
||||
@@ -47,6 +47,9 @@ public:
|
||||
void writeP25Ldu(const uint8_t* data, uint8_t length);
|
||||
void writeP25Lost();
|
||||
|
||||
void writeNXDNData(const uint8_t* data, uint8_t length);
|
||||
void writeNXDNLost();
|
||||
|
||||
void writeCalData(const uint8_t* data, uint8_t length);
|
||||
void writeRSSIData(const uint8_t* data, uint8_t length);
|
||||
|
||||
@@ -80,3 +83,4 @@ private:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ Boston, MA 02110-1301, USA.
|
||||
|
||||
CSerialRB::CSerialRB(uint16_t length) :
|
||||
m_length(length),
|
||||
m_buffer(NULL),
|
||||
m_head(0U),
|
||||
m_tail(0U),
|
||||
m_full(false)
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ Boston, MA 02110-1301, USA.
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
const uint16_t SERIAL_RINGBUFFER_SIZE = 370U;
|
||||
const uint16_t SERIAL_RINGBUFFER_SIZE = 2000U;
|
||||
|
||||
class CSerialRB {
|
||||
public:
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
||||
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
|
||||
* Copyright (c) 2017 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,14 +27,14 @@
|
||||
Pin definitions:
|
||||
|
||||
- Host communication:
|
||||
USART1 - TXD PA9 - RXD PA10 (MMDVM-Pi board, MMDVM-Pi F722 board, MMDVM-F4M board)
|
||||
USART1 - TXD PA9 - RXD PA10 (MMDVM-Pi board, MMDVM-Pi F722 board, MMDVM-F4M board, STM32F722-F7M board, STM32F4-DVM board)
|
||||
USART2 - TXD PA2 - RXD PA3 (Nucleo64 F446RE board, Morpho or Arduino header)
|
||||
USART3 - TXD PC10 - RXD PC11 (Discovery board)
|
||||
USART3 - TXD PD8 - RXD PD9 (Nucleo144 F767ZI board)
|
||||
|
||||
- Serial repeater:
|
||||
USART1 - TXD PA9 - RXD PA10 (Nucleo with Arduino header)
|
||||
UART5 - TXD PC12 - RXD PD2 (Discovery, MMDVM-Pi, MMDVM-Pi F722 board, MMDVM-F4M board, Nucleo64 with Morpho header and Nucleo144 F767ZI)
|
||||
UART5 - TXD PC12 - RXD PD2 (Discovery, MMDVM-Pi, MMDVM-Pi F722 board, MMDVM-F4M board, STM32F722-F7M board, STM32F4-DVM board, Nucleo64 with Morpho header and Nucleo144 F767ZI)
|
||||
*/
|
||||
|
||||
#if defined(STM32F4XX) || defined(STM32F7XX)
|
||||
@@ -50,7 +50,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
/* ************* USART1 ***************** */
|
||||
#if defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || (defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER))
|
||||
#if defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_F7M) || defined(STM32F722_PI) || defined(STM32F4_DVM) || (defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER))
|
||||
|
||||
volatile uint8_t TXSerialfifo1[TX_SERIAL_FIFO_SIZE];
|
||||
volatile uint8_t RXSerialfifo1[RX_SERIAL_FIFO_SIZE];
|
||||
@@ -841,7 +841,7 @@ void CSerialPort::beginInt(uint8_t n, int speed)
|
||||
case 1U:
|
||||
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
|
||||
InitUSART3(speed);
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI)
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM)
|
||||
InitUSART1(speed);
|
||||
#elif defined(STM32F4_NUCLEO)
|
||||
InitUSART2(speed);
|
||||
@@ -865,7 +865,7 @@ int CSerialPort::availableInt(uint8_t n)
|
||||
case 1U:
|
||||
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
|
||||
return AvailUSART3();
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI)
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM)
|
||||
return AvailUSART1();
|
||||
#elif defined(STM32F4_NUCLEO)
|
||||
return AvailUSART2();
|
||||
@@ -887,7 +887,7 @@ int CSerialPort::availableForWriteInt(uint8_t n)
|
||||
case 1U:
|
||||
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
|
||||
return AvailForWriteUSART3();
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI)
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM)
|
||||
return AvailForWriteUSART1();
|
||||
#elif defined(STM32F4_NUCLEO)
|
||||
return AvailForWriteUSART2();
|
||||
@@ -909,7 +909,7 @@ uint8_t CSerialPort::readInt(uint8_t n)
|
||||
case 1U:
|
||||
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
|
||||
return ReadUSART3();
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI)
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM)
|
||||
return ReadUSART1();
|
||||
#elif defined(STM32F4_NUCLEO)
|
||||
return ReadUSART2();
|
||||
@@ -933,7 +933,7 @@ void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool
|
||||
WriteUSART3(data, length);
|
||||
if (flush)
|
||||
TXSerialFlush3();
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI)
|
||||
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM)
|
||||
WriteUSART1(data, length);
|
||||
if (flush)
|
||||
TXSerialFlush1();
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
Jobs To Do
|
||||
----------
|
||||
|
||||
Jobs Done
|
||||
---------
|
||||
|
||||
1. Boxcar coefficients
|
||||
2. Symbols lengths for:
|
||||
D-Star
|
||||
DMR
|
||||
YSF
|
||||
P25
|
||||
3. Sample ring buffer sizing
|
||||
RX
|
||||
TX
|
||||
4. Hardware sample rates:
|
||||
Due
|
||||
STM32
|
||||
Teensy
|
||||
5. DC Offset coefficients
|
||||
6. TX Filters:
|
||||
D-Star
|
||||
DMR
|
||||
YSF
|
||||
P25
|
||||
7. TX Filter workspaces and clearing:
|
||||
D-Star
|
||||
DMR
|
||||
YSF
|
||||
P25
|
||||
8. CW Id sine wave, silence and construction
|
||||
9. RX Filters
|
||||
10. RX Filter workspaces and clearing:
|
||||
D-Star
|
||||
DMR
|
||||
YSF
|
||||
P25
|
||||
11. TX symbols.
|
||||
12. DC Offset workspace and clearing.
|
||||
13. DMR magic numbers.
|
||||
14. Watchdog values.
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
#if !defined(YSFDEFINES_H)
|
||||
#define YSFDEFINES_H
|
||||
|
||||
const unsigned int YSF_RADIO_SYMBOL_LENGTH = 10U; // At 48 kHz sample rate
|
||||
const unsigned int YSF_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||
|
||||
const unsigned int YSF_FRAME_LENGTH_BYTES = 120U;
|
||||
const unsigned int YSF_FRAME_LENGTH_BITS = YSF_FRAME_LENGTH_BYTES * 8U;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2018 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2017 by Andy Uribe CA6JAU
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -23,16 +23,10 @@
|
||||
|
||||
#include "YSFDefines.h"
|
||||
|
||||
// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179,
|
||||
-1548, -1769, -1795, -1597, -1172, -544, 237, 1092, 1927, 2637,
|
||||
3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739,
|
||||
-6442, -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822,
|
||||
19484, 23810, 27520, 30367, 32156, 32767, 32156, 30367, 27520,
|
||||
23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735,
|
||||
-6483, -6442, -5739, -4544, -3043, -1431, 116, 1447, 2454,
|
||||
3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, -1597,
|
||||
-1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t YSF_LEVELA_HI = 1893;
|
||||
@@ -47,16 +41,19 @@ const q15_t YSF_LEVELD_LO = -948;
|
||||
|
||||
const uint8_t YSF_START_SYNC = 0x77U;
|
||||
const uint8_t YSF_END_SYNC = 0xFFU;
|
||||
const uint8_t YSF_HANG = 0x00U;
|
||||
|
||||
CYSFTX::CYSFTX() :
|
||||
m_buffer(1500U),
|
||||
m_buffer(4000U),
|
||||
m_modFilter(),
|
||||
m_modState(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_loDev(false)
|
||||
m_loDev(false),
|
||||
m_txHang(4800U), // 4s
|
||||
m_txCount(0U)
|
||||
{
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
@@ -66,12 +63,14 @@ m_loDev(false)
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
|
||||
void CYSFTX::process()
|
||||
{
|
||||
if (m_buffer.getData() == 0U && m_poLen == 0U)
|
||||
if (m_buffer.getData() == 0U && m_poLen == 0U && m_txCount == 0U)
|
||||
return;
|
||||
|
||||
if (m_poLen == 0U) {
|
||||
// If we have YSF data to transmit, do so.
|
||||
if (m_poLen == 0U && m_buffer.getData() > 0U) {
|
||||
if (!m_tx) {
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = YSF_START_SYNC;
|
||||
@@ -86,20 +85,37 @@ void CYSFTX::process()
|
||||
}
|
||||
|
||||
if (m_poLen > 0U) {
|
||||
// Transmit YSF data.
|
||||
uint16_t space = io.getSpace();
|
||||
|
||||
|
||||
while (space > (4U * YSF_RADIO_SYMBOL_LENGTH)) {
|
||||
uint8_t c = m_poBuffer[m_poPtr++];
|
||||
writeByte(c);
|
||||
|
||||
// Reduce space and reset the hang timer.
|
||||
space -= 4U * YSF_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
if (m_duplex)
|
||||
m_txCount = m_txHang;
|
||||
|
||||
if (m_poPtr >= m_poLen) {
|
||||
m_poPtr = 0U;
|
||||
m_poLen = 0U;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (m_txCount > 0U) {
|
||||
// Transmit silence until the hang timer has expired.
|
||||
uint16_t space = io.getSpace();
|
||||
|
||||
while (space > (4U * YSF_RADIO_SYMBOL_LENGTH)) {
|
||||
writeSilence();
|
||||
|
||||
space -= 4U * YSF_RADIO_SYMBOL_LENGTH;
|
||||
m_txCount--;
|
||||
|
||||
if (m_txCount == 0U)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +163,16 @@ void CYSFTX::writeByte(uint8_t c)
|
||||
io.write(STATE_YSF, outBuffer, YSF_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CYSFTX::writeSilence()
|
||||
{
|
||||
q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U};
|
||||
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
io.write(STATE_YSF, outBuffer, YSF_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CYSFTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
@@ -160,8 +186,9 @@ uint8_t CYSFTX::getSpace() const
|
||||
return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
void CYSFTX::setLoDev(bool on)
|
||||
void CYSFTX::setParams(bool on, uint8_t txHang)
|
||||
{
|
||||
m_loDev = on;
|
||||
m_loDev = on;
|
||||
m_txHang = txHang * 1200U;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
uint8_t getSpace() const;
|
||||
|
||||
void setLoDev(bool on);
|
||||
void setParams(bool on, uint8_t txHang);
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
@@ -46,8 +46,11 @@ private:
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
bool m_loDev;
|
||||
uint32_t m_txHang;
|
||||
uint32_t m_txCount;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
void writeSilence();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ EOF
|
||||
"3") sed -e 's/\/\/ #define EXTERNAL_OSC 14400000/#define EXTERNAL_OSC 14400000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "14.400 MHz clock enabled";;
|
||||
"4") sed -e 's/\/\/ #define EXTERNAL_OSC 19200000/#define EXTERNAL_OSC 19200000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "19.200 MHz clock enabled";;
|
||||
"5") sed -e 's/\/\/ #define USE_COS_AS_LOCKOUT /#define USE_COS_AS_LOCKOUT/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "COS as Lockout enabled";;
|
||||
"6") sed -e 's/\/\/ #define ARDUINO_MODE_PINS/#define ARDUINO_MODE_PINS/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Mode pins enabled";;
|
||||
"6") sed -e 's/\/\/ #define MODE_PINS/#define MODE_PINS/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Mode pins enabled";;
|
||||
"7") sed -e 's/\/\/ #define ARDUINO_DUE_PAPA/#define ARDUINO_DUE_PAPA/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for the PAPA board enabled";;
|
||||
"8") sed -e 's/\/\/ #define ARDUINO_DUE_ZUM_V10/#define ARDUINO_DUE_ZUM_V10/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for ZUM V1.0 and V1.0.1 boards enabled";;
|
||||
"9") sed -e 's/\/\/ #define ARDUINO_DUE_NTH/#define ARDUINO_DUE_NTH/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for SP8NTH board enabled";;
|
||||
|
||||
Reference in New Issue
Block a user