initial commit of new codebase;

pull/2/head
Bryan Biedenkapp 4 years ago
commit 735e2af3d4

File diff suppressed because it is too large Load Diff

@ -0,0 +1,347 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* Some of the code is based on work of Guus Van Dooren PE1PLM:
* https://github.com/ki6zum/gmsk-dstar/blob/master/firmware/dvmega/dvmega.ino
*
* 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(__ADF7021_H__)
#define __ADF7021_H__
#include "Defines.h"
#include "IO.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define LOW 0
#define HIGH 1
/** Band Tables */
/** 136 - 174 mhz */
#define VHF_MIN 136000000
#define VHF_MAX 174000000
/** 216 - 225 mhz */
#define VHF_220_MIN 216000000
#define VHF_220_MAX 225000000
/** 380 - 431mhz */
#define UHF_380_MIN 380000000
#define UHF_380_MAX 431000000
/** 431 - 450mhz */
#define UHF_1_MIN 431000000
#define UHF_1_MAX 470000000
/** 450 - 470mhz */
#define UHF_2_MIN 450000000
#define UHF_2_MAX 470000000
/** 470 - 520mhz (T-band) */
#define UHF_T_MIN 470000000
#define UHF_T_MAX 520000000
/** 842 - 900mhz */
#define UHF_800_MIN 842000000
#define UHF_800_MAX 900000000
/** 900 - 950mhz */
#define UHF_900_MIN 900000000
#define UHF_900_MAX 950000000
#if defined(ENABLE_ADF7021)
#define ADF_BIT_READ(value, bit) (((value) >> (bit)) & 0x01)
#if defined(ADF7021_DISABLE_RC_4FSK)
#define ADF7021_EVEN_BIT true
#else
#define ADF7021_EVEN_BIT false
#endif // ADF7021_DISABLE_RC_4FSK
/*
- Most of the registers values are obteined from ADI eval software:
http://www.analog.com/en/products/rf-microwave/integrated-transceivers-transmitters-receivers/low-power-rf-transceivers/adf7021.html
- or ADF7021 datasheet formulas:
www.analog.com/media/en/technical-documentation/data-sheets/ADF7021.pdf
*/
/** Test modes */
// Enable SWD pin to access the demodulator output signal:
// (See application note AN-852 and ADF7021 datasheet, page 60)
// #define TEST_DAC
// Transmit the carrier frequency:
// #define TEST_TX
/*********************/
// Disable TX Raised Cosine filter for 4FSK modulation in ADF7021:
// #define ADF7021_DISABLE_RC_4FSK
// Support for ADF7021-N version:
// #define ADF7021_N_VER
// Enable AFC support for DMR, YSF, P25, and M17 (experimental):
// (AFC is already enabled by default in D-Star)
// #define ADF7021_ENABLE_4FSK_AFC
// Configure AFC with positive initial frequency offset:
// #define ADF7021_AFC_POS
/** Support for 14.7456 MHz TCXO (modified RF7021SE boards) */
#if defined(ADF7021_14_7456)
// R = 4
// DEMOD_CLK = 2.4576 MHz (DEFAULT
// DEMOD_CLK = 4.9152 MHz (DMR, P25)
#define ADF7021_PFD 3686400.0
/*
** VCO/Oscillator (Register 1)
*/
#define ADF7021_REG1_VHF 0x02175041 /** 136 - 174mhz */
#define ADF7021_REG1_VHF_220 0x021B5041 /** 219 - 225mhz */
#define ADF7021_REG1_UHF_380 0x021B5041 /** 380 - 431mhz */ // this could be problematic due to
// the external VCO control
#define ADF7021_REG1_UHF_1 0x00575041 /** 431 - 450mhz */
#define ADF7021_REG1_UHF_2 0x01D75041 /** 450 - 470mhz */
#define ADF7021_REG1_UHF_T 0x02235041 /** 470 - 520mhz */ // this could be problematic due to
// the external VCO control
#define ADF7021_REG1_800 0x00535041 /** 842 - 900mhz */
#define ADF7021_REG1_900 0x01D35041 /** 900 - 950mhz */
/*
** Transmit Modulation (Register 2)
*/
#define ADF7021_DEV_DEFAULT 43U
#define ADF7021_DEV_DMR 23U
#if defined(ENABLE_P25_WIDE)
#define ADF7021_DEV_P25 32U
#else
#define ADF7021_DEV_P25 22U
#endif // ENABLE_P25_WIDE
/*
** Tx/Rx Clock (Register 3)
*/
#define ADF7021_REG3_DEFAULT 0x2A4C4193
#if defined(TEST_DAC)
#define ADF7021_REG3_DMR 0x2A4C04D3
#define ADF7021_REG3_P25 0x2A4C04D3
#else
#define ADF7021_REG3_DMR 0x2A4C80D3
#define ADF7021_REG3_P25 0x2A4C80D3
#endif // TEST_DAC
/*
** Demodulator Setup (Register 4)
*/
// Discriminator bandwith, demodulator
// Bug in ADI evaluation software, use datasheet formula (4FSK)
#define ADF7021_DISC_BW_DEFAULT 522U // K=85
#define ADF7021_DISC_BW_DMR 393U // K=32
#define ADF7021_DISC_BW_P25 394U // K=32
// Post demodulator bandwith
#define ADF7021_POST_BW_DEFAULT 10U
#define ADF7021_POST_BW_DMR 80U
#define ADF7021_POST_BW_P25 6U
/*
** IF Coarse Cal Setup (Register 5)
*/
#define ADF7021_REG5 0x000024F5
/*
** IF Fine Cal Setup (Register 6)
*/
#define ADF7021_REG6 0x05070E16
/*
** AFC (Register 10)
*/
#define ADF7021_REG10_DEFAULT 0x0C96473A
#if defined(ADF7021_ENABLE_4FSK_AFC)
#define ADF7021_REG10_DMR 0x01FE473A
#define ADF7021_REG10_P25 0x01FE473A
#if defined(ADF7021_AFC_POS)
#define AFC_OFFSET_DMR -250
#define AFC_OFFSET_P25 -250
#else
#define AFC_OFFSET_DMR 250
#define AFC_OFFSET_P25 250
#endif // ADF7021_AFC_POS
#else
#define ADF7021_REG10_DMR 0x049E472A
#define ADF7021_REG10_P25 0x049E472A
#define AFC_OFFSET_DMR 0
#define AFC_OFFSET_P25 0
#endif // ADF7021_ENABLE_4FSK_AFC
/** Support for 12.2880 MHz TCXO */
#elif defined(ADF7021_12_2880)
// R = 2
// DEMOD_CLK = 2.4576 MHz (DEFAULT)
// DEMOD_CLK = 6.1440 MHz (DMR, P25)
#define ADF7021_PFD 6144000.0
/*
** VCO/Oscillator (Register 1)
*/
#define ADF7021_REG1_VHF 0x02175021 /** 136 - 174mhz */
#define ADF7021_REG1_VHF_220 0x021B5021 /** 219 - 225mhz */
#define ADF7021_REG1_UHF_380 0x021B5021 /** 380 - 431mhz */ // this could be problematic due to
// the external VCO control
#define ADF7021_REG1_UHF_1 0x00575021 /** 431 - 450mhz */
#define ADF7021_REG1_UHF_2 0x01D75021 /** 450 - 470mhz */
#define ADF7021_REG1_UHF_T 0x02235021 /** 470 - 520mhz */ // this could be problematic due to
// the external VCO control
#define ADF7021_REG1_800 0x00535021 /** 842 - 900mhz */
#define ADF7021_REG1_900 0x01D35021 /** 900 - 950mhz */
/*
** Transmit Modulation (Register 2)
*/
#define ADF7021_DEV_DEFAULT 26U
#define ADF7021_DEV_DMR 14U
#if defined(ENABLE_P25_WIDE)
#define ADF7021_DEV_P25 19U
#else
#define ADF7021_DEV_P25 13U
#endif // ENABLE_P25_WIDE
/*
** Tx/Rx Clock (Register 3)
*/
#define ADF7021_REG3_DEFAULT 0x29EC4153
#if defined(TEST_DAC)
#define ADF7021_REG3_DMR 0x29EC0493
#define ADF7021_REG3_P25 0x29EC0493
#else
#define ADF7021_REG3_DMR 0x29ECA093
#define ADF7021_REG3_P25 0x29ECA093
#endif // TEST_DAC
/*
** Demodulator Setup (Register 4)
*/
// Discriminator bandwith, demodulator
// Bug in ADI evaluation software, use datasheet formula (4FSK)
#define ADF7021_DISC_BW_DEFAULT 522U // K=85
#define ADF7021_DISC_BW_DMR 491U // K=32
#define ADF7021_DISC_BW_P25 493U // K=32
// Post demodulator bandwith
#define ADF7021_POST_BW_DEFAULT 10U
#define ADF7021_POST_BW_DMR 80U
#define ADF7021_POST_BW_P25 6U
/*
** IF Coarse Cal Setup (Register 5)
*/
#define ADF7021_REG5 0x00001ED5
/*
** IF Fine Cal Setup (Register 6)
*/
#define ADF7021_REG6 0x0505EBB6
/*
** AFC (Register 10)
*/
#define ADF7021_REG10_DEFAULT 0x0C96557A
#if defined(ADF7021_ENABLE_4FSK_AFC)
#define ADF7021_REG10_DMR 0x01FE557A
#define ADF7021_REG10_P25 0x01FE557A
#if defined(ADF7021_AFC_POS)
#define AFC_OFFSET_DMR -250
#define AFC_OFFSET_P25 -250
#else
#define AFC_OFFSET_DMR 250
#define AFC_OFFSET_P25 250
#endif // ADF7021_AFC_POS
#else
#define ADF7021_REG10_DMR 0x049E556A
#define ADF7021_REG10_P25 0x049E556A
#define AFC_OFFSET_DMR 0
#define AFC_OFFSET_P25 0
#endif // ADF7021_ENABLE_4FSK_AFC
#endif // ADF7021_12_2880
/*
** 3FSK/4FSK Demod (Register 13)
*/
// Slicer threshold for 4FSK demodulator
#define ADF7021_SLICER_TH_DEFAULT 0U
#if defined(ADF7021_N_VER)
#define ADF7021_SLICER_TH_DMR 51U
#define ADF7021_SLICER_TH_P25 43U
#else
#define ADF7021_SLICER_TH_DMR 57U
#define ADF7021_SLICER_TH_P25 47U
#endif // ADF7021_N_VER
#endif // ENABLE_ADF7021
#endif // __ADF7021_H__

@ -0,0 +1,739 @@
# Building instructions
This is a detailed guide for building the firmware of MMDVM_HS from the source code. For quick instructions, please see [README.md](README.md). This software runs on STM32F103 microcontroller. Also, Arduino with 3.3 V I/O (Arduino Due and Zero) and Teensy (3.1, 3.2, 3.5 or 3.6) are supported. You can build this code using Arduino IDE with Roger Clark's [STM32duino](https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/ZUMspot) package, or using command line tools with ARM GCC tools. The preferred method under Windows is using STM32duino, and under Linux or macOS (command line) is using [STM32F10X_Lib](https://github.com/juribeparada/STM32F10X_Lib).
# Index
- ZUMspot RPi
- ZUMspot Libre Kit
- ZUMspot USB
- MMDVM_HS_Hat
- Makefile options
- Config.h options
- Pinout definitions
- Hidden options
# ZUMspot RPi
Download latest Raspbian image and install to a micro SD
* See: https://www.raspberrypi.org/documentation/installation/installing-images/
* Configure your SD before booting. Useful article: https://styxit.com/2017/03/14/headless-raspberry-setup.html
Boot your Raspberry Pi. Run raspi-config and configure according to your preferences:
sudo raspi-config
Select at least:
* Expand filesystem
* Change default password
* Enable "Wait for Network at Boot"
* Disable Desktop GUI if you don't plan to use it
### Enable serial port in Raspberry Pi 3 or Pi Zero W
Edit /boot/cmdline.txt:
sudo nano /boot/cmdline.txt
(remove the text: console=serial0,115200)
Disable services:
sudo systemctl disable serial-getty@ttyAMA0.service
sudo systemctl disable bluetooth.service
Edit /boot/config.txt
sudo nano /boot/config.txt
and add the following lines at the end of /boot/config.txt:
enable_uart=1
dtoverlay=pi3-disable-bt
Reboot your RPi:
sudo reboot
### Enable serial port in Raspberry Pi 2
Edit /boot/cmdline.txt:
sudo nano /boot/cmdline.txt
(remove the text: console=serial0,115200)
Disable the service:
sudo systemctl disable serial-getty@ttyAMA0.service
Reboot your RPi:
sudo reboot
### Build the firmware and upload to ZUMspot RPi
If you are using Pi-Star, expand filesystem (if you haven't done before):
sudo pistar-expand
sudo reboot
Enable RW filesystem if you are using Pi-Star:
rpi-rw
Install the necessary software tools:
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi gdb-arm-none-eabi libstdc++-arm-none-eabi-newlib libnewlib-arm-none-eabi
cd ~
git clone https://git.code.sf.net/p/stm32flash/code stm32flash
cd stm32flash
make
sudo make install
Download the firmware sources:
cd ~
git clone https://github.com/juribeparada/MMDVM_HS
cd MMDVM_HS/
git submodule init
git submodule update
(Please do not download any different code inside MMDVM_HS folder)
Edit Config.h
nano Config.h
and enable:
#define ZUMSPOT_ADF7021
#define ENABLE_ADF7021
#define ADF7021_14_7456
#define STM32_USART1_HOST
#define ENABLE_SCAN_MODE
Build the firmware:
make
If you are using Pi-Star, stop services:
sudo pistar-watchdog.service stop
sudo systemctl stop mmdvmhost.timer
sudo systemctl stop mmdvmhost.service
Upload the firmware to ZUMspot RPi board:
sudo make zumspot-pi
Install MMDVMHost:
cd ~
git clone https://github.com/g4klx/MMDVMHost/
cd MMDVMHost/
make
Edit MMDVM.ini according your preferences
nano MMDVM.ini
(use Port=/dev/ttyAMA0 in [Modem])
Execute MMDVMHost:
./MMDVMHost MMDVM.ini
# ZUMspot Libre Kit
## Windows with Arduino IDE
Download and install the Arduino IDE:
https://www.arduino.cc/en/Main/Software
Run Arduino IDE. On the Tools menu, select the Boards manager, and install the "Arduino SAM" from the list of available boards.
Download STM32duino (Arduino for STM32) from this URL:
https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/ZUMspot
Unzip and change the extracted folder name "Arduino_STM32-ZUMspot" to "Arduino_STM32"
Copy Arduino_STM32 folder in:
My Documents/Arduino/hardware
Install the USB bootloader to STM32F103. Follow the instructions:
https://github.com/rogerclarkmelbourne/Arduino_STM32/wiki/stm32duino-bootloader
Connect the ZUMspot Libre Kit to your PC. Install the USB Mapple driver using the bat file (you may also check http://wiki.stm32duino.com/index.php?title=Windows_driver_installation):
My Documents/Arduino/hardware/Arduino_STM32/drivers/win/install_drivers.bat
You have to be sure that Windows detect your ZUMspot as an USB serial device COMx (please see Windows Device Manager).
Download the source (zip file) of MMDVM_HS from:
https://github.com/juribeparada/MMDVM_HS
Do not download or install the STM32F103 library (STM32F10X_Lib) this is not necessary under STM32duino.
Unzip MMDVM_HS-master.zip and change the folder name to "MMDVM_HS". The path name to this folder can't have spaces.
Start the Arduino IDE. Open the MMDVM_HS.ino file in the MMDVM_HS folder.
Under the menu "Tools" select "Board" and then select:
Board: Generic STM32F103C Series
Variant: STM32F103C8 (20k RAM, 64k Flash)
CPU Speed: 72 MHz (Normal)
Upload method: STM32duino bootloader (you have transfered the USB bootloader before)
Serial port: COMx (Maple Mini)
Edit Config.h:
#define LIBRE_KIT_ADF7021
#define ENABLE_ADF7021
#define ADF7021_14_7456
#define STM32_USB_HOST
#define ENABLE_SCAN_MODE
Click the Upload button in the IDE and wait for the transfer.
Once the transfer is completed, press the RESET button of the board or disconnect and connect the USB cable. You will see the LED (PC13) of the blue pill blinking. Once you connect with MMDVMHost, the LED will blink fast.
For further help with STM32duino and STM32F103 blue pill boards, please see the STM32duino [forum](http://www.stm32duino.com).
## Windows with command line
Download the source code (zip file) of MMDVM_HS from:
https://github.com/juribeparada/MMDVM_HS
Unzip MMDVM_HS-master.zip and change the folder name to "MMDVM_HS". The path name to this folder can't have spaces.
Download the ST libraries STM32F10X_Lib-master.zip from:
https://github.com/juribeparada/STM32F10X_Lib/
Extract the STM32F10X_Lib-master folder into the same folder as the MMDVM_HS. Change the folder name to "STM32F10X_Lib".
Download the GNU make utility:
http://gnuwin32.sourceforge.net/packages/make.htm
Download the binaries zip file and extract make.exe and put it in the same directory MMDVM_HS. Download the dependencies zip file and extract libintl3.dll and libiconv2.dll and put them in the same directory MMDVM_HS.
Download the GNU ARM embedded toolchain from here:
https://launchpad.net/gcc-arm-embedded/+download
Currently the direct link to the installer is here:
https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-win32.exe
Download STM32duino (Arduino for STM32) from this URL (only for USB drivers):
https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/ZUMspot
Unzip and copy Arduino_STM32-ZUMspot folder in (for example):
C:\Arduino_STM32-ZUMspot
Connect the ZUMspot Libre Kit to your PC. Install the USB Mapple driver using the bat file (you may also check http://wiki.stm32duino.com/index.php?title=Windows_driver_installation):
C:\Arduino_STM32-ZUMspot\drivers\win\install_drivers.bat
Once the USB driver is installed, you may delete "C:\Arduino_STM32-ZUMspot" folder.
Launch the "GCC Command Prompt" from "GNU Tools for ARM Embedded Processors" (Start Menu) and
cd to the folder where you put the MMDVM_HS folder.
Edit Config.h according your preferences. The default Config.h is OK for ZUMSpot Libre Kit.
Build the firmware:
make clean
make bl
Press the reset button of ZUMspot and upload the firmware:
make dfu
## Linux Raspbian
If you are using Pi-Star, expand filesystem (if you haven't done before):
sudo pistar-expand
sudo reboot
Enable RW filesystem if you are using Pi-Star:
rpi-rw
Install the necessary software tools:
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi gdb-arm-none-eabi libstdc++-arm-none-eabi-newlib libnewlib-arm-none-eabi
Download the sources:
cd ~
git clone https://github.com/juribeparada/MMDVM_HS
cd MMDVM_HS/
git submodule init
git submodule update
(Please do not download any different code inside MMDVM_HS folder)
Edit Config.h:
nano Config.h
and enable:
#define LIBRE_KIT_ADF7021
#define ENABLE_ADF7021
#define ADF7021_14_7456
#define STM32_USB_HOST
#define ENABLE_SCAN_MODE
Build the firmware with bootloader support:
make bl
If you are using Pi-Star, stop services:
sudo pistar-watchdog.service stop
sudo systemctl stop mmdvmhost.timer
sudo systemctl stop mmdvmhost.service
Upload bootloader and firmware to ZUMspot Libre Kit, using serial port first (you are using an USB-serial converter with device name /dev/ttyUSB0). Move BOOT0 jumper to 1, next press and release RESET and execute:
sudo make serial-bl devser=/dev/ttyUSB0
Move BOOT0 jumper to 0.
For following firmware updates, you could use the USB port directly:
sudo make dfu devser=/dev/ttyACM0
Install MMDVMHost:
cd ~
git clone https://github.com/g4klx/MMDVMHost/
cd MMDVMHost/
make
Edit MMDVM.ini according your preferences
nano MMDVM.ini
(use Port=/dev/ttyACM0 in [Modem])
Execute MMDVMHost:
./MMDVMHost MMDVM.ini
# ZUMspot USB
## Windows
Download and install the Arduino IDE:
https://www.arduino.cc/en/Main/Software
Run Arduino IDE. On the Tools menu, select the Boards manager, and install the "Arduino SAM" from the list of available boards.
Download STM32duino (Arduino for STM32) from this URL:
https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/ZUMspot
Unzip and change the extracted folder name "Arduino_STM32-ZUMspot" to "Arduino_STM32"
Copy Arduino_STM32 folder in:
My Documents/Arduino/hardware
Connect the ZUMspot USB to your PC. Install the USB Mapple driver using the bat file:
My Documents/Arduino/hardware/Arduino_STM32/drivers/win/install_drivers.bat
(you may also check: http://wiki.stm32duino.com/index.php?title=Windows_driver_installation)
You have to be sure that Windows detect your ZUMspot as an USB serial device COMx (please
see Windows Device Manager)
Download the source (zip file) of MMDVM_HS from:
https://github.com/juribeparada/MMDVM_HS
Do not download or install the STM32F103 library, STM32F10X_Lib, this is not necessary
under STM32duino.
Unzip MMDVM_HS-master.zip and change the folder name to "MMDVM_HS". The path name to this
folder can't have spaces.
Start the Arduino IDE. Open the MMDVM_HS.ino file in the MMDVM_HS folder.
Under the menu "Tools" select "Board" and then select:
Board: Generic STM32F103C Series
Variant: STM32F103C8 (20k RAM, 64k Flash)
CPU Speed: 72 MHz (Normal)
Upload method: STM32duino bootloader (you have transfered the USB bootloader before)
Serial port: COMx (Maple Mini)
Edit Config.h:
#define ZUMSPOT_ADF7021
#define ENABLE_ADF7021
#define ADF7021_14_7456
#define STM32_USB_HOST
#define ENABLE_SCAN_MODE
Click the Upload button in the IDE and wait for the transfer.
Once the transfer is completed, press the RESET button of the board or disconnect and
connect the USB cable.
## Linux Raspbian
If you are using Pi-Star, expand filesystem (if you haven't done before):
sudo pistar-expand
sudo reboot
Enable RW filesystem if you are using Pi-Star:
rpi-rw
Install the necessary software tools:
sudo apt-get update
sudo apt-get install gcc-arm-none-eabi gdb-arm-none-eabi libstdc++-arm-none-eabi-newlib libnewlib-arm-none-eabi
Download the sources:
cd ~
git clone https://github.com/juribeparada/MMDVM_HS
cd MMDVM_HS/
git submodule init
git submodule update
(Please do not download any different code inside MMDVM_HS folder)
Edit Config.h
nano Config.h
and enable:
#define ZUMSPOT_ADF7021
#define ENABLE_ADF7021
#define ADF7021_14_7456
#define STM32_USB_HOST
#define ENABLE_SCAN_MODE
Build the firmware with bootloader support:
make bl
If you are using Pi-Star, stop services:
sudo pistar-watchdog.service stop
sudo systemctl stop mmdvmhost.timer
sudo systemctl stop mmdvmhost.service
Upload the firmware to ZUMspot USB:
sudo make dfu devser=/dev/ttyACM0
Install MMDVMHost:
cd ~
git clone https://github.com/g4klx/MMDVMHost/
cd MMDVMHost/
make
Edit MMDVM.ini according your preferences
nano MMDVM.ini
(use Port=/dev/ttyACM0 in [Modem])
Execute MMDVMHost:
./MMDVMHost MMDVM.ini
# MMDVM_HS_Hat
Please check here for detailed instructions:
https://github.com/mathisschmieder/MMDVM_HS_Hat/blob/master/README.md
# Makefile options
- make clean: delete all objects files *.o, for starting a new firmware building.
- make: it builds a standard firmware (without USB bootloader support).
- make bl: it builds a firmware with USB bootloader support.
- make zumspot-pi: upload the firmware to a ZUMspot RPi version (using internal RPi serial port)
- make mmdvm_hs_hat: upload the firmware to MMDVM_HS_Hat board (using internal RPi serial port)
- make nano-hotspot: upload the firmware to Nano hotSPOT board (using internal serial port)
- make nano-dv: upload the firmware to NanoDV board (using internal serial port)
- make d2rg_mmdvm_hs: upload the firmware to D2RG MMDVM_HS board (using internal serial port)
- make skybridge: upload the firmware to SkyBridge HotSpot board (using internal serial port)
- make dfu [devser=/dev/ttyXXX]: upload firmware using USB bootloader. "devser" is optional, and it corresponds to the USB serial port device name. This option permits to perform a reset to enter to booloader mode (DFU). If you don't use "devser", you have to press the reset button of the ZUMspot just before using this command.
- make serial devser=/dev/ttyXXX: upload standard firmware using serial port bootloader method.
- make serial-bl devser=/dev/ttyXXX: upload firmware with USB bootloader support using serial port bootloader method.
- make serial-nobl devser=/dev/ttyXXX: upload firmware with USB support using serial port bootloader method, but without USB bootloader installation.
- make serial-bl-old devser=/dev/ttyXXX: same as "make serial-bl" but using bootloader with short reset pulse.
- make stlink: upload standard firmware using ST-Link interface.
- make stlink-bl: upload firmware with USB bootloader support using ST-Link interface.
- make stlink-nobl: upload firmware with USB support using ST-Link interface, but without USB bootloader.
- make stlink-bl-old: same as "make stlink-bl" but using bootloader with short reset pulse.
- make ocd: upload standard firmware using ST-Link interface. This method uses a local openocd installation.
- make ocd-nobl: upload firmware with USB support using ST-Link interface, but without USB bootloader. This method uses a local openocd installation.
- make ocd-bl: upload firmware with USB bootloader support using ST-Link interface. This method uses a local openocd installation.
- make ocd-bl-old: same as "make ocd-bl" but using bootloader with short reset pulse.
## Common Makefile commands
Serial programming (first programming, transfer the USB bootloader):
make clean
make bl
sudo make serial-bl devser=/dev/ttyUSB0
USB programming (you have already transfered the USB bootloader):
make clean
make bl
sudo make dfu (reset ZUMspot before) or
sudo make dfu devser=/dev/ttyACM0 (/dev/ttyACM0 is the device name of ZUMspot USB under Raspbian)
ZUMspot RPi (no USB support needed):
make clean
make
sudo make zumspot-pi
# Config.h options
- #define ZUMSPOT_ADF7021: enable pinouts support for ZUMspot RPi or ZUMspot USB. You have to enable this option if you have one of these products.
- #define LIBRE_KIT_ADF7021: enable this option if you have a ZUMspot Libre Kit (Board with modified RF7021SE and Blue Pill STM32F103).
- #define MMDVM_HS_HAT_REV12: enable this option if you have a MMDVM_HS_Hat board for RPi.
- #define MMDVM_HS_DUAL_HAT_REV10: enable this option if you have a MMDVM_HS_Dual_Hat board for RPi/USB.
- #define NANO_HOTSPOT: enable this option if you have a Nano hotSPOT (BI7JTA).
- #define NANO_DV_REV10: enable this option if you have a Nano DV (BG4TGO & BG5HHP).
- #define ENABLE_ADF7021: add support for ADF7021 (all boards, enabled by default).
- #define DUPLEX: enable duplex mode with dual ADF7021. It is still under development.
- #define ADF7021_14_7456: select this option if your board uses a 14.7456 MHz (enabled by default).
- #define ADF7021_12_2880: select this option if your board uses a 12.2880 MHz.
- #define STM32_USART1_HOST: enable direct serial host communication with ZUMspot (using USART1 PA9 and PA10 pins). Disable STM32_USB_HOST if you enable this option. Enable this if you have a ZUMspot RPi. You don't need to enable this option if you will transfer the bootloader.
- #define STM32_USB_HOST: enable USB host communication with ZUMspot (using STM32F103 USB interface). Disable STM32_USART1_HOST if you enable this option. Enable this if you have a ZUMspot USB or ZUMspot Libre Kit.
- #define ENABLE_SCAN_MODE: enable automatic mode detection in ZUMspot. This is based on scanning over all enabled modes, and you could have some detection delay. Enabled by default.
- #define SEND_RSSI_DATA: enable RSSI reports to MMDVMHost. It is already converted to dBm.
- #define SERIAL_REPEATER: enable a second serial port (USART2, pins PA2 and PA3) for Nextion LCD display.
- #define SERIAL_REPEATER_USART1: enable USART1 (pins PA9 and PA10) for Nextion LCD display. Do not use with STM32_USART1_HOST enabled, only with USB host communication.
- #define ENABLE_P25_WIDE: enable support for Motorola Wide P25 mod/demod in XTS3000 radios. Using this mode improves RX BER. You need to enable this mode in your radio for each conventional personalities.
- #define QUIET_MODE_LEDS: disable mode LEDs blink during scan mode.
# Pinout definitions
## Pinout definitions for ZUMspot Libre Kit
This is the carrier board or any board with RF7021SE + STM32F103.
Main RF7021SE board:
CE PC14
SLE PB8
SREAD PB7
SDATA PB6
SCLK PB5
DATA PB4 (TxRxData)*
DCLK PB3 (TxRxCLK)*
CLKOUT PA15 (jumper wire in RF7021SE, not needed with BIDIR_DATA_PIN enabled)
PAC PB14 (PTT LED)
VCC 3.3 V
GND Ground
Second RF7021SE board (duplex mode, experimental):
SLE PA6
DATA PA4 (TxRxData)*
DCLK PA5 (TxRxCLK)*
PAC NC
CLKOUT NC
VCC 3.3 V
GND Ground
SDATA, SREAD, SCLK and CE are shared with the main ADF7021.
Serial ports:
TXD PA9 (serial port host communication)
RXD PA10 (serial port host communication)
DISP_TXD PA2 (Nextion LCD serial repeater)
DISP_RXD PA3 (Nextion LCD serial repeater)
Status LEDs:
COS_LED PB15
PTT_LED PB14
NXDN_LED PA8
P25_LED PB0
YSF_LED PB1
DMR_LED PB13
DSTAR_LED PB12
Misc pins:
PIN_LED PC13 (status led)
PIN_DEB PB9 (debugging pin)
You could install a serie resistor (10 - 100 ohms) in each TxRxData and TxRxCLK lines, for reducing EMI.
## Pinout definitions for Arduino Due/Zero + RF7021SE
Use Arduino IDE with SAM support for building the code.
Main RF7021SE board:
CE 12
SLE 6
SREAD 5
SDATA 4 // 2 in Arduino Zero Pro
SCLK 3
DATA 7 (TxRxData)*
DCLK 8 (TxRxCLK)*
CLKOUT 2 // 4 in Arduino Zero Pro (jumper wire in RF7021SE, not needed with BIDIR_DATA_PIN enabled)
PAC 9 (PTT LED)
VCC 3.3 V
GND Ground
Serial ports:
USB Arduino Programming Port (host communication)
Status LEDs:
COS_LED 10
PTT_LED 9
NXDN_LED 18
P25_LED 17
YSF_LED 16
DMR_LED 15
DSTAR_LED 14
Misc pins:
PIN_LED 13
PIN_DEB 11
You could install a serie resistor (10 - 100 ohms) in each TxRxData and TxRxCLK lines, for reducing EMI.
## Pinout definitions for Teensy (3.1, 3.2, 3.5 or 3.6) + RF7021SE:
Use Teensyduino + Arduino IDE for building the code.
Main RF7021SE board:
CE 6
SLE 5
SREAD 4
SDATA 3
SCLK 2
DATA 7 (TxRxData)*
DCLK 8 (TxRxCLK)*
CLKOUT 22 (jumper wire in RF7021SE, not needed with BIDIR_DATA_PIN enabled)
PAC 14 (PTT LED)
VCC 3.3 V
GND Ground
Serial ports:
Teensy USB Port (host communication)
DISP_TXD 1 (Nextion LCD serial repeater)
DISP_RXD 0 (Nextion LCD serial repeater)
Status LEDs:
COS_LED 15
PTT_LED 14
NXDN_LED 20
P25_LED 19
YSF_LED 18
DMR_LED 17
DSTAR_LED 16
Misc pins:
PIN_LED 13
PIN_DEB 23
You could install a serie resistor (10 - 100 ohms) in each TxRxData and TxRxCLK lines, for reducing EMI.
# Hidden functions
You could enable two test modes, if you edit ADF7021.h file before compilation. Please always comment these two #defines for normal operation.
- #define TEST_DAC: Enable SWD pin to access the demodulator output signal. See application note AN-852 and ADF7021 datasheet, page 60.
- #define TEST_TX: Transmit the carrier frequency. This works only with D-Star mode enabled in MMDVM.ini. This test mode will transmit the carrier frequency defined with TXFrequency in MMDVM.ini. This could be useful to determine the frequency offset of your ZUMspot (with test equipment).
Also in ADF7021.h:
- #define ADF7021_N_VER: enable support for narrow band version of ADF7021 (ADF7021N). Disabled by default, in general all boards will have just ADF7021.
- #define ADF7021_ENABLE_4FSK_AFC: enable AFC support for DMR, YSF and P25. This is experimental, depending on your frequency offset this option will improve or not your BER reception.
- #define ADF7021_AFC_POS: enable this option if you can not receive any signal after enable the ADF7021_ENABLE_4FSK_AFC option.
- #define ADF7021_DISABLE_RC_4FSK: disable TX Raised Cosine filter for 4FSK modulation in ADF7021. Default TX pulse shaping filter for 4FSK is not optimum for DMR, YSF and P25. Activating this option might improve audio in 4FSK digital modes.
In Globals.h:
- #define BIDIR_DATA_PIN: enable Standard TX/RX Data Interface of ADF7021 (enabled by default, needed for scanning mode detection feature).
In IOSTM.cpp:
- #define PI_HAT_7021_REV_02: enable pinouts for first revision of ZUMspot RPi. In general is not used.

@ -0,0 +1,145 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "BitBuffer.h"
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the BitBuffer class.
/// </summary>
BitBuffer::BitBuffer(uint16_t length) :
m_length(length),
m_bits(NULL),
m_control(NULL),
m_head(0U),
m_tail(0U),
m_full(false),
m_overflow(false)
{
m_bits = new uint8_t[length / 8U];
m_control = new uint8_t[length / 8U];
}
/// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t BitBuffer::getSpace() const
{
uint16_t n = 0U;
if (m_tail == m_head)
n = m_full ? 0U : m_length;
else if (m_tail < m_head)
n = m_length - m_head + m_tail;
else
n = m_tail - m_head;
if (n > m_length)
n = 0U;
return n;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
uint16_t BitBuffer::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
else if (m_tail < m_head)
return m_head - m_tail;
else
return m_length - m_tail + m_head;
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool BitBuffer::put(uint8_t bit, uint8_t control)
{
if (m_full) {
m_overflow = true;
return false;
}
WRITE_BIT(m_bits, m_head, bit);
WRITE_BIT(m_control, m_head, control);
m_head++;
if (m_head >= m_length)
m_head = 0U;
if (m_head == m_tail)
m_full = true;
return true;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool BitBuffer::get(uint8_t& bit, uint8_t& control)
{
if (m_head == m_tail && !m_full)
return false;
bit = READ_BIT(m_bits, m_tail);
control = READ_BIT(m_control, m_tail);
m_full = false;
m_tail++;
if (m_tail >= m_length)
m_tail = 0U;
return true;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool BitBuffer::hasOverflowed()
{
bool overflow = m_overflow;
m_overflow = false;
return overflow;
}

@ -0,0 +1,83 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#if !defined(__BIT_RB_H__)
#define __BIT_RB_H__
#if defined(STM32F10X_MD)
#include "stm32f10x.h"
#elif defined(STM32F4XX)
#include "stm32f4xx.h"
#include <cstddef>
#endif
#include "Defines.h"
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a circular buffer for bit data.
// ---------------------------------------------------------------------------
class DSP_FW_API BitBuffer {
public:
/// <summary>Initializes a new instance of the BitBuffer class.</summary>
BitBuffer(uint16_t length);
/// <summary>Helper to get how much space the ring buffer has for samples.</summary>
uint16_t getSpace() const;
/// <summary></summary>
uint16_t getData() const;
/// <summary></summary>
bool put(uint8_t bit, uint8_t control);
/// <summary></summary>
bool get(uint8_t& bit, uint8_t& control);
/// <summary></summary>
bool hasOverflowed();
private:
uint16_t m_length;
volatile uint8_t* m_bits;
volatile uint8_t* m_control;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
bool m_overflow;
};
#endif // __BIT_RB_H__

@ -0,0 +1,202 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* 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 "Globals.h"
#include "CWIdTX.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// 4FSK symbol sequence (800 Hz "tone" at 4800 baud): +1 +3 +1 -1 -3 -1
// Bit sequence: 00 01 00 10 11 10
uint8_t TONE[] = { 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0 };
uint8_t SILENCE[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const uint8_t CYCLE_LENGTH = 12U;
const uint8_t DOT_LENGTH = 40U;
const struct {
uint8_t 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 },
{ '.', 0xBAEB8000U, 20U },
{ ' ', 0x00000000U, 4U },
{ 0U, 0x00000000U, 0U }
};
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the CWIdTX class.
/// </summary>
CWIdTX::CWIdTX() :
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_n(0U)
{
/* stub */
}
/// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void CWIdTX::process()
{
if (m_poLen == 0U)
return;
uint16_t space = io.getSpace();
while (space > CYCLE_LENGTH) {
bool b = _READ_BIT(m_poBuffer, m_poPtr);
if (b)
io.write(TONE, CYCLE_LENGTH);
else
io.write(SILENCE, CYCLE_LENGTH);
space -= CYCLE_LENGTH;
m_n++;
if (m_n >= DOT_LENGTH) {
m_poPtr++;
m_n = 0U;
}
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
}
/// <summary>
/// Write CW ID data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CWIdTX::write(const uint8_t* data, uint8_t length)
{
::memset(m_poBuffer, 0x00U, 300U * sizeof(uint8_t));
m_poLen = 8U;
m_poPtr = 0U;
m_n = 0U;
for (uint8_t i = 0U; i < length; i++) {
for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) {
if (SYMBOL_LIST[j].c == data[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(m_poBuffer, m_poLen, b);
if (m_poLen >= 295U) {
m_poLen = 0U;
return 4U;
}
}
break;
}
}
}
// An empty message
if (m_poLen == 8U) {
m_poLen = 0U;
return RSN_ILLEGAL_LENGTH;
}
m_poLen += 5U;
DEBUG2("CWIdTx: write(): message created with length", m_poLen);
return RSN_OK;
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void CWIdTX::reset()
{
m_poLen = 0U;
m_poPtr = 0U;
m_n = 0U;
}

@ -0,0 +1,64 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* 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.
*/
#if !defined(__CWID_TX_H__)
#define __CWID_TX_H__
#include "Defines.h"
// ---------------------------------------------------------------------------
// Class Declaration
// Implements logic to transmit a CW ID.
// ---------------------------------------------------------------------------
class DSP_FW_API CWIdTX {
public:
/// <summary>Initializes a new instance of the CWIdTX class.</summary>
CWIdTX();
/// <summary>Process local buffer and transmit on the air interface.</summary>
void process();
/// <summary>Write CW ID data to the local buffer.</summary>
uint8_t write(const uint8_t* data, uint8_t length);
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
private:
uint8_t m_poBuffer[300U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint8_t m_n;
};
#endif // __CWID_TX_H__

@ -0,0 +1,89 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* 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 "Globals.h"
#include "CalRSSI.h"
#include "Utils.h"
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the CalRSSI class.
/// </summary>
CalRSSI::CalRSSI() :
m_count(0U),
m_navg(0U),
m_accum(0U),
m_min(0xFFFFU),
m_max(0x0000U)
{
/* stub */
}
/// <summary>
/// Sample RSSI values from the air interface.
/// </summary>
void CalRSSI::process()
{
m_count++;
if (m_count >= 32000U) {
uint16_t rssi = io.readRSSI();
m_count = 0U;
m_navg++;
m_accum += rssi;
if (rssi > m_max)
m_max = rssi;
if (rssi < m_min)
m_min = rssi;
if (m_navg >= 6U) {
uint16_t ave = m_accum / 6U;
uint8_t buffer[6U];
buffer[0U] = (m_max >> 8) & 0xFFU;
buffer[1U] = (m_max >> 0) & 0xFFU;
buffer[2U] = (m_min >> 8) & 0xFFU;
buffer[3U] = (m_min >> 0) & 0xFFU;
buffer[4U] = (ave >> 8) & 0xFFU;
buffer[5U] = (ave >> 0) & 0xFFU;
serial.writeRSSIData(buffer, 6U);
m_navg = 0U;
m_accum = 0U;
m_min = 0xFFFFU;
m_max = 0x0000U;
}
}
}

@ -0,0 +1,56 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* 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(__CAL_RSSI_H__)
#define __CAL_RSSI_H__
#include "Defines.h"
// ---------------------------------------------------------------------------
// Class Declaration
// Implements logic for RSSI calibration mode.
// ---------------------------------------------------------------------------
class DSP_FW_API CalRSSI {
/// <summary>Initializes a new instance of the CalRSSI class.</summary>
CalRSSI();
/// <summary>Sample RSSI values from the air interface.</summary>
void process();
private:
uint32_t m_count;
uint8_t m_navg;
uint32_t m_accum;
uint16_t m_min;
uint16_t m_max;
};
#endif // __CAL_RSSI_H__

@ -0,0 +1,148 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018,2019,2020 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__DEFINES_H__)
#define __DEFINES_H__
#include <stdint.h>
#include <arm_math.h>
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
#ifndef _INT8_T_DECLARED
#ifndef __INT8_TYPE__
typedef signed char int8_t;
#endif // __INT8_TYPE__
#endif // _INT8_T_DECLARED
#ifndef _INT16_T_DECLARED
#ifndef __INT16_TYPE__
typedef short int16_t;
#endif // __INT16_TYPE__
#endif // _INT16_T_DECLARED
#ifndef _INT32_T_DECLARED
#ifndef __INT32_TYPE__
typedef int int32_t;
#endif // __INT32_TYPE__
#endif // _INT32_T_DECLARED
#ifndef _INT64_T_DECLARED
#ifndef __INT64_TYPE__
typedef long long int64_t;
#endif // __INT64_TYPE__
#endif // _INT64_T_DECLARED
#ifndef _UINT8_T_DECLARED
#ifndef __UINT8_TYPE__
typedef unsigned char uint8_t;
#endif // __UINT8_TYPE__
#endif // _UINT8_T_DECLARED
#ifndef _UINT16_T_DECLARED
#ifndef __UINT16_TYPE__
typedef unsigned short uint16_t;
#endif // __UINT16_TYPE__
#endif // _UINT16_T_DECLARED
#ifndef _UINT32_T_DECLARED
#ifndef __UINT32_TYPE__
typedef unsigned int uint32_t;
#endif // __UINT32_TYPE__
#endif // _UINT32_T_DECLARED
#ifndef _UINT64_T_DECLARED
#ifndef __UINT64_TYPE__
typedef unsigned long long uint64_t;
#endif // __UINT64_TYPE__
#endif // _UINT64_T_DECLARED
#ifndef __LONG64_TYPE__
typedef long long long64_t;
#endif // __LONG64_TYPE__
#ifndef __ULONG64_TYPE__
typedef unsigned long long ulong64_t;
#endif // __ULONG64_TYPE__
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define DSP_FW_API
// Allow the DMR protocol
#define ENABLE_DMR
// Allow the P25 protocol
#define ENABLE_P25
// Enable P25 Wide modulation
// #define ENABLE_P25_WIDE
// Enable ADF7021 support
#define ENABLE_ADF7021
// Bidirectional Data pin (Enable Standard TX/RX Data Interface of ADF7021)
#define BIDIR_DATA_PIN
// Enable full duplex support with dual ADF7021 (valid for homebrew hotspots only)
#define DUPLEX
// TCXO of the ADF7021
// For 14.7456 MHz:
#define ADF7021_14_7456
// For 12.2880 MHz:
// #define ADF7021_12_2880
// Configure receiver gain for ADF7021
// AGC automatic, default settings
#define AD7021_GAIN_AUTO
// AGC automatic with high LNA linearity
// #define AD7021_GAIN_AUTO_LIN
// AGC OFF, lowest gain
// #define AD7021_GAIN_LOW
// AGC OFF, highest gain
// #define AD7021_GAIN_HIGH
// Enable mode detection
#define ENABLE_SCAN_MODE
// Pass RSSI information to the host
// #define SEND_RSSI_DATA
// Enable for RPi 3B+, USB mode
// #define LONG_USB_RESET
const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
// ---------------------------------------------------------------------------
// Macros
// ---------------------------------------------------------------------------
#define _WRITE_BIT(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(p, i) (p[(i) >> 3] & BIT_MASK_TABLE[(i) & 7])
#endif // __DEFINES_H__

@ -0,0 +1,150 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Mathis Schmieder DB9MAT
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU
* Copyright (C) 2019 by Florian Wolters DF2ET
*
* 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 "Globals.h"
// ---------------------------------------------------------------------------
// Globals
// ---------------------------------------------------------------------------
DVM_STATE m_modemState = STATE_IDLE;
DVM_STATE m_calState = STATE_IDLE;
DVM_STATE m_modemStatePrev = STATE_IDLE;
bool m_cwIdState = false;
uint8_t m_cwIdTXLevel = 30;
uint32_t m_modeTimerCnt;
#ifdef ENABLE_DMR
bool m_dmrEnable = true;
#else
bool m_dmrEnable = false;
#endif
#ifdef ENABLE_P25
bool m_p25Enable = true;
#else
bool m_p25Enable = false;
#endif
bool m_duplex = false;
bool m_tx = false;
bool m_dcd = false;
uint8_t m_control;
#if defined(DUPLEX)
/** DMR BS */
dmr::DMRIdleRX dmrIdleRX;
dmr::DMRRX dmrRX;
dmr::DMRTX dmrTX;
#endif
/** DMR MS-DMO */
dmr::DMRDMORX dmrDMORX;
dmr::DMRDMOTX dmrDMOTX;
/** P25 */
p25::P25RX p25RX;
p25::P25TX p25TX;
/** Calibration */
dmr::CalDMR calDMR;
p25::CalP25 calP25;
CalRSSI calRSSI;
/** CW */
CWIdTX cwIdTX;
/** RS232 and Air Interface I/O */
SerialPort serial;
IO io;
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
void setup()
{
serial.start();
}
void loop()
{
io.process();
serial.process();
// The following is for transmitting
if (m_dmrEnable && m_modemState == STATE_DMR && m_calState == STATE_IDLE) {
#if defined(DUPLEX)
if (m_duplex)
dmrTX.process();
else
dmrDMOTX.process();
#else
dmrDMOTX.process();
#endif
}
if (m_p25Enable && m_modemState == STATE_P25)
p25TX.process();
if (m_modemState == STATE_DMR_DMO_CAL_1K || m_modemState == STATE_DMR_CAL_1K ||
m_modemState == STATE_DMR_LF_CAL || m_modemState == STATE_DMR_CAL)
calDMR.process();
if (m_modemState == STATE_P25_CAL_1K || m_modemState == STATE_P25_LF_CAL || m_modemState == STATE_P25_CAL)
calP25.process();
#if defined(SEND_RSSI_DATA)
if (m_calState == STATE_RSSI_CAL)
calRSSI.process();
#endif
if (m_modemState == STATE_CW || m_modemState == STATE_IDLE)
cwIdTX.process();
}
// ---------------------------------------------------------------------------
// Firmware Entry Point
// ---------------------------------------------------------------------------
int main()
{
setup();
for (;;)
loop();
}

@ -0,0 +1,132 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU
* Copyright (C) 2019 by Florian Wolters DF2ET
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__GLOBALS_H__)
#define __GLOBALS_H__
#if defined(STM32F10X_MD)
#include <stm32f10x.h>
#include "string.h"
#elif defined(STM32F4XX)
#include "stm32f4xx.h"
#include "string.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#include "string.h"
#endif
#include "Defines.h"
#include "SerialPort.h"
#if defined(DUPLEX)
#include "dmr/DMRIdleRX.h"
#include "dmr/DMRRX.h"
#include "dmr/DMRTX.h"
#endif
#include "dmr/DMRDMORX.h"
#include "dmr/DMRDMOTX.h"
#include "dmr/CalDMR.h"
#include "p25/P25RX.h"
#include "p25/P25TX.h"
#include "p25/CalP25.h"
#include "CalRSSI.h"
#include "CWIdTX.h"
#include "IO.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint8_t MARK_SLOT1 = 0x08U;
const uint8_t MARK_SLOT2 = 0x04U;
const uint8_t MARK_NONE = 0x00U;
// ---------------------------------------------------------------------------
// Macros
// ---------------------------------------------------------------------------
#define DEBUG1(a) serial.writeDebug((a))
#define DEBUG2(a,b) serial.writeDebug((a),(b))
#define DEBUG3(a,b,c) serial.writeDebug((a),(b),(c))
#define DEBUG4(a,b,c,d) serial.writeDebug((a),(b),(c),(d))
#define DEBUG5(a,b,c,d,e) serial.writeDebug((a),(b),(c),(d),(e))
#define DEBUG_DUMP(a,b) serial.writeDump((a),(b))
// ---------------------------------------------------------------------------
// Global Externs
// ---------------------------------------------------------------------------
extern DVM_STATE m_modemState;
extern DVM_STATE m_calState;
extern DVM_STATE m_modemStatePrev;
extern bool m_cwIdState;
extern uint8_t m_cwIdTXLevel;
extern uint32_t m_modeTimerCnt;
extern bool m_dmrEnable;
extern bool m_p25Enable;
extern bool m_duplex;
extern bool m_tx;
extern bool m_dcd;
extern uint8_t m_control;
extern SerialPort serial;
extern IO io;
#if defined(DUPLEX)
/** DMR BS */
extern dmr::DMRIdleRX dmrIdleRX;
extern dmr::DMRRX dmrRX;
extern dmr::DMRTX dmrTX;
#endif
/** DMR MS-DMO */
extern dmr::DMRDMORX dmrDMORX;
extern dmr::DMRDMOTX dmrDMOTX;
/** P25 BS */
extern p25::P25RX p25RX;
extern p25::P25TX p25TX;
/** Calibration */
extern dmr::CalDMR calDMR;
extern p25::CalP25 calP25;
extern CalRSSI calRSSI;
/** CW */
extern CWIdTX cwIdTX;
#endif // __GLOBALS_H__

444
IO.cpp

@ -0,0 +1,444 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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 "IO.h"
// ---------------------------------------------------------------------------
// Globals
// ---------------------------------------------------------------------------
uint32_t m_rxFrequency;
uint32_t m_txFrequency;
uint8_t m_rfPower;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the IO class.
/// </summary>
IO::IO():
m_started(false),
m_rxBuffer(1024U),
m_txBuffer(1024U),
m_ledCount(0U),
m_scanEnable(false),
m_scanPauseCnt(0U),
m_scanPos(0U),
m_ledValue(true),
m_watchdog(0U),
m_int1Counter(0U),
m_int2Counter(0U)
{
initInt();
CE(HIGH);
setLEDInt(HIGH);
setPTTInt(LOW);
setDMRInt(LOW);
setP25Int(LOW);
setCOSInt(LOW);
#if !defined(BIDIR_DATA_PIN)
setTXDInt(LOW);
#endif
SCLK(LOW);
SDATA(LOW);
SLE1(LOW);
#if defined(DUPLEX)
SLE2(LOW);
#endif
selfTest();
m_modeTimerCnt = 0U;
}
/// <summary>
/// Starts air interface sampler.
/// </summary>
void IO::start()
{
m_totalModes = 0U;
if (m_dmrEnable) {
m_modes[m_totalModes] = STATE_DMR;
m_totalModes++;
}
if (m_p25Enable) {
m_modes[m_totalModes] = STATE_P25;
m_totalModes++;
}
#if defined(ENABLE_SCAN_MODE)
if (m_totalModes > 1U) {
m_scanEnable = true;
}
else {
m_scanEnable = false;
setMode(m_modemState);
}
#else
m_scanEnable = false;
setMode(m_modemState);
#endif
if (m_started)
return;
startInt();
m_started = true;
}
/// <summary>
/// Process samples from air interface.
/// </summary>
void IO::process()
{
uint8_t bit;
uint32_t scantime;
uint8_t control;
m_ledCount++;
if (m_started) {
// Two seconds timeout
if (m_watchdog >= 19200U) {
if (m_modemState == STATE_DMR || m_modemState == STATE_P25) {
m_modemState = STATE_IDLE;
setMode(m_modemState);
}
m_watchdog = 0U;
}
if (m_ledCount >= 48000U) {
m_ledCount = 0U;
m_ledValue = !m_ledValue;
setLEDInt(m_ledValue);
}
}
else {
if (m_ledCount >= 480000U) {
m_ledCount = 0U;
m_ledValue = !m_ledValue;
setLEDInt(m_ledValue);
}
return;
}
// Switch off the transmitter if needed
if (m_txBuffer.getData() == 0U && m_tx) {
if (m_cwIdState) {
// check for CW ID end of transmission
m_cwIdState = false;
// restoring previous mode
if (m_totalModes) {
io.rf1Conf(m_modemStatePrev, true);
}
}
setRX(false);
}
if (m_modemStatePrev == STATE_DMR)
scantime = SCAN_TIME * 2U;
else if (m_modemStatePrev == STATE_P25)
scantime = SCAN_TIME;
else
scantime = SCAN_TIME;
if (m_modeTimerCnt >= scantime) {
m_modeTimerCnt = 0U;
if ((m_modemState == STATE_IDLE) && (m_scanPauseCnt == 0U) && m_scanEnable && !m_cwIdState) {
m_scanPos = (m_scanPos + 1U) % m_totalModes;
setMode(m_modes[m_scanPos]);
io.rf1Conf(m_modes[m_scanPos], true);
}
}
if (m_rxBuffer.getData() >= 1U) {
m_rxBuffer.get(bit, control);
if (m_modemStatePrev == STATE_DMR) {
#if defined(DUPLEX)
if (m_duplex) {
if (m_tx)
dmrRX.databit(bit, control);
else
dmrIdleRX.databit(bit);
}
else
dmrDMORX.databit(bit);
#else
dmrDMORX.databit(bit);
#endif
}
else if (m_modemStatePrev == STATE_P25) {
p25RX.databit(bit);
}
}
}
/// <summary>
/// Write bits to air interface.
/// </summary>
/// <param name="mode"></param>
/// <param name="samples"></param>
/// <param name="length"></param>
/// <param name="control"></param>
void IO::write(uint8_t* data, uint16_t length, const uint8_t* control)
{
if (!m_started)
return;
for (uint16_t i = 0U; i < length; i++) {
if (control == NULL)
m_txBuffer.put(data[i], MARK_NONE);
else
m_txBuffer.put(data[i], control[i]);
}
// switch the transmitter on if needed
if (!m_tx) {
setTX();
m_tx = true;
}
}
/// <summary>
/// Helper to get how much space the transmit ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t IO::getSpace() const
{
return m_txBuffer.getSpace();
}
/// <summary>
///
/// </summary>
/// <param name="dcd"></param>
void IO::setDecode(bool dcd)
{
if (dcd != m_dcd) {
m_scanPauseCnt = 1U;
setCOSInt(dcd ? true : false);
}
m_dcd = dcd;
}
/// <summary>
/// Helper to set the modem air interface state.
/// </summary>
void IO::setMode(DVM_STATE modemState)
{
setDMRInt(modemState == STATE_DMR);
setP25Int(modemState == STATE_P25);
}
/// <summary>
/// Sets the RF parameters.
/// </summary>
/// <param name="rxFreq"></param>
/// <param name="txFreq"></param>
/// <param name="rfPower"></param>
uint8_t IO::setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower)
{
m_rfPower = rfPower >> 2;
// check frequency ranges
if (!(
/** 136 - 174 mhz */
((rxFreq >= VHF_MIN) && (rxFreq < VHF_MAX)) || ((txFreq >= VHF_MIN) && (txFreq < VHF_MAX)) ||
/** 216 - 225 mhz */
((rxFreq >= VHF_220_MIN) && (rxFreq < VHF_220_MAX)) || ((txFreq >= VHF_220_MIN) && (txFreq < VHF_220_MAX)) ||
/** 380 - 431 mhz */
((rxFreq >= UHF_380_MIN) && (rxFreq < UHF_380_MAX)) || ((txFreq >= UHF_380_MIN) && (txFreq < UHF_380_MAX)) ||
/** 431 - 450 mhz */
((rxFreq >= UHF_1_MIN) && (rxFreq < UHF_1_MAX)) || ((txFreq >= UHF_1_MIN) && (txFreq < UHF_1_MAX)) ||
/** 450 - 470 mhz */
((rxFreq >= UHF_2_MIN) && (rxFreq < UHF_2_MAX)) || ((txFreq >= UHF_2_MIN) && (txFreq < UHF_2_MAX)) ||
/** 470 - 520 mhz */
((rxFreq >= UHF_T_MIN) && (rxFreq < UHF_T_MAX)) || ((txFreq >= UHF_T_MIN) && (txFreq < UHF_T_MAX)) ||
/** 842 - 900 mhz */
((rxFreq >= UHF_800_MIN) && (rxFreq < UHF_800_MAX)) || ((txFreq >= UHF_800_MIN) && (txFreq < UHF_800_MAX)) ||
/** 900 - 950 mhz */
((rxFreq >= UHF_900_MIN) && (rxFreq < UHF_900_MAX)) || ((txFreq >= UHF_900_MIN) && (txFreq < UHF_900_MAX))
))
return RSN_INVALID_REQUEST;
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
// check hotspot configuration for single or dual ADF7021
if (!(io.hasSingleADF7021())) {
// there are two ADF7021s on the board -- are they dual-band?
if (io.isDualBand()) {
// dual band
if ((txFreq <= VHF_220_MAX) && (rxFreq <= VHF_220_MAX)) {
// turn on VHF side
io.setBandVHF(true);
}
else if ((txFreq >= UHF_1_MIN) && (rxFreq >= UHF_1_MIN)) {
// turn on UHF side
io.setBandVHF(false);
}
}
else if (!io.isDualBand()) {
// duplex board
if ((txFreq < UHF_1_MIN) || (rxFreq < UHF_1_MIN)) {
// Reject VHF frequencies
return RSN_INVALID_REQUEST;
}
}
}
#endif
m_rxFrequency = rxFreq;
m_txFrequency = txFreq;
return RSN_OK;
}
/// <summary>
/// Flag indicating the TX ring buffer has overflowed.
/// </summary>
/// <returns></returns>
bool IO::hasTXOverflow()
{
return m_txBuffer.hasOverflowed();
}
/// <summary>
/// Flag indicating the RX ring buffer has overflowed.
/// </summary>
/// <returns></returns>
bool IO::hasRXOverflow()
{
return m_rxBuffer.hasOverflowed();
}
/// <summary>
///
/// </summary>
void IO::resetWatchdog()
{
m_watchdog = 0U;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
uint32_t IO::getWatchdog()
{
return m_watchdog;
}
/// <summary>
///
/// </summary>
void IO::selfTest()
{
bool ledValue = false;
uint32_t ledCount = 0U;
uint32_t blinks = 0U;
while (true) {
ledCount++;
delayUS(1000U);
if (ledCount >= 125U) {
ledCount = 0U;
ledValue = !ledValue;
setLEDInt(!ledValue);
setPTTInt(ledValue);
setDMRInt(ledValue);
setP25Int(ledValue);
setCOSInt(ledValue);
blinks++;
if (blinks > 5U)
break;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="int1"></param>
/// <param name="int2"></param>
void IO::getIntCounter(uint16_t& int1, uint16_t& int2)
{
int1 = m_int1Counter;
int2 = m_int2Counter;
m_int1Counter = 0U;
m_int2Counter = 0U;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/// <summary>
///
/// </summary>
/// <param name="rxFreq"></param>
/// <param name="txFreq"></param>
void IO::checkBand(uint32_t rxFreq, uint32_t txFreq)
{
// check hotspot configuration for single or dual ADF7021
if (!(io.hasSingleADF7021())) {
// there are two ADF7021s on the board -- are they dual-band?
if (io.isDualBand()) {
// dual band
if ((txFreq <= VHF_220_MAX) && (rxFreq <= VHF_220_MAX)) {
// turn on VHF side
io.setBandVHF(true);
}
else if ((txFreq >= UHF_1_MIN) && (rxFreq >= UHF_1_MIN)) {
// turn on UHF side
io.setBandVHF(false);
}
}
}
}
#endif

244
IO.h

@ -0,0 +1,244 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2017-2021 Bryan Biedenkapp N2PLL
*
* 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(__IO_H__)
#define __IO_H__
#include "Defines.h"
#include "Globals.h"
#include "BitBuffer.h"
#include "ADF7021.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define SCAN_TIME 1920
#define SCAN_PAUSE 20000
#if defined(DUPLEX)
#if defined(STM32_USB_HOST)
#define CAL_DLY_LOOP 98950U
#else
#define CAL_DLY_LOOP 96100U
#endif
#else
#if defined(STM32_USB_HOST)
#define CAL_DLY_LOOP 110850U
#else
#define CAL_DLY_LOOP 104600U
#endif
#endif
// ---------------------------------------------------------------------------
// Global Externs
// ---------------------------------------------------------------------------
extern uint32_t m_rxFrequency;
extern uint32_t m_txFrequency;
extern uint8_t m_rfPower;
// ---------------------------------------------------------------------------
// Class Declaration
// Implements the input/output data path with the radio air interface.
// ---------------------------------------------------------------------------
class DSP_FW_API IO {
public:
/// <summary>Initializes a new instance of the IO class.</summary>
IO();
/// <summary>Starts air interface sampler.</summary>
void start();
/// <summary>Process bits from air interface.</summary>
void process();
/// <summary>Write bits to air interface.</summary>
void write(uint8_t* data, uint16_t length, const uint8_t* control = NULL);
/// <summary>Helper to get how much space the transmit ring buffer has for samples.</summary>
uint16_t getSpace(void) const;
/// <summary></summary>
void setDecode(bool dcd);
/// <summary>Set modem mode.</summary>
void setMode(DVM_STATE modemState);
/// <summary>Hardware interrupt handler.</summary>
void interrupt1();
#if defined(DUPLEX)
/// <summary>Hardware interrupt handler.</summary>
void interrupt2();
#endif
/// <summary>Sets the ADF7021 RF configuration.</summary>
void rf1Conf(DVM_STATE modemState, bool reset);
#if defined(DUPLEX)
/// <summary>Sets the ADF7021 RF configuration.</summary>
void rf2Conf(DVM_STATE modemState);
#endif
/// <summary></summary>
void setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel);
/// <summary>Sets the RF parameters.</summary>
uint8_t setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower);
/// <summary>Flag indicating the TX ring buffer has overflowed.</summary>
bool hasTXOverflow(void);
/// <summary>Flag indicating the RX ring buffer has overflowed.</summary>
bool hasRXOverflow(void);
/// <summary></summary>
void resetWatchdog(void);
/// <summary></summary>
uint32_t getWatchdog(void);
/// <summary>Gets the CPU type the firmware is running on.</summary>
uint8_t getCPU() const;
/// <summary>Gets the unique identifier for the air interface.</summary>
void getUDID(uint8_t* buffer);
/// <summary></summary>
void updateCal();
/// <summary></summary>
void delayBit(void);
/// <summary></summary>
uint16_t readRSSI(void);
/// <summary></summary>
void selfTest();
/// <summary></summary>
void getIntCounter(uint16_t& int1, uint16_t& int2);
private:
bool m_started;
BitBuffer m_rxBuffer;
BitBuffer m_txBuffer;
uint32_t m_ledCount;
bool m_scanEnable;
uint32_t m_scanPauseCnt;
uint8_t m_scanPos;
uint8_t m_totalModes;
DVM_STATE m_modes[6];
bool m_ledValue;
volatile uint32_t m_watchdog;
volatile uint16_t m_int1Counter;
volatile uint16_t m_int2Counter;
/// <summary>Helper to check the frequencies are within band ranges of the ADF7021.</summary>
void checkBand(uint32_t rxFreq, uint32_t txFreq);
/// <summary></summary>
void setBandVHF(bool enable);
/// <summary></summary>
bool hasSingleADF7021();
/// <summary></summary>
bool isDualBand();
/// <summary></summary>
void configureBand();
/// <summary></summary>
void setTX();
/// <summary></summary>
void setRX(bool doSle = true);
/// <summary></summary>
void delayIfCal();
/// <summary></summary>
void delayReset();
/// <summary></summary>
void delayUS(uint32_t us);
// Hardware specific routines
/// <summary>Initializes hardware interrupts.</summary>
void initInt();
/// <summary>Starts hardware interrupts.</summary>
void startInt();
/// <summary></summary>
void SCLK(bool on);
/// <summary></summary>
void SDATA(bool on);
/// <summary></summary>
bool SREAD();
/// <summary></summary>
void SLE1(bool on);
#if defined(DUPLEX)
/// <summary></summary>
void SLE2(bool on);
/// <summary></summary>
bool RXD2();
#endif
/// <summary></summary>
void CE(bool on);
/// <summary></summary>
bool RXD1();
/// <summary></summary>
bool CLK();
/// <summary></summary>
void setTXDInt(bool on);
#if defined(BIDIR_DATA_PIN)
/// <summary></summary>
void setDataDirOut(bool dir);
/// <summary></summary>
void setRXDInt(bool on);
#endif
/// <summary></summary>
void setLEDInt(bool on);
/// <summary></summary>
void setPTTInt(bool on);
/// <summary></summary>
void setCOSInt(bool on);
/// <summary></summary>
void setDMRInt(bool on);
/// <summary></summary>
void setP25Int(bool on);
};
#endif

@ -0,0 +1,856 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "IO.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#if defined(STM32F10X_MD)
/**
* The STM32 factory-programmed UUID memory.
* Three values of 32 bits each starting at this address
* Use like this: STM32_UUID[0], STM32_UUID[1], STM32_UUID[2]
*/
#define STM32_UUID ((uint32_t *)0x1FFFF7E8)
#if defined(PI_HAT_7021_REV_02)
#define PIN_SCLK GPIO_Pin_4
#define PORT_SCLK GPIOB
#define PIN_SREAD GPIO_Pin_5
#define PORT_SREAD GPIOB
#define PIN_SDATA GPIO_Pin_6
#define PORT_SDATA GPIOB
#define PIN_SLE GPIO_Pin_7
#define PORT_SLE GPIOB
#define PIN_CE GPIO_Pin_14
#define PORT_CE GPIOC
#define PIN_RXD GPIO_Pin_3
#define PORT_RXD GPIOB
// TXD used in SPI Data mode of ADF7021
// TXD is TxRxCLK of ADF7021, standard TX/RX data interface
#define PIN_TXD GPIO_Pin_15
#define PORT_TXD GPIOA
#define PIN_TXD_INT GPIO_PinSource15
#define PORT_TXD_INT GPIO_PortSourceGPIOA
// CLKOUT used in SPI Data mode of ADF7021
#define PIN_CLKOUT GPIO_Pin_14
#define PORT_CLKOUT GPIOA
#define PIN_CLKOUT_INT GPIO_PinSource14
#define PORT_CLKOUT_INT GPIO_PortSourceGPIOA
#define PIN_LED GPIO_Pin_13
#define PORT_LED GPIOC
#define PIN_DEB GPIO_Pin_11
#define PORT_DEB GPIOA
#define PIN_DMR_LED GPIO_Pin_15
#define PORT_DMR_LED GPIOB
#define PIN_P25_LED GPIO_Pin_12
#define PORT_P25_LED GPIOA
#define PIN_PTT_LED GPIO_Pin_12
#define PORT_PTT_LED GPIOB
#define PIN_COS_LED GPIO_Pin_13
#define PORT_COS_LED GPIOB
#elif defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) || defined(LONESTAR_USB)
#define PIN_SCLK GPIO_Pin_5
#define PORT_SCLK GPIOB
#define PIN_SREAD GPIO_Pin_6
#define PORT_SREAD GPIOB
#define PIN_SDATA GPIO_Pin_7
#define PORT_SDATA GPIOB
#define PIN_SLE GPIO_Pin_8
#define PORT_SLE GPIOB
#define PIN_SLE2 GPIO_Pin_6
#define PORT_SLE2 GPIOA
#define PIN_CE GPIO_Pin_14
#define PORT_CE GPIOC
#define PIN_RXD GPIO_Pin_4
#define PORT_RXD GPIOB
#define PIN_SGL_DBL GPIO_Pin_12
#define PORT_SGL_DBL GPIOA
#define PIN_DL_DPX GPIO_Pin_15
#define PORT_DL_DPX GPIOC
#define PIN_SET_BAND GPIO_Pin_15
#define PORT_SET_BAND GPIOA
// TXD used in SPI Data mode of ADF7021
// TXD is TxRxCLK of ADF7021, standard TX/RX data interface
#define PIN_TXD GPIO_Pin_3
#define PORT_TXD GPIOB
#define PIN_TXD_INT GPIO_PinSource3
#define PORT_TXD_INT GPIO_PortSourceGPIOB
#if defined(DUPLEX)
#define PIN_RXD2 GPIO_Pin_11
#define PORT_RXD2 GPIOA
// TXD2 is TxRxCLK of the second ADF7021, standard TX/RX data interface
#define PIN_TXD2 GPIO_Pin_8
#define PORT_TXD2 GPIOA
#define PIN_TXD2_INT GPIO_PinSource8
#define PORT_TXD2_INT GPIO_PortSourceGPIOA
#endif
// CLKOUT used in SPI Data mode of ADF7021
#define PIN_CLKOUT GPIO_Pin_15
#define PORT_CLKOUT GPIOA
#define PIN_CLKOUT_INT GPIO_PinSource15
#define PORT_CLKOUT_INT GPIO_PortSourceGPIOA
#define PIN_LED GPIO_Pin_13
#define PORT_LED GPIOC
#define PIN_DEB GPIO_Pin_9
#define PORT_DEB GPIOB
#define PIN_DMR_LED GPIO_Pin_13
#define PORT_DMR_LED GPIOB
#define PIN_P25_LED GPIO_Pin_0
#define PORT_P25_LED GPIOB
#define PIN_PTT_LED GPIO_Pin_14
#define PORT_PTT_LED GPIOB
#define PIN_COS_LED GPIO_Pin_15
#define PORT_COS_LED GPIOB
#elif defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11)
#define PIN_SCLK GPIO_Pin_5
#define PORT_SCLK GPIOB
#define PIN_SREAD GPIO_Pin_7
#define PORT_SREAD GPIOB
#define PIN_SDATA GPIO_Pin_6
#define PORT_SDATA GPIOB
#define PIN_SLE GPIO_Pin_8
#define PORT_SLE GPIOB
#define PIN_SLE2 GPIO_Pin_6
#define PORT_SLE2 GPIOA
#define PIN_CE GPIO_Pin_14
#define PORT_CE GPIOC
#define PIN_RXD GPIO_Pin_4
#define PORT_RXD GPIOB
#define PIN_RXD2 GPIO_Pin_4
#define PORT_RXD2 GPIOA
// TXD used in SPI Data mode of ADF7021
// TXD is TxRxCLK of ADF7021, standard TX/RX data interface
#define PIN_TXD GPIO_Pin_3
#define PORT_TXD GPIOB
#define PIN_TXD_INT GPIO_PinSource3
#define PORT_TXD_INT GPIO_PortSourceGPIOB
// TXD2 is TxRxCLK of the second ADF7021, standard TX/RX data interface
#define PIN_TXD2 GPIO_Pin_5
#define PORT_TXD2 GPIOA
#define PIN_TXD2_INT GPIO_PinSource5
#define PORT_TXD2_INT GPIO_PortSourceGPIOA
// CLKOUT used in SPI Data mode of ADF7021
#define PIN_CLKOUT GPIO_Pin_15
#define PORT_CLKOUT GPIOA
#define PIN_CLKOUT_INT GPIO_PinSource15
#define PORT_CLKOUT_INT GPIO_PortSourceGPIOA
#define PIN_LED GPIO_Pin_13
#define PORT_LED GPIOC
#define PIN_DEB GPIO_Pin_9
#define PORT_DEB GPIOB
#define PIN_DMR_LED GPIO_Pin_13
#define PORT_DMR_LED GPIOB
#define PIN_P25_LED GPIO_Pin_0
#define PORT_P25_LED GPIOB
#define PIN_PTT_LED GPIO_Pin_14
#define PORT_PTT_LED GPIOB
#define PIN_COS_LED GPIO_Pin_15
#define PORT_COS_LED GPIOB
#else
#error "Either PI_HAT_7021_REV_02, ZUMSPOT_ADF7021, LONESTAR_USB, MMDVM_HS_HAT_REV12, MMDVM_HS_DUAL_HAT_REV10, NANO_HOTSPOT, NANO_DV_REV11, or SKYBRIDGE_HS need to be defined"
#endif
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
extern "C" {
#if defined(PI_HAT_7021_REV_02)
#if defined(BIDIR_DATA_PIN)
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line15) != RESET) {
io.interrupt1();
EXTI_ClearITPendingBit(EXTI_Line15);
}
}
#else
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line14) != RESET) {
io.interrupt1();
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
#endif // BIDIR_DATA_PIN
#elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
#if defined(BIDIR_DATA_PIN)
void EXTI3_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line3) != RESET) {
io.interrupt1();
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
#else
void EXTI15_10_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line15) != RESET) {
io.interrupt1();
EXTI_ClearITPendingBit(EXTI_Line15);
}
}
#endif // BIDIR_DATA_PIN
#if defined(DUPLEX)
void EXTI9_5_IRQHandler(void) {
#if defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS)
if (EXTI_GetITStatus(EXTI_Line8) != RESET) {
io.interrupt2();
EXTI_ClearITPendingBit(EXTI_Line8);
}
#else
if (EXTI_GetITStatus(EXTI_Line5) != RESET) {
io.interrupt2();
EXTI_ClearITPendingBit(EXTI_Line5);
}
#endif // ZUMSPOT_ADF7021 || SKYBRIDGE_HS
}
#endif
#endif
}
/// <summary>
/// Function delay_us() from stm32duino project
/// </summary>
/// <param name="us">Number of microseconds to delay.</param>
static inline void delay_us(uint32_t us)
{
us *= 12;
/* fudge for function call overhead */
us--;
asm volatile(
" mov r0, %[us] \n\t"
"1: subs r0, #1 \n\t"
" bhi 1b \n\t"
:
: [us] "r" (us)
: "r0");
}
/// <summary>
///
/// </summary>
static inline void delay_ns()
{
asm volatile(
"nop \n\t"
"nop \n\t"
"nop \n\t"
);
}
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
void IO::delayBit()
{
delay_ns();
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/// <summary>
///
/// </summary>
/// <param name="enable"></param>
void IO::setBandVHF(bool enable)
{
GPIO_WriteBit(PORT_SET_BAND, PIN_SET_BAND, vhf_on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::hasSingleADF7021()
{
return GPIO_ReadInputDataBit(PORT_SGL_DBL, PIN_SGL_DBL) == Bit_SET;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::isDualBand()
{
return GPIO_ReadInputDataBit(PORT_DL_DPX, PIN_DL_DPX) == Bit_SET;
}
#endif
/// <summary>
///
/// </summary>
void IO::delayIfCal()
{
delayUS(10000);
}
/// <summary>
///
/// </summary>
void IO::delayReset()
{
delayUS(300);
}
/// <summary>
///
/// </summary>
/// <param name="us"></param>
void IO::delayUS(uint32_t us)
{
::delay_us(us);
}
/// <summary>
/// Initializes hardware interrupts.
/// </summary>
void IO::initInt()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct);
EXTI_InitTypeDef EXTI_InitStructure;
#if defined(DUPLEX)
EXTI_InitTypeDef EXTI_InitStructure2;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
#if defined(PI_HAT_7021_REV_02)
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
#elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
#endif
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
// Pin defines if the board has a single ADF7021 or double
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SGL_DBL;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(PORT_SGL_DBL, &GPIO_InitStruct);
// Pin defines if the board is dual band or duplex
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_DL_DPX;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(PORT_DL_DPX, &GPIO_InitStruct);
// Pin will set UHF or VHF
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SET_BAND;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_SET_BAND, &GPIO_InitStruct);
// TODO: Remove this line
// GPIO_WriteBit(PORT_SET_BAND, PIN_SET_BAND, Bit_RESET);
#endif
#if defined(STM32_USB_HOST)
// Pin PA11,PA12 = LOW, USB Reset
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_12, Bit_RESET);
#endif
#if defined(LONG_USB_RESET)
// 10 ms delay
delayUS(10000U);
#else
volatile unsigned int delay;
for (delay = 0; delay < 512; delay++);
#endif
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
// Pin SCLK
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SCLK;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_SCLK, &GPIO_InitStruct);
// Pin SDATA
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SDATA;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_SDATA, &GPIO_InitStruct);
// Pin SREAD
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SREAD;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PORT_SREAD, &GPIO_InitStruct);
// Pin SLE
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SLE;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_SLE, &GPIO_InitStruct);
#if defined(DUPLEX)
// Pin SLE2
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SLE2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_SLE2, &GPIO_InitStruct);
// Pin RXD2
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_RXD2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PORT_RXD2, &GPIO_InitStruct);
#endif
// Pin CE
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_CE;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_CE, &GPIO_InitStruct);
// Pin RXD
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_RXD;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PORT_RXD, &GPIO_InitStruct);
// Pin TXD
// TXD is TxRxCLK of ADF7021, standard TX/RX data interface
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_TXD;
#if defined(BIDIR_DATA_PIN)
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
#else
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
#endif
GPIO_Init(PORT_TXD, &GPIO_InitStruct);
#if defined(DUPLEX)
GPIO_InitStruct.GPIO_Pin = PIN_TXD2;
GPIO_Init(PORT_TXD2, &GPIO_InitStruct);
#endif
// Pin TXRX_CLK
#if !defined(BIDIR_DATA_PIN)
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_CLKOUT;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PORT_CLKOUT, &GPIO_InitStruct);
#endif
// Pin LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_LED;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_LED, &GPIO_InitStruct);
// Pin Debug
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_DEB;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_DEB, &GPIO_InitStruct);
// DMR LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_DMR_LED;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_DMR_LED, &GPIO_InitStruct);
// P25 LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_P25_LED;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_P25_LED, &GPIO_InitStruct);
// PTT LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_PTT_LED;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_PTT_LED, &GPIO_InitStruct);
// COS LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_COS_LED;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_COS_LED, &GPIO_InitStruct);
#if defined(PI_HAT_7021_REV_02)
#if defined(BIDIR_DATA_PIN)
// Connect EXTI15 Line
GPIO_EXTILineConfig(PORT_TXD_INT, PIN_TXD_INT);
// Configure EXTI15 line
EXTI_InitStructure.EXTI_Line = EXTI_Line15;
#else
// Connect EXTI14 Line
GPIO_EXTILineConfig(PORT_CLKOUT_INT, PIN_CLKOUT_INT);
// Configure EXTI14 line
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
#endif
#elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
#if defined(BIDIR_DATA_PIN)
// Connect EXTI3 Line
GPIO_EXTILineConfig(PORT_TXD_INT, PIN_TXD_INT);
// Configure EXTI3 line
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
#else
// Connect EXTI15 Line
GPIO_EXTILineConfig(PORT_CLKOUT_INT, PIN_CLKOUT_INT);
// Configure EXTI15 line
EXTI_InitStructure.EXTI_Line = EXTI_Line15;
#endif
#if defined(DUPLEX)
// Connect EXTI5 Line
GPIO_EXTILineConfig(PORT_TXD2_INT, PIN_TXD2_INT);
// Configure EXT5 line
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
EXTI_InitStructure2.EXTI_Line = EXTI_Line8;
#else
EXTI_InitStructure2.EXTI_Line = EXTI_Line5;
#endif
#endif
#endif
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
#if defined(DUPLEX)
EXTI_InitStructure2.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure2.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure2.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure2);
#endif
}
/// <summary>
/// Starts hardware interrupts.
/// </summary>
void IO::startInt()
{
NVIC_InitTypeDef NVIC_InitStructure;
#if defined(DUPLEX)
NVIC_InitTypeDef NVIC_InitStructure2;
#endif
#if defined(PI_HAT_7021_REV_02)
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
#elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
#if defined(BIDIR_DATA_PIN)
// Enable and set EXTI3 Interrupt
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
#else
// Enable and set EXTI15 Interrupt
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
#endif
#if defined(DUPLEX)
NVIC_InitStructure2.NVIC_IRQChannel = EXTI9_5_IRQn;
#endif
#endif
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#if defined(DUPLEX)
NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure2.NVIC_IRQChannelSubPriority = 15;
NVIC_InitStructure2.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure2);
#endif
}
#if defined(BIDIR_DATA_PIN)
/// <summary>
///
/// </summary>
/// <remarks>RXD pin is bidirectional in standard interfaces</remarks>
/// <param name="dir"></param>
void IO::setDataDirOut(bool dir)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_RXD;
if (dir)
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
else
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(PORT_RXD, &GPIO_InitStruct);
}
#endif
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SCLK(bool on)
{
GPIO_WriteBit(PORT_SCLK, PIN_SCLK, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SDATA(bool on)
{
GPIO_WriteBit(PORT_SDATA, PIN_SDATA, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::SREAD()
{
return GPIO_ReadInputDataBit(PORT_SREAD, PIN_SREAD) == Bit_SET;
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SLE1(bool on)
{
GPIO_WriteBit(PORT_SLE, PIN_SLE, on ? Bit_SET : Bit_RESET);
}
#if defined(DUPLEX)
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SLE2(bool on)
{
GPIO_WriteBit(PORT_SLE2, PIN_SLE2, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::RXD2()
{
return GPIO_ReadInputDataBit(PORT_RXD2, PIN_RXD2) == Bit_SET;
}
#endif
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::CE(bool on)
{
GPIO_WriteBit(PORT_CE, PIN_CE, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::RXD1()
{
return GPIO_ReadInputDataBit(PORT_RXD, PIN_RXD) == Bit_SET;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::CLK()
{
#if defined(BIDIR_DATA_PIN)
return GPIO_ReadInputDataBit(PORT_TXD, PIN_TXD) == Bit_SET;
#else
return GPIO_ReadInputDataBit(PORT_CLKOUT, PIN_CLKOUT) == Bit_SET;
#endif
}
#if defined(BIDIR_DATA_PIN)
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setRXDInt(bool on)
{
GPIO_WriteBit(PORT_RXD, PIN_RXD, on ? Bit_SET : Bit_RESET);
}
#endif
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setTXDInt(bool on)
{
GPIO_WriteBit(PORT_TXD, PIN_TXD, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setLEDInt(bool on)
{
GPIO_WriteBit(PORT_LED, PIN_LED, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setPTTInt(bool on)
{
GPIO_WriteBit(PORT_PTT_LED, PIN_PTT_LED, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setCOSInt(bool on)
{
GPIO_WriteBit(PORT_COS_LED, PIN_COS_LED, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setDMRInt(bool on)
{
GPIO_WriteBit(PORT_DMR_LED, PIN_DMR_LED, on ? Bit_SET : Bit_RESET);
}
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setP25Int(bool on)
{
GPIO_WriteBit(PORT_P25_LED, PIN_P25_LED, on ? Bit_SET : Bit_RESET);
}
#endif // STM32F10X_MD

@ -0,0 +1,264 @@
The GNU General Public License, Version 2, June 1991 (GPLv2)
============================================================
> Copyright (C) 1989, 1991 Free Software Foundation, Inc.
> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
Preamble
--------
The licenses for most software are designed to take away your freedom to share
and change it. By contrast, the GNU General Public License is intended to
guarantee your freedom to share and change free software--to make sure the
software is free for all its users. This General Public License applies to most
of the Free Software Foundation's software and to any other program whose
authors commit to using it. (Some other Free Software Foundation software is
covered by the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the freedom to
distribute copies of free software (and charge for this service if you wish),
that you receive source code or can get it if you want it, that you can change
the software or use pieces of it in new free programs; and that you know you can
do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny
you these rights or to ask you to surrender the rights. These restrictions
translate to certain responsibilities for you if you distribute copies of the
software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a
fee, you must give the recipients all the rights that you have. You must make
sure that they, too, receive or can get the source code. And you must show them
these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer
you this license which gives you legal permission to copy, distribute and/or
modify the software.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If the
software is modified by someone else and passed on, we want its recipients to
know that what they have is not the original, so that any problems introduced by
others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish
to avoid the danger that redistributors of a free program will individually
obtain patent licenses, in effect making the program proprietary. To prevent
this, we have made it clear that any patent must be licensed for everyone's free
use or not licensed at all.
The precise terms and conditions for copying, distribution and modification
follow.
Terms And Conditions For Copying, Distribution And Modification
---------------------------------------------------------------
**0.** This License applies to any program or other work which contains a notice
placed by the copyright holder saying it may be distributed under the terms of
this General Public License. The "Program", below, refers to any such program or
work, and a "work based on the Program" means either the Program or any
derivative work under copyright law: that is to say, a work containing the
Program or a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is included without
limitation in the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by
this License; they are outside its scope. The act of running the Program is not
restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made by
running the Program). Whether that is true depends on what the Program does.
**1.** You may copy and distribute verbatim copies of the Program's source code
as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this License
and to the absence of any warranty; and give any other recipients of the Program
a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may at
your option offer warranty protection in exchange for a fee.
**2.** You may modify your copy or copies of the Program or any portion of it,
thus forming a work based on the Program, and copy and distribute such
modifications or work under the terms of Section 1 above, provided that you also
meet all of these conditions:
* **a)** You must cause the modified files to carry prominent notices stating
that you changed the files and the date of any change.
* **b)** You must cause any work that you distribute or publish, that in whole
or in part contains or is derived from the Program or any part thereof, to
be licensed as a whole at no charge to all third parties under the terms of
this License.
* **c)** If the modified program normally reads commands interactively when
run, you must cause it, when started running for such interactive use in the
most ordinary way, to print or display an announcement including an
appropriate copyright notice and a notice that there is no warranty (or
else, saying that you provide a warranty) and that users may redistribute
the program under these conditions, and telling the user how to view a copy
of this License. (Exception: if the Program itself is interactive but does
not normally print such an announcement, your work based on the Program is
not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be reasonably
considered independent and separate works in themselves, then this License, and
its terms, do not apply to those sections when you distribute them as separate
works. But when you distribute the same sections as part of a whole which is a
work based on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the entire whole,
and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise the
right to control the distribution of derivative or collective works based on the
Program.
In addition, mere aggregation of another work not based on the Program with the
Program (or with a work based on the Program) on a volume of a storage or
distribution medium does not bring the other work under the scope of this
License.
**3.** You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1 and 2
above provided that you also do one of the following:
* **a)** Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above on
a medium customarily used for software interchange; or,
* **b)** Accompany it with a written offer, valid for at least three years, to
give any third party, for a charge no more than your cost of physically
performing source distribution, a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
* **c)** Accompany it with the information you received as to the offer to
distribute corresponding source code. (This alternative is allowed only for
noncommercial distribution and only if you received the program in object
code or executable form with such an offer, in accord with Subsection b
above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means all the
source code for all modules it contains, plus any associated interface
definition files, plus the scripts used to control compilation and installation
of the executable. However, as a special exception, the source code distributed
need not include anything that is normally distributed (in either source or
binary form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component itself
accompanies the executable.
If distribution of executable or object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the source code
from the same place counts as distribution of the source code, even though third
parties are not compelled to copy the source along with the object code.
**4.** You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense or distribute the Program is void, and will automatically terminate
your rights under this License. However, parties who have received copies, or
rights, from you under this License will not have their licenses terminated so
long as such parties remain in full compliance.
**5.** You are not required to accept this License, since you have not signed
it. However, nothing else grants you permission to modify or distribute the
Program or its derivative works. These actions are prohibited by law if you do
not accept this License. Therefore, by modifying or distributing the Program (or
any work based on the Program), you indicate your acceptance of this License to
do so, and all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
**6.** Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these terms and
conditions. You may not impose any further restrictions on the recipients'
exercise of the rights granted herein. You are not responsible for enforcing
compliance by third parties to this License.
**7.** If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), conditions
are imposed on you (whether by court order, agreement or otherwise) that
contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the Program at all.
For example, if a patent license would not permit royalty-free redistribution of
the Program by all those who receive copies directly or indirectly through you,
then the only way you could satisfy both it and this License would be to refrain
entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply and the
section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or
other property right claims or to contest validity of any such claims; this
section has the sole purpose of protecting the integrity of the free software
distribution system, which is implemented by public license practices. Many
people have made generous contributions to the wide range of software
distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee cannot impose that
choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
**8.** If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original copyright
holder who places the Program under this License may add an explicit
geographical distribution limitation excluding those countries, so that
distribution is permitted only in or among countries not thus excluded. In such
case, this License incorporates the limitation as if written in the body of this
License.
**9.** The Free Software Foundation may publish revised and/or new versions of
the General Public License from time to time. Such new versions will be similar
in spirit to the present version, but may differ in detail to address new
problems or concerns.
Each version is given a distinguishing version number. If the Program specifies
a version number of this License which applies to it and "any later version",
you have the option of following the terms and conditions either of that version
or of any later version published by the Free Software Foundation. If the
Program does not specify a version number of this License, you may choose any
version ever published by the Free Software Foundation.
**10.** If you wish to incorporate parts of the Program into other free programs
whose distribution conditions are different, write to the author to ask for
permission. For software which is copyrighted by the Free Software Foundation,
write to the Free Software Foundation; we sometimes make exceptions for this.
Our decision will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software generally.
No Warranty
-----------
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

@ -0,0 +1,27 @@
# Digital Voice Modem Firmware (Hotspot)
The DVM firmware provides the embedded microcontroller implementation of a mixed-mode DMR/P25 or dedicated-mode DMR or P25 repeater system. The firmware; is the portion of a complete Over-The-Air modem implementation that uses an ADF7021 to provide a raw RF interface.
This project is a direct fork of the MMDVM (https://github.com/jelimoore/MMDVM_HS) project.
## Building
Please see the various Makefile's included in the project for more information. This project includes a few Makefiles to target different hardware. (All following information assumes familiarity with the standard Linux make system.)
* Makefile - This makefile is used for targeting a generic STM32F103/ADF7021 device.
* For STM32F103 using Ubuntu OS install the standard ARM embedded toolchain (typically arm-gcc-none-eabi).
1. Create a directory under "/opt" called "tools" and change to the directory:
```
mkdir -p /opt/tools
cd /opt/tools
```
2. Checkout ```https://github.com/juribeparada/STM32F10X_Lib``` to /opt/tools:
```git clone https://github.com/juribeparada/STM32F10X_Lib```
Use the ```make``` command to build the firmware, choosing the appropriate makefile with the -F switch.
## License
This project is licensed under the GPLv2 License - see the [LICENSE.md](LICENSE.md) file for details. Use of this project is intended, strictly for amateur and educational use ONLY. Any other use is at the risk of user and all commercial purposes are strictly forbidden.

@ -0,0 +1,134 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (c) 2020 by Jonathan Naylor G4KLX
* Copyright (c) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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(STM32F10X_MD) || defined(STM32F4XX)
#include "STM_UART.h"
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the STM_UART class.
/// </summary>
STM_UART::STM_UART() :
m_usart(NULL)
{
/* stub */
}
/// <summary></summary>
/// <param name="usart"></param>
void STM_UART::init(USART_TypeDef* usart)
{
m_usart = usart;
}
/// <summary></summary>
/// <param name="data"></param>
/// <param name="length"></param>
void STM_UART::write(const uint8_t* data, uint16_t length)
{
if (length == 0U || m_usart == NULL)
return;
m_txFifo.put(data[0]);
USART_ITConfig(m_usart, USART_IT_TXE, ENABLE);//switch TX IRQ is on
for (uint16_t i = 1U; i < length; i++) {
m_txFifo.put(data[i]);
}
USART_ITConfig(m_usart, USART_IT_TXE, ENABLE);//make sure TX IRQ is on
}
/// <summary></summary>
/// <returns></returns>
uint8_t STM_UART::read()
{
return m_rxFifo.get();
}
/// <summary></summary>
void STM_UART::handleIRQ()
{
if (m_usart == NULL)
return;
if (USART_GetITStatus(m_usart, USART_IT_RXNE)) {
if (!m_rxFifo.isFull())
m_rxFifo.put((uint8_t)USART_ReceiveData(m_usart));
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
if (USART_GetITStatus(m_usart, USART_IT_TXE)) {
if (!m_txFifo.isEmpty())
USART_SendData(m_usart, m_txFifo.get());
USART_ClearITPendingBit(m_usart, USART_IT_TXE);
if (m_txFifo.isEmpty()) // if there's no more data to transmit then turn off TX interrupts
USART_ITConfig(m_usart, USART_IT_TXE, DISABLE);
}
}
/// <summary>
/// Flushes the transmit shift register.
/// </summary>
/// <remarks>
/// This call is blocking!
/// </remarks>
void STM_UART::flush()
{
if (m_usart == NULL)
return;
// wait until the TXE shows the shift register is empty
while (USART_GetITStatus(m_usart, USART_FLAG_TXE))
;
}
/// <summary></summary>
/// <returns></returns>
uint16_t STM_UART::available()
{
return m_rxFifo.isEmpty() ? 0U : 1U;
}
/// <summary></summary>
/// <returns></returns>
uint16_t STM_UART::availableForWrite()
{
return m_txFifo.isFull() ? 0U : 1U;
}
#endif

@ -0,0 +1,137 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (c) 2020 by Jonathan Naylor G4KLX
* Copyright (c) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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(STM32F10X_MD) || defined(STM32F4XX)
#if !defined(__STM_UART_H__)
#define __STM_UART_H__
#include "Defines.h"
#if defined(STM32F10X_MD)
#include <stm32f10x.h>
#elif defined(STM32F4XX)
#include "stm32f4xx.h"
#endif
const uint16_t BUFFER_SIZE = 2048U; //needs to be a power of 2 !
const uint16_t BUFFER_MASK = BUFFER_SIZE - 1;
// ---------------------------------------------------------------------------
// Class Declaration
// This class represents a FIFO buffer on a STM32 UART.
// ---------------------------------------------------------------------------
class DSP_FW_API STM_UARTFIFO {
public:
/// <summary>Initializes a new instance of the STM_UARTFIFO class.</summary>
STM_UARTFIFO() :
m_head(0U),
m_tail(0U)
{
/* stub */
}
/// <summary></summary>
uint8_t get()
{
return m_buffer[BUFFER_MASK & (m_tail++)];
}
/// <summary></summary>
void put(uint8_t data)
{
m_buffer[BUFFER_MASK & (m_head++)] = data;
}
/// <summary>Helper to reset data values to defaults.</summary>
void reset()
{
m_tail = 0U;
m_head = 0U;
}
/// <summary></summary>
bool isEmpty()
{
return m_tail == m_head;
}
/// <summary></summary>
bool isFull()
{
return ((m_head + 1U) & BUFFER_MASK) == (m_tail & BUFFER_MASK);
}
private:
volatile uint8_t m_buffer[BUFFER_SIZE];
volatile uint16_t m_head;
volatile uint16_t m_tail;
};
// ---------------------------------------------------------------------------
// Class Declaration
// This class represents an STM32 UART.
// ---------------------------------------------------------------------------
class STM_UART {
public:
/// <summary>Initializes a new instance of the STM_UART class.</summary>
STM_UART();
/// <summary></summary>
void init(USART_TypeDef* usart);
/// <summary></summary>
uint8_t read();
/// <summary></summary>
void write(const uint8_t* data, uint16_t length);
/// <summary></summary>
void handleIRQ();
/// <summary>Flushes the transmit shift register.</summary>
void flush();
/// <summary></summary>
uint16_t available();
/// <summary></summary>
uint16_t availableForWrite();
private:
USART_TypeDef* m_usart;
STM_UARTFIFO m_rxFifo;
STM_UARTFIFO m_txFifo;
};
#endif // __SERIAL_PORT_H__
#endif

@ -0,0 +1,142 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "SerialBuffer.h"
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the SerialBuffer class.
/// </summary>
SerialBuffer::SerialBuffer(uint16_t length) :
m_length(length),
m_buffer(NULL),
m_head(0U),
m_tail(0U),
m_full(false)
{
m_buffer = new uint8_t[length];
}
/// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t SerialBuffer::getSpace() const
{
uint16_t n = 0U;
if (m_tail == m_head)
n = m_full ? 0U : m_length;
else if (m_tail < m_head)
n = m_length - m_head + m_tail;
else
n = m_tail - m_head;
if (n > m_length)
n = 0U;
return n;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
uint16_t SerialBuffer::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
else if (m_tail < m_head)
return m_head - m_tail;
else
return m_length - m_tail + m_head;
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void SerialBuffer::reset()
{
m_head = 0U;
m_tail = 0U;
m_full = false;
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool SerialBuffer::put(uint8_t c)
{
if (m_full)
return false;
m_buffer[m_head] = c;
m_head++;
if (m_head >= m_length)
m_head = 0U;
if (m_head == m_tail)
m_full = true;
return true;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
uint8_t SerialBuffer::peek() const
{
return m_buffer[m_tail];
}
/// <summary>
///
/// </summary>
/// <returns></returns>
uint8_t SerialBuffer::get()
{
uint8_t value = m_buffer[m_tail];
m_full = false;
m_tail++;
if (m_tail >= m_length)
m_tail = 0U;
return value;
}

@ -0,0 +1,89 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#if !defined(__SERIAL_RB_H__)
#define __SERIAL_RB_H__
#if defined(STM32F10X_MD)
#include "stm32f10x.h"
#elif defined(STM32F4XX)
#include "stm32f4xx.h"
#include <cstddef>
#endif
#include "Defines.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint16_t SERIAL_RINGBUFFER_SIZE = 1000U;
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a circular buffer for serial data.
// ---------------------------------------------------------------------------
class DSP_FW_API SerialBuffer {
public:
/// <summary>Initializes a new instance of the SerialBuffer class.</summary>
SerialBuffer(uint16_t length = SERIAL_RINGBUFFER_SIZE);
/// <summary>Helper to get how much space the ring buffer has for samples.</summary>
uint16_t getSpace() const;
/// <summary></summary>
uint16_t getData() const;
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary></summary>
bool put(uint8_t c);
/// <summary></summary>
uint8_t peek() const;
/// <summary></summary>
uint8_t get();
private:
uint16_t m_length;
volatile uint8_t* m_buffer;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
};
#endif // __SERIAL_RB_H__

File diff suppressed because it is too large Load Diff

@ -0,0 +1,218 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2018,2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2018,2021 Bryan Biedenkapp N2PLL
*
* 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(__SERIAL_PORT_H__)
#define __SERIAL_PORT_H__
#include "Defines.h"
#include "Globals.h"
#include "SerialBuffer.h"
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum DVM_STATE {
STATE_IDLE = 0U,
// DMR
STATE_DMR = 1U,
// Project 25
STATE_P25 = 2U,
// CW
STATE_CW = 10U,
// Calibration States
STATE_P25_LF_CAL = 91U,
STATE_P25_CAL_1K = 92U,
STATE_DMR_DMO_CAL_1K = 93U,
STATE_DMR_CAL_1K = 94U,
STATE_DMR_LF_CAL = 95U,
STATE_RSSI_CAL = 96U,
STATE_P25_CAL = 97U,
STATE_DMR_CAL = 98U,
STATE_INT_CAL = 99U
};
enum DVM_COMMANDS {
CMD_GET_VERSION = 0x00U,
CMD_GET_STATUS = 0x01U,
CMD_SET_CONFIG = 0x02U,
CMD_SET_MODE = 0x03U,
CMD_SET_SYMLVLADJ = 0x04U,
CMD_SET_RXLEVEL = 0x05U,
CMD_SET_RFPARAMS = 0x06U,
CMD_CAL_DATA = 0x08U,
CMD_RSSI_DATA = 0x09U,
CMD_SEND_CWID = 0x0AU,
CMD_DMR_DATA1 = 0x18U,
CMD_DMR_LOST1 = 0x19U,
CMD_DMR_DATA2 = 0x1AU,
CMD_DMR_LOST2 = 0x1BU,
CMD_DMR_SHORTLC = 0x1CU,
CMD_DMR_START = 0x1DU,
CMD_DMR_ABORT = 0x1EU,
CMD_P25_DATA = 0x31U,
CMD_P25_LOST = 0x32U,
CMD_P25_CLEAR = 0x33U,
CMD_ACK = 0x70U,
CMD_NAK = 0x7FU,
CMD_DEBUG1 = 0xF1U,
CMD_DEBUG2 = 0xF2U,
CMD_DEBUG3 = 0xF3U,
CMD_DEBUG4 = 0xF4U,
CMD_DEBUG5 = 0xF5U,
CMD_DEBUG_DUMP = 0xFAU,
};
enum CMD_REASON_CODE {
RSN_OK = 0U,
RSN_NAK = 1U,
RSN_ILLEGAL_LENGTH = 2U,
RSN_INVALID_REQUEST = 4U,
RSN_RINGBUFF_FULL = 8U,
RSN_INVALID_FDMA_PREAMBLE = 10U,
RSN_INVALID_MODE = 11U,
RSN_INVALID_DMR_CC = 12U,
RSN_INVALID_DMR_SLOT = 13U,
RSN_INVALID_DMR_START = 14U,
RSN_INVALID_DMR_RX_DELAY = 15U,
RSN_INVALID_P25_CORR_COUNT = 16U,
RSN_DMR_DISABLED = 63U,
RSN_P25_DISABLED = 64U,
};
const uint8_t DVM_FRAME_START = 0xFEU;
#define SERIAL_SPEED 115200
// ---------------------------------------------------------------------------
// Class Declaration
// Implements the RS232 serial bus to communicate with the HOST S/W.
// ---------------------------------------------------------------------------
class DSP_FW_API SerialPort {
public:
/// <summary>Initializes a new instance of the SerialPort class.</summary>
SerialPort();
/// <summary>Starts serial port communications.</summary>
void start();
/// <summary>Process data from serial port.</summary>
void process();
/// <summary>Write DMR frame data to serial port.</summary>
void writeDMRData(bool slot, const uint8_t* data, uint8_t length);
/// <summary>Write lost DMR frame data to serial port.</summary>
void writeDMRLost(bool slot);
/// <summary>Write P25 frame data to serial port.</summary>
void writeP25Data(const uint8_t* data, uint8_t length);
/// <summary>Write lost P25 frame data to serial port.</summary>
void writeP25Lost();
/// <summary>Write calibration frame data to serial port.</summary>
void writeCalData(const uint8_t* data, uint8_t length);
/// <summary>Write RSSI frame data to serial port.</summary>
void writeRSSIData(const uint8_t* data, uint8_t length);
/// <summary></summary>
void writeDebug(const char* text);
/// <summary></summary>
void writeDebug(const char* text, int16_t n1);
/// <summary></summary>
void writeDebug(const char* text, int16_t n1, int16_t n2);
/// <summary></summary>
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3);
/// <summary></summary>
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4);
/// <summary></summary>
void writeDump(const uint8_t* data, uint16_t length);
private:
uint8_t m_buffer[256U];
uint8_t m_ptr;
uint8_t m_len;
bool m_debug;
bool m_firstCal;
/// <summary>Write acknowlegement.</summary>
void sendACK();
/// <summary>Write negative acknowlegement.</summary>
void sendNAK(uint8_t err);
/// <summary>Write modem DSP status.</summary>
void getStatus();
/// <summary>Write modem DSP version.</summary>
void getVersion();
/// <summary>Helper to validate the passed modem state is valid.</summary>
uint8_t modemStateCheck(DVM_STATE state);
/// <summary>Set modem DSP configuration from serial port data.</summary>
uint8_t setConfig(const uint8_t* data, uint8_t length);
/// <summary>Set modem DSP mode from serial port data.</summary>
uint8_t setMode(const uint8_t* data, uint8_t length);
/// <summary>Sets the modem state.</summary>
void setMode(DVM_STATE modemState);
/// <summary>Sets the fine-tune symbol levels.</summary>
uint8_t setSymbolLvlAdj(const uint8_t* data, uint8_t length);
/// <summary>Sets the RF parameters.</summary>
uint8_t setRFParams(const uint8_t* data, uint8_t length);
// Hardware specific routines
/// <summary></summary>
void beginInt(uint8_t n, int speed);
/// <summary></summary>
int availableInt(uint8_t n);
/// <summary></summary>
int availableForWriteInt(uint8_t n);
/// <summary></summary>
uint8_t readInt(uint8_t n);
/// <summary></summary>
void writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush = false);
};
#endif // __SERIAL_PORT_H__

@ -0,0 +1,316 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "SerialPort.h"
#include "STM_UART.h"
/*
Pin definitions:
- Host communication:
1) USART1 - TXD PA9 - RXD PA10
2) USB VCOM
- Serial repeater
USART2 - TXD PA2 - RXD PA3
*/
#if defined(STM32F10X_MD)
#if defined(STM32_USB_HOST)
#include <usb_serial.h>
#endif
#if defined(STM32_USART1_HOST) && defined(STM32_USB_HOST)
#error "You have to select STM32_USART1_HOST or STM32_USB_HOST, but not both"
#endif
#if defined(STM32_USART1_HOST) || defined(SERIAL_REPEATER_USART1)
// ---------------------------------------------------------------------------
// Global Functions and Variables
// ---------------------------------------------------------------------------
extern "C" {
void USART1_IRQHandler();
void USART2_IRQHandler();
}
// ---------------------------------------------------------------------------
// UART1
// ---------------------------------------------------------------------------
static STM_UART m_USART1;
/// <summary>
///
/// </summary>
void USART1_IRQHandler()
{
m_USART1.handleIRQ();
}
/// <summary>
///
/// </summary>
/// <param name="speed"></param>
void InitUSART1(int speed)
{
// USART1 - TXD PA9 - RXD PA10
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// USART IRQ init
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15;
NVIC_Init(&NVIC_InitStructure);
// Configure USART as alternate function
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // Tx
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // Rx
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure USART baud rate
USART_StructInit(&USART_InitStructure);
USART_InitStructure.USART_BaudRate = speed;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
m_USART1.init(USART1);
}
#endif
// ---------------------------------------------------------------------------
// UART2
// ---------------------------------------------------------------------------
static STM_UART m_USART2;
/// <summary>
///
/// </summary>
void USART2_IRQHandler()
{
m_USART2.handleIRQ();
}
/// <summary>
///
/// </summary>
/// <param name="speed"></param>
void InitUSART2(int speed)
{
// USART2 - TXD PA2 - RXD PA3
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// USART IRQ init
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 15;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 15;
NVIC_Init(&NVIC_InitStructure);
// Configure USART as alternate function
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // Tx
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // Rx
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure USART baud rate
USART_StructInit(&USART_InitStructure);
USART_InitStructure.USART_BaudRate = speed;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
m_USART2.init(USART2);
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <param name="speed"></param>
void SerialPort::beginInt(uint8_t n, int speed)
{
switch (n) {
case 1U:
#if defined(STM32_USART1_HOST)
InitUSART1(speed);
#elif defined(STM32_USB_HOST)
usbserial.begin();
#endif
break;
case 3U:
InitUSART2(speed);
break;
default:
break;
}
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
int SerialPort::availableInt(uint8_t n)
{
switch (n) {
case 1U:
#if defined(STM32_USART1_HOST)
return m_USART1.available();
#elif defined(STM32_USB_HOST)
return usbserial.available();
#endif
case 3U:
m_USART2.available();
default:
return 0;
}
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
int SerialPort::availableForWriteInt(uint8_t n)
{
switch (n) {
case 1U:
#if defined(STM32_USART1_HOST)
return m_USART1.availableForWrite();
#elif defined(STM32_USB_HOST)
return usbserial.availableForWrite();
#endif
case 3U:
return m_USART2.availableForWrite();
default:
return 0;
}
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
uint8_t SerialPort::readInt(uint8_t n)
{
switch (n) {
case 1U:
#if defined(STM32_USART1_HOST)
return m_USART1.read();
#elif defined(STM32_USB_HOST)
return usbserial.read();
#endif
case 3U:
return m_USART2.read();
default:
return 0U;
}
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <param name="data"></param>
/// <param name="length"></param>
/// <param name="flush"></param>
void SerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush)
{
switch (n) {
case 1U:
#if defined(STM32_USART1_HOST)
m_USART1.write(data, length);
if (flush)
m_USART1.flush();
#elif defined(STM32_USB_HOST)
usbserial.write(data, length);
if (flush)
usbserial.flush();
#endif
break;
case 3U:
m_USART2.write(data, length);
if (flush)
m_USART2.flush();
break;
default:
break;
}
}
#endif

@ -0,0 +1,86 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2020 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 "Utils.h"
// ---------------------------------------------------------------------------
// Constants/Macros
// ---------------------------------------------------------------------------
const uint8_t BITS_TABLE[] = {
# define B2(n) n, n+1, n+1, n+2
# define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2)
# define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2)
B6(0), B6(1), B6(1), B6(2)
};
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
uint8_t countBits8(uint8_t bits)
{
return BITS_TABLE[bits];
}
uint8_t countBits16(uint16_t bits)
{
uint8_t* p = (uint8_t*)&bits;
uint8_t n = 0U;
n += BITS_TABLE[p[0U]];
n += BITS_TABLE[p[1U]];
return n;
}
uint8_t countBits32(uint32_t bits)
{
uint8_t* p = (uint8_t*)&bits;
uint8_t n = 0U;
n += BITS_TABLE[p[0U]];
n += BITS_TABLE[p[1U]];
n += BITS_TABLE[p[2U]];
n += BITS_TABLE[p[3U]];
return n;
}
uint8_t countBits64(uint64_t bits)
{
uint8_t* p = (uint8_t*)&bits;
uint8_t n = 0U;
n += BITS_TABLE[p[0U]];
n += BITS_TABLE[p[1U]];
n += BITS_TABLE[p[2U]];
n += BITS_TABLE[p[3U]];
n += BITS_TABLE[p[4U]];
n += BITS_TABLE[p[5U]];
n += BITS_TABLE[p[6U]];
n += BITS_TABLE[p[7U]];
return n;
}

@ -0,0 +1,53 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 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.
*/
#if !defined(__UTILS_H__)
#define __UTILS_H__
#if defined(STM32F10X_MD)
#include "stm32f10x.h"
#elif defined(STM32F4XX)
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#endif
#include "Defines.h"
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
DSP_FW_API uint8_t countBits8(uint8_t bits);
DSP_FW_API uint8_t countBits16(uint16_t bits);
DSP_FW_API uint8_t countBits32(uint32_t bits);
DSP_FW_API uint8_t countBits64(ulong64_t bits);
#endif // __UTILS_H__

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016-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.
*/
/* Memory areas */
MEMORY
{
ROM (rx) : ORIGIN = 0x08002000, LENGTH = 120K /* FLASH */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K /* Main RAM */
}
INCLUDE stm32f10x_link.ld

@ -0,0 +1,286 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2018,2019 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 "Globals.h"
#include "dmr/CalDMR.h"
using namespace dmr;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// Voice LC 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
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
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)
{ 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)
// 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
const uint8_t SHORTLC_1K[] = { 0x33U, 0x3AU, 0xA0U, 0x30U, 0x00U, 0x55U, 0xA6U, 0x5FU, 0x50U };
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the CalDMR class.
/// </summary>
CalDMR::CalDMR() :
m_transmit(false),
m_state(DMRCAL1K_IDLE),
m_frameStart(0U),
m_dmr1k(),
m_audioSeq(0),
m_count(0)
{
::memcpy(m_dmr1k, VOICE_1K, DMR_FRAME_LENGTH_BYTES + 1U);
}
/// <summary>
/// Process local state and transmit on the air interface.
/// </summary>
void CalDMR::process()
{
switch (m_calState) {
case STATE_DMR_CAL:
case STATE_DMR_LF_CAL:
if (m_transmit) {
dmrDMOTX.setCal(true);
dmrDMOTX.process();
}
else {
dmrDMOTX.setCal(false);
}
break;
case STATE_DMR_CAL_1K:
dmr1kcal();
break;
case STATE_DMR_DMO_CAL_1K:
dmrDMO1kcal();
break;
case STATE_INT_CAL:
// Simple interrupt counter for board diagnostics (TCXO, connections, etc)
// Not intended for precise interrupt frequency measurements
m_count++;
if (m_count >= CAL_DLY_LOOP) {
m_count = 0U;
uint16_t int1, int2;
io.getIntCounter(int1, int2);
DEBUG3("Counter INT1/INT2:", int1 >> 1U, int2);
}
break;
default:
break;
}
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
void CalDMR::createData1k(uint8_t n)
{
for (uint8_t i = 0; i < 5U; i++)
m_dmr1k[i + 15U] = SYNCEMB_1K[n][i + 1U];
m_dmr1k[14U] &= 0xF0U;
m_dmr1k[20U] &= 0x0FU;
m_dmr1k[14U] |= SYNCEMB_1K[n][0] & 0x0FU;
m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U;
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
void CalDMR::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;
}
/// <summary>
///
/// </summary>
void CalDMR::dmr1kcal()
{
dmrTX.process();
uint16_t space = dmrTX.getSpace2();
if (space < 1U)
return;
switch (m_state) {
case DMRCAL1K_VH:
dmrTX.setColorCode(1U);
dmrTX.writeShortLC(SHORTLC_1K, 9U);
dmrTX.writeData2(VH_1K, DMR_FRAME_LENGTH_BYTES + 1U);
dmrTX.setStart(true);
m_state = DMRCAL1K_VOICE;
break;
case DMRCAL1K_VOICE:
createData1k(m_audioSeq);
dmrTX.writeData2(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:
dmrTX.writeData2(VT_1K, DMR_FRAME_LENGTH_BYTES + 1U);
m_frameStart = dmrTX.getFrameCount();
m_state = DMRCAL1K_WAIT;
break;
case DMRCAL1K_WAIT:
if (dmrTX.getFrameCount() > (m_frameStart + 30U)) {
dmrTX.setStart(false);
dmrTX.resetFifo2();
m_audioSeq = 0U;
m_state = DMRCAL1K_IDLE;
}
break;
default:
m_state = DMRCAL1K_IDLE;
break;
}
}
/// <summary>
///
/// </summary>
void CalDMR::dmrDMO1kcal()
{
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;
}
}
/// <summary>
/// Write DMR calibration state.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CalDMR::write(const uint8_t* data, uint8_t length)
{
if (length != 1U)
return RSN_ILLEGAL_LENGTH;
m_transmit = data[0U] == 1U;
if (m_transmit && m_state == DMRCAL1K_IDLE && (m_modemState == STATE_DMR_CAL_1K || m_modemState == STATE_DMR_DMO_CAL_1K))
m_state = DMRCAL1K_VH;
if (m_transmit)
io.rf1Conf(STATE_DMR, true);
return RSN_OK;
}

@ -0,0 +1,90 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* 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(__CAL_DMR_H__)
#define __CAL_DMR_H__
#include "Defines.h"
#include "dmr/DMRDefines.h"
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum DMRCAL1K {
DMRCAL1K_IDLE,
DMRCAL1K_VH,
DMRCAL1K_VOICE,
DMRCAL1K_VT,
DMRCAL1K_WAIT
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements logic for DMR calibration mode.
// ---------------------------------------------------------------------------
class DSP_FW_API CalDMR {
public:
/// <summary>Initializes a new instance of the CalDMR class.</summary>
CalDMR();
/// <summary>Process local state and transmit on the air interface.</summary>
void process();
/// <summary></summary>
void createData1k(uint8_t n);
/// <summary></summary>
void createDataDMO1k(uint8_t n);
/// <summary></summary>
void dmr1kcal();
/// <summary></summary>
void dmrDMO1kcal();
/// <summary>Write DMR calibration state.</summary>
uint8_t write(const uint8_t* data, uint8_t length);
private:
bool m_transmit;
DMRCAL1K m_state;
uint32_t m_frameStart;
uint8_t m_dmr1k[DMR_FRAME_LENGTH_BYTES + 1U];
uint8_t m_audioSeq;
uint32_t m_count;
};
} // namespace dmr
#endif // __CAL_DMR_H__

@ -0,0 +1,354 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,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 "Globals.h"
#include "dmr/DMRDMORX.h"
#include "dmr/DMRSlotType.h"
#include "Utils.h"
using namespace dmr;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint8_t MAX_SYNC_BYTES_ERRS = 3U;
const uint8_t MAX_SYNC_LOST_FRAMES = 13U;
const uint16_t NOENDPTR = 9999U;
const uint8_t CONTROL_NONE = 0x00U;
const uint8_t CONTROL_VOICE = 0x20U;
const uint8_t CONTROL_DATA = 0x40U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRDMORX class.
/// </summary>
DMRDMORX::DMRDMORX() :
m_bitBuffer(0x00U),
m_buffer(),
m_dataPtr(0U),
m_syncPtr(0U),
m_startPtr(0U),
m_endPtr(NOENDPTR),
m_control(CONTROL_NONE),
m_syncCount(0U),
m_colorCode(0U),
m_state(DMORXS_NONE),
m_n(0U),
m_type(0U)
{
/* stub */
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRDMORX::reset()
{
m_syncPtr = 0U;
m_control = CONTROL_NONE;
m_syncCount = 0U;
m_state = DMORXS_NONE;
m_startPtr = 0U;
m_endPtr = NOENDPTR;
}
/// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void DMRDMORX::databit(bool bit)
{
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
if (m_state == DMORXS_NONE) {
correlateSync();
}
else {
uint16_t min = m_syncPtr + DMO_BUFFER_LENGTH_BITS - 2;
uint16_t max = m_syncPtr + 2;
if (min >= DMO_BUFFER_LENGTH_BITS)
min -= DMO_BUFFER_LENGTH_BITS;
if (max >= DMO_BUFFER_LENGTH_BITS)
max -= DMO_BUFFER_LENGTH_BITS;
if (min < max) {
if (m_dataPtr >= min && m_dataPtr <= max)
correlateSync();
}
else {
if (m_dataPtr >= min || m_dataPtr <= max)
correlateSync();
}
}
if (m_dataPtr == m_endPtr) {
frame[0U] = m_control;
bitsToBytes(m_startPtr, DMR_FRAME_LENGTH_BYTES, frame + 1U);
if (m_control == CONTROL_DATA) {
// Data sync
uint8_t colorCode;
uint8_t dataType;
CDMRSlotType slotType;
slotType.decode(frame + 1U, colorCode, dataType);
if (colorCode == m_colorCode) {
m_syncCount = 0U;
m_n = 0U;
frame[0U] |= dataType;
switch (dataType) {
case DT_DATA_HEADER:
DEBUG2("DMRDMORX: databit(): data header found pos", m_syncPtr);
writeRSSIData(frame);
m_state = DMORXS_DATA;
m_type = 0x00U;
break;
case DT_RATE_12_DATA:
case DT_RATE_34_DATA:
case DT_RATE_1_DATA:
if (m_state == DMORXS_DATA) {
DEBUG2("DMRDMORX: databit(): data payload found pos", m_syncPtr);
writeRSSIData(frame);
m_type = dataType;
}
break;
case DT_VOICE_LC_HEADER:
DEBUG2("DMRDMORX: databit(): voice header found pos", m_syncPtr);
writeRSSIData(frame);
m_state = DMORXS_VOICE;
break;
case DT_VOICE_PI_HEADER:
if (m_state == DMORXS_VOICE) {
DEBUG2("DMRDMORX: databit(): voice pi header found pos", m_syncPtr);
writeRSSIData(frame);
}
m_state = DMORXS_VOICE;
break;
case DT_TERMINATOR_WITH_LC:
if (m_state == DMORXS_VOICE) {
DEBUG2("DMRDMORX: databit(): voice terminator found pos", m_syncPtr);
writeRSSIData(frame);
reset();
}
break;
default: // DT_CSBK
DEBUG2("DMRDMORX: databit(): csbk found pos", m_syncPtr);
writeRSSIData(frame);
reset();
break;
}
}
}
else if (m_control == CONTROL_VOICE) {
// Voice sync
DEBUG2("DMRDMORX: databit(): voice sync found pos", m_syncPtr);
writeRSSIData(frame);
m_state = DMORXS_VOICE;
m_syncCount = 0U;
m_n = 0U;
}
else {
if (m_state != DMORXS_NONE) {
m_syncCount++;
if (m_syncCount >= MAX_SYNC_LOST_FRAMES) {
DEBUG1("DMRDMORX: databit(): sync timeout, lost lock");
serial.writeDMRLost(true);
reset();
}
}
if (m_state == DMORXS_VOICE) {
if (m_n >= 5U) {
frame[0U] = CONTROL_VOICE;
m_n = 0U;
}
else {
frame[0U] = ++m_n;
}
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
}
else if (m_state == DMORXS_DATA) {
if (m_type != 0x00U) {
frame[0U] = CONTROL_DATA | m_type;
writeRSSIData(frame);
}
}
}
// End of this slot, reset some items for the next slot.
m_control = CONTROL_NONE;
}
m_dataPtr++;
if (m_dataPtr >= DMO_BUFFER_LENGTH_BITS)
m_dataPtr = 0U;
io.setDecode(m_state != DMORXS_NONE);
}
/// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRDMORX::setColorCode(uint8_t colorCode)
{
m_colorCode = colorCode;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Frame synchronization correlator.
/// </summary>
void DMRDMORX::correlateSync()
{
if ((countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_DATA_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) || \
(countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_S2_DATA_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS)) {
m_control = CONTROL_DATA;
m_syncPtr = m_dataPtr;
m_startPtr = m_dataPtr + DMO_BUFFER_LENGTH_BITS - DMR_SLOT_TYPE_LENGTH_BITS / 2U - DMR_INFO_LENGTH_BITS / 2U - DMR_SYNC_LENGTH_BITS + 1;
if (m_startPtr >= DMO_BUFFER_LENGTH_BITS)
m_startPtr -= DMO_BUFFER_LENGTH_BITS;
m_endPtr = m_dataPtr + DMR_SLOT_TYPE_LENGTH_BITS / 2U + DMR_INFO_LENGTH_BITS / 2U;
if (m_endPtr >= DMO_BUFFER_LENGTH_BITS)
m_endPtr -= DMO_BUFFER_LENGTH_BITS;
m_modeTimerCnt = 0;
DEBUG4("DMRDMORX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
}
else if ((countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) || \
(countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_S2_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS)) {
m_control = CONTROL_VOICE;
m_syncPtr = m_dataPtr;
m_startPtr = m_dataPtr + DMO_BUFFER_LENGTH_BITS - DMR_SLOT_TYPE_LENGTH_BITS / 2U - DMR_INFO_LENGTH_BITS / 2U - DMR_SYNC_LENGTH_BITS + 1;
if (m_startPtr >= DMO_BUFFER_LENGTH_BITS)
m_startPtr -= DMO_BUFFER_LENGTH_BITS;
m_endPtr = m_dataPtr + DMR_SLOT_TYPE_LENGTH_BITS / 2U + DMR_INFO_LENGTH_BITS / 2U;
if (m_endPtr >= DMO_BUFFER_LENGTH_BITS)
m_endPtr -= DMO_BUFFER_LENGTH_BITS;
m_modeTimerCnt = 0;
DEBUG4("DMRDMORX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
}
}
/// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <param name="buffer"></param>
void DMRDMORX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{
for (uint8_t i = 0U; i < count; i++) {
buffer[i] = 0U;
buffer[i] |= _READ_BIT(m_buffer, start) << 7;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 6;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 5;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 4;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 3;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 2;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 1;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 0;
start++;
if (start >= DMO_BUFFER_LENGTH_BITS)
start -= DMO_BUFFER_LENGTH_BITS;
}
}
/// <summary>
///
/// </summary>
/// <param name="frame"></param>
void DMRDMORX::writeRSSIData(uint8_t* frame)
{
#if defined(SEND_RSSI_DATA)
uint16_t rssi = io.readRSSI();
frame[34U] = (rssi >> 8) & 0xFFU;
frame[35U] = (rssi >> 0) & 0xFFU;
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
#else
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
#endif
}

@ -0,0 +1,102 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,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(__DMR_DMO_RX_H__)
#define __DMR_DMO_RX_H__
#include "Defines.h"
#include "dmr/DMRDefines.h"
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint16_t DMO_BUFFER_LENGTH_BITS = 576U;
enum DMORX_STATE {
DMORXS_NONE,
DMORXS_VOICE,
DMORXS_DATA
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for DMR DMO mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API DMRDMORX {
public:
/// <summary>Initializes a new instance of the DMRDMORX class.</summary>
DMRDMORX();
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Sample DMR bits from the air interface.</summary>
void databit(bool bit);
/// <summary>Sets the DMR color code.</summary>
void setColorCode(uint8_t colorCode);
private:
uint64_t m_bitBuffer;
uint8_t m_buffer[DMO_BUFFER_LENGTH_BITS / 8U]; // 72 bytes
uint8_t frame[DMR_FRAME_LENGTH_BYTES + 3U];
uint16_t m_dataPtr;
uint16_t m_syncPtr;
uint16_t m_startPtr;
uint16_t m_endPtr;
uint8_t m_control;
uint8_t m_syncCount;
uint8_t m_colorCode;
DMORX_STATE m_state;
uint8_t m_n;
uint8_t m_type;
/// <summary>Frame synchronization correlator.</summary>
void correlateSync();
/// <summary></summary>
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
/// <summary></summary>
void writeRSSIData(uint8_t* frame);
};
} // namespace dmr
#endif // __DMR_DMO_RX_H__

@ -0,0 +1,228 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "dmr/DMRSlotType.h"
using namespace dmr;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// 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 };
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRDMOTX class.
/// </summary>
DMRDMOTX::DMRDMOTX() :
m_fifo(875U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_preambleCnt(DMRDMO_FIXED_DELAY),
m_cal(false)
{
/* stub */
}
/// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void DMRDMOTX::process()
{
if (m_poLen == 0U && m_fifo.getData() > 0U) {
if (!m_tx) {
for (uint16_t i = 0U; i < m_preambleCnt; i++)
m_poBuffer[i] = DMR_START_SYNC;
m_poLen = m_preambleCnt;
}
else {
for (unsigned int i = 0U; i < 72U; i++)
m_poBuffer[i] = DMR_START_SYNC;
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
m_poBuffer[i + 39U] = m_fifo.get();
m_poLen = 72U;
}
DEBUG2("DMRDMOTX: process(): poLen", m_poLen);
m_poPtr = 0U;
}
if (m_poLen > 0U) {
uint16_t space = io.getSpace();
while (space > 8U) {
uint8_t c = m_poBuffer[m_poPtr++];
writeByte(c);
space -= 8U;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
}
}
/// <summary>
/// Write data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRDMOTX::writeData(const uint8_t* data, uint8_t length)
{
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo.getSpace();
DEBUG3("DMRDMOTX: writeData(): dataLength/fifoLength", length, space);
if (space < DMR_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL;
for (uint8_t i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
m_fifo.put(data[i + 1U]);
return RSN_OK;
}
/// <summary>
///
/// </summary>
/// <param name="start"></param>
void DMRDMOTX::setCal(bool start)
{
m_cal = start ? true : false;
}
/// <summary>
/// Sets the FDMA preamble count.
/// </summary>
/// <param name="preambleCnt">Count of preambles.</param>
void DMRDMOTX::setPreambleCount(uint8_t preambleCnt)
{
uint32_t preambles = (uint32_t)((float)preambleCnt / 0.2083F);
m_preambleCnt = DMRDMO_FIXED_DELAY + preambles;
// clamp preamble count to 250ms maximum
if (m_preambleCnt > 1200U)
m_preambleCnt = 1200U;
}
/// <summary>
/// Sets the fine adjust 4FSK symbol levels.
/// </summary>
/// <param name="level3Adj">+3/-3 symbol adjust.</param>
/// <param name="level1Adj">+1/-1 symbol adjust.</param>
void DMRDMOTX::setSymbolLvlAdj(int8_t level3Adj, int8_t level1Adj)
{
/* ignored ADF7021 doesn't allow direct symbol level adjustments */
}
/// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t DMRDMOTX::getSpace() const
{
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
void DMRDMOTX::createCal()
{
// 1.2 kHz sine wave generation
if (m_calState == STATE_DMR_CAL) {
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = 0x5FU; // +3, +3, -3, -3 pattern for deviation cal.
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
}
// 80 Hz square wave generation
if (m_modemState == STATE_DMR_LF_CAL) {
for (unsigned int i = 0U; i < 7U; i++) {
m_poBuffer[i] = 0x55U; // +3, +3, ... pattern
}
m_poBuffer[7U] = 0x5FU; // +3, +3, -3, -3 pattern
for (unsigned int i = 8U; i < 15U; i++) {
m_poBuffer[i] = 0xFFU; // -3, -3, ... pattern
}
m_poLen = 15U;
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
m_poPtr = 0U;
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
void DMRDMOTX::writeByte(uint8_t c)
{
uint8_t bit;
uint8_t mask = 0x80U;
for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
if ((c & mask) == mask)
bit = 1U;
else
bit = 0U;
io.write(&bit, 1);
}
}

@ -0,0 +1,95 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* 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(__DMR_DMO_TX_H__)
#define __DMR_DMO_TX_H__
#include "Defines.h"
#include "dmr/DMRDefines.h"
#include "SerialBuffer.h"
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define DMRDMO_FIXED_DELAY 300 // 300 = 62.49ms
// Delay Value * 0.2083 = Preamble Length (ms)
// ---------------------------------------------------------------------------
// Class Declaration
// Implements transmitter logic for DMR DMO mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API DMRDMOTX {
public:
/// <summary>Initializes a new instance of the DMRDMOTX class.</summary>
DMRDMOTX();
/// <summary>Process local buffer and transmit on the air interface.</summary>
void process();
/// <summary>Write data to the local buffer.</summary>
uint8_t writeData(const uint8_t* data, uint8_t length);
/// <summary>Helper to set the calibration state for Tx.</summary>
void setCal(bool start);
/// <summary>Sets the FDMA preamble count.</summary>
void setPreambleCount(uint8_t preambleCnt);
/// <summary>Sets the fine adjust 4FSK symbol levels.</summary>
void setSymbolLvlAdj(int8_t level3Adj, int8_t level1Adj);
/// <summary>Helper to get how much space the ring buffer has for samples.</summary>
uint16_t getSpace() const;
private:
SerialBuffer m_fifo;
uint8_t m_poBuffer[80U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint32_t m_preambleCnt;
bool m_cal;
/// <summary></summary>
void createCal();
/// <summary></summary>
void writeByte(uint8_t c);
};
} // namespace dmr
#endif // __DMR_DMO_TX_H__

@ -0,0 +1,135 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* 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
* 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(__DMR_DEFINES_H__)
#define __DMR_DEFINES_H__
#include "Defines.h"
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t DMR_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
const uint32_t DMR_FRAME_LENGTH_BYTES = 33U;
const uint32_t DMR_FRAME_LENGTH_BITS = DMR_FRAME_LENGTH_BYTES * 8U;
const uint32_t DMR_FRAME_LENGTH_SYMBOLS = DMR_FRAME_LENGTH_BYTES * 4U;
const uint32_t DMR_FRAME_LENGTH_SAMPLES = DMR_FRAME_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_SYNC_LENGTH_BYTES = 6U;
const uint32_t DMR_SYNC_LENGTH_BITS = DMR_SYNC_LENGTH_BYTES * 8U;
const uint32_t DMR_SYNC_LENGTH_SYMBOLS = DMR_SYNC_LENGTH_BYTES * 4U;
const uint32_t DMR_SYNC_LENGTH_SAMPLES = DMR_SYNC_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_EMB_LENGTH_BITS = 16U;
const uint32_t DMR_EMB_LENGTH_SYMBOLS = 8U;
const uint32_t DMR_EMB_LENGTH_SAMPLES = DMR_EMB_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_EMBSIG_LENGTH_BITS = 32U;
const uint32_t DMR_EMBSIG_LENGTH_SYMBOLS = 16U;
const uint32_t DMR_EMBSIG_LENGTH_SAMPLES = DMR_EMBSIG_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_SLOT_TYPE_LENGTH_BITS = 20U;
const uint32_t DMR_SLOT_TYPE_LENGTH_SYMBOLS = 10U;
const uint32_t DMR_SLOT_TYPE_LENGTH_SAMPLES = DMR_SLOT_TYPE_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_INFO_LENGTH_BITS = 196U;
const uint32_t DMR_INFO_LENGTH_SYMBOLS = 98U;
const uint32_t DMR_INFO_LENGTH_SAMPLES = DMR_INFO_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_AUDIO_LENGTH_BITS = 216U;
const uint32_t DMR_AUDIO_LENGTH_SYMBOLS = 108U;
const uint32_t DMR_AUDIO_LENGTH_SAMPLES = DMR_AUDIO_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint32_t DMR_CACH_LENGTH_BYTES = 3U;
const uint32_t DMR_CACH_LENGTH_BITS = DMR_CACH_LENGTH_BYTES * 8U;
const uint32_t DMR_CACH_LENGTH_SYMBOLS = DMR_CACH_LENGTH_BYTES * 4U;
const uint32_t DMR_CACH_LENGTH_SAMPLES = DMR_CACH_LENGTH_SYMBOLS * DMR_RADIO_SYMBOL_LENGTH;
const uint8_t DMR_SYNC_BYTES_LENGTH = 7U;
const uint8_t DMR_MS_DATA_SYNC_BYTES[] = { 0x0DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x70U };
const uint8_t DMR_MS_VOICE_SYNC_BYTES[] = { 0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U };
const uint8_t DMR_BS_DATA_SYNC_BYTES[] = { 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U };
const uint8_t DMR_BS_VOICE_SYNC_BYTES[] = { 0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U };
const uint8_t DMR_S1_DATA_SYNC_BYTES[] = { 0x0FU, 0x7FU, 0xDDU, 0x5DU, 0xDFU, 0xD5U, 0x50U };
const uint8_t DMR_S1_VOICE_SYNC_BYTES[] = { 0x05U, 0xD5U, 0x77U, 0xF7U, 0x75U, 0x7FU, 0xF0U };
const uint8_t DMR_S2_DATA_SYNC_BYTES[] = { 0x0DU, 0x75U, 0x57U, 0xF5U, 0xFFU, 0x7FU, 0x50U };
const uint8_t DMR_S2_VOICE_SYNC_BYTES[] = { 0x07U, 0xDFU, 0xFDU, 0x5FU, 0x55U, 0xD5U, 0xF0U };
const uint8_t DMR_SYNC_BYTES_MASK[] = { 0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xF0U };
const uint8_t DMR_START_SYNC = 0x5FU;
const uint64_t DMR_MS_DATA_SYNC_BITS = 0x0000D5D7F77FD757U;
const uint64_t DMR_MS_VOICE_SYNC_BITS = 0x00007F7D5DD57DFDU;
const uint64_t DMR_BS_DATA_SYNC_BITS = 0x0000DFF57D75DF5DU;
const uint64_t DMR_BS_VOICE_SYNC_BITS = 0x0000755FD7DF75F7U;
const uint64_t DMR_S1_DATA_SYNC_BITS = 0x0000F7FDD5DDFD55U;
const uint64_t DMR_S1_VOICE_SYNC_BITS = 0x00005D577F7757FFU;
const uint64_t DMR_S2_DATA_SYNC_BITS = 0x0000D7557F5FF7F5U;
const uint64_t DMR_S2_VOICE_SYNC_BITS = 0x00007DFFD5F55D5FU;
const uint64_t DMR_SYNC_BITS_MASK = 0x0000FFFFFFFFFFFFU;
const uint32_t DMR_MS_DATA_SYNC_SYMBOLS = 0x0076286EU;
const uint32_t DMR_MS_VOICE_SYNC_SYMBOLS = 0x0089D791U;
const uint32_t DMR_BS_DATA_SYNC_SYMBOLS = 0x00439B4DU;
const uint32_t DMR_BS_VOICE_SYNC_SYMBOLS = 0x00BC64B2U;
const uint32_t DMR_S1_DATA_SYNC_SYMBOLS = 0x0021751FU;
const uint32_t DMR_S1_VOICE_SYNC_SYMBOLS = 0x00DE8AE0U;
const uint32_t DMR_S2_DATA_SYNC_SYMBOLS = 0x006F8C23U;
const uint32_t DMR_S2_VOICE_SYNC_SYMBOLS = 0x009073DCU;
const uint32_t DMR_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
// D 5 D 7 F 7 7 F D 7 5 7
// 11 01 01 01 11 01 01 11 11 11 01 11 01 11 11 11 11 01 01 11 01 01 01 11
// -3 +3 +3 +3 -3 +3 +3 -3 -3 -3 +3 -3 +3 -3 -3 -3 -3 +3 +3 -3 +3 +3 +3 -3
const int8_t DMR_MS_DATA_SYNC_SYMBOLS_VALUES[] = { -3, +3, +3, +3, -3, +3, +3, -3, -3, -3, +3, -3, +3, -3, -3, -3, -3, +3, +3, -3, +3, +3, +3, -3 };
// 7 F 7 D 5 D D 5 7 D F D
// 01 11 11 11 01 11 11 01 01 01 11 01 11 01 01 01 01 11 11 01 11 11 11 01
// +3 -3 -3 -3 +3 -3 -3 +3 +3 +3 -3 +3 -3 +3 +3 +3 +3 -3 -3 +3 -3 -3 -3 +3
const int8_t DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[] = { +3, -3, -3, -3, +3, -3, -3, +3, +3, +3, -3, +3, -3, +3, +3, +3, +3, -3, -3, +3, -3, -3, -3, +3 };
// Data Type(s)
const uint8_t DT_VOICE_PI_HEADER = 0U;
const uint8_t DT_VOICE_LC_HEADER = 1U;
const uint8_t DT_TERMINATOR_WITH_LC = 2U;
const uint8_t DT_CSBK = 3U;
const uint8_t DT_DATA_HEADER = 6U;
const uint8_t DT_RATE_12_DATA = 7U;
const uint8_t DT_RATE_34_DATA = 8U;
const uint8_t DT_IDLE = 9U;
const uint8_t DT_RATE_1_DATA = 10U;
} // namespace dmr
#endif // __DMR_DEFINES_H__

@ -0,0 +1,186 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,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 "Globals.h"
#include "dmr/DMRIdleRX.h"
#include "dmr/DMRSlotType.h"
#include "Utils.h"
using namespace dmr;
#if defined(DUPLEX)
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint8_t MAX_SYNC_BYTES_ERRS = 2U;
const uint16_t NOENDPTR = 9999U;
const uint8_t CONTROL_IDLE = 0x80U;
const uint8_t CONTROL_DATA = 0x40U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRIdleRX class.
/// </summary>
DMRIdleRX::DMRIdleRX() :
m_bitBuffer(0U),
m_buffer(),
m_dataPtr(0U),
m_endPtr(NOENDPTR),
m_colorCode(0U)
{
/* stub */
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRIdleRX::reset()
{
m_dataPtr = 0U;
m_endPtr = NOENDPTR;
}
/// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void DMRIdleRX::databit(bool bit)
{
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
if (countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_DATA_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) {
m_endPtr = m_dataPtr + DMR_SLOT_TYPE_LENGTH_BITS / 2U + DMR_INFO_LENGTH_BITS / 2U;
if (m_endPtr >= DMR_IDLE_LENGTH_BITS)
m_endPtr -= DMR_IDLE_LENGTH_BITS;
DEBUG3("DMRIdleRx: databit(): dataPtr/endPtr", m_dataPtr, m_endPtr);
}
if (m_dataPtr == m_endPtr) {
uint16_t ptr = m_endPtr + DMR_IDLE_LENGTH_BITS - DMR_FRAME_LENGTH_BITS + 1;
if (ptr >= DMR_IDLE_LENGTH_BITS)
ptr -= DMR_IDLE_LENGTH_BITS;
uint8_t frame[DMR_FRAME_LENGTH_BYTES + 1U];
bitsToBytes(ptr, DMR_FRAME_LENGTH_BYTES, frame + 1U);
uint8_t colorCode;
uint8_t dataType;
DMRSlotType slotType;
slotType.decode(frame + 1U, colorCode, dataType);
if (colorCode == m_colorCode && dataType == DT_CSBK) {
frame[0U] = CONTROL_IDLE | CONTROL_DATA | DT_CSBK;
serial.writeDMRData(false, frame, DMR_FRAME_LENGTH_BYTES + 1U);
}
m_endPtr = NOENDPTR;
}
m_dataPtr++;
if (m_dataPtr >= DMR_IDLE_LENGTH_BITS)
m_dataPtr = 0U;
}
/// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRIdleRX::setColorCode(uint8_t colorCode)
{
m_colorCode = colorCode;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <param name="buffer"></param>
void DMRIdleRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{
for (uint8_t i = 0U; i < count; i++) {
buffer[i] = 0U;
buffer[i] |= _READ_BIT(m_buffer, start) << 7;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 6;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 5;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 4;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 3;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 2;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 1;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 0;
start++;
if (start >= DMR_IDLE_LENGTH_BITS)
start -= DMR_IDLE_LENGTH_BITS;
}
}
#endif // DUPLEX

@ -0,0 +1,82 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015 by Jonathan Naylor G4KLX
* Copyright (C) 2017,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(__DMR_IDLE_RX_H__)
#define __DMR_IDLE_RX_H__
#include "Defines.h"
#include "dmr/DMRDefines.h"
#if defined(DUPLEX)
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint16_t DMR_IDLE_LENGTH_BITS = 320U;
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for idle DMR mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API DMRIdleRX {
public:
/// <summary>Initializes a new instance of the DMRIdleRX class.</summary>
DMRIdleRX();
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Sample DMR bits from the air interface.</summary>
void databit(bool bit);
/// <summary>Sets the DMR color code.</summary>
void setColorCode(uint8_t colorCode);
private:
uint64_t m_bitBuffer;
uint8_t m_buffer[DMR_IDLE_LENGTH_BITS / 8U];
uint16_t m_dataPtr;
uint16_t m_endPtr;
uint8_t m_colorCode;
/// <summary></summary>
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
};
} // namespace dmr
#endif // DUPLEX
#endif // __DMR_IDLE_RX_H__

@ -0,0 +1,108 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "dmr/DMRRX.h"
using namespace dmr;
#if defined(DUPLEX)
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRRX class.
/// </summary>
DMRRX::DMRRX() :
m_slot1RX(false),
m_slot2RX(true)
{
/* stub */
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRRX::reset()
{
m_slot1RX.reset();
m_slot2RX.reset();
}
/// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void DMRRX::databit(bool bit, const uint8_t control)
{
bool dcd1 = false;
bool dcd2 = false;
switch (control) {
case MARK_SLOT1:
m_slot1RX.start();
break;
case MARK_SLOT2:
m_slot2RX.start();
break;
default:
break;
}
dcd1 = m_slot1RX.databit(bit);
dcd2 = m_slot2RX.databit(bit);
io.setDecode(dcd1 || dcd2);
}
/// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRRX::setColorCode(uint8_t colorCode)
{
m_slot1RX.setColorCode(colorCode);
m_slot2RX.setColorCode(colorCode);
}
/// <summary>
/// Sets the number of samples to delay before processing.
/// </summary>
/// <param name="delay">Number of samples to delay.</param>
void DMRRX::setRxDelay(uint8_t delay)
{
m_slot1RX.setRxDelay(delay);
m_slot2RX.setRxDelay(delay);
}
#endif // DUPLEX

@ -0,0 +1,71 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__DMR_RX_H__)
#define __DMR_RX_H__
#include "Defines.h"
#include "dmr/DMRSlotRX.h"
#if defined(DUPLEX)
namespace dmr
{
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for duplex DMR mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API DMRRX {
public:
/// <summary>Initializes a new instance of the DMRRX class.</summary>
DMRRX();
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Sample DMR bits from the air interface.</summary>
void databit(bool bit, const uint8_t control);
/// <summary>Sets the DMR color code.</summary>
void setColorCode(uint8_t colorCode);
/// <summary>Sets the number of samples to delay before processing.</summary>
void setRxDelay(uint8_t delay);
private:
DMRSlotRX m_slot1RX;
DMRSlotRX m_slot2RX;
};
} // namespace dmr
#endif // DUPLEX
#endif // __DMR_RX_H__

@ -0,0 +1,391 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "dmr/DMRSlotRX.h"
#include "dmr/DMRSlotType.h"
#include "Utils.h"
using namespace dmr;
#if defined(DUPLEX)
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint16_t SCAN_START = 390U;
const uint16_t SCAN_END = 500U;
const uint8_t MAX_SYNC_BYTES_ERRS = 3U;
const uint8_t MAX_SYNC_LOST_FRAMES = 13U;
const uint16_t NOENDPTR = 9999U;
const uint8_t CONTROL_NONE = 0x00U;
const uint8_t CONTROL_VOICE = 0x20U;
const uint8_t CONTROL_DATA = 0x40U;
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRSlotRX class.
/// </summary>
DMRSlotRX::DMRSlotRX(bool slot) :
m_slot(false),
m_bitBuffer(0x00U),
m_buffer(),
m_dataPtr(0U),
m_syncPtr(0U),
m_startPtr(0U),
m_endPtr(NOENDPTR),
m_delayPtr(0U),
m_control(CONTROL_NONE),
m_syncCount(0U),
m_colorCode(0U),
m_delay(0U),
m_state(DMRRXS_NONE),
m_n(0U),
m_type(0U)
{
/* stub */
}
/// <summary>
/// Helper to set data values for start of Rx.
/// </summary>
void DMRSlotRX::start()
{
m_dataPtr = 0U;
m_delayPtr = 0U;
m_control = CONTROL_NONE;
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRSlotRX::reset()
{
m_syncPtr = 0U;
m_dataPtr = 0U;
m_delayPtr = 0U;
m_bitBuffer = 0U;
m_control = CONTROL_NONE;
m_syncCount = 0U;
m_state = DMRRXS_NONE;
m_startPtr = 0U;
m_endPtr = NOENDPTR;
}
/// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
bool DMRSlotRX::databit(bool bit)
{
uint16_t min, max;
m_delayPtr++;
if (m_delayPtr < m_delay)
return m_state != DMRRXS_NONE;
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
// Ensure that the buffer doesn't overflow
if (m_dataPtr > m_endPtr || m_dataPtr >= 576U)
return m_state != DMRRXS_NONE;
if (m_state == DMRRXS_NONE) {
if (m_dataPtr >= SCAN_START && m_dataPtr <= SCAN_END)
correlateSync();
}
else {
uint16_t min = m_syncPtr - 1U;
uint16_t max = m_syncPtr + 1U;
if (m_dataPtr >= min && m_dataPtr <= max)
correlateSync();
}
if (m_dataPtr == m_endPtr) {
uint8_t frame[DMR_FRAME_LENGTH_BYTES + 3U];
frame[0U] = m_control;
bitsToBytes(m_startPtr, DMR_FRAME_LENGTH_BYTES, frame + 1U);
if (m_control == CONTROL_DATA) {
// Data sync
uint8_t colorCode;
uint8_t dataType;
DMRSlotType slotType;
slotType.decode(frame + 1U, colorCode, dataType);
if (colorCode == m_colorCode) {
m_syncCount = 0U;
m_n = 0U;
frame[0U] |= dataType;
switch (dataType) {
case DT_DATA_HEADER:
DEBUG3("DMRSlotRX: databit(): data header found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
m_state = DMRRXS_DATA;
m_type = 0x00U;
break;
case DT_RATE_12_DATA:
case DT_RATE_34_DATA:
case DT_RATE_1_DATA:
if (m_state == DMRRXS_DATA) {
DEBUG3("DMRSlotRX: databit(): data payload found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
m_type = dataType;
}
break;
case DT_VOICE_LC_HEADER:
DEBUG3("DMRSlotRX: databit(): voice header found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
m_state = DMRRXS_VOICE;
break;
case DT_VOICE_PI_HEADER:
if (m_state == DMRRXS_VOICE) {
DEBUG3("DMRSlotRX: databit(): voice pi header found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
}
m_state = DMRRXS_VOICE;
break;
case DT_TERMINATOR_WITH_LC:
if (m_state == DMRRXS_VOICE) {
DEBUG3("DMRSlotRX: databit(): voice terminator found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
m_state = DMRRXS_NONE;
m_endPtr = NOENDPTR;
}
break;
default: // DT_CSBK
DEBUG3("DMRSlotRX: databit(): csbk found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
m_state = DMRRXS_NONE;
m_endPtr = NOENDPTR;
break;
}
}
}
else if (m_control == CONTROL_VOICE) {
// Voice sync
DEBUG3("DMRSlotRX: databit(): voice sync found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame);
m_state = DMRRXS_VOICE;
m_syncCount = 0U;
m_n = 0U;
}
else {
if (m_state != DMRRXS_NONE) {
m_syncCount++;
if (m_syncCount >= MAX_SYNC_LOST_FRAMES) {
DEBUG1("DMRSlotRX: databit(): sync timeout, lost lock");
serial.writeDMRLost(m_slot);
m_state = DMRRXS_NONE;
m_endPtr = NOENDPTR;
}
}
if (m_state == DMRRXS_VOICE) {
if (m_n >= 5U) {
frame[0U] = CONTROL_VOICE;
m_n = 0U;
}
else {
frame[0U] = ++m_n;
}
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
}
else if (m_state == DMRRXS_DATA) {
if (m_type != 0x00U) {
frame[0U] = CONTROL_DATA | m_type;
writeRSSIData(frame);
}
}
}
}
m_dataPtr++;
if (m_dataPtr >= DMR_BUFFER_LENGTH_BITS)
m_dataPtr = 0U;
return m_state != DMRRXS_NONE;
}
/// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRSlotRX::setColorCode(uint8_t colorCode)
{
m_colorCode = colorCode;
}
/// <summary>
/// Sets the number of samples to delay before processing.
/// </summary>
/// <param name="delay">Number of samples to delay.</param>
void DMRSlotRX::setRxDelay(uint8_t delay)
{
m_delay = delay;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Frame synchronization correlator.
/// </summary>
/// <param name="first"></param>
void DMRSlotRX::correlateSync()
{
if (countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_DATA_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) {
m_control = CONTROL_DATA;
m_syncPtr = m_dataPtr;
m_startPtr = m_dataPtr + DMR_BUFFER_LENGTH_BITS - DMR_SLOT_TYPE_LENGTH_BITS / 2U - DMR_INFO_LENGTH_BITS / 2U - DMR_SYNC_LENGTH_BITS + 1;
if (m_startPtr >= DMR_BUFFER_LENGTH_BITS)
m_startPtr -= DMR_BUFFER_LENGTH_BITS;
m_endPtr = m_dataPtr + DMR_SLOT_TYPE_LENGTH_BITS / 2U + DMR_INFO_LENGTH_BITS / 2U;
if (m_endPtr >= DMR_BUFFER_LENGTH_BITS)
m_endPtr -= DMR_BUFFER_LENGTH_BITS;
DEBUG4("DMRSlotRX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
}
else if (countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) {
m_control = CONTROL_VOICE;
m_syncPtr = m_dataPtr;
m_startPtr = m_dataPtr + DMR_BUFFER_LENGTH_BITS - DMR_SLOT_TYPE_LENGTH_BITS / 2U - DMR_INFO_LENGTH_BITS / 2U - DMR_SYNC_LENGTH_BITS + 1;
if (m_startPtr >= DMR_BUFFER_LENGTH_BITS)
m_startPtr -= DMR_BUFFER_LENGTH_BITS;
m_endPtr = m_dataPtr + DMR_SLOT_TYPE_LENGTH_BITS / 2U + DMR_INFO_LENGTH_BITS / 2U;
if (m_endPtr >= DMR_BUFFER_LENGTH_BITS)
m_endPtr -= DMR_BUFFER_LENGTH_BITS;
DEBUG4("DMRSlotRX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
}
}
/// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <param name="buffer"></param>
void DMRSlotRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{
for (uint8_t i = 0U; i < count; i++) {
buffer[i] = 0U;
buffer[i] |= _READ_BIT(m_buffer, start) << 7;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 6;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 5;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 4;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 3;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 2;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 1;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 0;
start++;
if (start >= DMR_BUFFER_LENGTH_BITS)
start -= DMR_BUFFER_LENGTH_BITS;
}
}
/// <summary>
///
/// </summary>
/// <param name="frame"></param>
void DMRSlotRX::writeRSSIData(uint8_t* frame)
{
#if defined(SEND_RSSI_DATA)
// Calculate RSSI average over a burst period. We don't take into account 2.5 ms at the beginning and 2.5 ms at the end
uint16_t start = m_startPtr + DMR_SYNC_LENGTH_SAMPLES / 2U;
uint32_t accum = 0U;
for (uint16_t i = 0U; i < (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES); i++)
accum += m_rssi[start++];
uint16_t avg = accum / (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES);
frame[34U] = (avg >> 8) & 0xFFU;
frame[35U] = (avg >> 0) & 0xFFU;
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
#else
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
#endif
}
#endif // DUPLEX

@ -0,0 +1,115 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__DMR_SLOT_RX_H__)
#define __DMR_SLOT_RX_H__
#include "Defines.h"
#include "dmr/DMRDefines.h"
#if defined(DUPLEX)
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint16_t DMR_BUFFER_LENGTH_BITS = 576U;
enum DMRRX_STATE {
DMRRXS_NONE,
DMRRXS_VOICE,
DMRRXS_DATA
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for DMR slots.
// ---------------------------------------------------------------------------
class DSP_FW_API DMRSlotRX {
public:
/// <summary>Initializes a new instance of the DMRSlotRX class.</summary>
DMRSlotRX(bool slot);
/// <summary>Helper to set data values for start of Rx.</summary>
void start();
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Sample DMR bits from the air interface.</summary>
bool databit(bool bit);
/// <summary>Sets the DMR color code.</summary>
void setColorCode(uint8_t colorCode);
/// <summary>Sets the number of samples to delay before processing.</summary>
void setRxDelay(uint8_t delay);
private:
bool m_slot;
uint64_t m_bitBuffer;
uint8_t m_buffer[DMR_BUFFER_LENGTH_BITS / 8U]; // 72 bytes
uint16_t m_dataPtr;
uint16_t m_syncPtr;
uint16_t m_startPtr;
uint16_t m_endPtr;
uint16_t m_delayPtr;
uint8_t m_control;
uint8_t m_syncCount;
uint8_t m_colorCode;
uint16_t m_delay;
DMRRX_STATE m_state;
uint8_t m_n;
uint8_t m_type;
/// <summary>Frame synchronization correlator.</summary>
void correlateSync();
/// <summary></summary>
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
/// <summary></summary>
void writeRSSIData(uint8_t* frame);
};
} // namespace dmr
#endif // DUPLEX
#endif // __DMR_SLOT_RX_H__

@ -0,0 +1,334 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 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
* 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 "Globals.h"
#include "dmr/DMRSlotType.h"
using namespace dmr;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint16_t ENCODING_TABLE_2087[] = {
0x0000U, 0xB08EU, 0xE093U, 0x501DU, 0x70A9U, 0xC027U, 0x903AU, 0x20B4U, 0x60DCU, 0xD052U, 0x804FU, 0x30C1U,
0x1075U, 0xA0FBU, 0xF0E6U, 0x4068U, 0x7036U, 0xC0B8U, 0x90A5U, 0x202BU, 0x009FU, 0xB011U, 0xE00CU, 0x5082U,
0x10EAU, 0xA064U, 0xF079U, 0x40F7U, 0x6043U, 0xD0CDU, 0x80D0U, 0x305EU, 0xD06CU, 0x60E2U, 0x30FFU, 0x8071U,
0xA0C5U, 0x104BU, 0x4056U, 0xF0D8U, 0xB0B0U, 0x003EU, 0x5023U, 0xE0ADU, 0xC019U, 0x7097U, 0x208AU, 0x9004U,
0xA05AU, 0x10D4U, 0x40C9U, 0xF047U, 0xD0F3U, 0x607DU, 0x3060U, 0x80EEU, 0xC086U, 0x7008U, 0x2015U, 0x909BU,
0xB02FU, 0x00A1U, 0x50BCU, 0xE032U, 0x90D9U, 0x2057U, 0x704AU, 0xC0C4U, 0xE070U, 0x50FEU, 0x00E3U, 0xB06DU,
0xF005U, 0x408BU, 0x1096U, 0xA018U, 0x80ACU, 0x3022U, 0x603FU, 0xD0B1U, 0xE0EFU, 0x5061U, 0x007CU, 0xB0F2U,
0x9046U, 0x20C8U, 0x70D5U, 0xC05BU, 0x8033U, 0x30BDU, 0x60A0U, 0xD02EU, 0xF09AU, 0x4014U, 0x1009U, 0xA087U,
0x40B5U, 0xF03BU, 0xA026U, 0x10A8U, 0x301CU, 0x8092U, 0xD08FU, 0x6001U, 0x2069U, 0x90E7U, 0xC0FAU, 0x7074U,
0x50C0U, 0xE04EU, 0xB053U, 0x00DDU, 0x3083U, 0x800DU, 0xD010U, 0x609EU, 0x402AU, 0xF0A4U, 0xA0B9U, 0x1037U,
0x505FU, 0xE0D1U, 0xB0CCU, 0x0042U, 0x20F6U, 0x9078U, 0xC065U, 0x70EBU, 0xA03DU, 0x10B3U, 0x40AEU, 0xF020U,
0xD094U, 0x601AU, 0x3007U, 0x8089U, 0xC0E1U, 0x706FU, 0x2072U, 0x90FCU, 0xB048U, 0x00C6U, 0x50DBU, 0xE055U,
0xD00BU, 0x6085U, 0x3098U, 0x8016U, 0xA0A2U, 0x102CU, 0x4031U, 0xF0BFU, 0xB0D7U, 0x0059U, 0x5044U, 0xE0CAU,
0xC07EU, 0x70F0U, 0x20EDU, 0x9063U, 0x7051U, 0xC0DFU, 0x90C2U, 0x204CU, 0x00F8U, 0xB076U, 0xE06BU, 0x50E5U,
0x108DU, 0xA003U, 0xF01EU, 0x4090U, 0x6024U, 0xD0AAU, 0x80B7U, 0x3039U, 0x0067U, 0xB0E9U, 0xE0F4U, 0x507AU,
0x70CEU, 0xC040U, 0x905DU, 0x20D3U, 0x60BBU, 0xD035U, 0x8028U, 0x30A6U, 0x1012U, 0xA09CU, 0xF081U, 0x400FU,
0x30E4U, 0x806AU, 0xD077U, 0x60F9U, 0x404DU, 0xF0C3U, 0xA0DEU, 0x1050U, 0x5038U, 0xE0B6U, 0xB0ABU, 0x0025U,
0x2091U, 0x901FU, 0xC002U, 0x708CU, 0x40D2U, 0xF05CU, 0xA041U, 0x10CFU, 0x307BU, 0x80F5U, 0xD0E8U, 0x6066U,
0x200EU, 0x9080U, 0xC09DU, 0x7013U, 0x50A7U, 0xE029U, 0xB034U, 0x00BAU, 0xE088U, 0x5006U, 0x001BU, 0xB095U,
0x9021U, 0x20AFU, 0x70B2U, 0xC03CU, 0x8054U, 0x30DAU, 0x60C7U, 0xD049U, 0xF0FDU, 0x4073U, 0x106EU, 0xA0E0U,
0x90BEU, 0x2030U, 0x702DU, 0xC0A3U, 0xE017U, 0x5099U, 0x0084U, 0xB00AU, 0xF062U, 0x40ECU, 0x10F1U, 0xA07FU,
0x80CBU, 0x3045U, 0x6058U, 0xD0D6U };
const uint32_t DECODING_TABLE_1987[] = {
0x00000U, 0x00001U, 0x00002U, 0x00003U, 0x00004U, 0x00005U, 0x00006U, 0x00007U, 0x00008U, 0x00009U, 0x0000AU, 0x0000BU, 0x0000CU,
0x0000DU, 0x0000EU, 0x24020U, 0x00010U, 0x00011U, 0x00012U, 0x00013U, 0x00014U, 0x00015U, 0x00016U, 0x00017U, 0x00018U, 0x00019U,
0x0001AU, 0x0001BU, 0x0001CU, 0x0001DU, 0x48040U, 0x01480U, 0x00020U, 0x00021U, 0x00022U, 0x00023U, 0x00024U, 0x00025U, 0x00026U,
0x24008U, 0x00028U, 0x00029U, 0x0002AU, 0x24004U, 0x0002CU, 0x24002U, 0x24001U, 0x24000U, 0x00030U, 0x00031U, 0x00032U, 0x08180U,
0x00034U, 0x00C40U, 0x00036U, 0x00C42U, 0x00038U, 0x43000U, 0x0003AU, 0x43002U, 0x02902U, 0x24012U, 0x02900U, 0x24010U, 0x00040U,
0x00041U, 0x00042U, 0x00043U, 0x00044U, 0x00045U, 0x00046U, 0x00047U, 0x00048U, 0x00049U, 0x0004AU, 0x02500U, 0x0004CU, 0x0004DU,
0x48010U, 0x48011U, 0x00050U, 0x00051U, 0x00052U, 0x21200U, 0x00054U, 0x00C20U, 0x48008U, 0x48009U, 0x00058U, 0x00059U, 0x48004U,
0x48005U, 0x48002U, 0x48003U, 0x48000U, 0x48001U, 0x00060U, 0x00061U, 0x00062U, 0x00063U, 0x00064U, 0x00C10U, 0x10300U, 0x0B000U,
0x00068U, 0x00069U, 0x01880U, 0x01881U, 0x40181U, 0x40180U, 0x24041U, 0x24040U, 0x00070U, 0x00C04U, 0x00072U, 0x00C06U, 0x00C01U,
0x00C00U, 0x00C03U, 0x00C02U, 0x05204U, 0x00C0CU, 0x48024U, 0x48025U, 0x05200U, 0x00C08U, 0x48020U, 0x48021U, 0x00080U, 0x00081U,
0x00082U, 0x00083U, 0x00084U, 0x00085U, 0x00086U, 0x00087U, 0x00088U, 0x00089U, 0x0008AU, 0x50200U, 0x0008CU, 0x0A800U, 0x01411U,
0x01410U, 0x00090U, 0x00091U, 0x00092U, 0x08120U, 0x00094U, 0x00095U, 0x04A00U, 0x01408U, 0x00098U, 0x00099U, 0x01405U, 0x01404U,
0x01403U, 0x01402U, 0x01401U, 0x01400U, 0x000A0U, 0x000A1U, 0x000A2U, 0x08110U, 0x000A4U, 0x000A5U, 0x42400U, 0x42401U, 0x000A8U,
0x000A9U, 0x01840U, 0x01841U, 0x40141U, 0x40140U, 0x24081U, 0x24080U, 0x000B0U, 0x08102U, 0x08101U, 0x08100U, 0x000B4U, 0x08106U,
0x08105U, 0x08104U, 0x20A01U, 0x20A00U, 0x08109U, 0x08108U, 0x01423U, 0x01422U, 0x01421U, 0x01420U, 0x000C0U, 0x000C1U, 0x000C2U,
0x000C3U, 0x000C4U, 0x000C5U, 0x000C6U, 0x000C7U, 0x000C8U, 0x000C9U, 0x01820U, 0x01821U, 0x20600U, 0x40120U, 0x16000U, 0x16001U,
0x000D0U, 0x000D1U, 0x42801U, 0x42800U, 0x03100U, 0x18200U, 0x03102U, 0x18202U, 0x000D8U, 0x000D9U, 0x48084U, 0x01444U, 0x48082U,
0x01442U, 0x48080U, 0x01440U, 0x000E0U, 0x32000U, 0x01808U, 0x04600U, 0x40109U, 0x40108U, 0x0180CU, 0x4010AU, 0x01802U, 0x40104U,
0x01800U, 0x01801U, 0x40101U, 0x40100U, 0x01804U, 0x40102U, 0x0A408U, 0x08142U, 0x08141U, 0x08140U, 0x00C81U, 0x00C80U, 0x00C83U,
0x00C82U, 0x0A400U, 0x0A401U, 0x01810U, 0x01811U, 0x40111U, 0x40110U, 0x01814U, 0x40112U, 0x00100U, 0x00101U, 0x00102U, 0x00103U,
0x00104U, 0x00105U, 0x00106U, 0x41800U, 0x00108U, 0x00109U, 0x0010AU, 0x02440U, 0x0010CU, 0x0010DU, 0x0010EU, 0x02444U, 0x00110U,
0x00111U, 0x00112U, 0x080A0U, 0x00114U, 0x00115U, 0x00116U, 0x080A4U, 0x00118U, 0x00119U, 0x15000U, 0x15001U, 0x02822U, 0x02823U,
0x02820U, 0x02821U, 0x00120U, 0x00121U, 0x00122U, 0x08090U, 0x00124U, 0x00125U, 0x10240U, 0x10241U, 0x00128U, 0x00129U, 0x0012AU,
0x24104U, 0x09400U, 0x400C0U, 0x02810U, 0x24100U, 0x00130U, 0x08082U, 0x08081U, 0x08080U, 0x31001U, 0x31000U, 0x02808U, 0x08084U,
0x02806U, 0x0808AU, 0x02804U, 0x08088U, 0x02802U, 0x02803U, 0x02800U, 0x02801U, 0x00140U, 0x00141U, 0x00142U, 0x02408U, 0x00144U,
0x00145U, 0x10220U, 0x10221U, 0x00148U, 0x02402U, 0x02401U, 0x02400U, 0x400A1U, 0x400A0U, 0x02405U, 0x02404U, 0x00150U, 0x00151U,
0x00152U, 0x02418U, 0x03080U, 0x03081U, 0x03082U, 0x03083U, 0x09801U, 0x09800U, 0x02411U, 0x02410U, 0x48102U, 0x09804U, 0x48100U,
0x48101U, 0x00160U, 0x00161U, 0x10204U, 0x10205U, 0x10202U, 0x40088U, 0x10200U, 0x10201U, 0x40085U, 0x40084U, 0x02421U, 0x02420U,
0x40081U, 0x40080U, 0x10208U, 0x40082U, 0x41402U, 0x080C2U, 0x41400U, 0x080C0U, 0x00D01U, 0x00D00U, 0x10210U, 0x10211U, 0x40095U,
0x40094U, 0x02844U, 0x080C8U, 0x40091U, 0x40090U, 0x02840U, 0x02841U, 0x00180U, 0x00181U, 0x00182U, 0x08030U, 0x00184U, 0x14400U,
0x22201U, 0x22200U, 0x00188U, 0x00189U, 0x0018AU, 0x08038U, 0x40061U, 0x40060U, 0x40063U, 0x40062U, 0x00190U, 0x08022U, 0x08021U,
0x08020U, 0x03040U, 0x03041U, 0x08025U, 0x08024U, 0x40C00U, 0x40C01U, 0x08029U, 0x08028U, 0x2C000U, 0x2C001U, 0x01501U, 0x01500U,
0x001A0U, 0x08012U, 0x08011U, 0x08010U, 0x40049U, 0x40048U, 0x08015U, 0x08014U, 0x06200U, 0x40044U, 0x30400U, 0x08018U, 0x40041U,
0x40040U, 0x40043U, 0x40042U, 0x08003U, 0x08002U, 0x08001U, 0x08000U, 0x08007U, 0x08006U, 0x08005U, 0x08004U, 0x0800BU, 0x0800AU,
0x08009U, 0x08008U, 0x40051U, 0x40050U, 0x02880U, 0x0800CU, 0x001C0U, 0x001C1U, 0x64000U, 0x64001U, 0x03010U, 0x40028U, 0x08C00U,
0x08C01U, 0x40025U, 0x40024U, 0x02481U, 0x02480U, 0x40021U, 0x40020U, 0x40023U, 0x40022U, 0x03004U, 0x03005U, 0x08061U, 0x08060U,
0x03000U, 0x03001U, 0x03002U, 0x03003U, 0x0300CU, 0x40034U, 0x30805U, 0x30804U, 0x03008U, 0x40030U, 0x30801U, 0x30800U, 0x4000DU,
0x4000CU, 0x08051U, 0x08050U, 0x40009U, 0x40008U, 0x10280U, 0x4000AU, 0x40005U, 0x40004U, 0x01900U, 0x40006U, 0x40001U, 0x40000U,
0x40003U, 0x40002U, 0x14800U, 0x08042U, 0x08041U, 0x08040U, 0x03020U, 0x40018U, 0x08045U, 0x08044U, 0x40015U, 0x40014U, 0x08049U,
0x08048U, 0x40011U, 0x40010U, 0x40013U, 0x40012U, 0x00200U, 0x00201U, 0x00202U, 0x00203U, 0x00204U, 0x00205U, 0x00206U, 0x00207U,
0x00208U, 0x00209U, 0x0020AU, 0x50080U, 0x0020CU, 0x0020DU, 0x0020EU, 0x50084U, 0x00210U, 0x00211U, 0x00212U, 0x21040U, 0x00214U,
0x00215U, 0x04880U, 0x04881U, 0x00218U, 0x00219U, 0x0E001U, 0x0E000U, 0x0021CU, 0x0021DU, 0x04888U, 0x0E004U, 0x00220U, 0x00221U,
0x00222U, 0x00223U, 0x00224U, 0x00225U, 0x10140U, 0x10141U, 0x00228U, 0x00229U, 0x0022AU, 0x24204U, 0x12401U, 0x12400U, 0x24201U,
0x24200U, 0x00230U, 0x00231U, 0x00232U, 0x21060U, 0x2A000U, 0x2A001U, 0x2A002U, 0x2A003U, 0x20881U, 0x20880U, 0x20883U, 0x20882U,
0x05040U, 0x05041U, 0x05042U, 0x24210U, 0x00240U, 0x00241U, 0x00242U, 0x21010U, 0x00244U, 0x46000U, 0x10120U, 0x10121U, 0x00248U,
0x00249U, 0x0024AU, 0x21018U, 0x20480U, 0x20481U, 0x20482U, 0x20483U, 0x00250U, 0x21002U, 0x21001U, 0x21000U, 0x18081U, 0x18080U,
0x21005U, 0x21004U, 0x12800U, 0x12801U, 0x21009U, 0x21008U, 0x05020U, 0x05021U, 0x48200U, 0x48201U, 0x00260U, 0x00261U, 0x10104U,
0x04480U, 0x10102U, 0x10103U, 0x10100U, 0x10101U, 0x62002U, 0x62003U, 0x62000U, 0x62001U, 0x05010U, 0x05011U, 0x10108U, 0x10109U,
0x0500CU, 0x21022U, 0x21021U, 0x21020U, 0x05008U, 0x00E00U, 0x10110U, 0x10111U, 0x05004U, 0x05005U, 0x05006U, 0x21028U, 0x05000U,
0x05001U, 0x05002U, 0x05003U, 0x00280U, 0x00281U, 0x00282U, 0x50008U, 0x00284U, 0x00285U, 0x04810U, 0x22100U, 0x00288U, 0x50002U,
0x50001U, 0x50000U, 0x20440U, 0x20441U, 0x50005U, 0x50004U, 0x00290U, 0x00291U, 0x04804U, 0x04805U, 0x04802U, 0x18040U, 0x04800U,
0x04801U, 0x20821U, 0x20820U, 0x50011U, 0x50010U, 0x0480AU, 0x01602U, 0x04808U, 0x01600U, 0x002A0U, 0x002A1U, 0x04441U, 0x04440U,
0x002A4U, 0x002A5U, 0x04830U, 0x04444U, 0x06100U, 0x20810U, 0x50021U, 0x50020U, 0x06104U, 0x20814U, 0x50025U, 0x50024U, 0x20809U,
0x20808U, 0x13000U, 0x08300U, 0x04822U, 0x2080CU, 0x04820U, 0x04821U, 0x20801U, 0x20800U, 0x20803U, 0x20802U, 0x20805U, 0x20804U,
0x04828U, 0x20806U, 0x002C0U, 0x002C1U, 0x04421U, 0x04420U, 0x20408U, 0x18010U, 0x2040AU, 0x18012U, 0x20404U, 0x20405U, 0x50041U,
0x50040U, 0x20400U, 0x20401U, 0x20402U, 0x20403U, 0x18005U, 0x18004U, 0x21081U, 0x21080U, 0x18001U, 0x18000U, 0x04840U, 0x18002U,
0x20414U, 0x1800CU, 0x21089U, 0x21088U, 0x20410U, 0x18008U, 0x20412U, 0x1800AU, 0x04403U, 0x04402U, 0x04401U, 0x04400U, 0x10182U,
0x04406U, 0x10180U, 0x04404U, 0x01A02U, 0x0440AU, 0x01A00U, 0x04408U, 0x20420U, 0x40300U, 0x20422U, 0x40302U, 0x04413U, 0x04412U,
0x04411U, 0x04410U, 0x18021U, 0x18020U, 0x10190U, 0x18022U, 0x20841U, 0x20840U, 0x01A10U, 0x20842U, 0x05080U, 0x05081U, 0x05082U,
0x05083U, 0x00300U, 0x00301U, 0x00302U, 0x00303U, 0x00304U, 0x00305U, 0x10060U, 0x22080U, 0x00308U, 0x00309U, 0x28800U, 0x28801U,
0x44402U, 0x44403U, 0x44400U, 0x44401U, 0x00310U, 0x00311U, 0x10C01U, 0x10C00U, 0x00314U, 0x00315U, 0x10070U, 0x10C04U, 0x00318U,
0x00319U, 0x28810U, 0x10C08U, 0x44412U, 0x00000U, 0x44410U, 0x44411U, 0x00320U, 0x60400U, 0x10044U, 0x10045U, 0x10042U, 0x0C800U,
0x10040U, 0x10041U, 0x06080U, 0x06081U, 0x06082U, 0x06083U, 0x1004AU, 0x0C808U, 0x10048U, 0x10049U, 0x58008U, 0x08282U, 0x08281U,
0x08280U, 0x10052U, 0x0C810U, 0x10050U, 0x10051U, 0x58000U, 0x58001U, 0x58002U, 0x08288U, 0x02A02U, 0x02A03U, 0x02A00U, 0x02A01U,
0x00340U, 0x00341U, 0x10024U, 0x10025U, 0x10022U, 0x10023U, 0x10020U, 0x10021U, 0x34001U, 0x34000U, 0x02601U, 0x02600U, 0x1002AU,
0x34004U, 0x10028U, 0x10029U, 0x0C400U, 0x0C401U, 0x21101U, 0x21100U, 0x60800U, 0x60801U, 0x10030U, 0x10031U, 0x0C408U, 0x34010U,
0x21109U, 0x21108U, 0x60808U, 0x60809U, 0x10038U, 0x28420U, 0x10006U, 0x10007U, 0x10004U, 0x10005U, 0x10002U, 0x10003U, 0x10000U,
0x10001U, 0x1000EU, 0x40284U, 0x1000CU, 0x1000DU, 0x1000AU, 0x40280U, 0x10008U, 0x10009U, 0x10016U, 0x10017U, 0x10014U, 0x10015U,
0x10012U, 0x10013U, 0x10010U, 0x10011U, 0x05104U, 0x44802U, 0x44801U, 0x44800U, 0x05100U, 0x05101U, 0x10018U, 0x28400U, 0x00380U,
0x00381U, 0x22005U, 0x22004U, 0x22003U, 0x22002U, 0x22001U, 0x22000U, 0x06020U, 0x06021U, 0x50101U, 0x50100U, 0x11800U, 0x11801U,
0x22009U, 0x22008U, 0x45001U, 0x45000U, 0x08221U, 0x08220U, 0x04902U, 0x22012U, 0x04900U, 0x22010U, 0x06030U, 0x45008U, 0x08229U,
0x08228U, 0x11810U, 0x11811U, 0x04908U, 0x22018U, 0x06008U, 0x06009U, 0x08211U, 0x08210U, 0x100C2U, 0x22022U, 0x100C0U, 0x22020U,
0x06000U, 0x06001U, 0x06002U, 0x06003U, 0x06004U, 0x40240U, 0x06006U, 0x40242U, 0x08203U, 0x08202U, 0x08201U, 0x08200U, 0x08207U,
0x08206U, 0x08205U, 0x08204U, 0x06010U, 0x20900U, 0x08209U, 0x08208U, 0x61002U, 0x20904U, 0x61000U, 0x61001U, 0x29020U, 0x29021U,
0x100A4U, 0x22044U, 0x100A2U, 0x22042U, 0x100A0U, 0x22040U, 0x20504U, 0x40224U, 0x0D005U, 0x0D004U, 0x20500U, 0x40220U, 0x0D001U,
0x0D000U, 0x03204U, 0x18104U, 0x08261U, 0x08260U, 0x03200U, 0x18100U, 0x03202U, 0x18102U, 0x11421U, 0x11420U, 0x00000U, 0x11422U,
0x03208U, 0x18108U, 0x0D011U, 0x0D010U, 0x29000U, 0x29001U, 0x10084U, 0x04500U, 0x10082U, 0x40208U, 0x10080U, 0x10081U, 0x06040U,
0x40204U, 0x06042U, 0x40206U, 0x40201U, 0x40200U, 0x10088U, 0x40202U, 0x29010U, 0x08242U, 0x08241U, 0x08240U, 0x10092U, 0x40218U,
0x10090U, 0x10091U, 0x11401U, 0x11400U, 0x11403U, 0x11402U, 0x40211U, 0x40210U, 0x10098U, 0x40212U, 0x00400U, 0x00401U, 0x00402U,
0x00403U, 0x00404U, 0x00405U, 0x00406U, 0x00407U, 0x00408U, 0x00409U, 0x0040AU, 0x02140U, 0x0040CU, 0x0040DU, 0x01091U, 0x01090U,
0x00410U, 0x00411U, 0x00412U, 0x00413U, 0x00414U, 0x00860U, 0x01089U, 0x01088U, 0x00418U, 0x38000U, 0x01085U, 0x01084U, 0x01083U,
0x01082U, 0x01081U, 0x01080U, 0x00420U, 0x00421U, 0x00422U, 0x00423U, 0x00424U, 0x00850U, 0x42080U, 0x42081U, 0x00428U, 0x00429U,
0x48801U, 0x48800U, 0x09100U, 0x12200U, 0x24401U, 0x24400U, 0x00430U, 0x00844U, 0x00432U, 0x00846U, 0x00841U, 0x00840U, 0x1C000U,
0x00842U, 0x00438U, 0x0084CU, 0x010A5U, 0x010A4U, 0x00849U, 0x00848U, 0x010A1U, 0x010A0U, 0x00440U, 0x00441U, 0x00442U, 0x02108U,
0x00444U, 0x00830U, 0x70001U, 0x70000U, 0x00448U, 0x02102U, 0x02101U, 0x02100U, 0x20280U, 0x20281U, 0x02105U, 0x02104U, 0x00450U,
0x00824U, 0x00452U, 0x00826U, 0x00821U, 0x00820U, 0x00823U, 0x00822U, 0x24802U, 0x02112U, 0x24800U, 0x02110U, 0x00829U, 0x00828U,
0x48400U, 0x010C0U, 0x00460U, 0x00814U, 0x04281U, 0x04280U, 0x00811U, 0x00810U, 0x00813U, 0x00812U, 0x54000U, 0x54001U, 0x02121U,
0x02120U, 0x00819U, 0x00818U, 0x0081BU, 0x0081AU, 0x00805U, 0x00804U, 0x41100U, 0x00806U, 0x00801U, 0x00800U, 0x00803U, 0x00802U,
0x0A080U, 0x0080CU, 0x0A082U, 0x0080EU, 0x00809U, 0x00808U, 0x0080BU, 0x0080AU, 0x00480U, 0x00481U, 0x00482U, 0x00483U, 0x00484U,
0x14100U, 0x42020U, 0x01018U, 0x00488U, 0x00489U, 0x01015U, 0x01014U, 0x20240U, 0x01012U, 0x01011U, 0x01010U, 0x00490U, 0x00491U,
0x0100DU, 0x0100CU, 0x0100BU, 0x0100AU, 0x01009U, 0x01008U, 0x40900U, 0x01006U, 0x01005U, 0x01004U, 0x01003U, 0x01002U, 0x01001U,
0x01000U, 0x004A0U, 0x004A1U, 0x42004U, 0x04240U, 0x42002U, 0x42003U, 0x42000U, 0x42001U, 0x30102U, 0x30103U, 0x30100U, 0x30101U,
0x4200AU, 0x01032U, 0x42008U, 0x01030U, 0x25000U, 0x25001U, 0x08501U, 0x08500U, 0x008C1U, 0x008C0U, 0x42010U, 0x01028U, 0x0A040U,
0x0A041U, 0x01025U, 0x01024U, 0x01023U, 0x01022U, 0x01021U, 0x01020U, 0x004C0U, 0x49000U, 0x04221U, 0x04220U, 0x20208U, 0x20209U,
0x08900U, 0x08901U, 0x20204U, 0x20205U, 0x02181U, 0x02180U, 0x20200U, 0x20201U, 0x20202U, 0x01050U, 0x0A028U, 0x008A4U, 0x0104DU,
0x0104CU, 0x008A1U, 0x008A0U, 0x01049U, 0x01048U, 0x0A020U, 0x0A021U, 0x01045U, 0x01044U, 0x20210U, 0x01042U, 0x01041U, 0x01040U,
0x04203U, 0x04202U, 0x04201U, 0x04200U, 0x00891U, 0x00890U, 0x42040U, 0x04204U, 0x0A010U, 0x0A011U, 0x01C00U, 0x04208U, 0x20220U,
0x40500U, 0x20222U, 0x40502U, 0x0A008U, 0x00884U, 0x04211U, 0x04210U, 0x00881U, 0x00880U, 0x00883U, 0x00882U, 0x0A000U, 0x0A001U,
0x0A002U, 0x0A003U, 0x0A004U, 0x00888U, 0x01061U, 0x01060U, 0x00500U, 0x00501U, 0x00502U, 0x02048U, 0x00504U, 0x14080U, 0x00506U,
0x14082U, 0x00508U, 0x02042U, 0x02041U, 0x02040U, 0x09020U, 0x09021U, 0x44200U, 0x02044U, 0x00510U, 0x00511U, 0x10A01U, 0x10A00U,
0x4A001U, 0x4A000U, 0x4A003U, 0x4A002U, 0x40880U, 0x40881U, 0x02051U, 0x02050U, 0x40884U, 0x01182U, 0x01181U, 0x01180U, 0x00520U,
0x60200U, 0x00522U, 0x60202U, 0x09008U, 0x09009U, 0x0900AU, 0x0900BU, 0x09004U, 0x09005U, 0x30080U, 0x02060U, 0x09000U, 0x09001U,
0x09002U, 0x09003U, 0x41042U, 0x08482U, 0x41040U, 0x08480U, 0x00941U, 0x00940U, 0x41044U, 0x00942U, 0x09014U, 0x09015U, 0x02C04U,
0x08488U, 0x09010U, 0x09011U, 0x02C00U, 0x02C01U, 0x00540U, 0x0200AU, 0x02009U, 0x02008U, 0x08882U, 0x0200EU, 0x08880U, 0x0200CU,
0x02003U, 0x02002U, 0x02001U, 0x02000U, 0x02007U, 0x02006U, 0x02005U, 0x02004U, 0x0C200U, 0x0C201U, 0x41020U, 0x02018U, 0x00921U,
0x00920U, 0x41024U, 0x00922U, 0x02013U, 0x02012U, 0x02011U, 0x02010U, 0x02017U, 0x02016U, 0x02015U, 0x02014U, 0x41012U, 0x0202AU,
0x41010U, 0x02028U, 0x26000U, 0x00910U, 0x10600U, 0x10601U, 0x02023U, 0x02022U, 0x02021U, 0x02020U, 0x09040U, 0x40480U, 0x02025U,
0x02024U, 0x41002U, 0x00904U, 0x41000U, 0x41001U, 0x00901U, 0x00900U, 0x41004U, 0x00902U, 0x4100AU, 0x02032U, 0x41008U, 0x02030U,
0x00909U, 0x00908U, 0x28201U, 0x28200U, 0x00580U, 0x14004U, 0x00582U, 0x14006U, 0x14001U, 0x14000U, 0x08840U, 0x14002U, 0x40810U,
0x40811U, 0x30020U, 0x020C0U, 0x14009U, 0x14008U, 0x01111U, 0x01110U, 0x40808U, 0x40809U, 0x08421U, 0x08420U, 0x14011U, 0x14010U,
0x01109U, 0x01108U, 0x40800U, 0x40801U, 0x40802U, 0x01104U, 0x40804U, 0x01102U, 0x01101U, 0x01100U, 0x03801U, 0x03800U, 0x30008U,
0x08410U, 0x14021U, 0x14020U, 0x42100U, 0x42101U, 0x30002U, 0x30003U, 0x30000U, 0x30001U, 0x09080U, 0x40440U, 0x30004U, 0x30005U,
0x08403U, 0x08402U, 0x08401U, 0x08400U, 0x08407U, 0x08406U, 0x08405U, 0x08404U, 0x40820U, 0x40821U, 0x30010U, 0x08408U, 0x40824U,
0x01122U, 0x01121U, 0x01120U, 0x08806U, 0x0208AU, 0x08804U, 0x02088U, 0x08802U, 0x14040U, 0x08800U, 0x08801U, 0x02083U, 0x02082U,
0x02081U, 0x02080U, 0x20300U, 0x40420U, 0x08808U, 0x02084U, 0x03404U, 0x03405U, 0x08814U, 0x02098U, 0x03400U, 0x03401U, 0x08810U,
0x08811U, 0x40840U, 0x40841U, 0x02091U, 0x02090U, 0x40844U, 0x01142U, 0x01141U, 0x01140U, 0x04303U, 0x04302U, 0x04301U, 0x04300U,
0x40409U, 0x40408U, 0x08820U, 0x08821U, 0x40405U, 0x40404U, 0x30040U, 0x020A0U, 0x40401U, 0x40400U, 0x40403U, 0x40402U, 0x41082U,
0x08442U, 0x41080U, 0x08440U, 0x00981U, 0x00980U, 0x41084U, 0x00982U, 0x0A100U, 0x11200U, 0x0A102U, 0x11202U, 0x40411U, 0x40410U,
0x40413U, 0x40412U, 0x00600U, 0x00601U, 0x00602U, 0x00603U, 0x00604U, 0x00605U, 0x00606U, 0x00607U, 0x00608U, 0x05800U, 0x0060AU,
0x05802U, 0x200C0U, 0x12020U, 0x44100U, 0x44101U, 0x00610U, 0x00611U, 0x10901U, 0x10900U, 0x51000U, 0x51001U, 0x51002U, 0x10904U,
0x00618U, 0x05810U, 0x01285U, 0x01284U, 0x51008U, 0x01282U, 0x01281U, 0x01280U, 0x00620U, 0x60100U, 0x040C1U, 0x040C0U, 0x12009U,
0x12008U, 0x21800U, 0x21801U, 0x12005U, 0x12004U, 0x12007U, 0x12006U, 0x12001U, 0x12000U, 0x12003U, 0x12002U, 0x00630U, 0x00A44U,
0x040D1U, 0x040D0U, 0x00A41U, 0x00A40U, 0x21810U, 0x00A42U, 0x12015U, 0x12014U, 0x00000U, 0x12016U, 0x12011U, 0x12010U, 0x12013U,
0x12012U, 0x00640U, 0x00641U, 0x040A1U, 0x040A0U, 0x20088U, 0x20089U, 0x2008AU, 0x040A4U, 0x20084U, 0x20085U, 0x19000U, 0x02300U,
0x20080U, 0x20081U, 0x20082U, 0x20083U, 0x0C100U, 0x0C101U, 0x21401U, 0x21400U, 0x00A21U, 0x00A20U, 0x00A23U, 0x00A22U, 0x20094U,
0x20095U, 0x19010U, 0x21408U, 0x20090U, 0x20091U, 0x20092U, 0x28120U, 0x04083U, 0x04082U, 0x04081U, 0x04080U, 0x00A11U, 0x00A10U,
0x10500U, 0x04084U, 0x200A4U, 0x0408AU, 0x04089U, 0x04088U, 0x200A0U, 0x12040U, 0x200A2U, 0x12042U, 0x00A05U, 0x00A04U, 0x04091U,
0x04090U, 0x00A01U, 0x00A00U, 0x00A03U, 0x00A02U, 0x05404U, 0x00A0CU, 0x28105U, 0x28104U, 0x05400U, 0x00A08U, 0x28101U, 0x28100U,
0x00680U, 0x00681U, 0x04061U, 0x04060U, 0x20048U, 0x20049U, 0x2004AU, 0x04064U, 0x20044U, 0x20045U, 0x50401U, 0x50400U, 0x20040U,
0x20041U, 0x20042U, 0x01210U, 0x68002U, 0x68003U, 0x68000U, 0x68001U, 0x04C02U, 0x0120AU, 0x04C00U, 0x01208U, 0x20054U, 0x01206U,
0x01205U, 0x01204U, 0x20050U, 0x01202U, 0x01201U, 0x01200U, 0x18800U, 0x04042U, 0x04041U, 0x04040U, 0x42202U, 0x04046U, 0x42200U,
0x04044U, 0x20064U, 0x0404AU, 0x04049U, 0x04048U, 0x20060U, 0x12080U, 0x20062U, 0x12082U, 0x18810U, 0x04052U, 0x04051U, 0x04050U,
0x4C009U, 0x4C008U, 0x42210U, 0x04054U, 0x20C01U, 0x20C00U, 0x20C03U, 0x20C02U, 0x4C001U, 0x4C000U, 0x01221U, 0x01220U, 0x2000CU,
0x04022U, 0x04021U, 0x04020U, 0x20008U, 0x20009U, 0x2000AU, 0x04024U, 0x20004U, 0x20005U, 0x20006U, 0x04028U, 0x20000U, 0x20001U,
0x20002U, 0x20003U, 0x2001CU, 0x04032U, 0x04031U, 0x04030U, 0x20018U, 0x18400U, 0x2001AU, 0x18402U, 0x20014U, 0x20015U, 0x20016U,
0x01244U, 0x20010U, 0x20011U, 0x20012U, 0x01240U, 0x04003U, 0x04002U, 0x04001U, 0x04000U, 0x20028U, 0x04006U, 0x04005U, 0x04004U,
0x20024U, 0x0400AU, 0x04009U, 0x04008U, 0x20020U, 0x20021U, 0x20022U, 0x0400CU, 0x04013U, 0x04012U, 0x04011U, 0x04010U, 0x00A81U,
0x00A80U, 0x04015U, 0x04014U, 0x0A200U, 0x11100U, 0x04019U, 0x04018U, 0x20030U, 0x20031U, 0x50800U, 0x50801U, 0x00700U, 0x60020U,
0x10811U, 0x10810U, 0x4400AU, 0x60024U, 0x44008U, 0x44009U, 0x44006U, 0x02242U, 0x44004U, 0x02240U, 0x44002U, 0x44003U, 0x44000U,
0x44001U, 0x0C040U, 0x10802U, 0x10801U, 0x10800U, 0x0C044U, 0x10806U, 0x10805U, 0x10804U, 0x23000U, 0x23001U, 0x10809U, 0x10808U,
0x44012U, 0x44013U, 0x44010U, 0x44011U, 0x60001U, 0x60000U, 0x60003U, 0x60002U, 0x60005U, 0x60004U, 0x10440U, 0x10441U, 0x60009U,
0x60008U, 0x44024U, 0x6000AU, 0x09200U, 0x12100U, 0x44020U, 0x44021U, 0x60011U, 0x60010U, 0x10821U, 0x10820U, 0x07003U, 0x07002U,
0x07001U, 0x07000U, 0x23020U, 0x60018U, 0x28045U, 0x28044U, 0x09210U, 0x28042U, 0x28041U, 0x28040U, 0x0C010U, 0x0C011U, 0x02209U,
0x02208U, 0x10422U, 0x10423U, 0x10420U, 0x10421U, 0x02203U, 0x02202U, 0x02201U, 0x02200U, 0x20180U, 0x20181U, 0x44040U, 0x02204U,
0x0C000U, 0x0C001U, 0x0C002U, 0x10840U, 0x0C004U, 0x0C005U, 0x0C006U, 0x10844U, 0x0C008U, 0x0C009U, 0x02211U, 0x02210U, 0x0C00CU,
0x28022U, 0x28021U, 0x28020U, 0x60041U, 0x60040U, 0x10404U, 0x04180U, 0x10402U, 0x10403U, 0x10400U, 0x10401U, 0x02223U, 0x02222U,
0x02221U, 0x02220U, 0x1040AU, 0x28012U, 0x10408U, 0x28010U, 0x0C020U, 0x0C021U, 0x41200U, 0x41201U, 0x00B01U, 0x00B00U, 0x10410U,
0x28008U, 0x11081U, 0x11080U, 0x28005U, 0x28004U, 0x28003U, 0x28002U, 0x28001U, 0x28000U, 0x52040U, 0x14204U, 0x22405U, 0x22404U,
0x14201U, 0x14200U, 0x22401U, 0x22400U, 0x20144U, 0x20145U, 0x44084U, 0x022C0U, 0x20140U, 0x20141U, 0x44080U, 0x44081U, 0x40A08U,
0x10882U, 0x10881U, 0x10880U, 0x14211U, 0x14210U, 0x1A008U, 0x10884U, 0x40A00U, 0x40A01U, 0x40A02U, 0x01304U, 0x1A002U, 0x01302U,
0x1A000U, 0x01300U, 0x60081U, 0x60080U, 0x04141U, 0x04140U, 0x60085U, 0x60084U, 0x104C0U, 0x04144U, 0x06400U, 0x06401U, 0x30200U,
0x30201U, 0x06404U, 0x40640U, 0x30204U, 0x30205U, 0x08603U, 0x08602U, 0x08601U, 0x08600U, 0x00000U, 0x08606U, 0x08605U, 0x08604U,
0x11041U, 0x11040U, 0x30210U, 0x11042U, 0x11045U, 0x11044U, 0x1A020U, 0x01320U, 0x52000U, 0x52001U, 0x04121U, 0x04120U, 0x20108U,
0x20109U, 0x08A00U, 0x08A01U, 0x20104U, 0x20105U, 0x02281U, 0x02280U, 0x20100U, 0x20101U, 0x20102U, 0x20103U, 0x0C080U, 0x0C081U,
0x0C082U, 0x04130U, 0x0C084U, 0x06808U, 0x08A10U, 0x08A11U, 0x11021U, 0x11020U, 0x11023U, 0x11022U, 0x20110U, 0x06800U, 0x20112U,
0x06802U, 0x04103U, 0x04102U, 0x04101U, 0x04100U, 0x10482U, 0x04106U, 0x10480U, 0x04104U, 0x11011U, 0x11010U, 0x04109U, 0x04108U,
0x20120U, 0x40600U, 0x20122U, 0x40602U, 0x11009U, 0x11008U, 0x22800U, 0x04110U, 0x1100DU, 0x1100CU, 0x22804U, 0x04114U, 0x11001U,
0x11000U, 0x11003U, 0x11002U, 0x11005U, 0x11004U, 0x28081U, 0x28080U };
#define X18 0x00040000 /* vector representation of X^{18} */
#define X11 0x00000800 /* vector representation of X^{11} */
#define MASK8 0xfffff800 /* auxiliary vector for testing */
#define GENPOL 0x00000c75 /* generator polinomial, g(x) */
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRSlotType class.
/// </summary>
DMRSlotType::DMRSlotType()
{
/* stub */
}
/// <summary>
/// Decodes DMR slot type.
/// </summary>
/// <param name="frame"></param>
/// <param name="colorCode"></param>
/// <param name="dataType"></param>
void DMRSlotType::decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& dataType) const
{
uint8_t slotType[3U];
slotType[0U] = (frame[12U] << 2) & 0xFCU;
slotType[0U] |= (frame[13U] >> 6) & 0x03U;
slotType[1U] = (frame[13U] << 2) & 0xC0U;
slotType[1U] |= (frame[19U] << 2) & 0x3CU;
slotType[1U] |= (frame[20U] >> 6) & 0x03U;
slotType[2U] = (frame[20U] << 2) & 0xF0U;
uint8_t code = decode2087(slotType);
colorCode = (code >> 4) & 0x0FU;
dataType = (code >> 0) & 0x0FU;
}
/// <summary>
/// Encodes DMR slot type.
/// </summary>
/// <param name="colorCode"></param>
/// <param name="dataType"></param>
/// <param name="frame"></param>
void DMRSlotType::encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) const
{
uint8_t slotType[3U];
slotType[0U] = (colorCode << 4) & 0xF0U;
slotType[0U] |= (dataType << 0) & 0x0FU;
uint16_t cksum = ENCODING_TABLE_2087[slotType[0U]];
slotType[1U] = (cksum >> 0) & 0xFFU;
slotType[2U] = (cksum >> 8) & 0xFFU;
frame[12U] = (frame[12U] & 0xC0U) | ((slotType[0U] >> 2) & 0x3FU);
frame[13U] = (frame[13U] & 0x0FU) | ((slotType[0U] << 6) & 0xC0U) | ((slotType[1U] >> 2) & 0x30U);
frame[19U] = (frame[19U] & 0xF0U) | ((slotType[1U] >> 2) & 0x0FU);
frame[20U] = (frame[20U] & 0x03U) | ((slotType[1U] << 6) & 0xC0U) | ((slotType[2U] >> 2) & 0x3CU);
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
uint8_t DMRSlotType::decode2087(const uint8_t* data) const
{
uint32_t code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5);
uint32_t syndrome = getSyndrome1987(code);
uint32_t error_pattern = DECODING_TABLE_1987[syndrome];
if (error_pattern != 0x00U)
code ^= error_pattern;
return code >> 11;
}
/// <summary>
///
/// </summary>
/// <remarks>
/// Compute the syndrome corresponding to the given pattern, i.e., the
/// remainder after dividing the pattern (when considering it as the vector
/// representation of a polynomial) by the generator polynomial, GENPOL.
/// In the program this pattern has several meanings: (1) pattern = infomation
/// bits, when constructing the encoding table; (2) pattern = error pattern,
/// when constructing the decoding table; and (3) pattern = received vector, to
/// obtain its syndrome in decoding.
/// </remarks>
/// <param name="pattern"></param>
/// <returns></returns>
uint32_t DMRSlotType::getSyndrome1987(uint32_t pattern) const
{
unsigned int aux = X18;
if (pattern >= X11) {
while (pattern & MASK8) {
while (!(aux & pattern))
aux = aux >> 1;
pattern ^= (aux / X11) * GENPOL;
}
}
return pattern;
}

@ -0,0 +1,60 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 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
* 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(__DMR_SLOT_TYPE_H__)
#define __DMR_SLOT_TYPE_H__
#include "Defines.h"
namespace dmr
{
// ---------------------------------------------------------------------------
// Class Declaration
//
// ---------------------------------------------------------------------------
class DSP_FW_API DMRSlotType {
public:
/// <summary>Initializes a new instance of the DMRSlotType class.</summary>
DMRSlotType();
/// <summary>Decodes DMR slot type.</summary>
void decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& dataType) const;
/// <summary>Encodes DMR slot type.</summary>
void encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) const;
private:
/// <summary></summary>
uint8_t decode2087(const uint8_t* data) const;
/// <summary></summary>
uint32_t getSyndrome1987(uint32_t pattern) const;
};
} // namespace dmr
#endif // __DMR_SLOT_TYPE_H__

@ -0,0 +1,466 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* 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 "Globals.h"
#include "dmr/DMRSlotType.h"
using namespace dmr;
#if defined(DUPLEX)
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// The PR FILL and BS Data Sync pattern.
const uint8_t IDLE_DATA[] = {
0x53U, 0xC2U, 0x5EU, 0xABU, 0xA8U, 0x67U, 0x1DU, 0xC7U, 0x38U, 0x3BU, 0xD9U,
0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U,
0xE4U, 0x65U, 0x17U, 0x1BU, 0x48U, 0xCAU, 0x6DU, 0x4FU, 0xC6U, 0x10U, 0xB4U
};
const uint8_t CACH_INTERLEAVE[] = {
1U, 2U, 3U, 5U, 6U, 7U, 9U, 10U, 11U, 13U, 15U, 16U, 17U, 19U, 20U, 21U, 23U,
25U, 26U, 27U, 29U, 30U, 31U, 33U, 34U, 35U, 37U, 39U, 40U, 41U, 43U, 44U, 45U, 47U,
49U, 50U, 51U, 53U, 54U, 55U, 57U, 58U, 59U, 61U, 63U, 64U, 65U, 67U, 68U, 69U, 71U,
73U, 74U, 75U, 77U, 78U, 79U, 81U, 82U, 83U, 85U, 87U, 88U, 89U, 91U, 92U, 93U, 95U
};
const uint8_t EMPTY_SHORT_LC[] = {
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U
};
const uint32_t STARTUP_COUNT = 20U;
const uint32_t ABORT_COUNT = 6U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the DMRTX class.
/// </summary>
DMRTX::DMRTX() :
m_fifo(),
m_state(DMRTXSTATE_IDLE),
m_idle(),
m_cachPtr(0U),
m_shortLC(),
m_newShortLC(),
m_markBuffer(),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_frameCount(0U),
m_abortCount(),
m_abort(),
m_control_old(0U)
{
::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U);
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
m_abort[0U] = false;
m_abort[1U] = false;
m_abortCount[0U] = 0U;
m_abortCount[1U] = 0U;
}
/// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void DMRTX::process()
{
if (m_state == DMRTXSTATE_IDLE)
return;
if (m_poLen == 0U) {
switch (m_state) {
case DMRTXSTATE_SLOT1:
createData(0U);
m_state = DMRTXSTATE_CACH2;
break;
case DMRTXSTATE_CACH2:
createCACH(1U, 0U);
m_state = DMRTXSTATE_SLOT2;
break;
case DMRTXSTATE_SLOT2:
createData(1U);
m_state = DMRTXSTATE_CACH1;
break;
case DMRTXSTATE_CAL:
break;
default:
createCACH(0U, 1U);
m_state = DMRTXSTATE_SLOT1;
break;
}
DEBUG2("DMRTX: process(): poLen", m_poLen);
}
if (m_poLen > 0U) {
uint16_t space = io.getSpace();
while (space > 8U) {
uint8_t c = m_poBuffer[m_poPtr];
uint8_t m = m_markBuffer[m_poPtr];
m_poPtr++;
writeByte(c, m);
space -= 8U;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
}
}
/// <summary>
/// Write slot 1 data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeData1(const uint8_t* data, uint8_t length)
{
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo[0U].getSpace();
DEBUG3("DMRTX: writeData1(): dataLength/fifoLength", length, space);
if (space < DMR_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL;
if (m_abort[0U]) {
m_fifo[0U].reset();
m_abort[0U] = false;
}
for (uint8_t i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
m_fifo[0U].put(data[i + 1U]);
// Start the TX if it isn't already on
if (!m_tx)
m_state = DMRTXSTATE_SLOT1;
return RSN_OK;
}
/// <summary>
/// Write slot 2 data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeData2(const uint8_t* data, uint8_t length)
{
if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo[1U].getSpace();
DEBUG3("DMRTX: writeData2(): dataLength/fifoLength", length, space);
if (space < DMR_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL;
if (m_abort[1U]) {
m_fifo[1U].reset();
m_abort[1U] = false;
}
for (uint8_t i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
m_fifo[1U].put(data[i + 1U]);
// Start the TX if it isn't already on
if (!m_tx)
m_state = DMRTXSTATE_SLOT1;
return RSN_OK;
}
/// <summary>
/// Write short LC data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeShortLC(const uint8_t* data, uint8_t length)
{
if (length != 9U)
return RSN_ILLEGAL_LENGTH;
::memset(m_newShortLC, 0x00U, 12U);
for (uint8_t i = 0U; i < 68U; i++) {
bool b = _READ_BIT(data, i);
uint8_t n = CACH_INTERLEAVE[i];
_WRITE_BIT(m_newShortLC, n, b);
}
return RSN_OK;
}
/// <summary>
/// Write abort data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeAbort(const uint8_t* data, uint8_t length)
{
if (length != 1U)
return RSN_ILLEGAL_LENGTH;
switch (data[0U]) {
case 1U:
m_abort[0U] = true;
m_abortCount[0U] = 0U;
return RSN_OK;
case 2U:
m_abort[1U] = true;
m_abortCount[1U] = 0U;
return RSN_OK;
default:
return RSN_INVALID_DMR_SLOT;
}
}
/// <summary>
/// Helper to set the start state for Tx.
/// </summary>
/// <param name="start"></param>
void DMRTX::setStart(bool start)
{
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
m_frameCount = 0U;
m_abortCount[0U] = 0U;
m_abortCount[1U] = 1U;
m_abort[0U] = false;
m_abort[1U] = false;
}
/// <summary>
/// Helper to get how much space the slot 1 ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint8_t DMRTX::getSpace1() const
{
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
}
/// <summary>
/// Helper to get how much space the slot 2 ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint8_t DMRTX::getSpace2() const
{
return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
}
/// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRTX::setColorCode(uint8_t colorCode)
{
::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES);
DMRSlotType slotType;
slotType.encode(colorCode, DT_IDLE, m_idle);
}
/// <summary>
/// Sets the fine adjust 4FSK symbol levels.
/// </summary>
/// <param name="level3Adj">+3/-3 symbol adjust.</param>
/// <param name="level1Adj">+1/-1 symbol adjust.</param>
void DMRTX::setSymbolLvlAdj(int8_t level3Adj, int8_t level1Adj)
{
/* ignored ADF7021 doesn't allow direct symbol level adjustments */
}
/// <summary>
/// Helper to reset data values to defaults for slot 1 FIFO.
/// </summary>
void DMRTX::resetFifo1()
{
m_fifo[0U].reset();
}
/// <summary>
/// Helper to reset data values to defaults for slot 2 FIFO.
/// </summary>
void DMRTX::resetFifo2()
{
m_fifo[1U].reset();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
uint32_t DMRTX::getFrameCount()
{
return m_frameCount;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="slotIndex"></param>
void DMRTX::createData(uint8_t slotIndex)
{
if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) {
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = m_fifo[slotIndex].get();
if (i == 8U)
m_markBuffer[i] = slotIndex == 0U ? MARK_SLOT1 : MARK_SLOT2;
else
m_markBuffer[i] = MARK_NONE;
}
}
else {
m_abort[slotIndex] = false;
// Transmit an idle message
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = m_idle[i];
if (i == 8U)
m_markBuffer[i] = slotIndex == 0U ? MARK_SLOT1 : MARK_SLOT2;
else
m_markBuffer[i] = MARK_NONE;
}
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
m_poPtr = 0U;
}
/// <summary>
///
/// </summary>
/// <param name="txSlotIndex"></param>
/// <param name="rxSlotIndex"></param>
void DMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
{
m_frameCount++;
m_abortCount[0U]++;
m_abortCount[1U]++;
if (m_cachPtr >= 12U)
m_cachPtr = 0U;
if (m_cachPtr == 0U) {
if (m_fifo[0U].getData() == 0U && m_fifo[1U].getData() == 0U)
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
else
::memcpy(m_shortLC, m_newShortLC, 12U);
}
::memcpy(m_poBuffer, m_shortLC + m_cachPtr, 3U);
m_markBuffer[0U] = MARK_NONE;
m_markBuffer[1U] = MARK_NONE;
m_markBuffer[2U] = rxSlotIndex == 1U ? MARK_SLOT1 : MARK_SLOT2;
bool at = false;
if (m_frameCount >= STARTUP_COUNT)
at = m_fifo[rxSlotIndex].getData() > 0U;
bool tc = txSlotIndex == 1U;
bool ls0 = true; // For 1 and 2
bool ls1 = true;
if (m_cachPtr == 0U) // For 0
ls1 = false;
else if (m_cachPtr == 9U) // For 3
ls0 = false;
bool h0 = at ^ tc ^ ls1;
bool h1 = tc ^ ls1 ^ ls0;
bool h2 = at ^ tc ^ ls0;
m_poBuffer[0U] |= at ? 0x80U : 0x00U;
m_poBuffer[0U] |= tc ? 0x08U : 0x00U;
m_poBuffer[1U] |= ls1 ? 0x80U : 0x00U;
m_poBuffer[1U] |= ls0 ? 0x08U : 0x00U;
m_poBuffer[1U] |= h0 ? 0x02U : 0x00U;
m_poBuffer[2U] |= h1 ? 0x20U : 0x00U;
m_poBuffer[2U] |= h2 ? 0x02U : 0x00U;
m_poLen = DMR_CACH_LENGTH_BYTES;
m_poPtr = 0U;
m_cachPtr += 3U;
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
/// <param name="control"></param>
void DMRTX::writeByte(uint8_t c, uint8_t control)
{
uint8_t bit;
uint8_t mask = 0x80U;
uint8_t control_tmp = m_control_old;
for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
if ((c & mask) == mask)
bit = 1U;
else
bit = 0U;
if (i == 7U) {
if (control == MARK_SLOT2)
control_tmp = true;
else if (control == MARK_SLOT1)
control_tmp = false;
m_control_old = control_tmp;
}
io.write(&bit, 1, &control_tmp);
}
}
#endif // DUPLEX

@ -0,0 +1,135 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* 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.
*/
#if !defined(__DMR_TX_H__)
#define __DMR_TX_H__
#include "Defines.h"
#include "dmr/DMRDefines.h"
#include "SerialBuffer.h"
#if defined(DUPLEX)
namespace dmr
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum DMRTXSTATE {
DMRTXSTATE_IDLE,
DMRTXSTATE_SLOT1,
DMRTXSTATE_CACH1,
DMRTXSTATE_SLOT2,
DMRTXSTATE_CACH2,
DMRTXSTATE_CAL
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for duplex DMR mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API DMRTX {
public:
/// <summary>Initializes a new instance of the DMRTX class.</summary>
DMRTX();
/// <summary>Process local buffer and transmit on the air interface.</summary>
void process();
/// <summary>Write slot 1 data to the local buffer.</summary>
uint8_t writeData1(const uint8_t* data, uint8_t length);
/// <summary>Write slot 2 data to the local buffer.</summary>
uint8_t writeData2(const uint8_t* data, uint8_t length);
/// <summary>Write short LC data to the local buffer.</summary>
uint8_t writeShortLC(const uint8_t* data, uint8_t length);
/// <summary>Write abort data to the local buffer.</summary>
uint8_t writeAbort(const uint8_t* data, uint8_t length);
/// <summary>Helper to set the start state for Tx.</summary>
void setStart(bool start);
/// <summary>Helper to get how much space the slot 1 ring buffer has for samples.</summary>
uint8_t getSpace1() const;
/// <summary>Helper to get how much space the slot 2 ring buffer has for samples.</summary>
uint8_t getSpace2() const;
/// <summary>Sets the DMR color code.</summary>
void setColorCode(uint8_t colorCode);
/// <summary>Sets the fine adjust 4FSK symbol levels.</summary>
void setSymbolLvlAdj(int8_t level3Adj, int8_t level1Adj);
/// <summary>Helper to reset data values to defaults for slot 1 FIFO.</summary>
void resetFifo1();
/// <summary>Helper to reset data values to defaults for slot 2 FIFO.</summary>
void resetFifo2();
/// <summary></summary>
uint32_t getFrameCount();
private:
SerialBuffer m_fifo[2U];
DMRTXSTATE m_state;
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
uint8_t m_cachPtr;
uint8_t m_shortLC[12U];
uint8_t m_newShortLC[12U];
uint8_t m_markBuffer[40U];
uint8_t m_poBuffer[40U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint32_t m_frameCount;
uint32_t m_abortCount[2U];
bool m_abort[2U];
uint8_t m_control_old;
/// <summary></summary>
void createData(uint8_t slotIndex);
/// <summary></summary>
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
/// <summary></summary>
void writeByte(uint8_t c, uint8_t control);
};
} // namespace dmr
#endif // DUPLEX
#endif // __DMR_TX_H__

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{3733f055-083f-4bf4-9233-25d471de58d9}</ProjectGuid>
<RootNamespace>dvmfirmwarehs</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>firmware-hs</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ADF7021.h" />
<ClInclude Include="BitBuffer.h" />
<ClInclude Include="CalRSSI.h" />
<ClInclude Include="CWIdTX.h" />
<ClInclude Include="Defines.h" />
<ClInclude Include="dmr\CalDMR.h" />
<ClInclude Include="dmr\DMRDefines.h" />
<ClInclude Include="dmr\DMRDMORX.h" />
<ClInclude Include="dmr\DMRDMOTX.h" />
<ClInclude Include="dmr\DMRIdleRX.h" />
<ClInclude Include="dmr\DMRRX.h" />
<ClInclude Include="dmr\DMRSlotRX.h" />
<ClInclude Include="dmr\DMRSlotType.h" />
<ClInclude Include="dmr\DMRTX.h" />
<ClInclude Include="Globals.h" />
<ClInclude Include="IO.h" />
<ClInclude Include="p25\CalP25.h" />
<ClInclude Include="p25\P25Defines.h" />
<ClInclude Include="p25\P25RX.h" />
<ClInclude Include="p25\P25TX.h" />
<ClInclude Include="SerialPort.h" />
<ClInclude Include="SerialBuffer.h" />
<ClInclude Include="STM_UART.h" />
<ClInclude Include="Utils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ADF7021.cpp" />
<ClCompile Include="BitBuffer.cpp" />
<ClCompile Include="CalRSSI.cpp" />
<ClCompile Include="CWIdTX.cpp" />
<ClCompile Include="dmr\CalDMR.cpp" />
<ClCompile Include="dmr\DMRDMORX.cpp" />
<ClCompile Include="dmr\DMRDMOTX.cpp" />
<ClCompile Include="dmr\DMRIdleRX.cpp" />
<ClCompile Include="dmr\DMRRX.cpp" />
<ClCompile Include="dmr\DMRSlotRX.cpp" />
<ClCompile Include="dmr\DMRSlotType.cpp" />
<ClCompile Include="dmr\DMRTX.cpp" />
<ClCompile Include="FirmwareMain.cpp" />
<ClCompile Include="IO.cpp" />
<ClCompile Include="IOSTM.cpp" />
<ClCompile Include="p25\CalP25.cpp" />
<ClCompile Include="p25\P25RX.cpp" />
<ClCompile Include="p25\P25TX.cpp" />
<ClCompile Include="SerialPort.cpp" />
<ClCompile Include="SerialBuffer.cpp" />
<ClCompile Include="SerialSTM.cpp" />
<ClCompile Include="STM_UART.cpp" />
<ClCompile Include="Utils.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\dmr">
<UniqueIdentifier>{03e2c302-d472-4e33-8ab9-53296423a641}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\p25">
<UniqueIdentifier>{19296f3e-bc16-4bc8-9faf-158ab22b8dc0}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\dmr">
<UniqueIdentifier>{493de3cc-83da-49f4-ab01-a199ac989d23}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\p25">
<UniqueIdentifier>{ce2a5db1-59c6-4fdc-8774-f1d85651cd34}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="dmr\CalDMR.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRDefines.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRDMORX.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRDMOTX.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRIdleRX.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRRX.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRSlotRX.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRSlotType.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="dmr\DMRTX.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="p25\P25Defines.h">
<Filter>Header Files\p25</Filter>
</ClInclude>
<ClInclude Include="p25\P25RX.h">
<Filter>Header Files\p25</Filter>
</ClInclude>
<ClInclude Include="p25\P25TX.h">
<Filter>Header Files\p25</Filter>
</ClInclude>
<ClInclude Include="ADF7021.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BitBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CalRSSI.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CWIdTX.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Globals.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IO.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialPort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SerialBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Defines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="STM_UART.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="p25\CalP25.h">
<Filter>Header Files\p25</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dmr\CalDMR.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRDMORX.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRDMOTX.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRIdleRX.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRRX.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRSlotRX.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRSlotType.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="dmr\DMRTX.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="p25\P25RX.cpp">
<Filter>Source Files\p25</Filter>
</ClCompile>
<ClCompile Include="p25\P25TX.cpp">
<Filter>Source Files\p25</Filter>
</ClCompile>
<ClCompile Include="ADF7021.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BitBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CalRSSI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CWIdTX.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FirmwareMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IO.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IOSTM.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialPort.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialBuffer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SerialSTM.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="STM_UART.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="p25\CalP25.cpp">
<Filter>Source Files\p25</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016-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.
*/
/* Memory areas */
MEMORY
{
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* FLASH */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K /* Main RAM */
}
INCLUDE stm32f10x_link.ld

@ -0,0 +1,144 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* 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 "Globals.h"
#include "p25/CalP25.h"
using namespace p25;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// Recommended 1011 Hz test pattern for P25 Phase 1 (ANSI/TIA-102.CAAA)
// NAC: 0x293, srcID: 1, dstID: TG1
unsigned char LDU1_1K[] = { 0x00,
0x55, 0x75, 0xF5, 0xFF, 0x77, 0xFF, 0x29, 0x35, 0x54, 0x7B, 0xCB, 0x19, 0x4D, 0x0D, 0xCE, 0x24, 0xA1, 0x24,
0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB9, 0x18, 0x44, 0xFC, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xE4, 0xE2, 0x4A, 0x10,
0x90, 0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4C, 0xFC, 0x16, 0x29, 0x62, 0x76, 0x0E, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x03, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x02, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x94,
0x89, 0xD8, 0x39, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x38, 0x24, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4,
0x18, 0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x70, 0xE2, 0x4A, 0x12, 0x40,
0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x16, 0x29, 0x62, 0x76, 0x0E, 0x6D, 0xE5, 0xD5, 0x48,
0xAD, 0xE3, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x08, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x24,
0xD8, 0x3B, 0xA1, 0x41, 0xC2, 0xD2, 0xBA, 0x38, 0x90, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4, 0x60,
0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0x94, 0xC8, 0xFB, 0x02, 0x35, 0xA4, 0xE2, 0x4A, 0x12, 0x43, 0x50,
0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x58, 0x29, 0x62, 0x76, 0x0E, 0xC0, 0x00, 0x00, 0x00, 0x0C,
0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB8, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xE4 };
unsigned char LDU2_1K[] = { 0x00,
0x55, 0x75, 0xF5, 0xFF, 0x77, 0xFF, 0x29, 0x3A, 0xB8, 0xA4, 0xEF, 0xB0, 0x9A, 0x8A, 0xCE, 0x24, 0xA1, 0x24,
0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB9, 0x18, 0x44, 0xFC, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xEC, 0xE2, 0x4A, 0x10,
0x90, 0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4C, 0xFC, 0x16, 0x29, 0x62, 0x76, 0x0E, 0x40, 0x00, 0x00,
0x00, 0x00, 0x03, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x02, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x94,
0x89, 0xD8, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x24, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4,
0x18, 0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x4A, 0x12, 0x40,
0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x16, 0x29, 0x62, 0x76, 0x0E, 0xE0, 0xE0, 0x00, 0x00,
0x00, 0x03, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x08, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x24,
0xD8, 0x39, 0xAE, 0x8B, 0x48, 0xB6, 0x49, 0x38, 0x90, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4, 0x60,
0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0xB9, 0xA8, 0xF4, 0xF1, 0xFD, 0x60, 0xE2, 0x4A, 0x12, 0x43, 0x50,
0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x58, 0x29, 0x62, 0x76, 0x0E, 0x40, 0x00, 0x00, 0x00, 0x0C,
0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB8, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xEC };
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the CalP25 class.
/// </summary>
CalP25::CalP25() :
m_transmit(false),
m_state(P25CAL1K_IDLE)
{
/* stub */
}
/// <summary>
/// Process local state and transmit on the air interface.
/// </summary>
void CalP25::process()
{
if (m_modemState == STATE_P25_CAL || m_modemState == STATE_P25_LF_CAL) {
m_state = P25CAL1K_IDLE;
if (m_transmit) {
p25TX.setCal(true);
p25TX.process();
}
else {
p25TX.setCal(false);
}
}
else {
p25TX.process();
uint16_t space = p25TX.getSpace();
if (space < 1U)
return;
switch (m_state) {
case P25CAL1K_LDU1:
p25TX.writeData(LDU1_1K, P25_LDU_FRAME_LENGTH_BYTES + 1U);
m_state = P25CAL1K_LDU2;
break;
case P25CAL1K_LDU2:
p25TX.writeData(LDU2_1K, P25_LDU_FRAME_LENGTH_BYTES + 1U);
if (!m_transmit)
m_state = P25CAL1K_IDLE;
else
m_state = P25CAL1K_LDU1;
break;
default:
m_state = P25CAL1K_IDLE;
break;
}
}
}
/// <summary>
/// Write P25 calibration data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CalP25::write(const uint8_t* data, uint8_t length)
{
if (length != 1U)
return RSN_ILLEGAL_LENGTH;
m_transmit = data[0U] == 1U;
if (m_transmit && m_state == P25CAL1K_IDLE)
m_state = P25CAL1K_LDU1;
if (m_transmit)
io.rf1Conf(STATE_P25, true);
return RSN_OK;
}

@ -0,0 +1,70 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* 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(__CAL_P25_H__)
#define __CAL_P25_H__
#include "Defines.h"
#include "p25/P25Defines.h"
namespace p25
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum P25CAL1K {
P25CAL1K_IDLE,
P25CAL1K_LDU1,
P25CAL1K_LDU2
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements logic for P25 calibration mode.
// ---------------------------------------------------------------------------
class DSP_FW_API CalP25 {
public:
/// <summary>Initializes a new instance of the CalP25 class.</summary>
CalP25();
/// <summary>Process local state and transmit on the air interface.</summary>
void process();
/// <summary>Write P25 calibration state.</summary>
uint8_t write(const uint8_t* data, uint8_t length);
private:
bool m_transmit;
P25CAL1K m_state;
};
} // namespace p25
#endif // __CAL_P25_H__

@ -0,0 +1,135 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2017-2018 Bryan Biedenkapp N2PLL
*
* 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.
*/
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* 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(__P25_DEFINES_H__)
#define __P25_DEFINES_H__
#include "Defines.h"
namespace p25
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t P25_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
const uint32_t P25_HDU_FRAME_LENGTH_BYTES = 99U;
const uint32_t P25_HDU_FRAME_LENGTH_BITS = P25_HDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_HDU_FRAME_LENGTH_SYMBOLS = P25_HDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_HDU_FRAME_LENGTH_SAMPLES = P25_HDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_TDU_FRAME_LENGTH_BYTES = 18U;
const uint32_t P25_TDU_FRAME_LENGTH_BITS = P25_TDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_TDU_FRAME_LENGTH_SYMBOLS = P25_TDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_TDU_FRAME_LENGTH_SAMPLES = P25_TDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_LDU_FRAME_LENGTH_BYTES = 216U;
const uint32_t P25_LDU_FRAME_LENGTH_BITS = P25_LDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_LDU_FRAME_LENGTH_SYMBOLS = P25_LDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_LDU_FRAME_LENGTH_SAMPLES = P25_LDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_TSDU_FRAME_LENGTH_BYTES = 45U;
const uint32_t P25_TSDU_FRAME_LENGTH_BITS = P25_TSDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_TSDU_FRAME_LENGTH_SYMBOLS = P25_TSDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_TSDU_FRAME_LENGTH_SAMPLES = P25_TSDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_TDULC_FRAME_LENGTH_BYTES = 54U;
const uint32_t P25_TDULC_FRAME_LENGTH_BITS = P25_TDULC_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_TDULC_FRAME_LENGTH_SYMBOLS = P25_TDULC_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_TDULC_FRAME_LENGTH_SAMPLES = P25_TDULC_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_SYNC_LENGTH_BYTES = 6U;
const uint32_t P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U;
const uint32_t P25_SYNC_LENGTH_SYMBOLS = P25_SYNC_LENGTH_BYTES * 4U;
const uint32_t P25_SYNC_LENGTH_SAMPLES = P25_SYNC_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_NID_LENGTH_BYTES = 2U;
const uint32_t P25_NID_LENGTH_BITS = P25_NID_LENGTH_BYTES * 8U;
const uint32_t P25_NID_LENGTH_SYMBOLS = P25_NID_LENGTH_BYTES * 4U;
const uint32_t P25_NID_LENGTH_SAMPLES = P25_NID_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_BYTES = 39U;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_BITS = P25_PDU_HDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_SYMBOLS = P25_PDU_HDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_SAMPLES = P25_PDU_HDU_FRAME_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;
const uint8_t P25_START_SYNC = 0x5FU;
const uint8_t P25_NULL = 0x00U;
const uint8_t P25_TEST_PATTERN[] = { 0x55, 0xFF };
const uint8_t P25_TEST_PATTERN_LENGTH = 2U;
const ulong64_t P25_SYNC_BITS = 0x00005575F5FF77FFU;
const ulong64_t P25_SYNC_BITS_MASK = 0x0000FFFFFFFFFFFFU;
// 5 5 7 5 F 5 F F 7 7 F F
// 01 01 01 01 01 11 01 01 11 11 01 01 11 11 11 11 01 11 01 11 11 11 11 11
// +3 +3 +3 +3 +3 -3 +3 +3 -3 -3 +3 +3 -3 -3 -3 -3 +3 -3 +3 -3 -3 -3 -3 -3
const int8_t P25_SYNC_SYMBOLS_VALUES[] = { +3, +3, +3, +3, +3, -3, +3, +3, -3, -3, +3, +3, -3, -3, -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 uint32_t P25_TX_BUFFER_LEN = 2000U;
// Data Unit ID(s)
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
} // namespace p25
#endif // __P25_DEFINES_H__

@ -0,0 +1,572 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "p25/P25RX.h"
#include "Utils.h"
using namespace p25;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint8_t MAX_SYNC_BYTES_START_ERRS = 2U;
const uint8_t MAX_SYNC_BYTES_ERRS = 4U;
const uint16_t MAX_SYNC_FRAMES = 7U;
const uint8_t CORRELATION_COUNTDOWN = 6U;
const uint16_t NOENDPTR = 9999U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the P25RX class.
/// </summary>
P25RX::P25RX() :
m_bitBuffer(0x00U),
m_buffer(),
m_dataPtr(0U),
m_minSyncPtr(0U),
m_maxSyncPtr(NOENDPTR),
m_startPtr(0U),
m_endPtr(NOENDPTR),
m_syncPtr(0U),
m_lostCount(0U),
m_countdown(0U),
m_nac(0xF7EU),
m_corrCountdown(CORRELATION_COUNTDOWN),
m_state(P25RXS_NONE),
m_duid(0xFFU)
{
/* stub */
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void P25RX::reset()
{
m_bitBuffer = 0x00U;
m_dataPtr = 0U;
m_minSyncPtr = 0U;
m_maxSyncPtr = NOENDPTR;
m_startPtr = 0U;
m_endPtr = NOENDPTR;
m_syncPtr = 0U;
m_lostCount = 0U;
m_countdown = 0U;
m_state = P25RXS_NONE;
m_duid = 0xFFU;
}
/// <summary>
/// Sample P25 bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void P25RX::databit(bool bit)
{
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
if (m_state == P25RXS_SYNC) {
processBit(bit);
}
else if (m_state == P25RXS_VOICE) {
processVoice(bit);
}
else if (m_state == P25RXS_DATA) {
processData(bit);
}
else {
bool ret = correlateSync();
if (ret) {
// on the first sync, start the countdown to the state change
if (m_countdown == 0U) {
io.setDecode(true);
m_countdown = m_corrCountdown;
DEBUG2("P25RX: databit(): correlation countdown", m_countdown);
}
}
if (m_countdown > 0U)
m_countdown--;
if (m_countdown == 1U) {
m_minSyncPtr = m_syncPtr + P25_HDU_FRAME_LENGTH_BITS - 1U;
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_maxSyncPtr = m_syncPtr + P25_HDU_FRAME_LENGTH_BITS + 1U;
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_state = P25RXS_SYNC;
m_countdown = 0U;
}
}
m_dataPtr++;
if (m_dataPtr >= P25_LDU_FRAME_LENGTH_BITS) {
m_duid = 0xFFU;
m_dataPtr = 0U;
}
}
/// <summary>
/// Sets the P25 NAC.
/// </summary>
/// <param name="nac">NAC.</param>
void P25RX::setNAC(uint16_t nac)
{
m_nac = nac;
}
/// <summary>
/// Sets the P25 sync correlation countdown.
/// </summary>
/// <param name="count">Correlation Countdown Count.</param>
void P25RX::setCorrCount(uint8_t count)
{
m_corrCountdown = count;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to process P25 samples.
/// </summary>
/// <param name="bit"></param>
void P25RX::processBit(bool bit)
{
if (m_minSyncPtr < m_maxSyncPtr) {
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
else {
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
// initial sample processing does not have an end pointer -- we simply wait till we've read
// the bits up to the maximum sync pointer
if (m_dataPtr == m_maxSyncPtr) {
DEBUG4("P25RX: processBit(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_maxSyncPtr);
DEBUG4("P25RX: processBit(): lostCount/maxSyncPtr/minSyncPtr", m_lostCount, m_maxSyncPtr, m_minSyncPtr);
if (!decodeNid(m_startPtr)) {
io.setDecode(false);
serial.writeP25Lost();
reset();
}
else {
switch (m_duid) {
case P25_DUID_HDU:
{
DEBUG1("P25RX: processBit(): sync found in HDU pos", m_syncPtr);
uint8_t frame[P25_HDU_FRAME_LENGTH_BYTES + 1U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_HDU_FRAME_LENGTH_BITS, frame);
frame[0U] = 0x01U;
serial.writeP25Data(frame, P25_HDU_FRAME_LENGTH_BYTES + 1U);
reset();
}
break;
case P25_DUID_TDU:
{
DEBUG1("P25RX: processBit(): sync found in TDU pos", m_syncPtr);
uint8_t frame[P25_TDU_FRAME_LENGTH_BYTES + 1U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_TDU_FRAME_LENGTH_BITS, frame);
frame[0U] = 0x01U;
serial.writeP25Data(frame, P25_TDU_FRAME_LENGTH_BYTES + 1U);
reset();
}
break;
case P25_DUID_LDU1:
m_state = P25RXS_VOICE;
return;
case P25_DUID_TSDU:
{
DEBUG1("P25RX: processBit(): sync found in TSDU pos", m_syncPtr);
uint8_t frame[P25_TSDU_FRAME_LENGTH_BYTES + 1U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_TSDU_FRAME_LENGTH_BITS, frame);
frame[0U] = 0x01U;
serial.writeP25Data(frame, P25_TSDU_FRAME_LENGTH_BYTES + 1U);
reset();
}
break;
case P25_DUID_LDU2:
m_state = P25RXS_VOICE;
return;
case P25_DUID_PDU:
m_state = P25RXS_DATA;
return;
case P25_DUID_TDULC:
{
DEBUG1("P25RX: processBit(): sync found in TDULC pos", m_syncPtr);
uint8_t frame[P25_TDULC_FRAME_LENGTH_BYTES + 1U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_TDULC_FRAME_LENGTH_BITS, frame);
frame[0U] = 0x01U;
serial.writeP25Data(frame, P25_TDULC_FRAME_LENGTH_BYTES + 1U);
reset();
}
break;
default:
{
DEBUG3("P25RX: processBit(): illegal DUID in NID", m_nac, m_duid);
reset();
}
return;
}
}
}
m_minSyncPtr = m_syncPtr + P25_LDU_FRAME_LENGTH_BITS - 1U;
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_maxSyncPtr = m_syncPtr + 1U;
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_lostCount = MAX_SYNC_FRAMES;
if (m_state == P25RXS_VOICE) {
processVoice(bit);
}
if (m_state == P25RXS_DATA) {
processData(bit);
}
}
/// <summary>
/// Helper to process LDU P25 bits.
/// </summary>
/// <param name="bit"></param>
void P25RX::processVoice(bool bit)
{
if (m_minSyncPtr < m_maxSyncPtr) {
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
else {
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
if (m_dataPtr == m_endPtr) {
if (m_lostCount == MAX_SYNC_FRAMES) {
m_minSyncPtr = m_syncPtr + P25_LDU_FRAME_LENGTH_BITS - 1U;
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_maxSyncPtr = m_syncPtr + 1U;
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
}
m_lostCount--;
DEBUG4("P25RX: processVoice(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
DEBUG4("P25RX: processVoice(): lostCount/maxSyncPtr/minSyncPtr", m_lostCount, m_maxSyncPtr, m_minSyncPtr);
// we've not seen a data sync for too long, signal sync lost and change to P25RXS_NONE
if (m_lostCount == 0U) {
DEBUG1("P25RX: processVoice(): sync timeout in LDU, lost lock");
io.setDecode(false);
serial.writeP25Lost();
reset();
}
else {
if (!decodeNid(m_startPtr)) {
io.setDecode(false);
serial.writeP25Lost();
reset();
}
else {
if (m_duid == P25_DUID_TDU) {
DEBUG1("P25RX: processBit(): sync found in TDU pos", m_syncPtr);
uint8_t frame[P25_TDU_FRAME_LENGTH_BYTES + 1U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_TDU_FRAME_LENGTH_BITS, frame);
frame[0U] = 0x01U;
serial.writeP25Data(frame, P25_TDU_FRAME_LENGTH_BYTES + 1U);
io.setDecode(false);
reset();
return;
}
DEBUG1("P25RX: processVoice(): sync found in LDU pos", m_syncPtr);
uint8_t frame[P25_LDU_FRAME_LENGTH_BYTES + 3U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_LDU_FRAME_LENGTH_BITS, frame);
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
#if defined(SEND_RSSI_DATA)
if (m_rssiCount > 0U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
frame[217U] = (rssi >> 8) & 0xFFU;
frame[218U] = (rssi >> 0) & 0xFFU;
serial.writeP25Data(false, frame, P25_LDU_FRAME_LENGTH_BYTES + 3U);
}
else {
serial.writeP25Data(false, frame, P25_LDU_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeP25Data(frame, P25_LDU_FRAME_LENGTH_BYTES + 1U);
#endif
}
}
}
}
/// <summary>
/// Helper to process PDU P25 bits.
/// </summary>
/// <param name="bit"></param>
void P25RX::processData(bool bit)
{
if (m_minSyncPtr < m_maxSyncPtr) {
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
else {
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
if (m_dataPtr == m_endPtr) {
if (m_lostCount == MAX_SYNC_FRAMES) {
m_minSyncPtr = m_syncPtr + P25_LDU_FRAME_LENGTH_BITS - 1U;
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_maxSyncPtr = m_syncPtr + 1U;
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_BITS;
}
m_lostCount--;
DEBUG4("P25RX: processData(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
DEBUG4("P25RX: processData(): lostCount/maxSyncPtr/minSyncPtr", m_lostCount, m_maxSyncPtr, m_minSyncPtr);
// we've not seen a data sync for too long, signal sync lost and change to P25RXS_NONE
if (m_lostCount == 0U) {
DEBUG1("P25RX: processData(): sync timeout in PDU, lost lock");
io.setDecode(false);
serial.writeP25Lost();
reset();
}
else {
if (!decodeNid(m_startPtr)) {
io.setDecode(false);
serial.writeP25Lost();
reset();
}
else {
DEBUG1("P25RX: processVoice(): sync found in PDU pos", m_syncPtr);
uint8_t frame[P25_LDU_FRAME_LENGTH_BYTES + 3U];
bitsToBytes(m_startPtr + P25_NID_LENGTH_BITS, P25_LDU_FRAME_LENGTH_BITS, frame);
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
#if defined(SEND_RSSI_DATA)
if (m_rssiCount > 0U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
frame[217U] = (rssi >> 8) & 0xFFU;
frame[218U] = (rssi >> 0) & 0xFFU;
serial.writeP25Data(false, frame, P25_LDU_FRAME_LENGTH_BYTES + 3U);
}
else {
serial.writeP25Data(false, frame, P25_LDU_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeP25Data(frame, P25_LDU_FRAME_LENGTH_BYTES + 1U);
#endif
}
}
}
}
/// <summary>
/// Frame synchronization correlator.
/// </summary>
/// <returns></returns>
bool P25RX::correlateSync()
{
uint8_t maxErrs;
if (m_state == P25RXS_NONE)
maxErrs = MAX_SYNC_BYTES_START_ERRS;
else
maxErrs = MAX_SYNC_BYTES_ERRS;
uint8_t errs = countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS);
if (errs <= maxErrs) {
DEBUG2("P25RX: correlateSync(): correlateSync errs", errs);
m_syncPtr = m_dataPtr;
m_startPtr = m_dataPtr + P25_LDU_FRAME_LENGTH_BITS - P25_SYNC_LENGTH_BITS + 1;
if (m_startPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_startPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_endPtr = m_dataPtr + P25_LDU_FRAME_LENGTH_BITS - P25_SYNC_LENGTH_BITS - 1U;
if (m_endPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_endPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_lostCount = MAX_SYNC_FRAMES;
DEBUG4("P25RX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
return true;
}
return false;
}
/// <summary>
/// Helper to decode the P25 NID.
/// </summary>
/// <param name="start"></param>
bool P25RX::decodeNid(uint16_t start)
{
uint16_t nidStartPtr = start + P25_SYNC_LENGTH_BITS;
if (nidStartPtr >= P25_LDU_FRAME_LENGTH_BITS)
nidStartPtr -= P25_LDU_FRAME_LENGTH_BITS;
uint8_t nid[P25_NID_LENGTH_BYTES];
bitsToBytes(m_startPtr, P25_NID_LENGTH_BITS, nid);
if (m_nac == 0xF7EU) {
m_duid = nid[1U] & 0x0FU;
DEBUG2("P25RX: decodeNid(): DUID for xDU", m_duid);
return true;
}
uint16_t nac = (nid[0U] << 4) | ((nid[1U] & 0xF0U) >> 4);
if (nac == m_nac) {
m_duid = nid[1U] & 0x0FU;
DEBUG2("P25RX: decodeNid(): DUID for xDU", m_duid);
return true;
}
else {
DEBUG3("P25RX: decodeNid(): invalid NAC found; nac != m_nac", nac, m_nac);
}
return false;
}
/// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <param name="buffer"></param>
void P25RX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{
for (uint8_t i = 0U; i < count; i++) {
buffer[i] = 0U;
buffer[i] |= _READ_BIT(m_buffer, start) << 7;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 6;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 5;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 4;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 3;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 2;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 1;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
buffer[i] |= _READ_BIT(m_buffer, start) << 0;
start++;
if (start >= P25_LDU_FRAME_LENGTH_BITS)
start -= P25_LDU_FRAME_LENGTH_BITS;
}
}

@ -0,0 +1,114 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__P25_RX_H__)
#define __P25_RX_H__
#include "Defines.h"
#include "p25/P25Defines.h"
namespace p25
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum P25RX_STATE {
P25RXS_NONE,
P25RXS_SYNC,
P25RXS_VOICE,
P25RXS_DATA
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for P25 mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API P25RX {
public:
/// <summary>Initializes a new instance of the P25RX class.</summary>
P25RX();
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Sample P25 bits from the air interface.</summary>
void databit(bool bit);
/// <summary>Sets the P25 NAC.</summary>
void setNAC(uint16_t nac);
/// <summary>Sets the P25 sync correlation countdown.</summary>
void setCorrCount(uint8_t count);
private:
uint64_t m_bitBuffer;
uint8_t m_buffer[P25_LDU_FRAME_LENGTH_BITS / 8U + 3U];
uint16_t m_dataPtr;
uint16_t m_minSyncPtr;
uint16_t m_maxSyncPtr;
uint16_t m_startPtr;
uint16_t m_endPtr;
uint16_t m_syncPtr;
uint16_t m_lostCount;
uint8_t m_countdown;
uint16_t m_nac;
uint8_t m_corrCountdown;
P25RX_STATE m_state;
uint8_t m_duid;
/// <summary>Helper to process P25 bits.</summary>
void processBit(bool bit);
/// <summary>Helper to process LDU P25 bits.</summary>
void processVoice(bool bit);
/// <summary>Helper to process PDU P25 bits.</summary>
void processData(bool bit);
/// <summary>Frame synchronization correlator.</summary>
bool correlateSync();
/// <summary>Helper to decode the P25 NID.</summary>
bool decodeNid(uint16_t start);
/// <summary></summary>
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
};
} // namespace p25
#endif // __P25_RX_H__

@ -0,0 +1,279 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Andy Uribe CA6JAU
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "p25/P25TX.h"
#include "p25/P25Defines.h"
using namespace p25;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the P25TX class.
/// </summary>
P25TX::P25TX() :
m_fifo(P25_TX_BUFFER_LEN),
m_state(P25TXSTATE_NORMAL),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_preambleCnt(P25_FIXED_DELAY),
m_tailCnt(0U)
{
/* stub */
}
/// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void P25TX::process()
{
if (m_fifo.getData() == 0U && m_poLen == 0U && m_tailCnt > 0U &&
m_state != P25TXSTATE_CAL) {
// transmit silence until the hang timer has expired
uint16_t space = io.getSpace();
while (space > 8U) {
writeByte(P25_START_SYNC);
space -= 8U;
m_tailCnt--;
if (m_tailCnt == 0U)
return;
if (m_fifo.getData() > 0U) {
m_tailCnt = 0U;
return;
}
}
if (m_fifo.getData() == 0U && m_poLen == 0U)
return;
}
if (m_poLen == 0U) {
if (m_state == P25TXSTATE_CAL) {
m_tailCnt = 0U;
createCal();
}
else {
if (m_fifo.getData() == 0U)
return;
createData();
}
DEBUG2("P25TX: process(): poLen", m_poLen);
}
if (m_poLen > 0U) {
uint16_t space = io.getSpace();
while (space > 8U) {
uint8_t c = m_poBuffer[m_poPtr++];
writeByte(c);
space -= 8U;
m_tailCnt = P25_FIXED_TAIL;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
}
}
/// <summary>
/// Write data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t P25TX::writeData(const uint8_t* data, uint8_t length)
{
if (length < (P25_TDU_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo.getSpace();
DEBUG3("P25TX: writeData(): dataLength/fifoLength", length, space);
if (space < length) {
m_fifo.reset();
return RSN_RINGBUFF_FULL;
}
m_fifo.put(length - 1U);
for (uint8_t i = 0U; i < (length - 1U); i++)
m_fifo.put(data[i + 1U]);
return RSN_OK;
}
/// <summary>
/// Clears the local buffer.
/// </summary>
void P25TX::clear()
{
m_fifo.reset();
}
/// <summary>
/// Sets the FDMA preamble count.
/// </summary>
/// <param name="preambleCnt">Count of preambles.</param>
void P25TX::setPreambleCount(uint8_t preambleCnt)
{
uint32_t preambles = (uint32_t)((float)preambleCnt / 0.2083F);
m_preambleCnt = P25_FIXED_DELAY + preambles;
// clamp preamble count to 250ms maximum
if (m_preambleCnt > 1200U)
m_preambleCnt = 1200U;
}
/// <summary>
/// Sets the fine adjust 4FSK symbol levels.
/// </summary>
/// <param name="level3Adj">+3/-3 symbol adjust.</param>
/// <param name="level1Adj">+1/-1 symbol adjust.</param>
void P25TX::setSymbolLvlAdj(int8_t level3Adj, int8_t level1Adj)
{
/* ignored ADF7021 doesn't allow direct symbol level adjustments */
}
/// <summary>
/// Helper to set the calibration state for Tx.
/// </summary>
/// <param name="start"></param>
void P25TX::setCal(bool start)
{
m_state = start ? P25TXSTATE_CAL : P25TXSTATE_NORMAL;
}
/// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint8_t P25TX::getSpace() const
{
return m_fifo.getSpace() / P25_LDU_FRAME_LENGTH_BYTES;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
void P25TX::createData()
{
if (!m_tx) {
for (uint16_t i = 0U; i < m_preambleCnt; i++)
m_poBuffer[m_poLen++] = P25_START_SYNC;
}
else {
uint8_t length = m_fifo.get();
DEBUG3("P25TX: createData(): dataLength/fifoSpace", length, m_fifo.getSpace());
for (uint8_t i = 0U; i < length; i++) {
m_poBuffer[m_poLen++] = m_fifo.get();
}
}
m_poPtr = 0U;
}
/// <summary>
///
/// </summary>
void P25TX::createCal()
{
// 1.2 kHz sine wave generation
if (m_modemState == STATE_P25_CAL) {
for (unsigned int i = 0U; i < P25_LDU_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = P25_START_SYNC;
}
m_poLen = P25_LDU_FRAME_LENGTH_BYTES;
}
// 80 Hz square wave generation
if (m_modemState == STATE_P25_LF_CAL) {
for (unsigned int i = 0U; i < 108U; i++) {
m_poBuffer[i] = 0x55U; // +3, +3, ... pattern
}
m_poBuffer[109U] = 0x5FU; // +3, +3, -3, -3 pattern
for (unsigned int i = 110U; i < 216U; i++) {
m_poBuffer[i] = 0xFFU; // -3, -3, ... pattern
}
m_poLen = P25_LDU_FRAME_LENGTH_BYTES;
}
m_poLen = P25_LDU_FRAME_LENGTH_BYTES;
m_poPtr = 0U;
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
void P25TX::writeByte(uint8_t c)
{
uint8_t bit;
uint8_t mask = 0x80U;
for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
if ((c & mask) == mask)
bit = 1U;
else
bit = 0U;
io.write(&bit, 1);
}
}
/// <summary>
///
/// </summary>
void P25TX::writeSilence()
{
uint8_t bit;
for (uint8_t i = 0U; i < 4U; i++) {
bit = 0U;
io.write(&bit, 1);
}
}

@ -0,0 +1,106 @@
/**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Andy Uribe CA6JAU
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* 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(__P25_TX_H__)
#define __P25_TX_H__
#include "Defines.h"
#include "SerialBuffer.h"
namespace p25
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define P25_FIXED_DELAY 300 // 300 = 62.49ms
// Delay Value * 0.2083 = Preamble Length (ms)
#define P25_FIXED_TAIL 600 // 600 = 500ms
enum P25TXSTATE {
P25TXSTATE_NORMAL,
P25TXSTATE_CAL
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements transmitter logic for P25 mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API P25TX {
public:
/// <summary>Initializes a new instance of the P25TX class.</summary>
P25TX();
/// <summary>Process local buffer and transmit on the air interface.</summary>
void process();
/// <summary>Write data to the local buffer.</summary>
uint8_t writeData(const uint8_t* data, uint8_t length);
/// <summary>Clears the local buffer.</summary>
void clear();
/// <summary>Sets the FDMA preamble count.</summary>
void setPreambleCount(uint8_t preambleCnt);
/// <summary>Sets the fine adjust 4FSK symbol levels.</summary>
void setSymbolLvlAdj(int8_t level3Adj, int8_t level1Adj);
/// <summary>Helper to set the calibration state for Tx.</summary>
void setCal(bool start);
/// <summary>Helper to get how much space the ring buffer has for samples.</summary>
uint8_t getSpace() const;
private:
SerialBuffer m_fifo;
P25TXSTATE m_state;
uint8_t m_poBuffer[1200U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint16_t m_preambleCnt;
uint16_t m_tailCnt;
/// <summary></summary>
void createData();
/// <summary></summary>
void createCal();
/// <summary></summary>
void writeByte(uint8_t c);
/// <summary></summary>
void writeSilence();
};
} // namespace p25
#endif // __P25_TX_H__

@ -0,0 +1,130 @@
/*
* Copyright (C) 2016 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.
*/
/* Required amount of heap and stack */
_min_heap_size = 0x1000;
_min_stack_size = 0x0800;
/* The entry point in the interrupt vector table */
ENTRY(Reset_Handler)
/* Stack start address (end of 20K RAM) */
_estack = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS
{
.text :
{
/* The interrupt vector table */
. = ALIGN(4);
KEEP(*(.isr_vector .isr_vector.*))
/* The program code */
. = ALIGN(4);
*(.text .text*)
*(.rodata .rodata*)
/* ARM-Thumb code */
*(.glue_7) *(.glue_7t)
. = ALIGN(4);
KEEP(*(.init))
KEEP(*(.fini))
/* EABI C++ global constructors support */
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
/* EABI C++ global constructors support */
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
/* EABI C++ global constructors support */
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
} > ROM
/* ARM sections containing exception unwinding information */
.ARM.extab : {
__extab_start = .;
*(.ARM.extab* .gnu.linkonce.armextab.*)
__extab_end = .;
} > ROM
/* ARM index entries for section unwinding */
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > ROM
/* Start address for the initialization values of the .data section */
_sidata = .;
/* The .data section (initialized data) */
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = . ; /* Start address for the .data section */
*(.data .data*)
. = ALIGN(4);
_edata = . ; /* End address for the .data section */
} > RAM
/* The .bss section (uninitialized data) */
.bss :
{
. = ALIGN(4);
_sbss = .; /* Start address for the .bss section */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = . ; /* End address for the .bss section */
__bss_end__ = _ebss;
} > RAM
/* Space for heap and stack */
.heap_stack :
{
end = . ; /* 'end' symbol defines heap location */
_end = end ;
. = . + _min_heap_size; /* Additional space for heap and stack */
. = . + _min_stack_size;
} > RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}

@ -0,0 +1,138 @@
/*
* Copyright (C) 2016 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.
*/
/* Required amount of heap and stack */
_min_heap_size = 0x1000;
_min_stack_size = 0x0800;
/* The entry point in the interrupt vector table */
ENTRY(Reset_Handler)
/* Memory areas */
MEMORY
{
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* FLASH */
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K /* Core Coupled Memory (CPU only access) */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* Main RAM (bus matrix)*/
}
/* Stack start address (end of 128K RAM) */
_estack = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS
{
.text :
{
/* The interrupt vector table */
. = ALIGN(4);
KEEP(*(.isr_vector .isr_vector.*))
/* The program code */
. = ALIGN(4);
*(.text .text*)
*(.rodata .rodata*)
/* ARM-Thumb code */
*(.glue_7) *(.glue_7t)
. = ALIGN(4);
KEEP(*(.init))
KEEP(*(.fini))
/* EABI C++ global constructors support */
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
/* EABI C++ global constructors support */
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
/* EABI C++ global constructors support */
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
} > ROM
/* ARM sections containing exception unwinding information */
.ARM.extab : {
__extab_start = .;
*(.ARM.extab* .gnu.linkonce.armextab.*)
__extab_end = .;
} > ROM
/* ARM index entries for section unwinding */
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > ROM
/* Start address for the initialization values of the .data section */
_sidata = .;
/* The .data section (initialized data) */
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = . ; /* Start address for the .data section */
*(.data .data*)
. = ALIGN(4);
_edata = . ; /* End address for the .data section */
} > RAM
/* The .bss section (uninitialized data) */
.bss :
{
. = ALIGN(4);
_sbss = .; /* Start address for the .bss section */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = . ; /* End address for the .bss section */
__bss_end__ = _ebss;
} > RAM
/* Space for heap and stack */
.heap_stack :
{
end = . ; /* 'end' symbol defines heap location */
_end = end ;
. = . + _min_heap_size; /* Additional space for heap and stack */
. = . + _min_stack_size;
} > RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}

@ -0,0 +1,137 @@
/*
* 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.
*/
/* Required amount of heap and stack */
_min_heap_size = 0x1000;
_min_stack_size = 0x0800;
/* The entry point in the interrupt vector table */
ENTRY(Reset_Handler)
/* Memory areas */
MEMORY
{
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* FLASH */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 512K /* Main RAM */
}
/* Stack start address (end of 512K RAM) */
_estack = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS
{
.text :
{
/* The interrupt vector table */
. = ALIGN(4);
KEEP(*(.isr_vector .isr_vector.*))
/* The program code */
. = ALIGN(4);
*(.text .text*)
*(.rodata .rodata*)
/* ARM-Thumb code */
*(.glue_7) *(.glue_7t)
. = ALIGN(4);
KEEP(*(.init))
KEEP(*(.fini))
/* EABI C++ global constructors support */
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
/* EABI C++ global constructors support */
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
/* EABI C++ global constructors support */
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
} > ROM
/* ARM sections containing exception unwinding information */
.ARM.extab : {
__extab_start = .;
*(.ARM.extab* .gnu.linkonce.armextab.*)
__extab_end = .;
} > ROM
/* ARM index entries for section unwinding */
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} > ROM
/* Start address for the initialization values of the .data section */
_sidata = .;
/* The .data section (initialized data) */
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = . ; /* Start address for the .data section */
*(.data .data*)
. = ALIGN(4);
_edata = . ; /* End address for the .data section */
} > RAM
/* The .bss section (uninitialized data) */
.bss :
{
. = ALIGN(4);
_sbss = .; /* Start address for the .bss section */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = . ; /* End address for the .bss section */
__bss_end__ = _ebss;
} > RAM
/* Space for heap and stack */
.heap_stack :
{
end = . ; /* 'end' symbol defines heap location */
_end = end ;
. = . + _min_heap_size; /* Additional space for heap and stack */
. = . + _min_stack_size;
} > RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.