Compare commits

...
123 Commits
Author SHA1 Message Date
Jonathan Naylor bd4096b6be Revert "Make CTCSS threshold RXLevel and RXBoost agnostic" 2020-04-25 23:48:02 +01:00
Jonathan Naylor f92d93772c Merge branch 'FM' of https://github.com/g4klx/MMDVM into FM 2020-04-25 22:32:37 +01:00
Jonathan Naylor b963b2b848 Add optional over deviation blanking. 2020-04-25 22:32:10 +01:00
Jonathan Naylor 9dadb51f11 Merge pull request #229 from F4FXL/FM
Make CTCSS threshold RXLevel and RXBoost agnostic
2020-04-25 22:13:09 +01:00
Geoffrey Merck 23cf22d87d remove Debug info 2020-04-25 23:07:41 +02:00
Geoffrey Merck d2d50e21e1 CTCSS threshold is level agnostic, experimental 2020-04-25 22:12:16 +02:00
Jonathan Naylor 8c38fdb36d Clean out the CTCSS decoder coefficient before initialising. 2020-04-25 17:37:37 +01:00
Jonathan Naylor 4fe1c11250 Remove memory leak and other configuration funnies. 2020-04-25 17:29:10 +01:00
Jonathan Naylor 394ab8dfd1 Merge pull request #228 from m0vse/FM
Fix for Ack/Callsign corruption in FM branch
2020-04-25 16:24:10 +01:00
m0vse 5295e0bdc3 Fix for Ack/Callsign corruption 2020-04-25 16:13:13 +01:00
Jonathan Naylor c638dbe55a Merge branch 'FM' of https://github.com/g4klx/MMDVM into FM 2020-04-25 15:17:53 +01:00
Jonathan Naylor 6ac6deba10 Add two more FM parameters. 2020-04-25 15:17:11 +01:00
Jonathan Naylor d2ce76fe89 Merge pull request #227 from F4FXL/FM
Fix STM32F10X compile complaining about duplicate stuff
2020-04-25 08:12:03 +01:00
Geoffrey Merck 0b27ec5443 Fix STM32F10X compile complaining about duplicate stuff 2020-04-25 08:05:31 +02:00
Jonathan Naylor 868d33ebd2 Rescale the CTCSS TX level. 2020-04-24 15:00:40 +01:00
Jonathan Naylor 9fab1e0823 Rescale some of the tone levels. 2020-04-24 13:13:25 +01:00
Jonathan Naylor 242bc7b322 Bump the version date. 2020-04-24 12:54:50 +01:00
Jonathan Naylor e5c25f55fe Remove the FM RX level. 2020-04-24 12:51:10 +01:00
Jonathan Naylor 33f88344c3 Run the holdoff timer permamnently. 2020-04-24 12:15:58 +01:00
Jonathan Naylor 103d937152 Remove unneeded line. 2020-04-24 11:52:47 +01:00
Jonathan Naylor e721dedf1e Merge pull request #225 from F4FXL/FM
Add FM Filtering
2020-04-24 08:46:45 +01:00
Geoffrey Merck 176e067ab9 Merge branch 'FM' of https://github.com/F4FXL/MMDVM into FM 2020-04-24 09:41:14 +02:00
Geoffrey Merck 2057ef5206 clean up 2020-04-24 09:40:06 +02:00
Geoffrey Merck 5c2659deaa Add FM filtering 2020-04-24 09:40:06 +02:00
Geoffrey Merck 4f9d5a8c70 3rd order IIR 2020-04-24 09:40:06 +02:00
Geoffrey Merck 8ecd8b67f4 Using 2 stage IIR filter 2020-04-24 09:40:06 +02:00
Geoffrey Merck 22c40e1d96 Remove bit roation 2020-04-24 09:40:03 +02:00
Geoffrey Merck a720ceeb54 clean up 2020-04-24 09:21:13 +02:00
Geoffrey Merck f8d082e2dd Add FM filtering 2020-04-24 09:10:50 +02:00
Geoffrey Merck 507a4bf64c 3rd order IIR 2020-04-23 22:37:29 +02:00
Jonathan Naylor 7a75db2aeb Remove the bit shift of the sample. 2020-04-23 21:30:41 +01:00
Geoffrey Merck ce0aebce48 Using 2 stage IIR filter 2020-04-23 21:16:52 +02:00
Geoffrey Merck 906de3ad11 Remove bit roation 2020-04-23 21:16:20 +02:00
Jonathan Naylor bf5ad25609 A working fixed point Goertzel based on simulations. 2020-04-23 12:36:34 +01:00
Jonathan Naylor f19009b132 Allow for two levels of the callsign. 2020-04-22 22:10:34 +01:00
Jonathan Naylor 526a53cd8c Use fixed point for the Goertzel algorithm. 2020-04-22 17:40:01 +01:00
Jonathan Naylor 57730d7f81 Implement an efficient FIR filter. 2020-04-22 14:37:23 +01:00
Jonathan Naylor f936bc6fe8 Use a shorter filter. 2020-04-22 14:15:07 +01:00
Jonathan Naylor 3f1f1d1586 Small tidy-ups. 2020-04-22 14:08:34 +01:00
Jonathan Naylor 7940025095 Merge pull request #223 from F4FXL/FM
One loop to rule them all !
2020-04-22 13:30:56 +01:00
Geoffrey Merck 63847ebe62 One loop to rule them all !
Change CFM::Samples and CFMCTCSSRX::process to be able to process all
stuff in one and only loop.
2020-04-22 11:04:27 +02:00
Jonathan Naylor 03f5056236 Small code cleanups. 2020-04-21 22:30:28 +01:00
Jonathan Naylor 23ff29c8f1 Merge pull request #222 from F4FXL/FM
Almost working FM
2020-04-21 22:09:58 +01:00
Geoffrey Merck 1b0229ca6c Attemp to do filtering 2020-04-21 23:03:30 +02:00
Geoffrey Merck ae6cbcd9e7 Minor clean up 2020-04-21 22:43:59 +02:00
Geoffrey Merck 1c599138ad GetAudio do not take any arguments 2020-04-21 22:00:11 +02:00
Geoffrey Merck 9170c398be Reenable CTCSS 2020-04-21 21:36:30 +02:00
Geoffrey Merck 04fd9838b5 Fix state machine wrong kerchunk 2020-04-21 21:00:09 +02:00
Geoffrey Merck 8d340cbfba (almost) One loop to rule all the samples
Avoid looping over and over the samples. One
2020-04-21 20:38:02 +02:00
Geoffrey Merck 87c3b57ae4 Stripping ut stuff, testing 2020-04-21 18:45:11 +02:00
Geoffrey Merck 7befef2f4a Avoid looping samples 2 times, convert each sample on the fly 2020-04-21 18:41:27 +02:00
Geoffrey Merck b2dcbb2df8 Merge remote-tracking branch 'upstream/FM' into FM 2020-04-21 07:03:35 +02:00
Jonathan Naylor 0a51b4c34e Store FM output in a ring buffer. 2020-04-20 21:57:21 +01:00
Geoffrey Merck 3b653dca6a Merge remote-tracking branch 'upstream/FM' into FM 2020-04-20 20:49:58 +02:00
Jonathan Naylor 046a1de51a Reduce the size of the main FM filter. 2020-04-20 13:54:11 +01:00
Jonathan Naylor ae58f8eb64 Don't square the threshold value. 2020-04-20 13:28:07 +01:00
Geoffrey Merck e4c351d5d0 delete old BS 2020-04-20 07:46:03 +02:00
Geoffrey Merck 0b0a59497f Merge branch 'FM' of https://github.com/F4FXL/MMDVM into FM 2020-04-20 07:43:07 +02:00
Geoffrey Merck 939b019592 delete old BS 2020-04-20 07:42:39 +02:00
Geoffrey Merck 3d54519dbf add CTCSS decoder 2020-04-20 07:42:39 +02:00
Geoffrey Merck 94f566aacc Add Goertzel 2020-04-20 07:42:39 +02:00
Geoffrey Merck a765e863c0 delete old BS 2020-04-20 07:42:39 +02:00
Geoffrey Merck 9e552ba1ab add CTCSS decoder 2020-04-20 07:39:19 +02:00
Geoffrey Merck b03fead62d Add Goertzel 2020-04-20 07:39:19 +02:00
Geoffrey Merck 2825c25607 Merge remote-tracking branch 'upstream/FM' into FM 2020-04-20 07:23:59 +02:00
Jonathan Naylor 5ed8eeda6f Allow kerchunk audio to pass through. 2020-04-19 21:40:51 +01:00
Jonathan Naylor 1aef1a39f2 Revert the CTCSS decoder to floats. 2020-04-19 21:21:10 +01:00
Jonathan Naylor a310bf0547 Adjust the coefficients again. 2020-04-19 19:46:44 +01:00
Geoffrey Merck 116b51bd8d delete old BS 2020-04-19 20:45:14 +02:00
Geoffrey Merck c05e7bf873 Merge branch 'FM' of https://github.com/F4FXL/MMDVM into FM 2020-04-19 20:44:18 +02:00
Geoffrey Merck c14ae56e2d delete old BS 2020-04-19 20:43:50 +02:00
Geoffrey Merck 32a71bfbf6 Use long based algorithm 2020-04-19 20:42:57 +02:00
Geoffrey Merck 7435074c0f add CTCSS decoder 2020-04-19 19:34:47 +02:00
Geoffrey Merck ec202d7fb9 Add Goertzel 2020-04-19 19:34:47 +02:00
Jonathan Naylor 6575cba982 Rescale the coefficient. 2020-04-19 17:59:04 +01:00
Jonathan Naylor a17bd9cc1d Don't scale up the sample values. 2020-04-19 17:37:24 +01:00
Jonathan Naylor f7e5c2436c Convert the Goertzel algorithm to Q15 and Q31. 2020-04-19 16:52:11 +01:00
Jonathan Naylor 85816d5bec Re-add COS. 2020-04-18 16:59:25 +01:00
Jonathan Naylor ebdba2bdb9 Convert to using floats. 2020-04-18 16:48:30 +01:00
Jonathan Naylor 6d5b864cba Start the Goertzel development. 2020-04-18 16:07:52 +01:00
Jonathan Naylor c8a450b00d Ensure all timers are stopped when we leave FM mode. 2020-04-18 15:51:49 +01:00
Jonathan Naylor 61ccd79c5f Set up the FM mode pins, partially. 2020-04-18 14:31:21 +01:00
Jonathan Naylor c55002534e Rename the Goertzel class to CTCSSRX. 2020-04-18 13:55:34 +01:00
Jonathan Naylor 8ca756ec22 Simplify the FM arguments. 2020-04-18 13:47:41 +01:00
Jonathan Naylor d1c106b3b8 Add state machine debugging messages. 2020-04-17 18:53:28 +01:00
Jonathan Naylor 564260b2f1 Add the main audio filter. 2020-04-17 18:45:42 +01:00
Jonathan Naylor 4a85122483 CTCSS tones are added to the audio, not replacing it. 2020-04-17 14:11:42 +01:00
Jonathan Naylor bc889f3d26 Handle some extra edge cases. 2020-04-17 13:11:21 +01:00
Jonathan Naylor 95e76f387c Complete the CW keyer. 2020-04-16 21:15:15 +01:00
Jonathan Naylor 4a5f985d02 More efficient CTCSS tone generation. 2020-04-16 14:27:33 +01:00
Jonathan Naylor bbe56f5082 Return configuration errors. 2020-04-16 14:00:31 +01:00
Jonathan Naylor 4b0c3d88f3 More work on the keyer. 2020-04-15 22:41:30 +01:00
Jonathan Naylor c103ac91b3 Add the CTCSS tone generator. 2020-04-15 19:49:26 +01:00
Jonathan Naylor ebd87584da Add the timeout tones. 2020-04-15 18:38:39 +01:00
Jonathan Naylor a9a985182f Add the timer class functionality. 2020-04-15 17:18:01 +01:00
Jonathan Naylor db6fde90e0 Add the intial state machine. 2020-04-15 16:31:49 +01:00
Jonathan Naylor 74228c506e Add stubs for FM classes. 2020-04-15 15:24:01 +01:00
Geoffrey Merck bcd43006ac Merge branch 'FM' of https://github.com/F4FXL/MMDVM into FM 2020-04-14 13:33:59 +02:00
Geoffrey Merck cf616caef9 add CTCSS decoder 2020-04-14 13:33:35 +02:00
Geoffrey Merck 7ab6e51c2c Add Goertzel 2020-04-14 13:33:35 +02:00
Jonathan Naylor 7a549e96ba Handle the net ack parameter. 2020-04-14 12:23:26 +01:00
Geoffrey Merck 3e4d709225 Merge remote-tracking branch 'upstream/FM' into FM 2020-04-14 12:16:54 +02:00
Geoffrey Merck 031563e727 add CTCSS decoder 2020-04-14 12:16:07 +02:00
Geoffrey Merck 3f80248477 Add Goertzel 2020-04-14 12:15:25 +02:00
Jonathan Naylor c84d81d91c Add the minimum time for a 'K' parameter. 2020-04-13 15:54:37 +01:00
Jonathan Naylor ef65268b45 First version of initialisation. 2020-04-12 15:28:56 +01:00
Jonathan Naylor 9d725b9479 Beginnings of FM controller support. 2020-04-11 22:08:17 +01:00
Andy CA6JAU b4647d8d1b Merge pull request #214 from veraabad/master
Fixed deploy-pi-f7
2019-10-23 21:22:43 -03:00
Abad Vera f28a22802d Fixed deploy-pi-f7 2019-10-23 14:36:34 -07:00
Jonathan Naylor 8a60fc1ecb Fix id string for the rpt_hat_tgo. 2019-07-15 09:06:43 +01:00
Jonathan Naylor dfad6d8409 Revert "Merge pull request #204 from shawnchain/support_udid"
This reverts commit b94b8afb77, reversing
changes made to 7c863671c9.
2019-06-04 10:12:53 +01:00
Jonathan Naylor b94b8afb77 Merge pull request #204 from shawnchain/support_udid
Fix HW_TYPE string of RPT_HAT_TGO for MMDVMCal and add UDID support
2019-05-17 08:08:06 +01:00
Shawn Chain 0f37bfa20d arduino_due support 2019-05-16 23:29:28 +08:00
Shawn Chain d6d03158d0 Fix HWType string of RPT_HAT_TGO to work with MMDVMCal and add udid support to the firmware 2019-05-16 23:22:27 +08:00
Jonathan Naylor 7c863671c9 Merge pull request #203 from shawnchain/stm32f1
add STM32F10X_Lib for stm32f105 support
2019-05-14 13:24:48 +01:00
Shawn Chain 2a6db2ab52 Make STM32F1_POG as default type when Makefile.CMSIS is used 2019-05-14 11:53:21 +08:00
Shawn Chain c8a345a301 rollback changes on Config.h 2019-05-14 11:43:29 +08:00
Shawn Chain 37efcb3d4f add STM32F10X_Lib 2019-05-13 21:59:32 +08:00
Jonathan Naylor ff7a9fdd4e Simplify the state change. 2019-03-01 08:00:20 +00:00
Jonathan Naylor 9ccbb842bf Merge branch 'pocsagcal' 2019-01-31 07:56:41 +00:00
Jonathan Naylor f785a5260a Merge branch 'master' of https://github.com/g4klx/MMDVM 2019-01-30 13:51:22 +00:00
Jonathan Naylor 03716daca0 Bump the version date. 2019-01-30 13:51:05 +00:00
phl0 9987140790 Fix damn typo 2019-01-30 14:49:21 +01:00
32 changed files with 1994 additions and 221 deletions
+3
View File
@@ -4,3 +4,6 @@
[submodule "STM32F7XX_Lib"] [submodule "STM32F7XX_Lib"]
path = STM32F7XX_Lib path = STM32F7XX_Lib
url = https://github.com/juribeparada/STM32F7XX_Lib.git url = https://github.com/juribeparada/STM32F7XX_Lib.git
[submodule "STM32F10X_Lib"]
path = STM32F10X_Lib
url = https://github.com/shawnchain/STM32F10X_Lib.git
+9 -5
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -44,11 +44,13 @@
// For the original Arduino Due pin layout // For the original Arduino Due pin layout
// #define ARDUINO_DUE_PAPA // #define ARDUINO_DUE_PAPA
#if defined(STM32F1)
// For the SQ6POG board
#define STM32F1_POG
#else
// For the ZUM V1.0 and V1.0.1 boards pin layout // For the ZUM V1.0 and V1.0.1 boards pin layout
#define ARDUINO_DUE_ZUM_V10 #define ARDUINO_DUE_ZUM_V10
#endif
// For the SQ6POG board
// #define STM32F1_POG
// For the SP8NTH board // For the SP8NTH board
// #define ARDUINO_DUE_NTH // #define ARDUINO_DUE_NTH
@@ -82,5 +84,7 @@
// Use the D-Star and DMR LEDs for POCSAG // Use the D-Star and DMR LEDs for POCSAG
// #define USE_ALTERNATE_POCSAG_LEDS // #define USE_ALTERNATE_POCSAG_LEDS
#endif // Use the D-Star and YSF LEDs for FM
// #define USE_ALTERNATE_FM_LEDS
#endif
+399
View File
@@ -0,0 +1,399 @@
/*
* Copyright (C) 2020 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 "FM.h"
CFM::CFM() :
m_callsign(),
m_rfAck(),
m_ctcssRX(),
m_ctcssTX(),
m_timeoutTone(),
m_state(FS_LISTENING),
m_callsignAtStart(false),
m_callsignAtEnd(false),
m_callsignTimer(),
m_timeoutTimer(),
m_holdoffTimer(),
m_kerchunkTimer(),
m_ackMinTimer(),
m_ackDelayTimer(),
m_hangTimer(),
m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),
m_filterStage2(32768, 0,-32768, 32768, -50339, 19052),
m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460),
m_blanking(),
m_useCOS(true),
m_rxBoost(1U)
{
}
void CFM::samples(bool cos, q15_t* samples, uint8_t length)
{
if (!m_useCOS)
cos = true;
uint8_t i = 0U;
for (; i < length; i++) {
q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access
CTCSSState ctcssState = m_ctcssRX.process(currentSample);
if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) {
//Not enough samples to determine if you have CTCSS, just carry on
continue;
} else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) {
//we had enough samples for CTCSS and we are in some other mode than FM
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos, i + 1U);
if (m_modemState != STATE_FM)
continue;
} else if (CTCSS_READY(ctcssState) && m_modemState == STATE_FM) {
//We had enough samples for CTCSS and we are in FM mode, trigger the state machine
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos, i + 1U);
if (m_modemState != STATE_FM)
break;
} else if (CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) {
//Not enough samples for CTCSS but we already are in FM, trigger the state machine
//but do not trigger the state machine on every single sample, save CPU!
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos, i + 1U);
}
// Only let audio through when relaying audio
if (m_state == FS_RELAYING || m_state == FS_KERCHUNK)
currentSample = m_blanking.process(currentSample);
else
currentSample = 0U;
currentSample *= m_rxBoost;
if (!m_callsign.isRunning())
currentSample += m_rfAck.getHighAudio();
if (!m_rfAck.isRunning()) {
if (m_state == FS_LISTENING)
currentSample += m_callsign.getHighAudio();
else
currentSample += m_callsign.getLowAudio();
}
if (!m_callsign.isRunning() && !m_rfAck.isRunning())
currentSample += m_timeoutTone.getAudio();
currentSample = q15_t(m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample))));
currentSample += m_ctcssTX.getAudio();
samples[i] = currentSample;
}
if (m_modemState == STATE_FM)
io.write(STATE_FM, samples, i);//only write the actual number of processed samples to IO
}
void CFM::process()
{
}
void CFM::reset()
{
m_ctcssRX.reset();
}
uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd)
{
m_callsignAtStart = callsignAtStart;
m_callsignAtEnd = callsignAtEnd;
uint16_t holdoffTime = holdoff * 60U;
uint16_t callsignTime = time * 60U;
m_holdoffTimer.setTimeout(holdoffTime, 0U);
m_callsignTimer.setTimeout(callsignTime, 0U);
if (holdoffTime > 0U)
m_holdoffTimer.start();
return m_callsign.setParams(callsign, speed, frequency, highLevel, lowLevel);
}
uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level)
{
m_ackDelayTimer.setTimeout(0U, delay);
m_ackMinTimer.setTimeout(minTime, 0U);
return m_rfAck.setParams(rfAck, speed, frequency, level, level);
}
uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev)
{
m_useCOS = useCOS;
m_rxBoost = q15_t(rxBoost);
m_timeoutTimer.setTimeout(timeout, 0U);
m_kerchunkTimer.setTimeout(kerchunkTime, 0U);
m_hangTimer.setTimeout(hangTime, 0U);
m_timeoutTone.setParams(timeoutLevel);
m_blanking.setParams(maxDev, timeoutLevel);
uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold);
if (ret != 0U)
return ret;
return m_ctcssTX.setParams(ctcssFrequency, ctcssLevel);
}
void CFM::stateMachine(bool validSignal, uint8_t length)
{
m_callsignTimer.clock(length);
m_timeoutTimer.clock(length);
m_holdoffTimer.clock(length);
m_kerchunkTimer.clock(length);
m_ackMinTimer.clock(length);
m_ackDelayTimer.clock(length);
m_hangTimer.clock(length);
switch (m_state) {
case FS_LISTENING:
listeningState(validSignal);
break;
case FS_KERCHUNK:
kerchunkState(validSignal);
break;
case FS_RELAYING:
relayingState(validSignal);
break;
case FS_RELAYING_WAIT:
relayingWaitState(validSignal);
break;
case FS_TIMEOUT:
timeoutState(validSignal);
break;
case FS_TIMEOUT_WAIT:
timeoutWaitState(validSignal);
break;
case FS_HANG:
hangState(validSignal);
break;
default:
break;
}
if (m_state == FS_LISTENING && m_modemState == STATE_FM) {
if (!m_callsign.isRunning() && !m_rfAck.isRunning()) {
DEBUG1("Change to STATE_IDLE");
m_modemState = STATE_IDLE;
m_callsignTimer.stop();
m_timeoutTimer.stop();
m_kerchunkTimer.stop();
m_ackMinTimer.stop();
m_ackDelayTimer.stop();
m_hangTimer.stop();
}
}
}
void CFM::listeningState(bool validSignal)
{
if (validSignal) {
if (m_kerchunkTimer.getTimeout() > 0U) {
DEBUG1("State to KERCHUNK");
m_state = FS_KERCHUNK;
m_kerchunkTimer.start();
} else {
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
if (m_callsignAtStart)
sendCallsign();
}
beginRelaying();
m_callsignTimer.start();
DEBUG1("Change to STATE_FM");
m_modemState = STATE_FM;
}
}
void CFM::kerchunkState(bool validSignal)
{
if (validSignal) {
if (m_kerchunkTimer.hasExpired()) {
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
m_kerchunkTimer.stop();
}
} else {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_kerchunkTimer.stop();
m_timeoutTimer.stop();
m_ackMinTimer.stop();
m_callsignTimer.stop();
}
}
void CFM::relayingState(bool validSignal)
{
if (validSignal) {
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT");
m_state = FS_TIMEOUT;
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_timeoutTone.start();
}
} else {
DEBUG1("State to RELAYING_WAIT");
m_state = FS_RELAYING_WAIT;
m_ackDelayTimer.start();
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::relayingWaitState(bool validSignal)
{
if (validSignal) {
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
if (m_ackMinTimer.isRunning()) {
if (m_ackMinTimer.hasExpired()) {
DEBUG1("Send ack");
m_rfAck.start();
m_ackMinTimer.stop();
}
} else {
DEBUG1("Send ack");
m_rfAck.start();
m_ackMinTimer.stop();
}
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
m_hangTimer.start();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::hangState(bool validSignal)
{
if (validSignal) {
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
DEBUG1("Stop ack");
m_rfAck.stop();
beginRelaying();
} else {
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_hangTimer.stop();
if (m_callsignAtEnd)
sendCallsign();
m_callsignTimer.stop();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::timeoutState(bool validSignal)
{
if (!validSignal) {
DEBUG1("State to TIMEOUT_WAIT");
m_state = FS_TIMEOUT_WAIT;
m_ackDelayTimer.start();
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::timeoutWaitState(bool validSignal)
{
if (validSignal) {
DEBUG1("State to TIMEOUT");
m_state = FS_TIMEOUT;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
m_timeoutTone.stop();
DEBUG1("Send ack");
m_rfAck.start();
m_ackDelayTimer.stop();
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_hangTimer.start();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::sendCallsign()
{
if (m_holdoffTimer.isRunning()) {
if (m_holdoffTimer.hasExpired()) {
DEBUG1("Send callsign");
m_callsign.start();
m_holdoffTimer.start();
}
} else {
DEBUG1("Send callsign");
m_callsign.start();
}
}
void CFM::beginRelaying()
{
m_timeoutTimer.start();
m_ackMinTimer.start();
}
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2020 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(FM_H)
#define FM_H
#include "Config.h"
#include "FMBlanking.h"
#include "FMCTCSSRX.h"
#include "FMCTCSSTX.h"
#include "FMTimeout.h"
#include "FMKeyer.h"
#include "FMTimer.h"
#include "FMDirectForm1.h"
enum FM_STATE {
FS_LISTENING,
FS_KERCHUNK,
FS_RELAYING,
FS_RELAYING_WAIT,
FS_TIMEOUT,
FS_TIMEOUT_WAIT,
FS_HANG
};
class CFM {
public:
CFM();
void samples(bool cos, q15_t* samples, uint8_t length);
void process();
void reset();
uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd);
uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level);
uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev);
private:
CFMKeyer m_callsign;
CFMKeyer m_rfAck;
CFMCTCSSRX m_ctcssRX;
CFMCTCSSTX m_ctcssTX;
CFMTimeout m_timeoutTone;
FM_STATE m_state;
bool m_callsignAtStart;
bool m_callsignAtEnd;
CFMTimer m_callsignTimer;
CFMTimer m_timeoutTimer;
CFMTimer m_holdoffTimer;
CFMTimer m_kerchunkTimer;
CFMTimer m_ackMinTimer;
CFMTimer m_ackDelayTimer;
CFMTimer m_hangTimer;
CFMDirectFormI m_filterStage1;
CFMDirectFormI m_filterStage2;
CFMDirectFormI m_filterStage3;
CFMBlanking m_blanking;
bool m_useCOS;
q15_t m_rxBoost;
void stateMachine(bool validSignal, uint8_t length);
void listeningState(bool validSignal);
void kerchunkState(bool validSignal);
void relayingState(bool validSignal);
void relayingWaitState(bool validSignal);
void timeoutState(bool validSignal);
void timeoutWaitState(bool validSignal);
void hangState(bool validSignal);
void sendCallsign();
void beginRelaying();
};
#endif
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 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 "FMBlanking.h"
// 2000 Hz sine wave at 24000 Hz sample rate
const q31_t TONE[] = {0, 16384, 28378, 32767, 28378, 16384, 0, -16383, -28377, -32767, -28377, -16383};
const uint8_t TONE_LEN = 12U;
const uint32_t BLEEP_LEN = 2400U; // 100ms
CFMBlanking::CFMBlanking() :
m_posValue(0),
m_negValue(0),
m_level(128 * 128),
m_running(false),
m_pos(0U),
m_n(0U)
{
}
void CFMBlanking::setParams(uint8_t value, uint8_t level)
{
m_posValue = q15_t(value * 128);
m_negValue = -m_posValue;
m_level = q15_t(level * 128);
}
q15_t CFMBlanking::process(q15_t sample)
{
if (m_posValue == 0)
return sample;
if (!m_running) {
if (sample >= m_posValue) {
m_running = true;
m_pos = 0U;
m_n = 0U;
} else if (sample <= m_negValue) {
m_running = true;
m_pos = 0U;
m_n = 0U;
}
}
if (!m_running)
return sample;
if (m_pos <= BLEEP_LEN) {
q31_t value = TONE[m_n++] * m_level;
sample = q15_t(__SSAT((value >> 15), 16));
if (m_n >= TONE_LEN)
m_n = 0U;
} else {
sample = 0;
}
m_pos++;
if (m_pos >= 12000U)
m_running = false;
return sample;
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2020 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(FMBlanking_H)
#define FMBlanking_H
#include "Config.h"
class CFMBlanking {
public:
CFMBlanking();
void setParams(uint8_t value, uint8_t level);
q15_t process(q15_t sample);
private:
q15_t m_posValue;
q15_t m_negValue;
q15_t m_level;
bool m_running;
uint32_t m_pos;
uint8_t m_n;
};
#endif
+172
View File
@@ -0,0 +1,172 @@
/*
* Copyright (C) 2020 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 "FMCTCSSRX.h"
const struct RX_CTCSS_TABLE {
uint8_t frequency;
q63_t coeffDivTwo;
} RX_CTCSS_TABLE_DATA[] = {
{ 67U, 2147153298},
{ 69U, 2147130228},
{ 71U, 2147103212},
{ 74U, 2147076297},
{ 77U, 2147047330},
{ 79U, 2147016195},
{ 82U, 2146982775},
{ 85U, 2146946945},
{ 88U, 2146907275},
{ 91U, 2146867538},
{ 94U, 2146822298},
{ 97U, 2146785526},
{100U, 2146747759},
{103U, 2146695349},
{107U, 2146637984},
{110U, 2146578604},
{114U, 2146513835},
{118U, 2146445080},
{123U, 2146370355},
{127U, 2146291161},
{131U, 2146205372},
{136U, 2146112589},
{141U, 2146014479},
{146U, 2145910829},
{151U, 2145796971},
{156U, 2145676831},
{159U, 2145604646},
{162U, 2145547790},
{165U, 2145468230},
{167U, 2145409363},
{171U, 2145324517},
{173U, 2145261046},
{177U, 2145170643},
{179U, 2145102321},
{183U, 2145006080},
{186U, 2144932648},
{189U, 2144830280},
{192U, 2144748638},
{196U, 2144639788},
{199U, 2144555290},
{203U, 2144436713},
{206U, 2144346237},
{210U, 2144217348},
{218U, 2143983951},
{225U, 2143735870},
{229U, 2143622139},
{233U, 2143469001},
{241U, 2143182299},
{250U, 2142874683},
{254U, 2142733729}};
const uint8_t CTCSS_TABLE_DATA_LEN = 50U;
// 4Hz bandwidth
const uint16_t N = 24000U / 4U;
CFMCTCSSRX::CFMCTCSSRX() :
m_coeffDivTwo(0),
m_threshold(0),
m_count(0U),
m_q0(0),
m_q1(0),
m_result(CTS_NONE)
{
}
uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold)
{
m_coeffDivTwo = 0;
for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) {
if (RX_CTCSS_TABLE_DATA[i].frequency == frequency) {
m_coeffDivTwo = RX_CTCSS_TABLE_DATA[i].coeffDivTwo;
break;
}
}
if (m_coeffDivTwo == 0)
return 4U;
m_threshold = q31_t(threshold);
return 0U;
}
CTCSSState CFMCTCSSRX::process(q15_t sample)
{
m_result = m_result & (~CTS_READY);
q31_t q2 = m_q1;
m_q1 = m_q0;
// Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1
q63_t t1 = m_coeffDivTwo * m_q1;
q31_t t2 = __SSAT((t1 >> 31), 31);
q31_t t3 = t2 * 2;
// m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample
m_q0 = t3 - q2 + q31_t(sample);
m_count++;
if (m_count == N) {
// Q31 multiplication, t2 = m_q0 * m_q0
q63_t t1 = q63_t(m_q0) * q63_t(m_q0);
q31_t t2 = __SSAT((t1 >> 31), 31);
// Q31 multiplication, t4 = m_q0 * m_q0
q63_t t3 = q63_t(m_q1) * q63_t(m_q1);
q31_t t4 = __SSAT((t3 >> 31), 31);
// Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2
q63_t t5 = q63_t(m_q0) * q63_t(m_q1);
q31_t t6 = __SSAT((t5 >> 31), 31);
q63_t t7 = t6 * m_coeffDivTwo;
q31_t t8 = __SSAT((t7 >> 31), 31);
q31_t t9 = t8 * 2;
// value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2
q31_t value = t2 + t4 - t9;
m_result = m_result | CTS_READY;
if (value >= m_threshold)
m_result = m_result | CTS_VALID;
else
m_result = m_result & ~CTS_VALID;
m_count = 0U;
m_q0 = 0;
m_q1 = 0;
}
return m_result;
}
CTCSSState CFMCTCSSRX::getState()
{
return m_result;
}
void CFMCTCSSRX::reset()
{
m_q0 = 0;
m_q1 = 0;
m_result = CTS_NONE;
m_count = 0U;
}
+73
View File
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2020 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(FMCTCSSRX_H)
#define FMCTCSSRX_H
#include "Config.h"
enum CTCSSState
{
CTS_NONE = 0,
CTS_READY = 1,
CTS_VALID = 2,
CTS_READY_VALID = CTS_READY | CTS_VALID
};
inline CTCSSState operator|(CTCSSState a, CTCSSState b)
{
return static_cast<CTCSSState>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
}
inline CTCSSState operator&(CTCSSState a, CTCSSState b)
{
return static_cast<CTCSSState>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b));
}
inline CTCSSState operator~(CTCSSState a)
{
return static_cast<CTCSSState>(~(static_cast<uint8_t>(a)));
}
#define CTCSS_READY(a) ((a & CTS_READY) != 0)
#define CTCSS_NOT_READY(a) ((a & CTS_READY) == 0)
#define CTCSS_VALID(a) ((a & CTS_VALID) != 0)
#define CTCSS_NOT_VALID(a) ((a & CTS_VALID) == 0)
class CFMCTCSSRX {
public:
CFMCTCSSRX();
uint8_t setParams(uint8_t frequency, uint8_t threshold);
CTCSSState process(q15_t samples);
CTCSSState getState();
void reset();
private:
q63_t m_coeffDivTwo;
q31_t m_threshold;
uint16_t m_count;
q31_t m_q0;
q31_t m_q1;
CTCSSState m_result;
};
#endif
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright (C) 2020 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 "FMCTCSSTX.h"
const struct TX_CTCSS_TABLE {
uint8_t frequency;
uint16_t length;
q15_t increment;
} TX_CTCSS_TABLE_DATA[] = {
{ 67U, 358U, 92},
{ 69U, 346U, 95},
{ 71U, 334U, 99},
{ 74U, 323U, 102},
{ 77U, 312U, 106},
{ 79U, 301U, 109},
{ 82U, 291U, 113},
{ 85U, 281U, 117},
{ 88U, 271U, 121},
{ 91U, 262U, 125},
{ 94U, 253U, 130},
{ 97U, 246U, 133},
{100U, 240U, 137},
{103U, 232U, 142},
{107U, 224U, 147},
{110U, 216U, 152},
{114U, 209U, 157},
{118U, 202U, 163},
{123U, 195U, 168},
{127U, 189U, 174},
{131U, 182U, 180},
{136U, 176U, 187},
{141U, 170U, 193},
{146U, 164U, 200},
{151U, 159U, 207},
{156U, 153U, 214},
{159U, 150U, 219},
{162U, 148U, 222},
{165U, 145U, 226},
{167U, 143U, 230},
{171U, 140U, 234},
{173U, 138U, 238},
{177U, 135U, 243},
{179U, 133U, 246},
{183U, 131U, 251},
{186U, 129U, 255},
{189U, 126U, 260},
{192U, 124U, 264},
{196U, 122U, 269},
{199U, 120U, 273},
{203U, 118U, 278},
{206U, 116U, 282},
{210U, 114U, 288},
{218U, 110U, 298},
{225U, 106U, 309},
{229U, 105U, 313},
{233U, 103U, 319},
{241U, 99U, 331},
{250U, 96U, 342},
{254U, 94U, 347}
};
const uint8_t CTCSS_TABLE_DATA_LEN = 50U;
CFMCTCSSTX::CFMCTCSSTX() :
m_values(NULL),
m_length(0U),
m_n(0U)
{
}
uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level)
{
const TX_CTCSS_TABLE* entry = NULL;
for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) {
if (TX_CTCSS_TABLE_DATA[i].frequency == frequency) {
entry = TX_CTCSS_TABLE_DATA + i;
break;
}
}
if (entry == NULL)
return 4U;
m_length = entry->length;
delete[] m_values;
m_values = new q15_t[m_length];
q15_t arg = 0;
for (uint16_t i = 0U; i < m_length; i++) {
q31_t value = ::arm_sin_q15(arg) * q15_t(level * 13);
m_values[i] = q15_t(__SSAT((value >> 15), 16));
arg += entry->increment;
}
return 0U;
}
q15_t CFMCTCSSTX::getAudio()
{
q15_t sample = m_values[m_n++];
if(m_n >= m_length)
m_n = 0U;
return sample;
}
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 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(FMCTCSSTX_H)
#define FMCTCSSTX_H
#include "Config.h"
class CFMCTCSSTX {
public:
CFMCTCSSTX();
uint8_t setParams(uint8_t frequency, uint8_t level);
q15_t getAudio();
private:
q15_t* m_values;
uint16_t m_length;
uint16_t m_n;
};
#endif
+111
View File
@@ -0,0 +1,111 @@
/*******************************************************************************
This header file has been taken from:
"A Collection of Useful C++ Classes for Digital Signal Processing"
By Vinnie Falco
Bernd Porr adapted it for Linux and turned it into a filter using
fixed point arithmetic.
--------------------------------------------------------------------------------
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
Copyright (c) 2009 by Vinnie Falco
Copyright (C) 2013-2017, Bernd Porr, [email protected]
Copyright (C) 2020 , Mario Molitor , [email protected]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*******************************************************************************/
// based on https://raw.githubusercontent.com/berndporr/iir_fixed_point/master/DirectFormI.h
#include "Globals.h"
#ifndef DIRECTFORMI_H_
#define DIRECTFORMI_H_
class CFMDirectFormI
{
public:
// convenience function which takes the a0 argument but ignores it!
CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2,
const q31_t, const q31_t a1, const q31_t a2)
{
// FIR coefficients
c_b0 = b0;
c_b1 = b1;
c_b2 = b2;
// IIR coefficients
c_a1 = a1;
c_a2 = a2;
reset();
}
CFMDirectFormI(const CFMDirectFormI &my)
{
// delay line
m_x2 = my.m_x2; // x[n-2]
m_y2 = my.m_y2; // y[n-2]
m_x1 = my.m_x1; // x[n-1]
m_y1 = my.m_y1; // y[n-1]
// coefficients
c_b0 = my.c_b0;
c_b1 = my.c_b1;
c_b2 = my.c_b2; // FIR
c_a1 = my.c_a1;
c_a2 = my.c_a2; // IIR
}
void reset ()
{
m_x1 = 0;
m_x2 = 0;
m_y1 = 0;
m_y2 = 0;
}
// filtering operation: one sample in and one out
inline q15_t filter(const q15_t in)
{
// calculate the output
register q31_t out_upscaled = c_b0 * in //F4FXL puting stauration here made everything quiet, not sure why
+ c_b1 * m_x1
+ c_b2 * m_x2
- c_a1 * m_y1
- c_a2 * m_y2;
q15_t out = __SSAT(out_upscaled >> 15, 15);
// update the delay lines
m_x2 = m_x1;
m_y2 = m_y1;
m_x1 = in;
m_y1 = out;
return out;
}
private:
// delay line
q31_t m_x2; // x[n-2]
q31_t m_y2; // y[n-2]
q31_t m_x1; // x[n-1]
q31_t m_y1; // y[n-1]
// coefficients
q31_t c_b0,c_b1,c_b2; // FIR
q31_t c_a1,c_a2; // IIR
};
#endif /* DIRECTFORMI_H */
+45
View File
@@ -0,0 +1,45 @@
# based on https://github.com/berndporr/iir_fixed_point/blob/master/gen_coeff.py
import numpy as np
import scipy.signal as signal
import pylab as pl
# Calculate the coefficients for a pure fixed point
# integer filter
# sampling rate
fs = 24000
# cutoffs
f1 = 300
f2 = 2700
# ripple
rp = 0.2
# scaling factor in bits, do not change !
q = 15
# scaling factor as facor...
scaling_factor = 2**q
# let's generate a sequence of 2nd order IIR filters
#sos = signal.butter(2,[f1/fs*2,f2/fs*2],'pass',output='sos')
sos = signal.cheby1(3,rp,[f1/fs*2,f2/fs*2],'bandpass', output='sos')
sos = np.round((sos) * scaling_factor)
# print coefficients
for biquad in sos:
for coeff in biquad:
print(int(coeff),",",sep="",end="")
#print((coeff),",",sep="",end="")
print("")
# plot the frequency response
b,a = signal.sos2tf(sos)
w,h = signal.freqz(b,a)
pl.plot(w/np.pi/2*fs,20*np.log(np.abs(h)))
pl.xlabel('frequency/Hz');
pl.ylabel('gain/dB');
pl.ylim(top=1,bottom=-20);
pl.xlim(left=250, right=12000);
pl.show()
+210
View File
@@ -0,0 +1,210 @@
/*
* Copyright (C) 2020 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 "FMKeyer.h"
const struct {
char c;
uint32_t pattern;
uint8_t length;
} SYMBOL_LIST[] = {
{'A', 0xB8000000U, 8U},
{'B', 0xEA800000U, 12U},
{'C', 0xEBA00000U, 14U},
{'D', 0xEA000000U, 10U},
{'E', 0x80000000U, 4U},
{'F', 0xAE800000U, 12U},
{'G', 0xEE800000U, 12U},
{'H', 0xAA000000U, 10U},
{'I', 0xA0000000U, 6U},
{'J', 0xBBB80000U, 16U},
{'K', 0xEB800000U, 12U},
{'L', 0xBA800000U, 12U},
{'M', 0xEE000000U, 10U},
{'N', 0xE8000000U, 8U},
{'O', 0xEEE00000U, 14U},
{'P', 0xBBA00000U, 14U},
{'Q', 0xEEB80000U, 16U},
{'R', 0xBA000000U, 10U},
{'S', 0xA8000000U, 8U},
{'T', 0xE0000000U, 6U},
{'U', 0xAE000000U, 10U},
{'V', 0xAB800000U, 12U},
{'W', 0xBB800000U, 12U},
{'X', 0xEAE00000U, 14U},
{'Y', 0xEBB80000U, 16U},
{'Z', 0xEEA00000U, 14U},
{'1', 0xBBBB8000U, 20U},
{'2', 0xAEEE0000U, 18U},
{'3', 0xABB80000U, 16U},
{'4', 0xAAE00000U, 14U},
{'5', 0xAA800000U, 12U},
{'6', 0xEAA00000U, 14U},
{'7', 0xEEA80000U, 16U},
{'8', 0xEEEA0000U, 18U},
{'9', 0xEEEE8000U, 20U},
{'0', 0xEEEEE000U, 22U},
{'/', 0xEAE80000U, 16U},
{'?', 0xAEEA0000U, 18U},
{',', 0xEEAEE000U, 22U},
{'-', 0xEAAE0000U, 18U},
{'=', 0xEAB80000U, 16U},
{' ', 0x00000000U, 4U},
{0U, 0x00000000U, 0U}
};
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
#define WRITE_BIT_FM(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_BIT_FM(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
CFMKeyer::CFMKeyer() :
m_wanted(false),
m_poBuffer(),
m_poLen(0U),
m_poPos(0U),
m_dotLen(0U),
m_dotPos(0U),
m_audio(NULL),
m_audioLen(0U),
m_audioPos(0U),
m_highLevel(0U),
m_lowLevel(0)
{
}
uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel)
{
m_poLen = 0U;
for (uint8_t i = 0U; text[i] != '\0'; i++) {
for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) {
if (SYMBOL_LIST[j].c == text[i]) {
uint32_t MASK = 0x80000000U;
for (uint8_t k = 0U; k < SYMBOL_LIST[j].length; k++, m_poLen++, MASK >>= 1) {
bool b = (SYMBOL_LIST[j].pattern & MASK) == MASK;
WRITE_BIT_FM(m_poBuffer, m_poLen, b);
if (m_poLen >= 995U) {
m_poLen = 0U;
return 4U;
}
}
break;
}
}
}
m_highLevel = q15_t(highLevel);
m_lowLevel = q15_t(lowLevel);
m_dotLen = 24000U / speed; // In samples
m_audioLen = 24000U / frequency; // In samples
delete[] m_audio;
m_audio = new bool[m_audioLen];
for (uint16_t i = 0U; i < m_audioLen; i++) {
if (i < (m_audioLen / 2U))
m_audio[i] = true;
else
m_audio[i] = false;
}
return 0U;
}
q15_t CFMKeyer::getHighAudio()
{
q15_t output = 0U;
if (!m_wanted)
return 0U;
bool b = READ_BIT_FM(m_poBuffer, m_poPos);
if (b)
output = m_audio[m_audioPos] ? m_highLevel : -m_highLevel;
m_audioPos++;
if (m_audioPos >= m_audioLen)
m_audioPos = 0U;
m_dotPos++;
if (m_dotPos >= m_dotLen) {
m_dotPos = 0U;
m_poPos++;
if (m_poPos >= m_poLen) {
stop();
return output;
}
}
return output;
}
q15_t CFMKeyer::getLowAudio()
{
q15_t output = 0U;
if (!m_wanted)
return 0U;
bool b = READ_BIT_FM(m_poBuffer, m_poPos);
if (b)
output = m_audio[m_audioPos] ? m_lowLevel : -m_lowLevel;
m_audioPos++;
if (m_audioPos >= m_audioLen)
m_audioPos = 0U;
m_dotPos++;
if (m_dotPos >= m_dotLen) {
m_dotPos = 0U;
m_poPos++;
if (m_poPos >= m_poLen) {
stop();
return output;
}
}
return output;
}
void CFMKeyer::start()
{
if (isRunning())
return;
m_wanted = true;
m_poPos = 0U;
m_dotPos = 0U;
m_audioPos = 0U;
}
void CFMKeyer::stop()
{
m_wanted = false;
m_poPos = 0U;
m_dotPos = 0U;
m_audioPos = 0U;
}
bool CFMKeyer::isRunning() const
{
return m_poPos > 0U || m_dotPos > 0U || m_audioPos > 0U;
}
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 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(FMKeyer_H)
#define FMKeyer_H
#include "Config.h"
class CFMKeyer {
public:
CFMKeyer();
uint8_t setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel);
q15_t getHighAudio();
q15_t getLowAudio();
void start();
void stop();
bool isRunning() const;
private:
bool m_wanted;
uint8_t m_poBuffer[1000U];
uint16_t m_poLen;
uint16_t m_poPos;
uint16_t m_dotLen;
uint16_t m_dotPos;
bool* m_audio;
uint16_t m_audioLen;
uint16_t m_audioPos;
q15_t m_highLevel;
q15_t m_lowLevel;
};
#endif
+76
View File
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2020 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 "FMTimeout.h"
// 400 Hz sine wave at 24000 Hz sample rate
const q15_t BUSY_AUDIO[] = {0, 3426, 6813, 10126, 13328, 16384, 19261, 21926, 24351, 26510, 28378, 29935, 31164, 32052, 32588, 32767, 32588, 32052, 31164, 29935, 28378, 26510, 24351,
21926, 19261, 16384, 13328, 10126, 6813, 3425, 0, -3425, -6813, -10126, -13328, -16384, -19261, -21926, -24351, -26510, -28378, -29935, -31164, -32052,
-32588, -32768, -32588, -32052, -31164, -29935, -28378, -26510, -24351, -21926, -19261, -16384, -13328, -10126, -6813, -3425};
const uint8_t BUSY_AUDIO_LEN = 60U;
CFMTimeout::CFMTimeout() :
m_level(128 * 128),
m_running(false),
m_pos(0U),
m_n(0U)
{
}
void CFMTimeout::setParams(uint8_t level)
{
m_level = q15_t(level * 128);
}
q15_t CFMTimeout::getAudio()
{
q15_t sample = 0U;
if (!m_running)
return sample;
if (m_pos > 12000U) {
q31_t sample = BUSY_AUDIO[m_n] * m_level;
sample = q15_t(__SSAT((sample >> 15), 16));
m_n++;
if (m_n >= BUSY_AUDIO_LEN)
m_n = 0U;
} else {
sample = 0U;
}
m_pos++;
if (m_pos >= 24000U)
m_pos = 0U;
return sample;
}
void CFMTimeout::start()
{
m_running = true;
m_pos = 0U;
m_n = 0U;
}
void CFMTimeout::stop()
{
m_running = false;
}
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 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(FMTimeout_H)
#define FMTimeout_H
#include "Config.h"
class CFMTimeout {
public:
CFMTimeout();
void setParams(uint8_t level);
void start();
void stop();
q15_t getAudio();
private:
q15_t m_level;
bool m_running;
uint32_t m_pos;
uint8_t m_n;
};
#endif
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2009,2010,2015,2020 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 "FMTimer.h"
CFMTimer::CFMTimer() :
m_timeout(0U),
m_timer(0U)
{
}
void CFMTimer::setTimeout(uint16_t secs, uint32_t msecs)
{
m_timeout = (secs * 24000U) + (msecs * 24U);
}
uint32_t CFMTimer::getTimeout() const
{
return m_timeout / 24U;
}
void CFMTimer::start()
{
if (m_timeout > 0U)
m_timer = 1U;
}
void CFMTimer::stop()
{
m_timer = 0U;
}
void CFMTimer::clock(uint8_t length)
{
if (m_timer > 0U && m_timeout > 0U)
m_timer += length;
}
bool CFMTimer::isRunning() const
{
return m_timer > 0U;
}
bool CFMTimer::hasExpired() const
{
if (m_timeout == 0U || m_timer == 0U)
return false;
if (m_timer > m_timeout)
return true;
return false;
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2009,2010,2015,2020 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(FMTimer_H)
#define FMTimer_H
#include "Config.h"
class CFMTimer {
public:
CFMTimer();
void setTimeout(uint16_t secs, uint32_t msecs);
uint32_t getTimeout() const;
void start();
void stop();
void clock(uint8_t length);
bool isRunning() const;
bool hasExpired() const;
private:
uint32_t m_timeout;
uint32_t m_timer;
};
#endif
+6 -2
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -50,6 +50,7 @@ enum MMDVM_STATE {
STATE_P25 = 4, STATE_P25 = 4,
STATE_NXDN = 5, STATE_NXDN = 5,
STATE_POCSAG = 6, STATE_POCSAG = 6,
STATE_FM = 10,
// Dummy states start at 90 // Dummy states start at 90
STATE_NXDNCAL1K = 91, STATE_NXDNCAL1K = 91,
@@ -90,6 +91,7 @@ enum MMDVM_STATE {
#include "CWIdTX.h" #include "CWIdTX.h"
#include "Debug.h" #include "Debug.h"
#include "IO.h" #include "IO.h"
#include "FM.h"
const uint8_t MARK_SLOT1 = 0x08U; const uint8_t MARK_SLOT1 = 0x08U;
const uint8_t MARK_SLOT2 = 0x04U; const uint8_t MARK_SLOT2 = 0x04U;
@@ -114,6 +116,7 @@ extern bool m_ysfEnable;
extern bool m_p25Enable; extern bool m_p25Enable;
extern bool m_nxdnEnable; extern bool m_nxdnEnable;
extern bool m_pocsagEnable; extern bool m_pocsagEnable;
extern bool m_fmEnable;
extern bool m_duplex; extern bool m_duplex;
@@ -144,6 +147,8 @@ extern CNXDNTX nxdnTX;
extern CPOCSAGTX pocsagTX; extern CPOCSAGTX pocsagTX;
extern CFM fm;
extern CCalDStarRX calDStarRX; extern CCalDStarRX calDStarRX;
extern CCalDStarTX calDStarTX; extern CCalDStarTX calDStarTX;
extern CCalDMR calDMR; extern CCalDMR calDMR;
@@ -155,4 +160,3 @@ extern CCalRSSI calRSSI;
extern CCWIdTX cwIdTX; extern CCWIdTX cwIdTX;
#endif #endif
+41 -68
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
* Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016 by Colin Durbridge G4EML
* *
@@ -80,6 +80,7 @@ m_ysfTXLevel(128 * 128),
m_p25TXLevel(128 * 128), m_p25TXLevel(128 * 128),
m_nxdnTXLevel(128 * 128), m_nxdnTXLevel(128 * 128),
m_pocsagTXLevel(128 * 128), m_pocsagTXLevel(128 * 128),
m_fmTXLevel(128 * 128),
m_rxDCOffset(DC_OFFSET), m_rxDCOffset(DC_OFFSET),
m_txDCOffset(DC_OFFSET), m_txDCOffset(DC_OFFSET),
m_ledCount(0U), m_ledCount(0U),
@@ -144,6 +145,7 @@ void CIO::selfTest()
setP25Int(ledValue); setP25Int(ledValue);
setNXDNInt(ledValue); setNXDNInt(ledValue);
setPOCSAGInt(ledValue); setPOCSAGInt(ledValue);
setFMInt(ledValue);
#endif #endif
delayInt(250); delayInt(250);
} }
@@ -155,105 +157,56 @@ void CIO::selfTest()
setP25Int(false); setP25Int(false);
setNXDNInt(false); setNXDNInt(false);
setPOCSAGInt(false); setPOCSAGInt(false);
setFMInt(false);
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true); setDMRInt(true);
setYSFInt(false);
setP25Int(false);
setNXDNInt(false);
setPOCSAGInt(false);
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(true); setYSFInt(true);
setP25Int(false);
setNXDNInt(false);
setPOCSAGInt(false);
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(true);
setP25Int(true); setP25Int(true);
setNXDNInt(false);
setPOCSAGInt(false);
#if !defined(USE_ALTERNATE_NXDN_LEDS)
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(true);
setP25Int(true);
setNXDNInt(true); setNXDNInt(true);
setPOCSAGInt(false); #endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(true);
setP25Int(true);
setNXDNInt(true);
setPOCSAGInt(true); setPOCSAGInt(true);
#endif
#if !defined(USE_ALTERNATE_FM_LEDS)
delayInt(250);
setFMInt(true);
delayInt(250); delayInt(250);
setFMInt(false);
#endif
setDStarInt(true); #if !defined(USE_ALTERNATE_POCSAG_LEDS)
setDMRInt(true); delayInt(250);
setYSFInt(true);
setP25Int(true);
setNXDNInt(true);
setPOCSAGInt(false); setPOCSAGInt(false);
#endif
#if !defined(USE_ALTERNATE_NXDN_LEDS)
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(true);
setP25Int(true);
setNXDNInt(false); setNXDNInt(false);
setPOCSAGInt(false); #endif
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(true);
setP25Int(false); setP25Int(false);
setNXDNInt(false);
setPOCSAGInt(false);
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(true);
setYSFInt(false); setYSFInt(false);
setP25Int(false);
setNXDNInt(false);
setPOCSAGInt(false);
delayInt(250); delayInt(250);
setDStarInt(true);
setDMRInt(false); setDMRInt(false);
setYSFInt(false);
setP25Int(false);
setNXDNInt(false);
setPOCSAGInt(false);
delayInt(250); delayInt(250);
setDStarInt(false); setDStarInt(false);
setDMRInt(false);
setYSFInt(false);
setP25Int(false);
setNXDNInt(false);
setPOCSAGInt(false);
#endif #endif
} }
@@ -402,6 +355,15 @@ void CIO::process()
dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE); dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE);
} }
} }
if (m_fmEnable) {
bool cos = getCOSInt();
#if defined(USE_DCBLOCKER)
fm.samples(cos, dcSamples, RX_BLOCK_SIZE);
#else
fm.samples(cos, samples, RX_BLOCK_SIZE);
#endif
}
} else if (m_modemState == STATE_DSTAR) { } else if (m_modemState == STATE_DSTAR) {
if (m_dstarEnable) { if (m_dstarEnable) {
q15_t GMSKVals[RX_BLOCK_SIZE]; q15_t GMSKVals[RX_BLOCK_SIZE];
@@ -460,6 +422,13 @@ void CIO::process()
nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE);
} }
} else if (m_modemState == STATE_FM) {
bool cos = getCOSInt();
#if defined(USE_DCBLOCKER)
fm.samples(cos, dcSamples, RX_BLOCK_SIZE);
#else
fm.samples(cos, samples, RX_BLOCK_SIZE);
#endif
} else if (m_modemState == STATE_DSTARCAL) { } else if (m_modemState == STATE_DSTARCAL) {
q15_t GMSKVals[RX_BLOCK_SIZE]; q15_t GMSKVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
@@ -505,6 +474,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t
case STATE_POCSAG: case STATE_POCSAG:
txLevel = m_pocsagTXLevel; txLevel = m_pocsagTXLevel;
break; break;
case STATE_FM:
txLevel = m_fmTXLevel;
break;
default: default:
txLevel = m_cwIdTXLevel; txLevel = m_cwIdTXLevel;
break; break;
@@ -553,10 +525,11 @@ void CIO::setMode()
setP25Int(m_modemState == STATE_P25); setP25Int(m_modemState == STATE_P25);
setNXDNInt(m_modemState == STATE_NXDN); setNXDNInt(m_modemState == STATE_NXDN);
setPOCSAGInt(m_modemState == STATE_POCSAG); setPOCSAGInt(m_modemState == STATE_POCSAG);
setFMInt(m_modemState == STATE_FM);
#endif #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, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, 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, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset)
{ {
m_pttInvert = pttInvert; m_pttInvert = pttInvert;
@@ -568,6 +541,7 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx
m_p25TXLevel = q15_t(p25TXLevel * 128); m_p25TXLevel = q15_t(p25TXLevel * 128);
m_nxdnTXLevel = q15_t(nxdnTXLevel * 128); m_nxdnTXLevel = q15_t(nxdnTXLevel * 128);
m_pocsagTXLevel = q15_t(pocsagTXLevel * 128); m_pocsagTXLevel = q15_t(pocsagTXLevel * 128);
m_fmTXLevel = q15_t(fmTXLevel * 128);
m_rxDCOffset = DC_OFFSET + rxDCOffset; m_rxDCOffset = DC_OFFSET + rxDCOffset;
m_txDCOffset = DC_OFFSET + txDCOffset; m_txDCOffset = DC_OFFSET + txDCOffset;
@@ -618,4 +592,3 @@ bool CIO::hasLockout() const
{ {
return m_lockout; return m_lockout;
} }
+3 -2
View File
@@ -42,7 +42,7 @@ public:
void interrupt(); 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, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, 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, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset);
void getOverflow(bool& adcOverflow, bool& dacOverflow); void getOverflow(bool& adcOverflow, bool& dacOverflow);
@@ -86,6 +86,7 @@ private:
q15_t m_p25TXLevel; q15_t m_p25TXLevel;
q15_t m_nxdnTXLevel; q15_t m_nxdnTXLevel;
q15_t m_pocsagTXLevel; q15_t m_pocsagTXLevel;
q15_t m_fmTXLevel;
uint16_t m_rxDCOffset; uint16_t m_rxDCOffset;
uint16_t m_txDCOffset; uint16_t m_txDCOffset;
@@ -118,9 +119,9 @@ private:
void setP25Int(bool on); void setP25Int(bool on);
void setNXDNInt(bool on); void setNXDNInt(bool on);
void setPOCSAGInt(bool on); void setPOCSAGInt(bool on);
void setFMInt(bool on);
void delayInt(unsigned int dly); void delayInt(unsigned int dly);
}; };
#endif #endif
+17 -2
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
* Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016 by Colin Durbridge G4EML
* *
@@ -35,6 +35,7 @@
#define PIN_P25 19 #define PIN_P25 19
#define PIN_NXDN 20 #define PIN_NXDN 20
#define PIN_POCSAG 4 #define PIN_POCSAG 4
#define PIN_FM 5
#define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #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_ISR_EOC_Chan ADC_ISR_EOC7
#define ADC_CDR_Chan 7 #define ADC_CDR_Chan 7
@@ -50,6 +51,7 @@
#define PIN_P25 6 #define PIN_P25 6
#define PIN_NXDN 5 #define PIN_NXDN 5
#define PIN_POCSAG 4 #define PIN_POCSAG 4
#define PIN_FM 3
#define ADC_CHER_Chan (1<<13) // ADC on Due pin A11 - Due AD13 - (1 << 13) #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_ISR_EOC_Chan ADC_ISR_EOC13
#define ADC_CDR_Chan 13 #define ADC_CDR_Chan 13
@@ -67,6 +69,7 @@
#define PIN_P25 6 #define PIN_P25 6
#define PIN_NXDN 5 #define PIN_NXDN 5
#define PIN_POCSAG 4 #define PIN_POCSAG 4
#define PIN_FM 3
#define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #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_ISR_EOC_Chan ADC_ISR_EOC7
#define ADC_CDR_Chan 7 #define ADC_CDR_Chan 7
@@ -107,6 +110,9 @@ void CIO::initInt()
#if !defined(USE_ALTERNATE_POCSAG_LEDS) #if !defined(USE_ALTERNATE_POCSAG_LEDS)
pinMode(PIN_POCSAG, OUTPUT); pinMode(PIN_POCSAG, OUTPUT);
#endif #endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
pinMode(PIN_FM, OUTPUT);
#endif
#endif #endif
} }
@@ -258,10 +264,19 @@ void CIO::setPOCSAGInt(bool on)
#endif #endif
} }
void CIO::setFMInt(bool on)
{
#if defined(USE_ALTERNATE_FM_LEDS)
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
digitalWrite(PIN_YSF, on ? HIGH : LOW);
#else
digitalWrite(PIN_FM, on ? HIGH : LOW);
#endif
}
void CIO::delayInt(unsigned int dly) void CIO::delayInt(unsigned int dly)
{ {
delay(dly); delay(dly);
} }
#endif #endif
+18 -1
View File
@@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM * Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2017,2018,2020 by Jonathan Naylor G4KLX
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -1438,6 +1438,23 @@ void CIO::setPOCSAGInt(bool on)
#endif #endif
} }
void CIO::setFMInt(bool on)
{
#if defined(USE_ALTERNATE_FM_LEDS)
GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET);
GPIO_WriteBit(PORT_YSF, PIN_YSF, on ? Bit_SET : Bit_RESET);
#if defined(MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && (defined(STM32F4_NUCLEO) || defined(STM32F722_RPT_HAT))
GPIO_WriteBit(PORT_MDSTAR, PIN_MDSTAR, on ? Bit_SET : Bit_RESET);
GPIO_WriteBit(PORT_MYSF, PIN_MYSF, on ? Bit_SET : Bit_RESET);
#endif
#else
GPIO_WriteBit(PORT_FM, PIN_FM, on ? Bit_SET : Bit_RESET);
#if defined(MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && (defined(STM32F4_NUCLEO) || defined(STM32F722_RPT_HAT))
GPIO_WriteBit(PORT_MFM, PIN_MFM, on ? Bit_SET : Bit_RESET);
#endif
#endif
}
// Simple delay function for STM32 // Simple delay function for STM32
// Example from: http://thehackerworkshop.com/?p=1209 // Example from: http://thehackerworkshop.com/?p=1209
void CIO::delayInt(unsigned int dly) void CIO::delayInt(unsigned int dly)
+22 -5
View File
@@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM * Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
* Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Wojciech Krutnik N0CALL * Copyright (C) 2017 by Wojciech Krutnik N0CALL
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -41,6 +41,7 @@ YSF PB8 output
P25 PB9 output P25 PB9 output
NXDN PB10 output NXDN PB10 output
POCSAG PB11 output POCSAG PB11 output
FM PB12 output
RX PB0 analog input (ADC1_8) RX PB0 analog input (ADC1_8)
RSSI PB1 analog input (ADC2_9) RSSI PB1 analog input (ADC2_9)
@@ -84,6 +85,9 @@ USART1_RXD PA10 input (AF)
#define PIN_POCSAG 11 #define PIN_POCSAG 11
#define PORT_POCSAG GPIOB #define PORT_POCSAG GPIOB
#define BB_POCSAG *((bitband_t)BITBAND_PERIPH(&PORT_POCSAG->ODR, PIN_POCSAG)) #define BB_POCSAG *((bitband_t)BITBAND_PERIPH(&PORT_POCSAG->ODR, PIN_POCSAG))
#define PIN_FM 12
#define PORT_FM GPIOB
#define BB_FM *((bitband_t)BITBAND_PERIPH(&PORT_FM->ODR, PIN_FM))
#define PIN_RX 0 #define PIN_RX 0
#define PIN_RX_ADC_CH 8 #define PIN_RX_ADC_CH 8
@@ -154,9 +158,9 @@ void GPIOConfigPin(GPIO_TypeDef *port_ptr, uint32_t pin, uint32_t mode_cnf_value
#if defined(STM32F1_POG) #if defined(STM32F1_POG)
void FancyLEDEffect() void FancyLEDEffect()
{ {
bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25}; bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25, &BB_NXDN, &BB_POCSAG, &BB_FM};
for(int i=0; i<7; i++){ for(int i=0; i<10; i++){
*foo[i] = 0x01; *foo[i] = 0x01;
} }
GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1); GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1);
@@ -172,12 +176,12 @@ void FancyLEDEffect()
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01; *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01;
*foo[0] = 0x01; *foo[0] = 0x01;
for(int i=1; i<7; i++){ for(int i=1; i<10; i++){
delay(SystemCoreClock/1000*10); delay(SystemCoreClock/1000*10);
*foo[i-1] = 0x00; *foo[i-1] = 0x00;
*foo[i] = 0x01; *foo[i] = 0x01;
} }
for(int i=5; i>=0; i--){ for(int i=10; i>=0; i--){
delay(SystemCoreClock/1000*10); delay(SystemCoreClock/1000*10);
*foo[i+1] = 0x00; *foo[i+1] = 0x00;
*foo[i] = 0x01; *foo[i] = 0x01;
@@ -228,6 +232,9 @@ static inline void GPIOInit()
#if !defined(USE_ALTERNATE_POCSAG_LEDS) #if !defined(USE_ALTERNATE_POCSAG_LEDS)
GPIOConfigPin(PORT_POCSAG, PIN_POCSAG, GPIO_CRL_MODE0_1); GPIOConfigPin(PORT_POCSAG, PIN_POCSAG, GPIO_CRL_MODE0_1);
#endif #endif
#if !defined(USE_ALTERNATE_FM_LEDS)
GPIOConfigPin(PORT_FM, PIN_FM, GPIO_CRL_MODE0_1);
#endif
GPIOConfigPin(PORT_RX, PIN_RX, 0); GPIOConfigPin(PORT_RX, PIN_RX, 0);
#if defined(SEND_RSSI_DATA) #if defined(SEND_RSSI_DATA)
@@ -460,6 +467,16 @@ void CIO::setPOCSAGInt(bool on)
#endif #endif
} }
void CIO::setFMInt(bool on)
{
#if defined(USE_ALTERNATE_FM_LEDS)
BB_DSTAR = !!on;
BB_YSF = !!on;
#else
BB_FM = !!on;
#endif
}
void CIO::delayInt(unsigned int dly) void CIO::delayInt(unsigned int dly)
{ {
delay(dly); delay(dly);
+16 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2016,2017,2018,2020 by Jonathan Naylor G4KLX
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -37,9 +37,11 @@
#if defined(__MK20DX256__) #if defined(__MK20DX256__)
#define PIN_NXDN 2 #define PIN_NXDN 2
#define PIN_POCSAG 3 #define PIN_POCSAG 3
#define PIN_FM 4
#else #else
#define PIN_NXDN 24 #define PIN_NXDN 24
#define PIN_POCSAG 25 #define PIN_POCSAG 25
#define PIN_FM 26
#endif #endif
#define PIN_ADC 5 // A0, Pin 14 #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 #define PIN_RSSI 4 // Teensy 3.5/3.6, A16, Pin 35. Teensy 3.1/3.2, A17, Pin 28
@@ -76,6 +78,9 @@ void CIO::initInt()
#if !defined(USE_ALTERNATE_POCSAG_LEDS) #if !defined(USE_ALTERNATE_POCSAG_LEDS)
pinMode(PIN_POCSAG, OUTPUT); pinMode(PIN_POCSAG, OUTPUT);
#endif #endif
#if !defined(USE_ALTERNATE_FM_LEDS)
pinMode(PIN_FM, OUTPUT);
#endif
#endif #endif
} }
@@ -243,6 +248,16 @@ void CIO::setPOCSAGInt(bool on)
#endif #endif
} }
void CIO::setFMInt(bool on)
{
#if defined(USE_ALTERNATE_FM_LEDS)
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
digitalWrite(PIN_YSF, on ? HIGH : LOW);
#else
digitalWrite(PIN_FM, on ? HIGH : LOW);
#endif
}
void CIO::delayInt(unsigned int dly) void CIO::delayInt(unsigned int dly)
{ {
delay(dly); delay(dly);
+7 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Mathis Schmieder DB9MAT * Copyright (C) 2016 by Mathis Schmieder DB9MAT
* Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016 by Colin Durbridge G4EML
* *
@@ -32,6 +32,7 @@ bool m_ysfEnable = true;
bool m_p25Enable = true; bool m_p25Enable = true;
bool m_nxdnEnable = true; bool m_nxdnEnable = true;
bool m_pocsagEnable = true; bool m_pocsagEnable = true;
bool m_fmEnable = true;
bool m_duplex = true; bool m_duplex = true;
@@ -59,6 +60,8 @@ CNXDNTX nxdnTX;
CPOCSAGTX pocsagTX; CPOCSAGTX pocsagTX;
CFM fm;
CCalDStarRX calDStarRX; CCalDStarRX calDStarRX;
CCalDStarTX calDStarTX; CCalDStarTX calDStarTX;
CCalDMR calDMR; CCalDMR calDMR;
@@ -106,6 +109,9 @@ void loop()
if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy())) if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy()))
pocsagTX.process(); pocsagTX.process();
if (m_fmEnable && m_modemState == STATE_FM)
fm.process();
if (m_modemState == STATE_DSTARCAL) if (m_modemState == STATE_DSTARCAL)
calDStarTX.process(); calDStarTX.process();
+7 -2
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@ bool m_ysfEnable = true;
bool m_p25Enable = true; bool m_p25Enable = true;
bool m_nxdnEnable = true; bool m_nxdnEnable = true;
bool m_pocsagEnable = true; bool m_pocsagEnable = true;
bool m_fmEnable = true;
bool m_duplex = true; bool m_duplex = true;
@@ -56,6 +57,8 @@ CNXDNTX nxdnTX;
CPOCSAGTX pocsagTX; CPOCSAGTX pocsagTX;
CFM fm;
CCalDStarRX calDStarRX; CCalDStarRX calDStarRX;
CCalDStarTX calDStarTX; CCalDStarTX calDStarTX;
CCalDMR calDMR; CCalDMR calDMR;
@@ -103,6 +106,9 @@ void loop()
if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy())) if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy()))
pocsagTX.process(); pocsagTX.process();
if (m_fmEnable && m_modemState == STATE_FM)
fm.process();
if (m_modemState == STATE_DSTARCAL) if (m_modemState == STATE_DSTARCAL)
calDStarTX.process(); calDStarTX.process();
@@ -121,4 +127,3 @@ void loop()
if (m_modemState == STATE_IDLE) if (m_modemState == STATE_IDLE)
cwIdTX.process(); cwIdTX.process();
} }
+1 -4
View File
@@ -342,13 +342,10 @@ deploy-dvm: deploy-pi
deploy-pi-f7: deploy-pi-f7:
ifneq ($(wildcard /usr/local/bin/stm32flash),) ifneq ($(wildcard /usr/local/bin/stm32flash),)
-/usr/local/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0 -/usr/local/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0
-/usr/local/bin/stm32ld /dev/ttyAMA0 57600 bin/$(BINBIN_F7)
/usr/local/bin/stm32flash -v -w bin/$(BINBIN_F7) -g 0x0 -R -c /dev/ttyAMA0 /usr/local/bin/stm32flash -v -w bin/$(BINBIN_F7) -g 0x0 -R -c /dev/ttyAMA0
endif
ifneq ($(wildcard /usr/bin/stm32flash),) else ifneq ($(wildcard /usr/bin/stm32flash),)
-/usr/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0 -/usr/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0
-/usr/bin/stm32ld /dev/ttyAMA0 57600 bin/$(BINBIN_F7)
/usr/bin/stm32flash -v -w bin/$(BINBIN_F7) -g 0x0 -R -c /dev/ttyAMA0 /usr/bin/stm32flash -v -w bin/$(BINBIN_F7) -g 0x0 -R -c /dev/ttyAMA0
endif endif
+2 -2
View File
@@ -25,7 +25,7 @@ STARTUP:=$(SYSDIR)/startup_stm32f105xc.S
STARTUP_DEFS= STARTUP_DEFS=
# Include directory for CMSIS # Include directory for CMSIS
CMSISDIR:=/opt/STM32Cube_FW_F1_V1.4.0/Drivers/CMSIS CMSISDIR:=STM32F10X_Lib/CMSIS
# Libraries # Libraries
LIBDIR:= LIBDIR:=
@@ -65,7 +65,7 @@ COMMON_FLAGS =-mthumb -mlittle-endian -mcpu=$(MCPU)
COMMON_FLAGS+= -Wall COMMON_FLAGS+= -Wall
COMMON_FLAGS+= -I. -I$(CMSISDIR)/Include -I$(CMSISDIR)/Device/ST/STM32F1xx/Include -I$(SYSDIR) COMMON_FLAGS+= -I. -I$(CMSISDIR)/Include -I$(CMSISDIR)/Device/ST/STM32F1xx/Include -I$(SYSDIR)
COMMON_FLAGS+= $(addprefix -I,$(INCDIR)) COMMON_FLAGS+= $(addprefix -I,$(INCDIR))
COMMON_FLAGS+= -D$(MCU) -DMADEBYMAKEFILE COMMON_FLAGS+= -D$(MCU) -DMADEBYMAKEFILE -DSTM32F1
COMMON_FLAGS+= -Os -flto -ffunction-sections -fdata-sections COMMON_FLAGS+= -Os -flto -ffunction-sections -fdata-sections
COMMON_FLAGS+= -g COMMON_FLAGS+= -g
# Assembler flags # Assembler flags
Submodule
+1
Submodule STM32F10X_Lib added at 417e0c2f4a
+148 -114
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2013,2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2013,2015-2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016 by Colin Durbridge G4EML
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@@ -65,6 +65,10 @@ const uint8_t MMDVM_NXDN_LOST = 0x41U;
const uint8_t MMDVM_POCSAG_DATA = 0x50U; const uint8_t MMDVM_POCSAG_DATA = 0x50U;
const uint8_t MMDVM_FM_PARAMS1 = 0x60U;
const uint8_t MMDVM_FM_PARAMS2 = 0x61U;
const uint8_t MMDVM_FM_PARAMS3 = 0x62U;
const uint8_t MMDVM_ACK = 0x70U; const uint8_t MMDVM_ACK = 0x70U;
const uint8_t MMDVM_NAK = 0x7FU; const uint8_t MMDVM_NAK = 0x7FU;
@@ -92,12 +96,12 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U;
#endif #endif
#if defined(STM32F4_RPT_HAT_TGO) #if defined(STM32F4_RPT_HAT_TGO)
#define HW_TYPE "MMDVM_RPT_HAT_TGO" #define HW_TYPE "MMDVM RPT_HAT_TGO"
#else #else
#define HW_TYPE "MMDVM" #define HW_TYPE "MMDVM"
#endif #endif
#define DESCRIPTION "20180723 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG)" #define DESCRIPTION "20200425 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)"
#if defined(GITVERSION) #if defined(GITVERSION)
#define concat(h, a, b, c) h " " a " " b " GitID #" c "" #define concat(h, a, b, c) h " " a " " b " GitID #" c ""
@@ -168,6 +172,8 @@ void CSerialPort::getStatus()
reply[3U] |= 0x10U; reply[3U] |= 0x10U;
if (m_pocsagEnable) if (m_pocsagEnable)
reply[3U] |= 0x20U; reply[3U] |= 0x20U;
if (m_fmEnable)
reply[3U] |= 0x40U;
reply[4U] = uint8_t(m_modemState); reply[4U] = uint8_t(m_modemState);
@@ -256,7 +262,7 @@ void CSerialPort::getVersion()
uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
{ {
if (length < 18U) if (length < 19U)
return 4U; return 4U;
bool rxInvert = (data[0U] & 0x01U) == 0x01U; bool rxInvert = (data[0U] & 0x01U) == 0x01U;
@@ -273,6 +279,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
bool p25Enable = (data[1U] & 0x08U) == 0x08U; bool p25Enable = (data[1U] & 0x08U) == 0x08U;
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U; bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
bool pocsagEnable = (data[1U] & 0x20U) == 0x20U; bool pocsagEnable = (data[1U] & 0x20U) == 0x20U;
bool fmEnable = (data[1U] & 0x40U) == 0x40U;
uint8_t txDelay = data[2U]; uint8_t txDelay = data[2U];
if (txDelay > 50U) if (txDelay > 50U)
@@ -280,7 +287,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
MMDVM_STATE modemState = MMDVM_STATE(data[3U]); 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_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 && modemState != STATE_POCSAGCAL) if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL)
return 4U; return 4U;
if (modemState == STATE_DSTAR && !dstarEnable) if (modemState == STATE_DSTAR && !dstarEnable)
return 4U; return 4U;
@@ -294,6 +301,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
return 4U; return 4U;
if (modemState == STATE_POCSAG && !pocsagEnable) if (modemState == STATE_POCSAG && !pocsagEnable)
return 4U; return 4U;
if (modemState == STATE_FM && !fmEnable)
return 4U;
uint8_t rxLevel = data[4U]; uint8_t rxLevel = data[4U];
@@ -318,6 +327,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
uint8_t pocsagTXLevel = data[17U]; uint8_t pocsagTXLevel = data[17U];
uint8_t fmTXLevel = data[18U];
m_modemState = modemState; m_modemState = modemState;
m_dstarEnable = dstarEnable; m_dstarEnable = dstarEnable;
@@ -326,6 +337,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
m_p25Enable = p25Enable; m_p25Enable = p25Enable;
m_nxdnEnable = nxdnEnable; m_nxdnEnable = nxdnEnable;
m_pocsagEnable = pocsagEnable; m_pocsagEnable = pocsagEnable;
m_fmEnable = fmEnable;
m_duplex = !simplex; m_duplex = !simplex;
dstarTX.setTXDelay(txDelay); dstarTX.setTXDelay(txDelay);
@@ -343,13 +355,80 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
ysfTX.setParams(ysfLoDev, ysfTXHang); ysfTX.setParams(ysfLoDev, ysfTXHang);
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, txDCOffset, rxDCOffset); io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset);
io.start(); io.start();
return 0U; return 0U;
} }
uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length)
{
if (length < 8U)
return 4U;
uint8_t speed = data[0U];;
uint16_t frequency = data[1U] * 10U;
uint8_t time = data[2U];
uint8_t holdoff = data[3U];
uint8_t highLevel = data[4U];
uint8_t lowLevel = data[5U];
bool callAtStart = (data[6U] & 0x01U) == 0x01U;
bool callAtEnd = (data[6U] & 0x02U) == 0x02U;
char callsign[50U];
uint8_t n = 0U;
for (uint8_t i = 7U; i < length; i++, n++)
callsign[n] = data[i];
callsign[n] = '\0';
return fm.setCallsign(callsign, speed, frequency, time, holdoff, highLevel, lowLevel, callAtStart, callAtEnd);
}
uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length)
{
if (length < 6U)
return 4U;
uint8_t speed = data[0U];
uint16_t frequency = data[1U] * 10U;
uint8_t minTime = data[2U];
uint16_t delay = data[3U] * 10U;
uint8_t level = data[4U];
char ack[50U];
uint8_t n = 0U;
for (uint8_t i = 5U; i < length; i++, n++)
ack[n] = data[i];
ack[n] = '\0';
return fm.setAck(ack, speed, frequency, minTime, delay, level);
}
uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length)
{
if (length < 10U)
return 4U;
uint16_t timeout = data[0U] * 5U;
uint8_t timeoutLevel = data[1U];
uint8_t ctcssFrequency = data[2U];
uint8_t ctcssThreshold = data[3U];
uint8_t ctcssLevel = data[4U];
uint8_t kerchunkTime = data[5U];
uint8_t hangTime = data[6U];
bool useCOS = (data[7U] & 0x01U) == 0x01U;
uint8_t rxBoost = data[8U];
uint8_t maxDev = data[9U];
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost, maxDev);
}
uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
{ {
if (length < 1U) if (length < 1U)
@@ -360,7 +439,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
if (modemState == m_modemState) if (modemState == m_modemState)
return 0U; return 0U;
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 && modemState != STATE_POCSAGCAL) if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL)
return 4U; return 4U;
if (modemState == STATE_DSTAR && !m_dstarEnable) if (modemState == STATE_DSTAR && !m_dstarEnable)
return 4U; return 4U;
@@ -374,6 +453,8 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
return 4U; return 4U;
if (modemState == STATE_POCSAG && !m_pocsagEnable) if (modemState == STATE_POCSAG && !m_pocsagEnable)
return 4U; return 4U;
if (modemState == STATE_FM && !m_fmEnable)
return 4U;
setMode(modemState); setMode(modemState);
@@ -385,157 +466,80 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
switch (modemState) { switch (modemState) {
case STATE_DMR: case STATE_DMR:
DEBUG1("Mode set to DMR"); DEBUG1("Mode set to DMR");
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_DSTAR: case STATE_DSTAR:
DEBUG1("Mode set to D-Star"); DEBUG1("Mode set to D-Star");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_YSF: case STATE_YSF:
DEBUG1("Mode set to System Fusion"); DEBUG1("Mode set to System Fusion");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_P25: case STATE_P25:
DEBUG1("Mode set to P25"); DEBUG1("Mode set to P25");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_NXDN: case STATE_NXDN:
DEBUG1("Mode set to NXDN"); DEBUG1("Mode set to NXDN");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
cwIdTX.reset();
break; break;
case STATE_POCSAG: case STATE_POCSAG:
DEBUG1("Mode set to POCSAG"); DEBUG1("Mode set to POCSAG");
dmrIdleRX.reset(); break;
dmrDMORX.reset(); case STATE_FM:
dmrRX.reset(); DEBUG1("Mode set to FM");
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_DSTARCAL: case STATE_DSTARCAL:
DEBUG1("Mode set to D-Star Calibrate"); DEBUG1("Mode set to D-Star Calibrate");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_DMRCAL: case STATE_DMRCAL:
DEBUG1("Mode set to DMR Calibrate"); DEBUG1("Mode set to DMR Calibrate");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_RSSICAL: case STATE_RSSICAL:
DEBUG1("Mode set to RSSI Calibrate"); DEBUG1("Mode set to RSSI Calibrate");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_LFCAL: case STATE_LFCAL:
DEBUG1("Mode set to 80 Hz Calibrate"); DEBUG1("Mode set to 80 Hz Calibrate");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_DMRCAL1K: case STATE_DMRCAL1K:
DEBUG1("Mode set to DMR BS 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; break;
case STATE_P25CAL1K: case STATE_P25CAL1K:
DEBUG1("Mode set to P25 1011 Hz Calibrate"); DEBUG1("Mode set to P25 1011 Hz Calibrate");
dmrIdleRX.reset();
dmrDMORX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break; break;
case STATE_DMRDMO1K: case STATE_DMRDMO1K:
DEBUG1("Mode set to DMR MS 1031 Hz Calibrate"); 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; break;
case STATE_NXDNCAL1K: case STATE_NXDNCAL1K:
DEBUG1("Mode set to NXDN 1031 Hz Calibrate"); DEBUG1("Mode set to NXDN 1031 Hz Calibrate");
break;
case STATE_POCSAGCAL:
DEBUG1("Mode set to POCSAG Calibrate");
break;
default: // STATE_IDLE
DEBUG1("Mode set to Idle");
break;
}
if (modemState != STATE_DSTAR)
dstarRX.reset();
if (modemState != STATE_DMR) {
dmrIdleRX.reset(); dmrIdleRX.reset();
dmrDMORX.reset(); dmrDMORX.reset();
dmrRX.reset(); dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break;
default:
DEBUG1("Mode set to Idle");
// STATE_IDLE
break;
} }
if (modemState != STATE_YSF)
ysfRX.reset();
if (modemState != STATE_P25)
p25RX.reset();
if (modemState != STATE_NXDN)
nxdnRX.reset();
if (modemState != STATE_FM)
fm.reset();
cwIdTX.reset();
m_modemState = modemState; m_modemState = modemState;
io.setMode(); io.setMode();
@@ -608,6 +612,36 @@ void CSerialPort::process()
sendACK(); sendACK();
break; break;
case MMDVM_FM_PARAMS1:
err = setFMParams1(m_buffer + 3U, m_len - 3U);
if (err == 0U) {
sendACK();
} else {
DEBUG2("Received invalid FM params 1", err);
sendNAK(err);
}
break;
case MMDVM_FM_PARAMS2:
err = setFMParams2(m_buffer + 3U, m_len - 3U);
if (err == 0U) {
sendACK();
} else {
DEBUG2("Received invalid FM params 2", err);
sendNAK(err);
}
break;
case MMDVM_FM_PARAMS3:
err = setFMParams3(m_buffer + 3U, m_len - 3U);
if (err == 0U) {
sendACK();
} else {
DEBUG2("Received invalid FM params 3", err);
sendNAK(err);
}
break;
case MMDVM_CAL_DATA: case MMDVM_CAL_DATA:
if (m_modemState == STATE_DSTARCAL) if (m_modemState == STATE_DSTARCAL)
err = calDStarTX.write(m_buffer + 3U, m_len - 3U); err = calDStarTX.write(m_buffer + 3U, m_len - 3U);
+3 -1
View File
@@ -73,6 +73,9 @@ private:
uint8_t setConfig(const uint8_t* data, uint8_t length); uint8_t setConfig(const uint8_t* data, uint8_t length);
uint8_t setMode(const uint8_t* data, uint8_t length); uint8_t setMode(const uint8_t* data, uint8_t length);
void setMode(MMDVM_STATE modemState); void setMode(MMDVM_STATE modemState);
uint8_t setFMParams1(const uint8_t* data, uint8_t length);
uint8_t setFMParams2(const uint8_t* data, uint8_t length);
uint8_t setFMParams3(const uint8_t* data, uint8_t length);
// Hardware versions // Hardware versions
void beginInt(uint8_t n, int speed); void beginInt(uint8_t n, int speed);
@@ -83,4 +86,3 @@ private:
}; };
#endif #endif