├── .gitignore ├── CMakeLists.txt ├── Dockerfile ├── LICENSE.md ├── README.md ├── docker_build.sh ├── docs └── infos_for_sensors.txt ├── legacy-code ├── f_rtty.c ├── f_rtty.h └── main_old.txt ├── openocd_rs41.cfg ├── src ├── CMakeLists.txt ├── arm-gcc-link.ld ├── bmp280_handler.c ├── bmp280_handler.h ├── codecs │ ├── aprs │ │ ├── aprs.c │ │ ├── aprs.h │ │ ├── aprs_position.c │ │ ├── aprs_position.h │ │ ├── aprs_weather.c │ │ └── aprs_weather.h │ ├── ax25 │ │ ├── ax25.c │ │ └── ax25.h │ ├── bell │ │ ├── bell.c │ │ └── bell.h │ ├── cats │ │ ├── cats.c │ │ ├── cats.h │ │ ├── crc.c │ │ ├── crc.h │ │ ├── interleaver.c │ │ ├── interleaver.h │ │ ├── ldpc.c │ │ ├── ldpc.h │ │ ├── ldpc_matrices.c │ │ ├── ldpc_matrices.h │ │ ├── whisker.c │ │ ├── whisker.h │ │ ├── whiten.c │ │ └── whiten.h │ ├── fsk │ │ └── fsk.h │ ├── horus │ │ ├── horus_common.c │ │ ├── horus_common.h │ │ ├── horus_l2.c │ │ ├── horus_l2.h │ │ ├── horus_packet_v1.c │ │ ├── horus_packet_v1.h │ │ ├── horus_packet_v2.c │ │ └── horus_packet_v2.h │ ├── jtencode │ │ ├── jtencode.cpp │ │ ├── jtencode.h │ │ └── lib │ │ │ ├── COPYING │ │ │ ├── JTEncode.cpp │ │ │ ├── JTEncode.h │ │ │ ├── README.md │ │ │ ├── crc14.c │ │ │ ├── crc14.h │ │ │ ├── encode_rs.h │ │ │ ├── encode_rs_int.cpp │ │ │ ├── generator.h │ │ │ ├── init_rs.h │ │ │ ├── init_rs_int.cpp │ │ │ ├── int.h │ │ │ └── rs_common.h │ ├── mfsk │ │ ├── mfsk.c │ │ └── mfsk.h │ ├── morse │ │ ├── morse.c │ │ └── morse.h │ └── raw │ │ ├── raw.c │ │ └── raw.h ├── config.c ├── config.h ├── config_internal.h ├── drivers │ ├── bmp280 │ │ ├── bmp280.c │ │ └── bmp280.h │ ├── pulse_counter │ │ ├── pulse_counter.c │ │ └── pulse_counter.h │ ├── radsens │ │ ├── radsens.cpp │ │ └── radsens.h │ ├── si4032 │ │ ├── si4032.c │ │ └── si4032.h │ ├── si4063 │ │ ├── si4063.c │ │ └── si4063.h │ ├── si5351 │ │ ├── README.md │ │ ├── licence.txt │ │ ├── si5351.cpp │ │ └── si5351.h │ ├── si5351fast │ │ ├── si5351mcu.cpp │ │ └── si5351mcu.h │ └── ubxg6010 │ │ ├── ubxg6010.c │ │ └── ubxg6010.h ├── gpio.h ├── gps.h ├── hal │ ├── clock_calibration.c │ ├── clock_calibration.h │ ├── cmsis │ │ ├── core_cm3.h │ │ ├── core_cmFunc.h │ │ └── core_cmInstr.h │ ├── cmsis_boot │ │ ├── startup │ │ │ └── startup_stm32f10x_md_vl.c │ │ ├── stm32f10x.h │ │ ├── stm32f10x_conf.h │ │ ├── system_stm32f10x.c │ │ └── system_stm32f10x.h │ ├── datatimer.c │ ├── datatimer.h │ ├── delay.c │ ├── delay.h │ ├── hal.h │ ├── i2c.c │ ├── i2c.h │ ├── millis.c │ ├── millis.h │ ├── pwm.c │ ├── pwm.h │ ├── spi.c │ ├── spi.h │ ├── stm_lib │ │ ├── inc │ │ │ ├── misc.h │ │ │ ├── stm32f10x_adc.h │ │ │ ├── stm32f10x_dma.h │ │ │ ├── stm32f10x_exti.h │ │ │ ├── stm32f10x_flash.h │ │ │ ├── stm32f10x_gpio.h │ │ │ ├── stm32f10x_i2c.h │ │ │ ├── stm32f10x_pwr.h │ │ │ ├── stm32f10x_rcc.h │ │ │ ├── stm32f10x_spi.h │ │ │ ├── stm32f10x_tim.h │ │ │ └── stm32f10x_usart.h │ │ └── src │ │ │ ├── misc.c │ │ │ ├── stm32f10x_adc.c │ │ │ ├── stm32f10x_dma.c │ │ │ ├── stm32f10x_exti.c │ │ │ ├── stm32f10x_flash.c │ │ │ ├── stm32f10x_gpio.c │ │ │ ├── stm32f10x_i2c.c │ │ │ ├── stm32f10x_pwr.c │ │ │ ├── stm32f10x_rcc.c │ │ │ ├── stm32f10x_spi.c │ │ │ ├── stm32f10x_tim.c │ │ │ └── stm32f10x_usart.c │ ├── system.c │ ├── system.h │ ├── usart_ext.c │ ├── usart_ext.h │ ├── usart_gps.c │ └── usart_gps.h ├── locator.c ├── locator.h ├── log.c ├── log.h ├── main.c ├── payload.h ├── radio.c ├── radio.h ├── radio_internal.h ├── radio_payload_aprs_position.c ├── radio_payload_aprs_position.h ├── radio_payload_aprs_weather.c ├── radio_payload_aprs_weather.h ├── radio_payload_cats.c ├── radio_payload_cats.h ├── radio_payload_cw.c ├── radio_payload_cw.h ├── radio_payload_fsq.c ├── radio_payload_fsq.h ├── radio_payload_horus_v1.c ├── radio_payload_horus_v1.h ├── radio_payload_horus_v2.c ├── radio_payload_horus_v2.h ├── radio_payload_jtencode.c ├── radio_payload_jtencode.h ├── radio_payload_wspr.c ├── radio_payload_wspr.h ├── radio_si4032.c ├── radio_si4032.h ├── radio_si4063.c ├── radio_si4063.h ├── radio_si5351.c ├── radio_si5351.h ├── radsens_handler.cpp ├── radsens_handler.h ├── si5351_handler.cpp ├── si5351_handler.h ├── si5351_test.cpp ├── si5351_test.h ├── strlcpy.c ├── strlcpy.h ├── syscalls │ ├── semihosting.c │ ├── semihosting.h │ └── syscalls.c ├── telemetry.c ├── telemetry.h ├── template.c ├── template.h ├── utils.c └── utils.h └── tests ├── CMakeLists.txt ├── bell_test.c ├── morse_test.c └── template_test.c /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /cmake-build-debug 3 | /build 4 | /samples 5 | *~ 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(RS41ng_top) 3 | 4 | SET(CMAKE_SYSTEM_NAME "Generic") 5 | SET(CMAKE_SYSTEM_VERSION 1) 6 | 7 | SET(UNIX 1) 8 | 9 | include_directories(src) 10 | link_directories(src) 11 | add_subdirectory(src) 12 | 13 | add_subdirectory(tests) 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:36 2 | 3 | RUN dnf install -y \ 4 | gcc-c++ \ 5 | arm-none-eabi-gcc-cs \ 6 | arm-none-eabi-gcc-cs-c++ \ 7 | arm-none-eabi-binutils-cs \ 8 | arm-none-eabi-newlib \ 9 | cmake 10 | 11 | COPY docker_build.sh /build.sh 12 | RUN chmod +x /build.sh 13 | 14 | ENTRYPOINT ["/bin/bash", "/build.sh"] 15 | -------------------------------------------------------------------------------- /docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to compile RS41ng on any platform using Docker 4 | # This script is copied into the Docker image when it is built. 5 | # If you plan to change this script, please make sure to rebuild the Docker image. 6 | 7 | set -e # Exit if failed 8 | 9 | SOURCE_PATH="/usr/local/src/RS41ng" 10 | 11 | # Sanity check 12 | if [ ! -d "${SOURCE_PATH}" ] 13 | then 14 | echo "Source directory does not exist, please run the Docker command given in README to mount the source directory" 15 | exit 1 16 | fi 17 | 18 | # Build RS41ng 19 | cd "${SOURCE_PATH}" 20 | rm -rf build 21 | mkdir -p build 22 | cd build 23 | cmake .. 24 | make -j$(nproc) 25 | 26 | echo "RS41ng build complete" 27 | -------------------------------------------------------------------------------- /docs/infos_for_sensors.txt: -------------------------------------------------------------------------------- 1 | DF8OEs investigations about sensor access 2 | ------------------------------------------ 3 | 4 | 5 | 6 | Temperature and humidity sensor directly goes to STM. There is no interface 7 | chip to handle them - all is done by software. 8 | 9 | 10 | 11 | Pinout of sensor boom: 12 | ---------------------- 13 | 1 - GND 14 | 2 - temperature sensor (1) 15 | 3 - GND 16 | 4 - humidity sensor1 (1) 17 | 5 - GND 18 | 6 - humidity sensor1 (2) 19 | 7 - GND 20 | 8 - heater humidity sensor 21 | 9 - heater humidity sensor 22 | 10 - GND 23 | 11 - humidity sensor2 (1) 24 | 12 - GND 25 | 13 - temperature sensor (2) and humidity sensor2 (2) 26 | 14 - GND 27 | 15 - GND 28 | 16 - GND 29 | 17 - GND 30 | 18 - GND 31 | 19 - GND 32 | 20 - GND 33 | 34 | Counted from left to right if you position flexsensor in front of you, pins near 35 | to you, sensor away from you, copper upside 36 | 37 | 38 | 39 | All connections leading to sensor boom do have corresponding test points on PCB: 40 | -------------------------------------------------------------------------------- 41 | Position PCB in front of you, flatconnector upside, sensor boom showing away from 42 | you, TX antenna leading to you. I draw connector as a row of "+++". Testpoints are 43 | small round copper isles (numbers correspond to sensor boom, G == GND, ? == unknown): 44 | 45 | 46 | 47 | 48 | 11 49 | +++++++++++++++++++++++ 50 | 51 | G 4 52 | 53 | 54 | 8 6 2 55 | 56 | 57 | 13 ? 9 58 | 59 | 60 | Investigations about temperature sensor: 61 | ---------------------------------------- 62 | Temperature sensor is resistance-based. If you insert a resistor between 63 | pin2 and pin13 of flatconnector, the following telemetry is transmitted: 64 | 65 | 330R --> -168° 66 | 560R --> -113° 67 | 820R --> -52° 68 | 1K --> -4.1° 69 | 1.5K --> 121° 70 | 2.2K --> 319° 71 | 72 | 73 | Pressure sensor module: 74 | ----------------------- 75 | 76 | This module is connected to SPI2 of STM. It uses SPI_NSS and only data output 77 | from module - SPI output from STM is not connected to module. So I think it is 78 | a kind of polling. 79 | 80 | 81 | 82 | 2 be continued -------------------------------------------------------------------------------- /legacy-code/f_rtty.c: -------------------------------------------------------------------------------- 1 | #include "f_rtty.h" 2 | 3 | uint8_t start_bits; 4 | rttyStates send_rtty(char *buffer) { 5 | static uint8_t nr_bit = 0; 6 | nr_bit++; 7 | if (start_bits){ 8 | start_bits--; 9 | return rttyOne; 10 | } 11 | 12 | if (nr_bit == 1) { 13 | return rttyZero; 14 | } 15 | if (nr_bit > 1 && nr_bit < (RTTY_7BIT ? 9 : 10)) { 16 | if ((*(buffer) >> (nr_bit - 2)) & 0x01) { 17 | return rttyOne; 18 | } else { 19 | return rttyZero; 20 | } 21 | } 22 | 23 | #ifdef RTTY_7BIT 24 | nr_bit++; 25 | #endif 26 | 27 | if (nr_bit == 10) { 28 | return rttyOne; 29 | } 30 | #ifdef RTTY_USE_2_STOP_BITS 31 | if (nr_bit == 11) { 32 | return rttyOne; 33 | } 34 | #endif 35 | nr_bit = 0; 36 | return rttyEnd; 37 | } 38 | -------------------------------------------------------------------------------- /legacy-code/f_rtty.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "config.h" 3 | 4 | typedef enum { 5 | rttyZero = 0, 6 | rttyOne = 1, 7 | rttyEnd = 2 8 | } rttyStates; 9 | static const uint8_t RTTY_PRE_START_BITS = 40; 10 | 11 | rttyStates send_rtty(char *); 12 | extern uint8_t start_bits; 13 | -------------------------------------------------------------------------------- /openocd_rs41.cfg: -------------------------------------------------------------------------------- 1 | # Copied and adapted from random internet sources. 2 | 3 | # Unlocking is not needed, but every 1k block of Flash Ram is write protected 4 | # - see: http://openocd.org/doc/html/Flash-Commands.html#flashprotect 5 | 6 | # Typical usages: 7 | # openocd -f ./openocd_rs41.cfg -c "init; halt; flash protect 0 0 31 off; exit" 8 | # to unlock 64k of Flash for programming 9 | # openocd -f ./openocd_rs41.cfg -c "program RS41ng.elf verify reset exit" 10 | # to program the connected rs41 11 | # openocd -f ./openocd_rs41.cfg -c "init; reset; exit" 12 | # will reset the connected rs41 13 | # 14 | 15 | # Debugging with semihosting, use GDB commands: 16 | # target remote localhost:3333 17 | # monitor arm semihosting enable 18 | # load build/src/RS41ng.elf 19 | # monitor reset halt 20 | # continue 21 | 22 | # Normal use is with cheap STLINKv2 clone 23 | source [find interface/stlink-v2.cfg] 24 | # Alternative is to connect directly to a Raspberry Pi3 25 | # Find AdaFruit tutorial to build and run OpenOCD on RPi: 26 | # source [find interface/raspberrypi2-native.cfg] 27 | # bcm2835gpio_swd_nums 25 24 28 | # bcm2835gpio_speed_coeffs 194938 48 29 | # bcm2835gpio_srst_num 18 30 | # reset_config srst_only srst_push_pull 31 | 32 | set WORKAREASIZE 0x2000 33 | 34 | # RS41 uses stm32f1 cortex m3 35 | source [find target/stm32f1x.cfg] 36 | 37 | # example script: 38 | proc RS41 {will_fail} { 39 | init 40 | sleep 200 41 | reset 42 | halt 43 | wait_halt 44 | stm32f1x unlock $will_fail 45 | sleep 10 46 | shutdown 47 | } 48 | 49 | # 50 | # Uncommon usage: 51 | # openocd -f ./openocd_rs41.cfg -c "init; halt; stm32f1x options_read 0; exit" 52 | # check setings before changing them 53 | # openocd -f ./openocd_rs41.cfg -c "init; halt; stm32f1x unlock 0; exit" 54 | # is not needed just to re-program RS41 55 | # openocd -f ./openocd_rs41.cfg -c "RS41 0" 56 | # to run a macro written above 57 | # 58 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(RS41ng C CXX) 2 | 3 | set(CMAKE_FIND_ROOT_PATH /usr/arm-none-eabi) 4 | 5 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 6 | 7 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 8 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 9 | 10 | set(COLLECT_GCC ${TOOLCHAIN_PATH}/arm-none-eabi-gcc) 11 | 12 | if (UNIX) 13 | set(TOOLCHAIN_PATH /usr/bin) 14 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/arm-none-eabi-gcc) 15 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/arm-none-eabi-g++) 16 | set(CMAKE_OBJCOPY ${TOOLCHAIN_PATH}/arm-none-eabi-objcopy) 17 | else () 18 | set(TOOLCHAIN_PATH C:\\) 19 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/arm-none-eabi-gcc.exe) 20 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/arm-none-eabi-g++.exe) 21 | set(CMAKE_OBJCOPY ${TOOLCHAIN_PATH}/arm-none-eabi-objcopy.exe) 22 | endif () 23 | 24 | add_definitions(-DSTM32F100C8) 25 | add_definitions(-DSTM32F10X_MD_VL) 26 | add_definitions(-DUSE_STDPERIPH_DRIVER) 27 | add_definitions(-DSUPPORT_CPLUSPLUS) 28 | add_definitions(-D__ASSEMBLY__) 29 | 30 | SET(LINKER_SCRIPT arm-gcc-link.ld) 31 | SET(COMMON_FLAGS " -mcpu=cortex-m3 -mthumb -Wall -ffunction-sections -fdata-sections -O3 -nostartfiles") 32 | SET(CMAKE_CXX_FLAGS "${COMMON_FLAGS} -std=c++11") 33 | SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -std=gnu99") 34 | SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=${CMAKE_BINARY_DIR}/${PROJECT_NAME}.map -lstdc++ -O3 -mcpu=cortex-m3 -mthumb -Wl,--gc-sections --specs=nano.specs -T ${LINKER_SCRIPT}") 35 | # -u _printf_float 36 | #SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=${CMAKE_BINARY_DIR}/${PROJECT_NAME}.map -lstdc++ -O3 -mcpu=cortex-m3 -mthumb -Wl,--gc-sections --specs=rdimon.specs -lc -lrdimon -T ${LINKER_SCRIPT}") 37 | 38 | file(GLOB_RECURSE USER_SOURCES "*.c") 39 | file(GLOB_RECURSE USER_SOURCES_CXX "*.cpp") 40 | file(GLOB_RECURSE USER_HEADERS "*.h") 41 | 42 | include_directories(hal/cmsis 43 | hal/cmsis_boot 44 | hal/stm_lib/inc 45 | ..) 46 | 47 | add_executable(${PROJECT_NAME}.elf ${USER_SOURCES} ${USER_SOURCES_CXX} ${USER_HEADERS} ${HAL_SOURCES} ${LINKER_SCRIPT}) 48 | 49 | set(HEX_FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.hex) 50 | set(BIN_FILE ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin) 51 | add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD 52 | COMMAND ${CMAKE_OBJCOPY} -Oihex $ ${HEX_FILE} 53 | COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${BIN_FILE} 54 | COMMENT "Building ${HEX_FILE} \nBuilding ${BIN_FILE}" 55 | COMMAND ${TOOLCHAIN_PATH}/arm-none-eabi-size ${PROJECT_NAME}.elf) 56 | 57 | set(CMAKE_CXX_STANDARD 11) 58 | 59 | add_custom_target(program 60 | DEPENDS ${PROJECT_NAME}.elf 61 | 62 | if (UNIX) 63 | #TODO 64 | else () 65 | COMMAND C:/Programs/stlink-1.3.0-win64/bin/st-flash --reset write ${BIN_FILE} 0x08000000 66 | endif () 67 | COMMENT "flashing ${BIN_FILE}") 68 | -------------------------------------------------------------------------------- /src/arm-gcc-link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | /* Internal Memory Map*/ 3 | MEMORY 4 | { 5 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 0x00010000 6 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00002000 7 | } 8 | 9 | _eram = 0x20000000 + 0x00002000; 10 | __HeapSize = 0x800; 11 | SECTIONS 12 | { 13 | .text : 14 | { 15 | KEEP(*(.isr_vector)) 16 | *(.text*) 17 | 18 | KEEP(*(.init)) 19 | KEEP(*(.fini)) 20 | 21 | /* .ctors */ 22 | *crtbegin.o(.ctors) 23 | *crtbegin?.o(.ctors) 24 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 25 | *(SORT(.ctors.*)) 26 | *(.ctors) 27 | 28 | /* .dtors */ 29 | *crtbegin.o(.dtors) 30 | *crtbegin?.o(.dtors) 31 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 32 | *(SORT(.dtors.*)) 33 | *(.dtors) 34 | 35 | *(.rodata*) 36 | 37 | KEEP(*(.eh_fram e*)) 38 | } > rom 39 | 40 | .ARM.extab : 41 | { 42 | *(.ARM.extab* .gnu.linkonce.armextab.*) 43 | } > rom 44 | 45 | __exidx_start = .; 46 | .ARM.exidx : 47 | { 48 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 49 | } > rom 50 | __exidx_end = .; 51 | __etext = .; 52 | 53 | /* _sidata is used in coide startup code */ 54 | _sidata = __etext; 55 | 56 | .data : AT (__etext) 57 | { 58 | __data_start__ = .; 59 | 60 | /* _sdata is used in coide startup code */ 61 | _sdata = __data_start__; 62 | 63 | *(vtable) 64 | *(.data*) 65 | 66 | . = ALIGN(8); 67 | /* preinit data */ 68 | PROVIDE_HIDDEN (__preinit_array_start = .); 69 | KEEP(*(.preinit_array)) 70 | PROVIDE_HIDDEN (__preinit_array_end = .); 71 | 72 | . = ALIGN(8); 73 | /* init data */ 74 | PROVIDE_HIDDEN (__init_array_start = .); 75 | KEEP(*(SORT(.init_array.*))) 76 | KEEP(*(.init_array)) 77 | PROVIDE_HIDDEN (__init_array_end = .); 78 | 79 | . = ALIGN(8); 80 | /* finit data */ 81 | PROVIDE_HIDDEN (__fini_array_start = .); 82 | KEEP(*(SORT(.fini_array.*))) 83 | KEEP(*(.fini_array)) 84 | PROVIDE_HIDDEN (__fini_array_end = .); 85 | 86 | KEEP(*(.jcr*)) 87 | . = ALIGN(8); 88 | /* All data end */ 89 | __data_end__ = .; 90 | 91 | /* _edata is used in coide startup code */ 92 | _edata = __data_end__; 93 | } > ram 94 | 95 | .bss : 96 | { 97 | . = ALIGN(8); 98 | __bss_start__ = .; 99 | _sbss = __bss_start__; 100 | *(.bss*) 101 | *(COMMON) 102 | . = ALIGN(8); 103 | __bss_end__ = .; 104 | _ebss = __bss_end__; 105 | } > ram 106 | 107 | .heap : 108 | { 109 | . = ALIGN(8); 110 | __HeapStart = .; 111 | __end__ = .; 112 | _end = __end__; 113 | end = __end__; 114 | *(.heap*) 115 | . = . + __HeapSize; 116 | __HeapLimit = .; 117 | } > ram 118 | 119 | /* .co_stack section doesn't contains any symbols. It is only 120 | * used for linker to calculate size of stack sections, and assign 121 | * values to stack symbols later */ 122 | .co_stack (NOLOAD): 123 | { 124 | . = ALIGN(8); 125 | __StackStart = .; 126 | *(.co_stack .co_stack.*) 127 | . = ALIGN(8); 128 | __StackEnd = .; 129 | } > ram 130 | 131 | /* NOTE: These variables don't seem to affect anything. 132 | * 133 | * Set stack top to end of ram , and stack limit move down by 134 | * size of co_stack section */ 135 | __StackTop = ORIGIN(ram) + LENGTH(ram); 136 | __StackLimit = __StackTop - SIZEOF(.co_stack); 137 | PROVIDE(__stack = __StackTop); 138 | 139 | /* Check if data + heap + stack exceeds ram limit */ 140 | ASSERT(__StackLimit >= __HeapLimit, "region ram overflowed with stack") 141 | 142 | .last_section (NOLOAD): 143 | { 144 | . = ALIGN(8); 145 | } > ram 146 | } 147 | -------------------------------------------------------------------------------- /src/bmp280_handler.c: -------------------------------------------------------------------------------- 1 | #include "drivers/bmp280/bmp280.h" 2 | #include "bmp280_handler.h" 3 | #include "log.h" 4 | 5 | bmp280 bmp280_dev; 6 | 7 | static bool bmp280_initialization_required = true; 8 | 9 | bool bmp280_handler_init() 10 | { 11 | bmp280_dev.port = &DEFAULT_I2C_PORT; 12 | bmp280_dev.addr = SENSOR_BMP280_I2C_ADDRESS; 13 | 14 | bmp280_params_t bmp280_params = { 15 | .mode = BMP280_MODE_NORMAL, 16 | .filter = BMP280_FILTER_16, 17 | .oversampling_pressure = BMP280_ULTRA_HIGH_RES, 18 | .oversampling_temperature = BMP280_ULTRA_HIGH_RES, 19 | .oversampling_humidity = BMP280_ULTRA_HIGH_RES, 20 | .standby = BMP280_STANDBY_250, 21 | }; 22 | 23 | bool success = bmp280_init(&bmp280_dev, &bmp280_params); 24 | bmp280_initialization_required = !success; 25 | return success; 26 | } 27 | 28 | bool bmp280_read(int32_t *temperature_celsius_100, uint32_t *pressure_mbar_100, uint32_t *humidity_percentage_100) 29 | { 30 | int32_t temperature_raw; 31 | uint32_t pressure_raw; 32 | uint32_t humidity_raw; 33 | 34 | bool success = bmp280_read_fixed(&bmp280_dev, &temperature_raw, &pressure_raw, &humidity_raw); 35 | if (!success) { 36 | log_error("BMP280 read failed\n"); 37 | return false; 38 | } 39 | 40 | if (temperature_celsius_100) { 41 | *temperature_celsius_100 = temperature_raw; 42 | } 43 | if (pressure_mbar_100) { 44 | // Pressure unit is Pascal (= mbar * 100) * 256 45 | *pressure_mbar_100 = (uint32_t) (((float) pressure_raw) / 256.0f); 46 | } 47 | // NOTE: Only BME280 sensors will report humidity. For BMP280 humidity readings will be zero. 48 | if (humidity_percentage_100) { 49 | *humidity_percentage_100 = (uint32_t) (((float) humidity_raw) * 100.0f / 1024.0f); 50 | } 51 | 52 | return true; 53 | } 54 | 55 | bool bmp280_read_telemetry(telemetry_data *data) 56 | { 57 | bool success; 58 | 59 | if (bmp280_initialization_required) { 60 | log_info("BMP280 re-init\n"); 61 | success = bmp280_handler_init(); 62 | log_info("BMP280 re-init: %d\n", success); 63 | if (!success) { 64 | data->temperature_celsius_100 = 0; 65 | data->pressure_mbar_100 = 0; 66 | data->humidity_percentage_100 = 0; 67 | return false; 68 | } 69 | } 70 | 71 | success = bmp280_read(&data->temperature_celsius_100, &data->pressure_mbar_100, &data->humidity_percentage_100); 72 | 73 | if (!success) { 74 | log_info("BMP280 re-init\n"); 75 | success = bmp280_handler_init(); 76 | log_info("BMP280 re-init: %d\n", success); 77 | 78 | if (success) { 79 | success = bmp280_read(&data->temperature_celsius_100, &data->pressure_mbar_100, &data->humidity_percentage_100); 80 | } 81 | 82 | if (!success) { 83 | data->temperature_celsius_100 = 0; 84 | data->pressure_mbar_100 = 0; 85 | data->humidity_percentage_100 = 0; 86 | } 87 | } 88 | 89 | bmp280_initialization_required = !success; 90 | 91 | return success; 92 | } 93 | -------------------------------------------------------------------------------- /src/bmp280_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef __BMP280_HANDLER_H 2 | #define __BMP280_HANDLER_H 3 | 4 | #include 5 | #include "telemetry.h" 6 | 7 | bool bmp280_handler_init(); 8 | bool bmp280_read(int32_t *temperature_celsius_100, uint32_t *pressure_mbar_100, uint32_t *humidity_percentage_100); 9 | bool bmp280_read_telemetry(telemetry_data *data); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/codecs/aprs/aprs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "aprs.h" 4 | 5 | volatile uint16_t aprs_packet_counter = 0; 6 | 7 | void convert_degrees_to_dmh(long x, int16_t *degrees, uint8_t *minutes, uint8_t *h_minutes) 8 | { 9 | uint8_t sign = (uint8_t) (x > 0 ? 1 : 0); 10 | if (!sign) { 11 | x = -(x); 12 | } 13 | *degrees = (int16_t) (x / 1000000); 14 | x = x - (*degrees * 1000000); 15 | x = (x) * 60 / 10000; 16 | *minutes = (uint8_t) (x / 100); 17 | *h_minutes = (uint8_t) (x - (*minutes * 100)); 18 | if (!sign) { 19 | *degrees = (int16_t ) -*degrees; 20 | } 21 | } 22 | 23 | void aprs_generate_timestamp(char *timestamp, size_t length, telemetry_data *data) 24 | { 25 | snprintf(timestamp, length, "/%02d%02d%02dz", data->gps.hours, data->gps.minutes, data->gps.seconds); 26 | } 27 | -------------------------------------------------------------------------------- /src/codecs/aprs/aprs.h: -------------------------------------------------------------------------------- 1 | #ifndef __APRS_H 2 | #define __APRS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "gps.h" 8 | #include "telemetry.h" 9 | 10 | void convert_degrees_to_dmh(long x, int16_t *degrees, uint8_t *minutes, uint8_t *h_minutes); 11 | void aprs_generate_timestamp(char *timestamp, size_t length, telemetry_data *data); 12 | 13 | extern volatile uint16_t aprs_packet_counter; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/codecs/aprs/aprs_position.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "aprs.h" 6 | #include "aprs_position.h" 7 | 8 | size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *data, 9 | char symbol_table, char symbol, bool include_timestamp, char *comment) 10 | { 11 | char timestamp[12]; 12 | 13 | int16_t la_degrees, lo_degrees; 14 | uint8_t la_minutes, la_h_minutes, lo_minutes, lo_h_minutes; 15 | 16 | convert_degrees_to_dmh(data->gps.latitude_degrees_1000000 / 10, &la_degrees, &la_minutes, &la_h_minutes); 17 | convert_degrees_to_dmh(data->gps.longitude_degrees_1000000 / 10, &lo_degrees, &lo_minutes, &lo_h_minutes); 18 | 19 | int16_t heading_degrees = (int16_t) ((float) data->gps.heading_degrees_100000 / 100000.0f); 20 | int16_t ground_speed_knots = (int16_t) (((float) data->gps.ground_speed_cm_per_second / 100.0f) * 3.6f / 1.852f); 21 | int32_t altitude_feet = (data->gps.altitude_mm / 1000) * 3280 / 1000; 22 | 23 | if (include_timestamp) { 24 | aprs_generate_timestamp(timestamp, sizeof(timestamp), data); 25 | } else { 26 | strncpy(timestamp, "!", sizeof(timestamp)); 27 | } 28 | 29 | aprs_packet_counter++; 30 | 31 | return snprintf((char *) payload, 32 | length, 33 | ("%s%02d%02d.%02u%c%c%03d%02u.%02u%c%c%03d/%03d/A=%06d/P%dS%dT%02dV%04dC%02d%s"), 34 | timestamp, 35 | abs(la_degrees), la_minutes, la_h_minutes, 36 | la_degrees > 0 ? 'N' : 'S', 37 | symbol_table, 38 | abs(lo_degrees), lo_minutes, lo_h_minutes, 39 | lo_degrees > 0 ? 'E' : 'W', 40 | symbol, 41 | heading_degrees, 42 | ground_speed_knots, 43 | (int) altitude_feet, 44 | aprs_packet_counter, 45 | data->gps.satellites_visible, 46 | (int) data->internal_temperature_celsius_100 / 100, 47 | data->battery_voltage_millivolts, 48 | (int16_t) ((float) data->gps.climb_cm_per_second / 100.0f), 49 | comment 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/codecs/aprs/aprs_position.h: -------------------------------------------------------------------------------- 1 | #ifndef __APRS_POSITION_H 2 | #define __APRS_POSITION_H 3 | 4 | #include 5 | #include 6 | 7 | #include "gps.h" 8 | #include "telemetry.h" 9 | 10 | size_t aprs_generate_position(uint8_t *payload, size_t length, telemetry_data *data, 11 | char symbol_table, char symbol, bool include_timestamp, char *comment); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/codecs/aprs/aprs_weather.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "aprs.h" 6 | #include "aprs_weather.h" 7 | 8 | size_t aprs_generate_weather_report(uint8_t *payload, size_t length, telemetry_data *data, 9 | bool include_timestamp, char *comment) 10 | { 11 | char timestamp[12]; 12 | 13 | int16_t la_degrees, lo_degrees; 14 | uint8_t la_minutes, la_h_minutes, lo_minutes, lo_h_minutes; 15 | 16 | convert_degrees_to_dmh(data->gps.latitude_degrees_1000000 / 10, &la_degrees, &la_minutes, &la_h_minutes); 17 | convert_degrees_to_dmh(data->gps.longitude_degrees_1000000 / 10, &lo_degrees, &lo_minutes, &lo_h_minutes); 18 | 19 | if (include_timestamp) { 20 | aprs_generate_timestamp(timestamp, sizeof(timestamp), data); 21 | } else { 22 | strncpy(timestamp, "!", sizeof(timestamp)); 23 | } 24 | 25 | int32_t temperature_fahrenheits = (data->temperature_celsius_100 * 9 / 5) / 100 + 32; 26 | uint32_t pressure_mbar_10 = data->pressure_mbar_100 / 10; 27 | uint16_t humidity_percentage = data->humidity_percentage_100 / 100; 28 | if (humidity_percentage > 99) { 29 | humidity_percentage = 0; // Zero represents 100% 30 | } else if (humidity_percentage < 1) { 31 | humidity_percentage = 1; 32 | } 33 | 34 | aprs_packet_counter++; 35 | 36 | return snprintf((char *) payload, 37 | length, 38 | ("%s%02d%02d.%02u%c%c%03d%02u.%02u%c%c.../...g...t%03dh%02ub%05u%s"), 39 | timestamp, 40 | abs(la_degrees), la_minutes, la_h_minutes, 41 | la_degrees > 0 ? 'N' : 'S', 42 | '/', // Primary symbol table 43 | abs(lo_degrees), lo_minutes, lo_h_minutes, 44 | lo_degrees > 0 ? 'E' : 'W', 45 | '_', // Weather station symbol 46 | (int) temperature_fahrenheits, 47 | (unsigned int) humidity_percentage, 48 | (unsigned int) pressure_mbar_10, 49 | comment 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/codecs/aprs/aprs_weather.h: -------------------------------------------------------------------------------- 1 | #ifndef __APRS_WEATHER_H 2 | #define __APRS_WEATHER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "gps.h" 8 | #include "telemetry.h" 9 | 10 | size_t aprs_generate_weather_report(uint8_t *payload, size_t length, telemetry_data *data, 11 | bool include_timestamp, char *comment); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/codecs/ax25/ax25.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ax25.h" 4 | 5 | static inline uint16_t ax25_calculate_crc_for_bit(uint16_t crc, bool bit) 6 | { 7 | uint16_t result = crc; 8 | uint16_t temp; 9 | 10 | // XOR lsb of CRC with the latest bit 11 | temp = crc ^ (bit ? 1U : 0U); 12 | // Shift 16-bit CRC one bit to the right 13 | result >>= 1U; 14 | 15 | // If XOR result from above has lsb set 16 | if (temp & 0x0001U) { 17 | result ^= 0x8408U; 18 | } 19 | 20 | return result; 21 | } 22 | 23 | static inline uint16_t ax25_calculate_crc_for_byte(uint16_t crc, uint8_t byte) 24 | { 25 | uint8_t temp = byte; 26 | for (uint16_t i = 0; i < 8; i++, temp >>= 1U) { 27 | bool bit = ((temp & 1U) == 1U) ? true : false; 28 | crc = ax25_calculate_crc_for_bit(crc, bit); 29 | } 30 | 31 | return crc; 32 | } 33 | 34 | static uint16_t ax25_calculate_crc(uint16_t length, uint8_t *data) 35 | { 36 | uint16_t crc = 0xFFFF; 37 | 38 | for (uint16_t i = 0; i < length; i++) { 39 | uint8_t byte = data[i]; 40 | crc = ax25_calculate_crc_for_byte(crc, byte); 41 | } 42 | 43 | return crc; 44 | } 45 | 46 | static uint16_t ax25_encode_digipeater_path(char *input, char *packet_data) 47 | { 48 | uint16_t digipeaters_length = strlen(input); 49 | uint16_t packet_data_index = 0; 50 | 51 | for (uint16_t index = 0; index < digipeaters_length; index++) { 52 | if (input[index] == ',' || index == digipeaters_length - 1) { 53 | if (input[index] != ',') { 54 | packet_data[packet_data_index] = input[index] == '-' ? ' ' : input[index]; 55 | packet_data_index++; 56 | } 57 | 58 | uint16_t fill_count = 7 - (packet_data_index % 7); 59 | while (fill_count > 0 && fill_count < 7) { 60 | packet_data[packet_data_index] = ' '; 61 | fill_count--; 62 | packet_data_index++; 63 | } 64 | 65 | continue; 66 | } 67 | 68 | packet_data[packet_data_index] = input[index] == '-' ? ' ' : input[index]; 69 | packet_data_index++; 70 | } 71 | 72 | return packet_data_index; 73 | } 74 | 75 | uint16_t ax25_encode_packet_aprs(char *source, uint8_t source_ssid, char *destination, uint8_t destination_ssid, 76 | char *digipeater_addresses, char *information_field, uint16_t length, uint8_t *packet_data) 77 | { 78 | // TODO: use length to limit packet size 79 | 80 | ax25_packet_header *header = (ax25_packet_header *) packet_data; 81 | 82 | header->flag = AX25_PACKET_FLAG; 83 | 84 | size_t call_length = strlen(source); 85 | memset(header->source, ' ', sizeof(header->source)); 86 | memcpy(header->source, source, call_length < 6 ? call_length : 6); 87 | header->source_ssid = (uint8_t) (source_ssid >= 'A' ? source_ssid - 7 : source_ssid); 88 | 89 | call_length = strlen(destination); 90 | memset(header->destination, ' ', sizeof(header->destination)); 91 | memcpy(header->destination, destination, call_length < 6 ? call_length : 6); 92 | header->destination_ssid = (uint8_t) (destination_ssid >= 'A' ? destination_ssid - 7 : destination_ssid); 93 | 94 | char *digipeater_addresses_start = ((char *) header) + 1 + 14; 95 | uint16_t digipeater_addresses_length = ax25_encode_digipeater_path(digipeater_addresses, digipeater_addresses_start); 96 | 97 | // Perform bit-shifting for all addresses 98 | uint8_t *actual_data_start = ((uint8_t *) header) + 1; 99 | for (uint16_t i = 0; i < 14 + digipeater_addresses_length; i++) { 100 | actual_data_start[i] = actual_data_start[i] << 1U; 101 | } 102 | actual_data_start[13 + digipeater_addresses_length] |= 1U; 103 | 104 | ax25_packet_header_end *header_end = (ax25_packet_header_end *) (((uint8_t *) header) + 1 + 14 + 105 | digipeater_addresses_length); 106 | 107 | header_end->control_field = AX25_CONTROL_FIELD_UI_FRAME; 108 | header_end->protocol_id = AX25_PROTOCOL_ID_NO_LAYER_3; 109 | 110 | uint16_t info_length = strlen(information_field); 111 | strcpy(header_end->information_field, information_field); 112 | 113 | uint16_t crc_length = 14 + digipeater_addresses_length + 2 + info_length; 114 | uint16_t crc = ax25_calculate_crc(crc_length, actual_data_start); 115 | 116 | ax25_packet_footer *footer = (ax25_packet_footer *) (((uint8_t *) header_end->information_field) + info_length); 117 | 118 | // CRC is stored MSB first 119 | footer->frame_check_sequence[0] = (crc & 0xFFU) ^ 0xFFU; 120 | footer->frame_check_sequence[1] = (crc >> 8U) ^ 0xFFU; 121 | footer->flag = AX25_PACKET_FLAG; 122 | 123 | return 1 + 14 + digipeater_addresses_length + 2 + info_length + 2 + 1; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /src/codecs/ax25/ax25.h: -------------------------------------------------------------------------------- 1 | #ifndef __AX25_H 2 | #define __AX25_H 3 | 4 | #include 5 | 6 | #define AX25_PACKET_FLAG 0x7E 7 | 8 | #define AX25_CONTROL_FIELD_UI_FRAME 0x03 9 | #define AX25_PROTOCOL_ID_NO_LAYER_3 0xF0 10 | 11 | typedef struct _ax25_packet_header { 12 | uint8_t flag; 13 | char destination[6]; 14 | uint8_t destination_ssid; 15 | char source[6]; 16 | uint8_t source_ssid; 17 | } ax25_packet_header; 18 | 19 | typedef struct _ax25_packet_header_end { 20 | uint8_t control_field; 21 | uint8_t protocol_id; 22 | char information_field[]; 23 | } ax25_packet_header_end; 24 | 25 | typedef struct _ax25_packet_footer { 26 | uint8_t frame_check_sequence[2]; 27 | uint8_t flag; 28 | } ax25_packet_footer; 29 | 30 | uint16_t ax25_encode_packet_aprs(char *source, uint8_t source_ssid, char *destination, uint8_t destination_ssid, 31 | char *digipeater_addresses, char *information_field, uint16_t length, uint8_t *packet_data); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/codecs/bell/bell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "codecs/ax25/ax25.h" 5 | #include "bell.h" 6 | 7 | #define FSK_TONE_INDEX_BELL_SPACE 0 8 | #define FSK_TONE_INDEX_BELL_MARK 1 9 | 10 | #define BELL_TONE_COUNT 2 11 | 12 | typedef struct _bell_encoder { 13 | uint32_t symbol_rate; 14 | uint16_t flag_field_count; 15 | fsk_tone *tones; 16 | 17 | uint16_t data_length; 18 | uint8_t *data; 19 | 20 | uint16_t current_byte_index; 21 | uint8_t current_bit_index; 22 | uint8_t current_byte; 23 | uint8_t bit_stuffing_counter; 24 | 25 | uint16_t current_flag_field_count; 26 | int8_t current_tone_index; 27 | } bell_encoder; 28 | 29 | fsk_tone bell202_tones[] = { 30 | { 31 | .index = FSK_TONE_INDEX_BELL_SPACE, 32 | .frequency_hz_100 = 2200 * 100, 33 | }, 34 | { 35 | .index = FSK_TONE_INDEX_BELL_MARK, 36 | .frequency_hz_100 = 1200 * 100, 37 | }, 38 | }; 39 | 40 | fsk_tone bell103_tones[] = { 41 | { 42 | .index = FSK_TONE_INDEX_BELL_SPACE, 43 | .frequency_hz_100 = 1070 * 100, 44 | }, 45 | { 46 | .index = FSK_TONE_INDEX_BELL_MARK, 47 | .frequency_hz_100 = 1270 * 100, 48 | }, 49 | }; 50 | 51 | void bell_encoder_new(fsk_encoder *encoder, uint32_t symbol_rate, uint16_t flag_field_count, fsk_tone *tones) 52 | { 53 | encoder->priv = malloc(sizeof(bell_encoder)); 54 | memset(encoder->priv, 0, sizeof(bell_encoder)); 55 | 56 | bell_encoder *bell = (bell_encoder *) encoder->priv; 57 | bell->symbol_rate = symbol_rate; 58 | bell->flag_field_count = flag_field_count; 59 | bell->tones = tones; 60 | } 61 | 62 | void bell_encoder_destroy(fsk_encoder *encoder) 63 | { 64 | if (encoder->priv != NULL) { 65 | free(encoder->priv); 66 | encoder->priv = NULL; 67 | } 68 | } 69 | 70 | void bell_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data) 71 | { 72 | bell_encoder *bell = (bell_encoder *) encoder->priv; 73 | 74 | bell->data = data; 75 | bell->data_length = data_length; 76 | 77 | bell->current_tone_index = FSK_TONE_INDEX_BELL_MARK; 78 | bell->current_byte_index = 0; 79 | bell->current_bit_index = 0; 80 | bell->current_byte = data[0]; 81 | bell->bit_stuffing_counter = 0; 82 | bell->current_flag_field_count = 0; 83 | } 84 | 85 | void bell_encoder_get_tones(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones) 86 | { 87 | bell_encoder *bell = (bell_encoder *) encoder->priv; 88 | 89 | *tone_count = BELL_TONE_COUNT; 90 | *tones = bell->tones; 91 | } 92 | 93 | uint32_t bell_encoder_get_tone_spacing(fsk_encoder *encoder) 94 | { 95 | return 0; 96 | } 97 | 98 | uint32_t bell_encoder_get_symbol_rate(fsk_encoder *encoder) 99 | { 100 | bell_encoder *bell = (bell_encoder *) encoder->priv; 101 | return bell->symbol_rate; 102 | } 103 | 104 | uint32_t bell_encoder_get_symbol_delay(fsk_encoder *encoder) 105 | { 106 | return 0; 107 | } 108 | 109 | static inline void bell_encoder_toggle_tone(fsk_encoder *encoder) 110 | { 111 | bell_encoder *bell = (bell_encoder *) encoder->priv; 112 | 113 | bell->current_tone_index = 114 | (bell->current_tone_index == FSK_TONE_INDEX_BELL_MARK) 115 | ? FSK_TONE_INDEX_BELL_SPACE 116 | : FSK_TONE_INDEX_BELL_MARK; 117 | } 118 | 119 | int8_t bell_encoder_next_tone(fsk_encoder *encoder) 120 | { 121 | bell_encoder *bell = (bell_encoder *) encoder->priv; 122 | 123 | if (bell->current_byte_index >= bell->data_length) { 124 | return -1; 125 | } 126 | 127 | bool is_flag = bell->current_byte == AX25_PACKET_FLAG; 128 | 129 | if (is_flag) { 130 | bell->bit_stuffing_counter = 0; 131 | } 132 | 133 | bool bit = (bell->current_byte >> bell->current_bit_index) & 1U; 134 | 135 | if (bit) { 136 | bell->bit_stuffing_counter++; 137 | if (bell->bit_stuffing_counter == 5) { 138 | int8_t previous_tone_index = bell->current_tone_index; 139 | bell_encoder_toggle_tone(encoder); 140 | bell->bit_stuffing_counter = 0; 141 | return previous_tone_index; 142 | } 143 | } else { 144 | bell_encoder_toggle_tone(encoder); 145 | bell->bit_stuffing_counter = 0; 146 | } 147 | 148 | bell->current_bit_index = (bell->current_bit_index + 1) % 8; 149 | 150 | if (bell->current_bit_index == 0) { 151 | if (bell->current_byte_index == 0 && bell->current_flag_field_count < bell->flag_field_count) { 152 | bell->current_flag_field_count++; 153 | } else { 154 | // return -1; 155 | bell->current_byte_index++; 156 | } 157 | if (bell->current_byte_index < bell->data_length) { 158 | bell->current_byte = bell->data[bell->current_byte_index]; 159 | } 160 | } 161 | 162 | return bell->current_tone_index; 163 | } 164 | 165 | fsk_encoder_api bell_fsk_encoder_api = { 166 | .get_tones = bell_encoder_get_tones, 167 | .get_tone_spacing = bell_encoder_get_tone_spacing, 168 | .get_symbol_rate = bell_encoder_get_symbol_rate, 169 | .get_symbol_delay = bell_encoder_get_symbol_delay, 170 | .set_data = bell_encoder_set_data, 171 | .next_tone = bell_encoder_next_tone, 172 | }; 173 | -------------------------------------------------------------------------------- /src/codecs/bell/bell.h: -------------------------------------------------------------------------------- 1 | #ifndef __BELL_H 2 | #define __BELL_H 3 | 4 | #include 5 | #include 6 | 7 | #include "codecs/fsk/fsk.h" 8 | 9 | #define BELL_FLAG_FIELD_COUNT_1200 45 10 | #define BELL_FLAG_FIELD_COUNT_300 45 11 | 12 | void bell_encoder_new(fsk_encoder *encoder, uint32_t symbol_rate, uint16_t flag_field_count, fsk_tone *tones); 13 | void bell_encoder_destroy(fsk_encoder *encoder); 14 | void bell_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data); 15 | void bell_encoder_get_tones(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones); 16 | uint32_t bell_encoder_get_symbol_rate(fsk_encoder *encoder); 17 | int8_t bell_encoder_next_tone(fsk_encoder *encoder); 18 | 19 | extern fsk_tone bell202_tones[]; 20 | extern fsk_tone bell103_tones[]; 21 | extern fsk_encoder_api bell_fsk_encoder_api; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/codecs/cats/cats.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cats.h" 4 | #include "crc.h" 5 | #include "whiten.h" 6 | #include "ldpc.h" 7 | #include "interleaver.h" 8 | 9 | cats_packet cats_create(uint8_t *data) 10 | { 11 | cats_packet c = {.data = data, .len = 0}; 12 | return c; 13 | } 14 | 15 | // This makes changes to packet->data. 16 | // Don't call it more than once, and don't use 17 | // the packet after calling! 18 | size_t cats_fully_encode(cats_packet packet, uint8_t *out) 19 | { 20 | // 0. CRC 21 | size_t new_len = cats_append_crc(packet.data, packet.len); 22 | // 1. whiten 23 | cats_whiten(packet.data, new_len); 24 | // 2. ldpc 25 | new_len = cats_ldpc_encode(packet.data, new_len); 26 | // 3. length 27 | out[0] = new_len; 28 | out[1] = new_len >> 8; 29 | // 4. interleave 30 | cats_interleave(out + 2, packet.data, new_len); 31 | 32 | return new_len + 2; 33 | } 34 | -------------------------------------------------------------------------------- /src/codecs/cats/cats.h: -------------------------------------------------------------------------------- 1 | #ifndef __CATS_H 2 | #define __CATS_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct _cats_packet { 8 | uint8_t *data; 9 | size_t len; 10 | } cats_packet; 11 | 12 | cats_packet cats_create(uint8_t *payload); 13 | size_t cats_fully_encode(cats_packet packet, uint8_t *out); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/codecs/cats/crc.c: -------------------------------------------------------------------------------- 1 | #include "crc.h" 2 | 3 | uint16_t calc_crc(uint8_t *data, size_t length); 4 | 5 | size_t cats_append_crc(uint8_t *data, size_t len) 6 | { 7 | uint16_t crc = calc_crc(data, len); 8 | data[len++] = crc; 9 | data[len++] = crc >> 8; 10 | 11 | return len; 12 | } 13 | 14 | // https://stackoverflow.com/questions/69850602/converting-c-to-java-for-crc16-ibm-sdlc 15 | uint16_t calc_crc(uint8_t *data, size_t length) 16 | { 17 | uint8_t *ptr = data; 18 | uint8_t crcbyte1 = 0xFF; 19 | uint8_t crcbyte2 = 0xFF; 20 | for (int i = 0; i < length; i++) { 21 | uint8_t r1 = *ptr++ ^ crcbyte2; 22 | r1 = (r1 << 4) ^ r1; 23 | crcbyte2 = (r1 << 4) | (r1 >> 4); 24 | crcbyte2 = (crcbyte2 & 0x0F ) ^ crcbyte1; 25 | crcbyte1 = r1; 26 | r1 = (r1 << 3) | (r1 >> 5); 27 | crcbyte2 = crcbyte2 ^ (r1 & 0xF8); 28 | crcbyte1 = crcbyte1 ^ (r1 & 0x07); 29 | } 30 | 31 | return ~((crcbyte1 << 8) | crcbyte2); 32 | } 33 | -------------------------------------------------------------------------------- /src/codecs/cats/crc.h: -------------------------------------------------------------------------------- 1 | #ifndef __CATS_CRC_H 2 | #define __CATS_CRC_H 3 | 4 | #include 5 | #include 6 | 7 | size_t cats_append_crc(uint8_t *data, size_t len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/codecs/cats/interleaver.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "interleaver.h" 4 | 5 | static inline bool get_bit(uint8_t *arr, size_t i); 6 | static inline void set_bit(uint8_t *arr, size_t i, bool value); 7 | 8 | void cats_interleave(uint8_t *dest, uint8_t *src, size_t len) 9 | { 10 | size_t bit_len = len * 8; 11 | 12 | size_t out_i = 0; 13 | for(int i = 0; i < 32; i++) { 14 | for(int j = 0; j < bit_len; j += 32) { 15 | if(i + j >= bit_len) { 16 | continue; 17 | } 18 | 19 | set_bit(dest, out_i, get_bit(src, i + j)); 20 | out_i++; 21 | } 22 | } 23 | } 24 | 25 | static inline bool get_bit(uint8_t *arr, size_t i) 26 | { 27 | size_t byte_idx = i / 8; 28 | int bit_idx = 7 - (i % 8); 29 | 30 | return (arr[byte_idx] >> bit_idx) & 1; 31 | } 32 | 33 | 34 | static inline void set_bit(uint8_t *arr, size_t i, bool value) 35 | { 36 | size_t byte_idx = i / 8; 37 | int bit_idx = 7 - (i % 8); 38 | 39 | if(value) { 40 | arr[byte_idx] |= 1 << bit_idx; 41 | } 42 | else { 43 | arr[byte_idx] &= ~(1 << bit_idx); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/codecs/cats/interleaver.h: -------------------------------------------------------------------------------- 1 | #ifndef __CATS_INTERLEAVER_H 2 | #define __CATS_INTERLEAVER_H 3 | 4 | #include 5 | #include 6 | 7 | void cats_interleave(uint8_t *dest, uint8_t *src, size_t len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/codecs/cats/ldpc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ldpc.h" 5 | #include "ldpc_matrices.h" 6 | 7 | #define GET_BIT(byte, bit) (((byte) & (1 << (7-(bit)))) != 0) 8 | #define SET_BIT(byte, bit) (byte |= 1 << 7-bit) 9 | #define CLEAR_BIT(byte, bit) (byte &= ~(1 << 7-bit)) 10 | #define FLIP_BIT(byte, bit) (byte ^= (1 << (7 - bit))) 11 | 12 | cats_ldpc_code_t *cats_ldpc_pick_code(size_t len) 13 | { 14 | if (len >= 128) { 15 | return &tm2048; 16 | } else if (len >= 32) { 17 | return &tc512; 18 | } else if (len >= 16) { 19 | return &tc256; 20 | } else { 21 | return &tc128; 22 | } 23 | } 24 | 25 | // For more information on this, see the CATS standard 26 | // https://gitlab.scd31.com/cats/cats-standard 27 | // Section 5.2 28 | // returns # of bytes written to parity_out 29 | size_t cats_ldpc_encode_chunk(uint8_t *data, cats_ldpc_code_t *code, uint8_t *parity_out) 30 | { 31 | // Code parameters 32 | int data_length_bits = code->data_length_bits; 33 | int parity_length_bits = code->code_length_bits - code->data_length_bits; 34 | int circ_size = (int) code->circulant_size; 35 | const uint64_t* gc = code->matrix; 36 | int row_len = parity_length_bits / 64; 37 | 38 | memset(parity_out, 0x00, parity_length_bits / 8); 39 | 40 | for (int offset = 0; offset < circ_size; offset++) { 41 | for (int crow = 0; crow < data_length_bits / circ_size; crow++) { 42 | int bit = crow * circ_size + offset; 43 | if (GET_BIT(data[bit / 8], bit % 8)) { 44 | for (int idx = 0; idx < row_len; idx++) { 45 | uint64_t circ = gc[(crow * row_len) + idx]; 46 | for (int j = 0; j < 8; j++) { 47 | parity_out[idx * 8 + j] ^= (uint8_t)(circ >> ((7 - j) * 8)); 48 | } 49 | } 50 | } 51 | } 52 | 53 | for (int block = 0; block < parity_length_bits / circ_size; block++) { 54 | uint8_t* parityblock = &parity_out[block * circ_size / 8]; 55 | uint8_t carry = parityblock[0] >> 7; 56 | for (int x = (circ_size / 8) - 1; x >= 0; x--) { 57 | uint8_t c = parityblock[x] >> 7; 58 | parityblock[x] = (parityblock[x] << 1) | carry; 59 | carry = c; 60 | } 61 | } 62 | } 63 | 64 | return parity_length_bits / 8; 65 | } 66 | 67 | size_t cats_ldpc_encode(uint8_t *data, size_t len) 68 | { 69 | // Didn't implement the 512-byte LDPC variant - unnecessary and uses a lot of space 70 | // This means we can only encode up to 511 bytes 71 | assert(len < 512); 72 | 73 | // A 128 byte array is needed to support CATS packets up to 511 bytes. 74 | uint8_t parity[128]; 75 | 76 | // Split data into blocks and encode each block 77 | size_t i = 0; 78 | while (i < len) { 79 | cats_ldpc_code_t *code = cats_ldpc_pick_code(len - i); 80 | int data_length_bits = code->data_length_bits; 81 | int data_length = data_length_bits / 8; 82 | 83 | uint8_t chunk[code->code_length_bits / 8]; 84 | memset(chunk, 0xAA, data_length); 85 | memcpy(chunk, data + i, (len - i < data_length) ? (len - i) : data_length); 86 | 87 | size_t parity_len = cats_ldpc_encode_chunk(chunk, code, parity); 88 | memcpy(data + len + i, parity, parity_len); // Parity 89 | 90 | i += parity_len; 91 | } 92 | 93 | size_t new_len = (len * 2) + (i - len) + 2; // (Data+Parity) + (Padded parity) + length 94 | data[new_len - 2] = len; 95 | data[new_len - 1] = len >> 8; 96 | return new_len; 97 | } 98 | -------------------------------------------------------------------------------- /src/codecs/cats/ldpc.h: -------------------------------------------------------------------------------- 1 | #ifndef __CATS_LDPC_H 2 | #define __CATS_LDPC_H 3 | 4 | #include 5 | #include 6 | 7 | size_t cats_ldpc_encode(uint8_t *data, size_t len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/codecs/cats/ldpc_matrices.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ldpc_matrices.h" 5 | 6 | cats_ldpc_code_t tc128 = { 7 | .code_length_bits = 128, 8 | .data_length_bits = 64, 9 | .punctured_bits = 0, 10 | .bf_working_len = 128 + 0, 11 | .circulant_size = 128/8, 12 | .matrix = tc128_matrix, 13 | .matrix_len = 4 14 | }; 15 | 16 | cats_ldpc_code_t tc256 = { 17 | .code_length_bits = 256, 18 | .data_length_bits = 128, 19 | .punctured_bits = 0, 20 | .bf_working_len = 256 + 0, 21 | .circulant_size = 256/8, 22 | .matrix = tc256_matrix, 23 | .matrix_len = 8 24 | }; 25 | 26 | cats_ldpc_code_t tc512 = { 27 | .code_length_bits = 512, 28 | .data_length_bits = 256, 29 | .punctured_bits = 0, 30 | .bf_working_len = 512 + 0, 31 | .circulant_size = 512/8, 32 | .matrix = tc512_matrix, 33 | .matrix_len = 16 34 | }; 35 | 36 | cats_ldpc_code_t tm2048 = { 37 | .code_length_bits = 2048, 38 | .data_length_bits = 1024, 39 | .punctured_bits = 512, 40 | .bf_working_len = 2048 + 512, 41 | .circulant_size = 512/4, 42 | .matrix = tm2048_matrix, 43 | .matrix_len = 128 44 | }; 45 | 46 | const uint64_t tc128_matrix[] = { 47 | 0x0E69166BEF4C0BC2, 0x7766137EBB248418, 0xC480FEB9CD53A713, 0x4EAA22FA465EEA11 48 | }; 49 | 50 | const uint64_t tc256_matrix[] = { 51 | 0x73F5E8390220CE51, 0x36ED68E9F39EB162, 0xBAC812C0BCD24379, 0x4786D9285A09095C, 52 | 0x7DF83F76A5FF4C38, 0x8E6C0D4E025EB712, 0xBAA37B3260CB31C5, 0xD0F66A31FAF511BC 53 | }; 54 | 55 | const uint64_t tc512_matrix[] = { 56 | 0x1D21794A22761FAE, 0x59945014257E130D, 0x74D6054003794014, 0x2DADEB9CA25EF12E, 57 | 0x60E0B6623C5CE512, 0x4D2C81ECC7F469AB, 0x20678DBFB7523ECE, 0x2B54B906A9DBE98C, 58 | 0xF6739BCF54273E77, 0x167BDA120C6C4774, 0x4C071EFF5E32A759, 0x3138670C095C39B5, 59 | 0x28706BD045300258, 0x2DAB85F05B9201D0, 0x8DFDEE2D9D84CA88, 0xB371FAE63A4EB07E 60 | }; 61 | 62 | const uint64_t tm2048_matrix[] = { 63 | 0xCFA794F49FA5A0D8, 0x8BB31D8FCA7EA8BB, 0xA7AE7EE8A68580E3, 0xE922F9E13359B284, 64 | 0x91F72AE8F2D6BF78, 0x30A1F83B3CDBD463, 0xCE95C0EC1F609370, 0xD7E791C870229C1E, 65 | 0x71EF3FDF60E28784, 0x78934DB285DEC9DC, 0x0E95C103008B6BCD, 0xD2DAF85CAE732210, 66 | 0x8326EE83C1FBA56F, 0xDD15B2DDB31FE7F2, 0x3BA0BB43F83C67BD, 0xA1F6AEE46AEF4E62, 67 | 0x565083780CA89ACA, 0xA70CCFB4A888AE35, 0x1210FAD0EC9602CC, 0x8C96B0A86D3996A3, 68 | 0xC0B07FDDA73454C2, 0x5295F72BD5004E80, 0xACCF973FC30261C9, 0x90525AA0CBA006BD, 69 | 0x9F079F09A405F7F8, 0x7AD98429096F2A7E, 0xEB8C9B13B84C06E4, 0x2843A47689A9C528, 70 | 0xDAAA1A175F598DCF, 0xDBAD426CA43AD479, 0x1BA78326E75F38EB, 0x6ED09A45303A6425, 71 | 0x48F42033B7B9A051, 0x49DC839C90291E98, 0x9B2CEBE50A7C2C26, 0x4FC6E7D674063589, 72 | 0xF5B6DEAEBF72106B, 0xA9E6676564C17134, 0x6D5954558D235191, 0x50AAF88D7008E634, 73 | 0x1FA962FBAB864A5F, 0x867C9D6CF4E087AA, 0x5D7AA674BA4B1D8C, 0xD7AE9186F1D3B23B, 74 | 0x047F112791EE97B6, 0x3FB7B58FF3B94E95, 0x93BE39A6365C66B8, 0x77AD316965A72F5B, 75 | 0x1B58F88E49C00DC6, 0xB35855BFF228A088, 0x5C8ED47B61EEC66B, 0x5004FB6E65CBECF3, 76 | 0x77789998FE80925E, 0x0237F570E04C5F5B, 0xED677661EB7FC382, 0x5AB5D5D968C0808C, 77 | 0x2BDB828B19593F41, 0x671B8D0D41DF136C, 0xCB47553C9B3F0EA0, 0x16CC1554C35E6A7D, 78 | 0x97587FEA91D2098E, 0x126EA73CC78658A6, 0xADE19711208186CA, 0x95C7417A15690C45, 79 | 0xBE9C169D889339D9, 0x654C976A85CFD9F7, 0x47C4148E3B4712DA, 0xA3BAD1AD71873D3A, 80 | 0x1CD630C342C5EBB9, 0x183ADE9BEF294E8E, 0x7014C077A5F96F75, 0xBE566C866964D01C, 81 | 0xE72AC43A35AD2166, 0x72EBB3259B77F9BB, 0x18DA8B09194FA1F0, 0xE876A080C9D6A39F, 82 | 0x809B168A3D88E8E9, 0x3D995CE5232C2DC2, 0xC7CFA44A363F628A, 0x668D46C398CAF96F, 83 | 0xD57DBB24AE27ACA1, 0x716F8EA1B8AA1086, 0x7B7796F4A86F1FD5, 0x4C7576AD01C68953, 84 | 0xE75BE79902448236, 0x8F069658F7AAAFB0, 0x975F3AF795E78D25, 0x5871C71B4F4B77F6, 85 | 0x65CD9C359BB2A82D, 0x5353E007166BDD41, 0x2C5447314DB027B1, 0x0B130071AD0398D1, 86 | 0xDE19BC7A6BBCF6A0, 0xFF021AABF12920A5, 0x58BAED484AF89E29, 0xD4DBC170CEF1D369, 87 | 0x4C330B2D11E15B5C, 0xB3815E09605338A6, 0x75E3D1A3541E0E28, 0x4F6556D68D3C8A9E, 88 | 0xE5BB3B297DB62CD2, 0x907F09996967A0F4, 0xFF33AEEE2C8A4A52, 0xFCCF5C39D355C39C, 89 | 0x5FE5F09ABA6BCCE0, 0x2A73401E5F87EAC2, 0xD75702F4F57670DF, 0xA70B1C002F523EEA, 90 | 0x6CE1CE2E05D420CB, 0x867EC0166B8E53A9, 0x9DF9801A1C33058D, 0xD116A0AE7278BBB9, 91 | 0x4CF0B0C792DD8FDB, 0x3ECEAE6F2B7F663D, 0x106A1C296E47C14C, 0x1498B045D57DEFB5, 92 | 0x968F6D8C790263C3, 0x53CF307EF90C1F21, 0x66E6B632F6614E58, 0x267EF096C37718A3, 93 | 0x3D46E5D10E993EB6, 0xDF81518F885EDA1B, 0x6FF518FD48BB8E9D, 0xDBED4AC0F4F5EB89, 94 | 0xBCC64D21A65DB379, 0xABE2E4DC21F109FF, 0x2EC0CE7B5D40973D, 0x13ECF713B01C6F10 95 | }; 96 | -------------------------------------------------------------------------------- /src/codecs/cats/ldpc_matrices.h: -------------------------------------------------------------------------------- 1 | #ifndef __CATS_LDPC_MATRICES_H 2 | #define __CATS_LDPC_MATRICES_H 3 | 4 | typedef struct cats_ldpc_code { 5 | // Code length in bits (data+parity) 6 | int code_length_bits; 7 | // Data length in bits 8 | int data_length_bits; 9 | int punctured_bits; 10 | int bf_working_len; 11 | size_t circulant_size; 12 | size_t matrix_len; 13 | const uint64_t* matrix; 14 | } cats_ldpc_code_t; 15 | 16 | extern cats_ldpc_code_t tc128; 17 | extern cats_ldpc_code_t tc256; 18 | extern cats_ldpc_code_t tc512; 19 | extern cats_ldpc_code_t tm2048; 20 | 21 | extern const uint64_t tc128_matrix[]; 22 | extern const uint64_t tc256_matrix[]; 23 | extern const uint64_t tc512_matrix[]; 24 | extern const uint64_t tm2048_matrix[]; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/codecs/cats/whisker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "whisker.h" 5 | 6 | // Hardware IDs defined here: https://cats.radio/pages/hardware-ids.html 7 | // It's just a way to tell where CATS packets come from. It doesn't affect anything beyond this 8 | #ifdef RS41 9 | #define HARDWARE_ID 0x7d00 10 | #endif 11 | #ifdef DFM17 12 | #define HARDWARE_ID 0x7d01 13 | #endif 14 | 15 | #define CATS_IDENT_TYPE 0x00 16 | #define CATS_GPS_TYPE 0x02 17 | #define CATS_COMMENT_TYPE 0x03 18 | #define CATS_NODE_INFO_TYPE 0x09 19 | 20 | #define CATS_NODE_INFO_HARDWARE_ID_PRESENT 1 21 | #define CATS_NODE_INFO_SOFTWARE_ID_PRESENT 2 22 | #define CATS_NODE_INFO_ANTENNA_GAIN_PRESENT 16 23 | #define CATS_NODE_INFO_TX_POWER_PRESENT 32 24 | #define CATS_NODE_INFO_VOLTAGE_PRESENT 64 25 | #define CATS_NODE_INFO_TX_TEMP_PRESENT 128 26 | 27 | // corresponds to (21 / 4 = 5.25) dBi antenna gain 28 | #define CATS_ANTENNA_GAIN 21 29 | 30 | // Software IDs are basically just a version number. 31 | // Can increment this whenever something big changes, and then 32 | // you can track what version of your software is being used on CATS 33 | #define SOFTWARE_ID 0x00 34 | 35 | static inline void cats_push_f16(cats_packet *p, float val) 36 | { 37 | union { 38 | float f; 39 | uint32_t u; 40 | } fu = { .f = val }; 41 | 42 | assert(sizeof fu.f == sizeof fu.u); 43 | 44 | uint32_t f32 = fu.u; 45 | uint32_t sign = (f32 >> 16) & 0x8000; 46 | uint32_t exponent = ((f32 >> 23) - 127 + 15) & 0x1F; 47 | uint32_t mantissa = f32 & 0x007FFFFF; 48 | uint32_t f16 = sign | (exponent << 10) | (mantissa >> 13); 49 | 50 | p->data[p->len++] = f16; 51 | p->data[p->len++] = f16 >> 8; 52 | } 53 | 54 | static inline void cats_push_f32(cats_packet *p, float val) 55 | { 56 | union { 57 | float f; 58 | uint32_t u; 59 | } fu = { .f = val }; 60 | 61 | assert(sizeof fu.f == sizeof fu.u); 62 | 63 | uint32_t f32 = fu.u; 64 | p->data[p->len++] = f32; 65 | p->data[p->len++] = f32 >> 8; 66 | p->data[p->len++] = f32 >> 16; 67 | p->data[p->len++] = f32 >> 24; 68 | } 69 | 70 | static inline void cats_push_u16(cats_packet *p, uint16_t val) 71 | { 72 | for (int i = 0; i < 2; i++) { 73 | p->data[p->len++] = val >> (i * 8); 74 | } 75 | } 76 | 77 | static inline void cats_push_u32(cats_packet *p, uint32_t val) 78 | { 79 | 80 | for (int i = 0; i < 4; i++) { 81 | p->data[p->len++] = val >> (i * 8); 82 | } 83 | } 84 | 85 | void cats_append_identification_whisker(cats_packet *packet, char *callsign, uint8_t ssid, uint16_t icon) 86 | { 87 | packet->data[packet->len++] = CATS_IDENT_TYPE; 88 | size_t callsign_len = strlen(callsign); 89 | packet->data[packet->len++] = callsign_len + 3; // len 90 | packet->data[packet->len++] = icon & 0xFF; // icon 91 | packet->data[packet->len++] = (icon >> 8) & 0xFF; // icon 92 | memcpy(packet->data + packet->len, callsign, callsign_len); //callsign 93 | packet->len += callsign_len; 94 | packet->data[packet->len++] = ssid; // ssid 95 | } 96 | 97 | void cats_append_gps_whisker(cats_packet *packet, gps_data gps) 98 | { 99 | packet->data[packet->len++] = CATS_GPS_TYPE; 100 | packet->data[packet->len++] = 14; // len 101 | int32_t lat_converted = gps.latitude_degrees_1000000 * (1LL<<31) / 90LL / 10000000LL; 102 | int32_t lon_converted = gps.longitude_degrees_1000000 * (1LL<<31) / 180LL / 10000000LL; 103 | cats_push_u32(packet, lat_converted); // lat 104 | cats_push_u32(packet, lon_converted); // lon 105 | cats_push_f16(packet, gps.altitude_mm / 1000.0); // alt 106 | packet->data[packet->len++] = 0; // max error = 0m 107 | packet->data[packet->len++] = gps.heading_degrees_100000 / 100000.0 * 128.0 / 180.0; // heading 108 | cats_push_f16(packet, gps.ground_speed_cm_per_second / 100.0); // horizontal speed 109 | } 110 | 111 | void cats_append_comment_whisker(cats_packet *packet, char *comment) 112 | { 113 | packet->data[packet->len++] = CATS_COMMENT_TYPE; 114 | size_t comment_len = strlen(comment); 115 | assert(comment_len <= 255); 116 | packet->data[packet->len++] = comment_len; 117 | memcpy(packet->data + packet->len, comment, comment_len); 118 | packet->len += comment_len; 119 | } 120 | 121 | void cats_append_node_info_whisker(cats_packet *packet, telemetry_data *data) 122 | { 123 | uint8_t *d = packet->data; 124 | size_t *l = &packet->len; 125 | 126 | bool has_altitude = GPS_HAS_FIX(data->gps); 127 | float altitude = data->gps.altitude_mm / 1000.0; 128 | 129 | d[(*l)++] = CATS_NODE_INFO_TYPE; 130 | d[(*l)++] = has_altitude ? 14 : 10; // len 131 | // bitmask 132 | d[(*l)++] = 0; 133 | d[(*l)++] = (CATS_IS_BALLOON << 2) | (has_altitude << 1); 134 | d[(*l)++] = 135 | CATS_NODE_INFO_HARDWARE_ID_PRESENT | 136 | CATS_NODE_INFO_SOFTWARE_ID_PRESENT | 137 | CATS_NODE_INFO_ANTENNA_GAIN_PRESENT | 138 | CATS_NODE_INFO_TX_POWER_PRESENT | 139 | CATS_NODE_INFO_VOLTAGE_PRESENT | 140 | CATS_NODE_INFO_TX_TEMP_PRESENT; 141 | 142 | cats_push_u16(packet, HARDWARE_ID); 143 | d[(*l)++] = SOFTWARE_ID; 144 | d[(*l)++] = CATS_ANTENNA_GAIN; // vertical antenna -> ~5.25 dBi gain 145 | d[(*l)++] = CATS_REPORTED_TX_POWER_DBM * 4.0; // TX power 146 | d[(*l)++] = data->battery_voltage_millivolts / 100; // voltage 147 | d[(*l)++] = data->internal_temperature_celsius_100 / 100; // transmitter temperature 148 | if (has_altitude) { 149 | cats_push_f32(packet, altitude); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/codecs/cats/whisker.h: -------------------------------------------------------------------------------- 1 | #ifndef __WHISKER_H 2 | #define __WHISKER_H 3 | 4 | #include 5 | 6 | #include "cats.h" 7 | #include "../../gps.h" 8 | #include "../../telemetry.h" 9 | 10 | void cats_append_identification_whisker(cats_packet *packet, char *callsign, uint8_t ssid, uint16_t icon); 11 | void cats_append_gps_whisker(cats_packet *packet, gps_data gps); 12 | void cats_append_comment_whisker(cats_packet *packet, char *message); 13 | void cats_append_node_info_whisker(cats_packet *packet, telemetry_data *data); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/codecs/cats/whiten.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "whiten.h" 4 | 5 | uint8_t lfsr_byte(uint16_t *state); 6 | void lfsr(uint16_t *state); 7 | 8 | void cats_whiten(uint8_t *data, uint8_t len) 9 | { 10 | uint16_t state = 0xE9CF; 11 | 12 | for (int i = 0; i < len; i++) { 13 | uint8_t b = lfsr_byte(&state); 14 | data[i] ^= b; 15 | } 16 | } 17 | 18 | uint8_t lfsr_byte(uint16_t *state) 19 | { 20 | uint8_t out = 0; 21 | for (int i = 7; i >= 0; i--) { 22 | out |= (*state & 1) << i; 23 | lfsr(state); 24 | } 25 | 26 | return out; 27 | } 28 | 29 | void lfsr(uint16_t *state) 30 | { 31 | bool lsb = *state & 1; 32 | *state >>= 1; 33 | if (lsb) { 34 | *state ^= 0xB400; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/codecs/cats/whiten.h: -------------------------------------------------------------------------------- 1 | #ifndef __CATS_WHITEN_H 2 | #define __CATS_WHITEN_H 3 | 4 | #include 5 | #include 6 | 7 | void cats_whiten(uint8_t *data, uint8_t len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/codecs/fsk/fsk.h: -------------------------------------------------------------------------------- 1 | #ifndef __FSK_H 2 | #define __FSK_H 3 | 4 | #define FSK_TONE_COUNT_MAX 20 5 | 6 | typedef struct _fsk_tone { 7 | int8_t index; 8 | uint32_t frequency_hz_100; 9 | } fsk_tone; 10 | 11 | typedef struct _fsk_encoder { 12 | void *priv; 13 | } fsk_encoder; 14 | 15 | typedef struct _fsk_encoder_api { 16 | /** 17 | * @param encoder 18 | * @param tone_count Set to number of tones in returned array or 0 if not used 19 | * @param tones Set to point to array of FSK tones or NULL if not used 20 | */ 21 | void (*get_tones)(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones); 22 | 23 | /** 24 | * @param encoder 25 | * @return Tone spacing in 1/100th of Hz or 0 if not used 26 | */ 27 | uint32_t (*get_tone_spacing)(fsk_encoder *encoder); 28 | 29 | /** 30 | * @param encoder 31 | * @return Symbol rate in symbols per second or 0 if not used 32 | */ 33 | uint32_t (*get_symbol_rate)(fsk_encoder *encoder); 34 | 35 | /** 36 | * @param encoder 37 | * @return Symbol delay in 1/100th of millisecond or 0 if not used 38 | */ 39 | uint32_t (*get_symbol_delay)(fsk_encoder *encoder); 40 | 41 | /** 42 | * @param encoder 43 | * @return Pointer to packet data 44 | */ 45 | uint8_t *(*get_data)(fsk_encoder *encoder); 46 | 47 | /** 48 | * @param encoder 49 | * @return Length of packet data 50 | */ 51 | uint16_t (*get_data_len)(fsk_encoder *encoder); 52 | 53 | void (*set_data)(fsk_encoder *encoder, uint16_t data_length, uint8_t *data); 54 | 55 | int8_t (*next_tone)(fsk_encoder *encoder); 56 | } fsk_encoder_api; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_common.c: -------------------------------------------------------------------------------- 1 | #include "horus_common.h" 2 | 3 | uint16_t calculate_crc16_checksum(char *string, int len) 4 | { 5 | uint16_t crc = 0xffff; 6 | char i; 7 | int ptr = 0; 8 | while (ptr < len) { 9 | ptr++; 10 | crc = crc ^ (*(string++) << 8); 11 | for (i = 0; i < 8; i++) { 12 | if (crc & 0x8000) 13 | crc = (uint16_t)((crc << 1) ^ 0x1021); 14 | else 15 | crc <<= 1; 16 | } 17 | } 18 | return crc; 19 | } 20 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_common.h: -------------------------------------------------------------------------------- 1 | #ifndef __HORUS_COMMON_H 2 | #define __HORUS_COMMON_H 3 | 4 | #include 5 | 6 | uint16_t calculate_crc16_checksum(char *string, int len); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_l2.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------*\ 2 | 3 | FILE........: horus_l2.h 4 | AUTHOR......: David Rowe 5 | DATE CREATED: Dec 2015 6 | 7 | \*---------------------------------------------------------------------------*/ 8 | 9 | #ifndef __HORUS_L2__ 10 | #define __HORUS_L2__ 11 | 12 | int horus_l2_get_num_tx_data_bytes(int num_payload_data_bytes); 13 | 14 | /* returns number of output bytes in output_tx_data */ 15 | int horus_l2_encode_tx_packet(unsigned char *output_tx_data, 16 | unsigned char *input_payload_data, 17 | int num_payload_data_bytes); 18 | 19 | void horus_l2_decode_rx_packet(unsigned char *output_payload_data, 20 | unsigned char *input_rx_data, 21 | int num_payload_data_bytes); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_packet_v1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "horus_packet_v1.h" 3 | #include "horus_common.h" 4 | 5 | volatile uint16_t horus_v1_packet_counter = 0; 6 | 7 | size_t horus_packet_v1_create(uint8_t *payload, size_t length, telemetry_data *data, uint8_t payload_id) 8 | { 9 | if (length < sizeof(horus_packet_v1)) { 10 | return 0; 11 | } 12 | 13 | gps_data *gps_data = &data->gps; 14 | 15 | float float_lat = (float) gps_data->latitude_degrees_1000000 / 10000000.0f; 16 | float float_lon = (float) gps_data->longitude_degrees_1000000 / 10000000.0f; 17 | 18 | uint8_t volts_scaled = (uint8_t)(255 * (float) data->battery_voltage_millivolts / 5000.0f); 19 | 20 | horus_v1_packet_counter++; 21 | 22 | // Assemble a binary packet 23 | horus_packet_v1 horus_packet; 24 | 25 | horus_packet.PayloadID = payload_id; 26 | horus_packet.Counter = horus_v1_packet_counter; 27 | horus_packet.Hours = gps_data->hours; 28 | horus_packet.Minutes = gps_data->minutes; 29 | horus_packet.Seconds = gps_data->seconds; 30 | horus_packet.Latitude = float_lat; 31 | horus_packet.Longitude = float_lon; 32 | horus_packet.Altitude = (uint16_t)((gps_data->altitude_mm > 0 ? gps_data->altitude_mm : 0) / 1000); 33 | horus_packet.Speed = (uint8_t)((float) gps_data->ground_speed_cm_per_second * 0.036); 34 | 35 | // Temporary pDOP info, to determine suitable pDOP limits. 36 | // float pDop = (float)gps_data.pDOP/10.0; 37 | // if (pDop>255.0){ 38 | // pDop = 255.0; 39 | // } 40 | // mfsk_buffer.Speed = (uint8_t)pDop; 41 | horus_packet.BattVoltage = volts_scaled; 42 | horus_packet.Sats = gps_data->satellites_visible; 43 | horus_packet.Temp = (int8_t) ((float) data->internal_temperature_celsius_100 / 100.0f); 44 | 45 | // Add onto the sats_raw value to indicate if the GPS is in regular tracking (+100) 46 | // or power optimized tracker (+200) modes. 47 | if (gps_data->power_safe_mode_state == POWER_SAFE_MODE_STATE_TRACKING) { 48 | horus_packet.Sats += 100; 49 | } else if (gps_data->power_safe_mode_state == POWER_SAFE_MODE_STATE_POWER_OPTIMIZED_TRACKING) { 50 | horus_packet.Sats += 200; 51 | } else if (gps_data->power_safe_mode_state == POWER_SAFE_MODE_STATE_INACTIVE) { 52 | // Inactive = Most parts of the receiver are switched off 53 | horus_packet.Sats += 50; 54 | } 55 | 56 | horus_packet.Checksum = (uint16_t) calculate_crc16_checksum((char *) &horus_packet, sizeof(horus_packet) - 2); 57 | 58 | memcpy(payload, &horus_packet, sizeof(horus_packet)); 59 | 60 | return sizeof(horus_packet); 61 | } 62 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_packet_v1.h: -------------------------------------------------------------------------------- 1 | #ifndef __HORUS_PACKET_V1_H 2 | #define __HORUS_PACKET_V1_H 3 | 4 | #include 5 | #include 6 | #include "telemetry.h" 7 | 8 | // Horus Binary v1 Packet Format 9 | // See: https://github.com/projecthorus/horusdemodlib/wiki/4-Packet-Format-Details#horus-binary-v1-22-bytes 10 | // Note that we need to pack this to 1-byte alignment, hence the #pragma flags below 11 | // Refer: https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html 12 | #pragma pack(push, 1) 13 | typedef struct _horus_packet_v1 { 14 | uint8_t PayloadID; // Payload ID (0-255) 15 | uint16_t Counter; // Sequence number 16 | uint8_t Hours; // Time of day, Hours 17 | uint8_t Minutes; // Time of day, Minutes 18 | uint8_t Seconds; // Time of day, Seconds 19 | float Latitude; // Latitude in degrees 20 | float Longitude; // Longitude in degrees 21 | uint16_t Altitude; // Altitude in meters 22 | uint8_t Speed; // Speed in km/h (although spec says: Knots (1-255 knots)) 23 | uint8_t Sats; // Number of GPS satellites visible 24 | int8_t Temp; // Temperature in Celsius, as a signed value (-128 to +128, though sensor limited to -64 to +64 deg C) 25 | uint8_t BattVoltage; // 0 = 0v, 255 = 5.0V, linear steps in-between. 26 | uint16_t Checksum; // CRC16-CCITT Checksum. 27 | } horus_packet_v1; // __attribute__ ((packed)); // Doesn't work? 28 | #pragma pack(pop) 29 | 30 | size_t horus_packet_v1_create(uint8_t *payload, size_t length, telemetry_data *data, uint8_t payload_id); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_packet_v2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "horus_packet_v2.h" 3 | #include "horus_common.h" 4 | #include "config.h" 5 | 6 | volatile uint16_t horus_v2_packet_counter = 0; 7 | 8 | size_t horus_packet_v2_create(uint8_t *payload, size_t length, telemetry_data *data, uint16_t payload_id) 9 | { 10 | if (length < sizeof(horus_packet_v2)) { 11 | return 0; 12 | } 13 | 14 | gps_data *gps_data = &data->gps; 15 | 16 | float float_lat = (float) gps_data->latitude_degrees_1000000 / 10000000.0f; 17 | float float_lon = (float) gps_data->longitude_degrees_1000000 / 10000000.0f; 18 | 19 | uint8_t volts_scaled = (uint8_t) (255 * (float) data->battery_voltage_millivolts / 5000.0f); 20 | 21 | horus_v2_packet_counter++; 22 | 23 | // Assemble a binary packet 24 | horus_packet_v2 horus_packet; 25 | 26 | horus_packet.PayloadID = payload_id; 27 | horus_packet.Counter = horus_v2_packet_counter; 28 | horus_packet.Hours = gps_data->hours; 29 | horus_packet.Minutes = gps_data->minutes; 30 | horus_packet.Seconds = gps_data->seconds; 31 | horus_packet.Latitude = float_lat; 32 | horus_packet.Longitude = float_lon; 33 | horus_packet.Altitude = (uint16_t) ((gps_data->altitude_mm > 0 ? gps_data->altitude_mm : 0) / 1000); 34 | horus_packet.Speed = (uint8_t) ((float) gps_data->ground_speed_cm_per_second * 0.036); 35 | 36 | horus_packet.BattVoltage = volts_scaled; 37 | horus_packet.Sats = gps_data->satellites_visible; 38 | horus_packet.Temp = (int8_t) ((float) data->internal_temperature_celsius_100 / 100.0f); 39 | 40 | // Add onto the sats_raw value to indicate if the GPS is in regular tracking (+100) 41 | // or power optimized tracker (+200) modes. 42 | if (gps_data->power_safe_mode_state == POWER_SAFE_MODE_STATE_TRACKING) { 43 | horus_packet.Sats += 100; 44 | } else if (gps_data->power_safe_mode_state == POWER_SAFE_MODE_STATE_POWER_OPTIMIZED_TRACKING) { 45 | horus_packet.Sats += 200; 46 | } else if (gps_data->power_safe_mode_state == POWER_SAFE_MODE_STATE_INACTIVE) { 47 | // Inactive = Most parts of the receiver are switched off 48 | horus_packet.Sats += 50; 49 | } 50 | 51 | memset(horus_packet.CustomData, 0, sizeof(horus_packet.CustomData)); 52 | 53 | uint8_t *custom_data_pointer = horus_packet.CustomData; 54 | 55 | if (radsens_enabled) { 56 | // Unit: µR/h 57 | uint16_t ext_radiation_intensity_uR_h = (uint16_t) data->radiation_intensity_uR_h; 58 | memcpy(custom_data_pointer, &ext_radiation_intensity_uR_h, sizeof(ext_radiation_intensity_uR_h)); 59 | custom_data_pointer += sizeof(ext_radiation_intensity_uR_h); 60 | } else { 61 | // Unit: cm/s 62 | int16_t gps_climb_cm_per_second = (int16_t) gps_data->climb_cm_per_second; 63 | memcpy(custom_data_pointer, &gps_climb_cm_per_second, sizeof(gps_climb_cm_per_second)); 64 | custom_data_pointer += sizeof(gps_climb_cm_per_second); 65 | } 66 | 67 | // Unit: Celsius * 10 68 | int16_t ext_temp_celsius_10 = (int16_t) (data->temperature_celsius_100 / 10.0f); 69 | memcpy(custom_data_pointer, &ext_temp_celsius_10, sizeof(ext_temp_celsius_10)); 70 | custom_data_pointer += sizeof(ext_temp_celsius_10); 71 | 72 | // Unit: % 73 | uint8_t ext_humidity_percentage = (uint8_t) (data->humidity_percentage_100 / 100.0f); 74 | memcpy(custom_data_pointer, &ext_humidity_percentage, sizeof(ext_humidity_percentage)); 75 | custom_data_pointer += sizeof(ext_humidity_percentage); 76 | 77 | // Unit: mbar * 10 78 | uint16_t ext_pressure_mbar = (uint16_t) (data->pressure_mbar_100 / 10.0f); 79 | memcpy(custom_data_pointer, &ext_pressure_mbar, sizeof(ext_pressure_mbar)); 80 | 81 | if (pulse_counter_enabled || radsens_enabled) { 82 | // Unit: pulse count 83 | custom_data_pointer += sizeof(ext_pressure_mbar); 84 | uint16_t ext_pulse_count = (uint16_t) data->pulse_count; 85 | memcpy(custom_data_pointer, &ext_pulse_count, sizeof(ext_pulse_count)); 86 | } 87 | 88 | horus_packet.Checksum = (uint16_t) calculate_crc16_checksum((char *) &horus_packet, 89 | sizeof(horus_packet) - sizeof(horus_packet.Checksum)); 90 | 91 | memcpy(payload, &horus_packet, sizeof(horus_packet)); 92 | 93 | return sizeof(horus_packet); 94 | } 95 | -------------------------------------------------------------------------------- /src/codecs/horus/horus_packet_v2.h: -------------------------------------------------------------------------------- 1 | #ifndef __HORUS_PACKET_V2_H 2 | #define __HORUS_PACKET_V2_H 3 | 4 | #include 5 | #include 6 | #include "telemetry.h" 7 | 8 | // Horus Binary v2 Packet Format 9 | // See: https://github.com/projecthorus/horusdemodlib/wiki/5-Customising-a-Horus-Binary-v2-Packet 10 | // Note that we need to pack this to 1-byte alignment, hence the #pragma flags below 11 | // Refer: https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html 12 | #pragma pack(push, 1) 13 | typedef struct _horus_packet_v2 { 14 | uint16_t PayloadID; // Payload ID (0-65535) 15 | uint16_t Counter; // Sequence number 16 | uint8_t Hours; // Time of day, Hours 17 | uint8_t Minutes; // Time of day, Minutes 18 | uint8_t Seconds; // Time of day, Seconds 19 | float Latitude; // Latitude in degrees 20 | float Longitude; // Longitude in degrees 21 | uint16_t Altitude; // Altitude in meters 22 | uint8_t Speed; // Speed in km/h 23 | uint8_t Sats; // Number of GPS satellites visible 24 | int8_t Temp; // Temperature in Celsius, as a signed value (-128 to +128, though sensor limited to -64 to +64 deg C) 25 | uint8_t BattVoltage; // 0 = 0v, 255 = 5.0V, linear steps in-between. 26 | uint8_t CustomData[9]; // Custom data, see: https://github.com/projecthorus/horusdemodlib/wiki/5-Customising-a-Horus-Binary-v2-Packet#interpreting-the-custom-data-section 27 | uint16_t Checksum; // CRC16-CCITT Checksum. 28 | } horus_packet_v2; // __attribute__ ((packed)); // Doesn't work? 29 | #pragma pack(pop) 30 | 31 | size_t horus_packet_v2_create(uint8_t *payload, size_t length, telemetry_data *data, uint16_t payload_id); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/codecs/jtencode/jtencode.h: -------------------------------------------------------------------------------- 1 | #ifndef __JTENCODE_H 2 | #define __JTENCODE_H 3 | 4 | #include "codecs/fsk/fsk.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef enum _jtencode_mode_type { 11 | JTENCODE_MODE_JT9 = 0, 12 | JTENCODE_MODE_JT65, 13 | JTENCODE_MODE_JT4, 14 | JTENCODE_MODE_WSPR, 15 | JTENCODE_MODE_FT8, 16 | JTENCODE_MODE_FSQ_2, 17 | JTENCODE_MODE_FSQ_3, 18 | JTENCODE_MODE_FSQ_4_5, 19 | JTENCODE_MODE_FSQ_6, 20 | } jtencode_mode_type; 21 | 22 | bool jtencode_encoder_new(fsk_encoder *encoder, size_t symbol_data_length, uint8_t *symbol_data, 23 | jtencode_mode_type mode_type, char *wspr_callsign, char *wspr_locator, uint8_t wspr_dbm, 24 | char *fsq_callsign_from); 25 | void jtencode_encoder_destroy(fsk_encoder *encoder); 26 | 27 | extern fsk_encoder_api jtencode_fsk_encoder_api; 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/codecs/jtencode/lib/crc14.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Functions and types for CRC checks. 4 | * 5 | * Generated on Thu Dec 6 17:52:34 2018 6 | * by pycrc v0.9.1, https://pycrc.org 7 | * using the configuration: 8 | * - Width = 14 9 | * - Poly = 0x2757 10 | * - XorIn = Undefined 11 | * - ReflectIn = Undefined 12 | * - XorOut = Undefined 13 | * - ReflectOut = Undefined 14 | * - Algorithm = bit-by-bit 15 | */ 16 | #include "crc14.h" /* include the header file generated with pycrc */ 17 | #include 18 | #include 19 | #include 20 | 21 | static crc_t crc_reflect(crc_t data, size_t data_len); 22 | 23 | 24 | 25 | crc_t crc_reflect(crc_t data, size_t data_len) 26 | { 27 | unsigned int i; 28 | crc_t ret; 29 | 30 | ret = data & 0x01; 31 | for (i = 1; i < data_len; i++) { 32 | data >>= 1; 33 | ret = (ret << 1) | (data & 0x01); 34 | } 35 | return ret; 36 | } 37 | 38 | 39 | crc_t crc_init(const crc_cfg_t *cfg) 40 | { 41 | unsigned int i; 42 | bool bit; 43 | crc_t crc = cfg->xor_in; 44 | for (i = 0; i < 14; i++) { 45 | bit = crc & 0x01; 46 | if (bit) { 47 | crc = ((crc ^ 0x2757) >> 1) | 0x2000; 48 | } else { 49 | crc >>= 1; 50 | } 51 | } 52 | return crc & 0x3fff; 53 | } 54 | 55 | 56 | crc_t crc_update(const crc_cfg_t *cfg, crc_t crc, const void *data, size_t data_len) 57 | { 58 | const unsigned char *d = (const unsigned char *)data; 59 | unsigned int i; 60 | bool bit; 61 | unsigned char c; 62 | 63 | while (data_len--) { 64 | if (cfg->reflect_in) { 65 | c = crc_reflect(*d++, 8); 66 | } else { 67 | c = *d++; 68 | } 69 | for (i = 0; i < 8; i++) { 70 | bit = crc & 0x2000; 71 | crc = (crc << 1) | ((c >> (7 - i)) & 0x01); 72 | if (bit) { 73 | crc ^= 0x2757; 74 | } 75 | } 76 | crc &= 0x3fff; 77 | } 78 | return crc & 0x3fff; 79 | } 80 | 81 | 82 | crc_t crc_finalize(const crc_cfg_t *cfg, crc_t crc) 83 | { 84 | unsigned int i; 85 | bool bit; 86 | 87 | for (i = 0; i < 14; i++) { 88 | bit = crc & 0x2000; 89 | crc <<= 1; 90 | if (bit) { 91 | crc ^= 0x2757; 92 | } 93 | } 94 | if (cfg->reflect_out) { 95 | crc = crc_reflect(crc, 14); 96 | } 97 | return (crc ^ cfg->xor_out) & 0x3fff; 98 | } 99 | -------------------------------------------------------------------------------- /src/codecs/jtencode/lib/crc14.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Functions and types for CRC checks. 4 | * 5 | * Generated on Thu Dec 6 17:52:01 2018 6 | * by pycrc v0.9.1, https://pycrc.org 7 | * using the configuration: 8 | * - Width = 14 9 | * - Poly = 0x2757 10 | * - XorIn = Undefined 11 | * - ReflectIn = Undefined 12 | * - XorOut = Undefined 13 | * - ReflectOut = Undefined 14 | * - Algorithm = bit-by-bit 15 | * 16 | * This file defines the functions crc_init(), crc_update() and crc_finalize(). 17 | * 18 | * The crc_init() function returns the inital \c crc value and must be called 19 | * before the first call to crc_update(). 20 | * Similarly, the crc_finalize() function must be called after the last call 21 | * to crc_update(), before the \c crc is being used. 22 | * is being used. 23 | * 24 | * The crc_update() function can be called any number of times (including zero 25 | * times) in between the crc_init() and crc_finalize() calls. 26 | * 27 | * This pseudo-code shows an example usage of the API: 28 | * \code{.c} 29 | * crc_cfg_t cfg = { 30 | * 0, // reflect_in 31 | * 0, // xor_in 32 | * 0, // reflect_out 33 | * 0, // xor_out 34 | * }; 35 | * crc_t crc; 36 | * unsigned char data[MAX_DATA_LEN]; 37 | * size_t data_len; 38 | * 39 | * crc = crc_init(&cfg); 40 | * while ((data_len = read_data(data, MAX_DATA_LEN)) > 0) { 41 | * crc = crc_update(&cfg, crc, data, data_len); 42 | * } 43 | * crc = crc_finalize(&cfg, crc); 44 | * \endcode 45 | */ 46 | #ifndef CRC14_H 47 | #define CRC14_H 48 | 49 | #include 50 | #include 51 | #include 52 | 53 | #ifdef __cplusplus 54 | extern "C" { 55 | #endif 56 | 57 | 58 | /** 59 | * The definition of the used algorithm. 60 | * 61 | * This is not used anywhere in the generated code, but it may be used by the 62 | * application code to call algorithm-specific code, if desired. 63 | */ 64 | #define CRC_ALGO_BIT_BY_BIT 1 65 | 66 | 67 | /** 68 | * The type of the CRC values. 69 | * 70 | * This type must be big enough to contain at least 14 bits. 71 | */ 72 | typedef uint_fast16_t crc_t; 73 | 74 | 75 | /** 76 | * The configuration type of the CRC algorithm. 77 | */ 78 | typedef struct { 79 | bool reflect_in; /*!< Whether the input shall be reflected or not */ 80 | crc_t xor_in; /*!< The initial value of the register */ 81 | bool reflect_out; /*!< Whether the output shall be reflected or not */ 82 | crc_t xor_out; /*!< The value which shall be XOR-ed to the final CRC value */ 83 | } crc_cfg_t; 84 | 85 | 86 | /** 87 | * Calculate the initial crc value. 88 | * 89 | * \param[in] cfg A pointer to an initialised crc_cfg_t structure. 90 | * \return The initial crc value. 91 | */ 92 | crc_t crc_init(const crc_cfg_t *cfg); 93 | 94 | 95 | /** 96 | * Update the crc value with new data. 97 | * 98 | * \param[in] crc The current crc value. 99 | * \param[in] cfg A pointer to an initialised crc_cfg_t structure. 100 | * \param[in] data Pointer to a buffer of \a data_len bytes. 101 | * \param[in] data_len Number of bytes in the \a data buffer. 102 | * \return The updated crc value. 103 | */ 104 | crc_t crc_update(const crc_cfg_t *cfg, crc_t crc, const void *data, size_t data_len); 105 | 106 | 107 | /** 108 | * Calculate the final crc value. 109 | * 110 | * \param[in] cfg A pointer to an initialised crc_cfg_t structure. 111 | * \param[in] crc The current crc value. 112 | * \return The final crc value. 113 | */ 114 | crc_t crc_finalize(const crc_cfg_t *cfg, crc_t crc); 115 | 116 | 117 | #ifdef __cplusplus 118 | } /* closing brace for extern "C" */ 119 | #endif 120 | 121 | #endif /* CRC14_H */ 122 | -------------------------------------------------------------------------------- /src/codecs/jtencode/lib/encode_rs.h: -------------------------------------------------------------------------------- 1 | /* The guts of the Reed-Solomon encoder, meant to be #included 2 | * into a function body with the following typedefs, macros and variables supplied 3 | * according to the code parameters: 4 | 5 | * data_t - a typedef for the data symbol 6 | * data_t data[] - array of NN-NROOTS-PAD and type data_t to be encoded 7 | * data_t parity[] - an array of NROOTS and type data_t to be written with parity symbols 8 | * NROOTS - the number of roots in the RS code generator polynomial, 9 | * which is the same as the number of parity symbols in a block. 10 | Integer variable or literal. 11 | * 12 | * NN - the total number of symbols in a RS block. Integer variable or literal. 13 | * PAD - the number of pad symbols in a block. Integer variable or literal. 14 | * ALPHA_TO - The address of an array of NN elements to convert Galois field 15 | * elements in index (log) form to polynomial form. Read only. 16 | * INDEX_OF - The address of an array of NN elements to convert Galois field 17 | * elements in polynomial form to index (log) form. Read only. 18 | * MODNN - a function to reduce its argument modulo NN. May be inline or a macro. 19 | * GENPOLY - an array of NROOTS+1 elements containing the generator polynomial in index form 20 | 21 | * The memset() and memmove() functions are used. The appropriate header 22 | * file declaring these functions (usually ) must be included by the calling 23 | * program. 24 | 25 | * Copyright 2004, Phil Karn, KA9Q 26 | * May be used under the terms of the GNU Lesser General Public License (LGPL) 27 | */ 28 | 29 | 30 | #undef A0 31 | #define A0 (NN) /* Special reserved value encoding zero in index form */ 32 | 33 | { 34 | int i, j; 35 | data_t feedback; 36 | 37 | memset(parity,0,NROOTS*sizeof(data_t)); 38 | 39 | for(i=0;i) must be included by the calling 29 | * program. 30 | */ 31 | 32 | #include 33 | #include "JTEncode.h" 34 | #include "int.h" 35 | #include "rs_common.h" 36 | 37 | void JTEncode::encode_rs_int(void *p, data_t *data, data_t *parity) 38 | { 39 | struct rs *rs = (struct rs *)p; 40 | 41 | #undef A_0 42 | #define A_0 (NN) /* Special reserved value encoding zero in index form */ 43 | 44 | { 45 | int i, j; 46 | data_t feedback; 47 | 48 | memset(parity,0,NROOTS*sizeof(data_t)); 49 | 50 | for(i=0;i 8*sizeof(data_t)){ 14 | goto done; 15 | } 16 | 17 | if(fcr < 0 || fcr >= (1<= (1<= (1<= ((1<mm = symsize; 31 | rs->nn = (1<pad = pad; 33 | 34 | rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); 35 | if(rs->alpha_to == NULL){ 36 | free(rs); 37 | rs = NULL; 38 | goto done; 39 | } 40 | rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); 41 | if(rs->index_of == NULL){ 42 | free(rs->alpha_to); 43 | free(rs); 44 | rs = NULL; 45 | goto done; 46 | } 47 | 48 | /* Generate Galois field lookup tables */ 49 | rs->index_of[0] = A0; /* log(zero) = -inf */ 50 | rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */ 51 | sr = 1; 52 | for(i=0;inn;i++){ 53 | rs->index_of[sr] = i; 54 | rs->alpha_to[i] = sr; 55 | sr <<= 1; 56 | if(sr & (1<nn; 59 | } 60 | if(sr != 1){ 61 | /* field generator polynomial is not primitive! */ 62 | free(rs->alpha_to); 63 | free(rs->index_of); 64 | free(rs); 65 | rs = NULL; 66 | goto done; 67 | } 68 | 69 | /* Form RS code generator polynomial from its roots */ 70 | rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1)); 71 | if(rs->genpoly == NULL){ 72 | free(rs->alpha_to); 73 | free(rs->index_of); 74 | free(rs); 75 | rs = NULL; 76 | goto done; 77 | } 78 | rs->fcr = fcr; 79 | rs->prim = prim; 80 | rs->nroots = nroots; 81 | 82 | /* Find prim-th root of 1, used in decoding */ 83 | for(iprim=1;(iprim % prim) != 0;iprim += rs->nn) 84 | ; 85 | rs->iprim = iprim / prim; 86 | 87 | rs->genpoly[0] = 1; 88 | for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) { 89 | rs->genpoly[i+1] = 1; 90 | 91 | /* Multiply rs->genpoly[] by @**(root + x) */ 92 | for (j = i; j > 0; j--){ 93 | if (rs->genpoly[j] != 0) 94 | rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)]; 95 | else 96 | rs->genpoly[j] = rs->genpoly[j-1]; 97 | } 98 | /* rs->genpoly[0] can never be zero */ 99 | rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)]; 100 | } 101 | /* convert rs->genpoly[] to index form for quicker encoding */ 102 | for (i = 0; i <= nroots; i++) 103 | rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; 104 | done:; 105 | 106 | //} 107 | -------------------------------------------------------------------------------- /src/codecs/jtencode/lib/init_rs_int.cpp: -------------------------------------------------------------------------------- 1 | /* Initialize a RS codec 2 | * 3 | * Copyright 2002 Phil Karn, KA9Q 4 | * May be used under the terms of the GNU Lesser General Public License (LGPL) 5 | * 6 | * Slightly modified by Jason Milldrum NT7S, 2015 to fit into the Arduino framework 7 | */ 8 | 9 | #include 10 | #include 11 | #include "JTEncode.h" 12 | #include "rs_common.h" 13 | 14 | void JTEncode::free_rs_int(void * p) 15 | { 16 | struct rs *rs = (struct rs *)p; 17 | 18 | free(rs->alpha_to); 19 | free(rs->index_of); 20 | free(rs->genpoly); 21 | free(rs); 22 | } 23 | 24 | void * JTEncode::init_rs_int(int symsize, int gfpoly, int fcr, int prim, 25 | int nroots, int pad) 26 | { 27 | struct rs *rs; 28 | 29 | int i, j, sr,root,iprim; 30 | 31 | rs = ((struct rs *)0); 32 | /* Check parameter ranges */ 33 | if(symsize < 0 || (unsigned) symsize > 8*sizeof(data_t)){ 34 | goto done; 35 | } 36 | 37 | if(fcr < 0 || fcr >= (1<= (1<= (1<= ((1<mm = symsize; 51 | rs->nn = (1<pad = pad; 53 | 54 | rs->alpha_to = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); 55 | if(rs->alpha_to == NULL){ 56 | free(rs); 57 | rs = ((struct rs *)0); 58 | goto done; 59 | } 60 | rs->index_of = (data_t *)malloc(sizeof(data_t)*(rs->nn+1)); 61 | if(rs->index_of == NULL){ 62 | free(rs->alpha_to); 63 | free(rs); 64 | rs = ((struct rs *)0); 65 | goto done; 66 | } 67 | 68 | /* Generate Galois field lookup tables */ 69 | rs->index_of[0] = A_0; /* log(zero) = -inf */ 70 | rs->alpha_to[A_0] = 0; /* alpha**-inf = 0 */ 71 | sr = 1; 72 | for(i=0;inn;i++){ 73 | rs->index_of[sr] = i; 74 | rs->alpha_to[i] = sr; 75 | sr <<= 1; 76 | if(sr & (1<nn; 79 | } 80 | if(sr != 1){ 81 | /* field generator polynomial is not primitive! */ 82 | free(rs->alpha_to); 83 | free(rs->index_of); 84 | free(rs); 85 | rs = ((struct rs *)0); 86 | goto done; 87 | } 88 | 89 | /* Form RS code generator polynomial from its roots */ 90 | rs->genpoly = (data_t *)malloc(sizeof(data_t)*(nroots+1)); 91 | if(rs->genpoly == NULL){ 92 | free(rs->alpha_to); 93 | free(rs->index_of); 94 | free(rs); 95 | rs = ((struct rs *)0); 96 | goto done; 97 | } 98 | rs->fcr = fcr; 99 | rs->prim = prim; 100 | rs->nroots = nroots; 101 | 102 | /* Find prim-th root of 1, used in decoding */ 103 | for(iprim=1;(iprim % prim) != 0;iprim += rs->nn) 104 | ; 105 | rs->iprim = iprim / prim; 106 | 107 | rs->genpoly[0] = 1; 108 | for (i = 0,root=fcr*prim; i < nroots; i++,root += prim) { 109 | rs->genpoly[i+1] = 1; 110 | 111 | /* Multiply rs->genpoly[] by @**(root + x) */ 112 | for (j = i; j > 0; j--){ 113 | if (rs->genpoly[j] != 0) 114 | rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)]; 115 | else 116 | rs->genpoly[j] = rs->genpoly[j-1]; 117 | } 118 | /* rs->genpoly[0] can never be zero */ 119 | rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)]; 120 | } 121 | /* convert rs->genpoly[] to index form for quicker encoding */ 122 | for (i = 0; i <= nroots; i++) 123 | rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; 124 | done:; 125 | 126 | return rs; 127 | } 128 | -------------------------------------------------------------------------------- /src/codecs/jtencode/lib/int.h: -------------------------------------------------------------------------------- 1 | /* Stuff specific to the general (integer) version of the Reed-Solomon codecs 2 | * 3 | * Copyright 2003, Phil Karn, KA9Q 4 | * May be used under the terms of the GNU Lesser General Public License (LGPL) 5 | */ 6 | #ifndef INT_H_ 7 | #define INT_H_ 8 | 9 | #include 10 | 11 | typedef uint8_t data_t; 12 | //typedef unsigned int data_t; 13 | 14 | #define MODNN(x) modnn(rs,x) 15 | #define MM (rs->mm) 16 | #define NN (rs->nn) 17 | #define ALPHA_TO (rs->alpha_to) 18 | #define INDEX_OF (rs->index_of) 19 | #define GENPOLY (rs->genpoly) 20 | #define NROOTS (rs->nroots) 21 | #define FCR (rs->fcr) 22 | #define PRIM (rs->prim) 23 | #define IPRIM (rs->iprim) 24 | #define PAD (rs->pad) 25 | #define A_0 (NN) 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/codecs/jtencode/lib/rs_common.h: -------------------------------------------------------------------------------- 1 | /* Stuff common to all the general-purpose Reed-Solomon codecs 2 | * Copyright 2004 Phil Karn, KA9Q 3 | * May be used under the terms of the GNU Lesser General Public License (LGPL) 4 | */ 5 | 6 | #ifndef RS_COMMON_H_ 7 | #define RS_COMMON_H_ 8 | 9 | #include "int.h" 10 | 11 | /* Reed-Solomon codec control block */ 12 | struct rs { 13 | int mm; /* Bits per symbol */ 14 | int nn; /* Symbols per block (= (1<= rs->nn) { 27 | x -= rs->nn; 28 | x = (x >> rs->mm) + (x & rs->nn); 29 | } 30 | return x; 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/codecs/mfsk/mfsk.h: -------------------------------------------------------------------------------- 1 | #ifndef __MFSK_H 2 | #define __MFSK_H 3 | 4 | #include 5 | 6 | #include "codecs/fsk/fsk.h" 7 | 8 | typedef enum _mfsk_type { 9 | MFSK_2 = 2, 10 | MFSK_4 = 4, 11 | MFSK_16 = 16, 12 | } mfsk_type; 13 | 14 | void mfsk_encoder_new(fsk_encoder *encoder, mfsk_type type, uint32_t symbol_rate, uint32_t tone_spacing_hz_100); 15 | void mfsk_encoder_destroy(fsk_encoder *encoder); 16 | fsk_tone *mfsk_get_idle_tone(fsk_encoder *encoder); 17 | void mfsk_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data); 18 | void mfsk_encoder_get_tones(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones); 19 | uint32_t mfsk_encoder_get_tone_spacing(fsk_encoder *encoder); 20 | uint32_t mfsk_encoder_get_symbol_rate(fsk_encoder *encoder); 21 | uint32_t mfsk_encoder_get_symbol_delay(fsk_encoder *encoder); 22 | int8_t mfsk_encoder_next_tone(fsk_encoder *encoder); 23 | 24 | extern fsk_encoder_api mfsk_fsk_encoder_api; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/codecs/morse/morse.h: -------------------------------------------------------------------------------- 1 | #ifndef __MORSE_H 2 | #define __MORSE_H 3 | 4 | #include 5 | 6 | #include "codecs/fsk/fsk.h" 7 | 8 | void morse_encoder_new(fsk_encoder *encoder, uint32_t symbol_rate); 9 | void morse_encoder_destroy(fsk_encoder *encoder); 10 | void morse_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data); 11 | void morse_encoder_get_tones(fsk_encoder *encoder, int8_t *tone_count, fsk_tone **tones); 12 | uint32_t morse_encoder_get_symbol_rate(fsk_encoder *encoder); 13 | uint32_t morse_encoder_get_symbol_delay(fsk_encoder *encoder); 14 | int8_t morse_encoder_next_tone(fsk_encoder *encoder); 15 | 16 | extern fsk_encoder_api morse_fsk_encoder_api; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/codecs/raw/raw.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "raw.h" 4 | 5 | typedef struct _raw_encoder { 6 | uint8_t *data; 7 | uint16_t len; 8 | } raw_encoder; 9 | 10 | void raw_encoder_new(fsk_encoder *encoder) 11 | { 12 | encoder->priv = malloc(sizeof(raw_encoder)); 13 | } 14 | 15 | void raw_encoder_destroy(fsk_encoder *encoder) 16 | { 17 | if (encoder->priv != NULL) { 18 | free(encoder->priv); 19 | encoder->priv = NULL; 20 | } 21 | } 22 | 23 | void raw_encoder_set_data(fsk_encoder *encoder, uint16_t data_length, uint8_t *data) 24 | { 25 | raw_encoder *raw = (raw_encoder *) encoder->priv; 26 | raw->data = data; 27 | raw->len = data_length; 28 | } 29 | 30 | uint8_t *raw_encoder_get_data(fsk_encoder *encoder) 31 | { 32 | raw_encoder *raw = (raw_encoder *) encoder->priv; 33 | return raw->data; 34 | } 35 | 36 | uint16_t raw_encoder_get_data_len(fsk_encoder *encoder) 37 | { 38 | raw_encoder *raw = (raw_encoder *) encoder->priv; 39 | return raw->len; 40 | } 41 | 42 | fsk_encoder_api raw_fsk_encoder_api = { 43 | .set_data = raw_encoder_set_data, 44 | .get_data = raw_encoder_get_data, 45 | .get_data_len = raw_encoder_get_data_len, 46 | }; 47 | -------------------------------------------------------------------------------- /src/codecs/raw/raw.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAW_H 2 | #define __RAW_H 3 | 4 | #include 5 | 6 | #include "codecs/fsk/fsk.h" 7 | 8 | void raw_encoder_new(fsk_encoder *encoder); 9 | void raw_encoder_destroy(fsk_encoder *encoder); 10 | 11 | extern fsk_encoder_api raw_fsk_encoder_api; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The tracker firmware will transmit each of the message templates defined here in rotation, one by one, 3 | * starting again from the beginning once the last message for a particular mode is transmitted. 4 | * 5 | * Supported variable references in templates: 6 | * 7 | * $cs - Call sign 8 | * $loc4 - Locator (4 chars) 9 | * $loc6 - Locator (6 chars) 10 | * $loc8 - Locator (8 chars) 11 | * $loc12 - Locator (12 chars) 12 | * $bv - Battery voltage in millivolts (up to 4 chars) 13 | * $te - External temperature in C (up to 3 chars) 14 | * $ti - Internal temperature in C (up to 3 chars) 15 | * $hu - Humidity percentage (up to 3 chars) 16 | * $pr - Atmospheric pressure in millibars (up to 4 chars) 17 | * $tow - GPS time of week in milliseconds 18 | * $hh - Current hour (2 chars) 19 | * $mm - Current minute (2 chars) 20 | * $ss - Current second (2 chars) 21 | * $sv - GPS satellites visible (up to 2 chars) 22 | * $lat - Latitude in degrees * 1000 (up to 6 chars) 23 | * $lon - Longitude in degrees * 1000 (up to 6 chars) 24 | * $alt - Altitude in meters (up to 5 chars) 25 | * $gs - Ground speed in km/h (up to 3 chars) 26 | * $cl - Climb in m/s (up to 2 chars) 27 | * $he - Heading in degrees (up to 3 chars) 28 | * $pc - Pulse counter value (wraps to zero at 65535, 16-bit unsigned value) 29 | * $ri - Radiation intensity in µR/h (up to 5 chars) 30 | * $dc - Data counter value, increases by one every time telemetry is read (wraps to zero at 65535, 16-bit unsigned value) 31 | * $gu - GPS data update indicator, 1 if GPS data was updated since time telemetry was read, 0 otherwise 32 | * $ct - Clock calibration trim value (0-31, only for DFM-17) 33 | * $cc - Clock calibration change count (only for DFM-17) 34 | * 35 | * Allowed message lengths: 36 | * 37 | * APRS comment - Free text up to 127 chars 38 | * FT8 - Free text up to 13 chars (Type 0.0 free text message, Type 0.5 telemetry message) 39 | * JT65 - Free text up to 13 chars (Plaintext Type 6 message) 40 | * JT9 - Free text up to 13 chars (Plaintext Type 6 message) 41 | * JT4 - Free text up to 13 chars (Plaintext Type 6 message) 42 | * FSQ - Call sign up to 20 chars, free text up to 130 chars 43 | * WSPR - Call sign up to 6 chars, locator 4 chars, output power in dBm 44 | */ 45 | 46 | #include 47 | #include "config.h" 48 | 49 | bool leds_enabled = LEDS_ENABLE; 50 | bool bmp280_enabled = SENSOR_BMP280_ENABLE; 51 | bool radsens_enabled = SENSOR_RADSENS_ENABLE; 52 | bool si5351_enabled = RADIO_SI5351_ENABLE; 53 | bool gps_nmea_output_enabled = GPS_NMEA_OUTPUT_VIA_SERIAL_PORT_ENABLE; 54 | bool pulse_counter_enabled = PULSE_COUNTER_ENABLE; 55 | 56 | volatile bool system_initialized = false; 57 | 58 | /** 59 | * CW mode messages. 60 | * Maximum length: 64 characters. 61 | */ 62 | char *cw_message_templates[] = { 63 | "$cs", 64 | // "$cs $loc6 $altm $gs km/h $tiC", 65 | // "$cs $loc6", 66 | // "$alt m", 67 | // "$gs km/h $ti C", 68 | NULL 69 | }; 70 | 71 | /** 72 | * "Pip" mode messages. Transmitted as CW, because a single "pip" can be represented as the 'E' character. 73 | * Maximum length: 64 characters. 74 | */ 75 | char *pip_message_templates[] = { 76 | "E", // An 'E' character in CW represents a single "pip". 77 | NULL 78 | }; 79 | 80 | /** 81 | * APRS mode comment messages. 82 | * Maximum length: depends on the packet contents, but keeping this under 100 characters is usually safe. 83 | * Note that many hardware APRS receivers show a limited number of APRS comment characters, such as 43 or 67 chars. 84 | */ 85 | char *aprs_comment_templates[] = { 86 | // " B$bu $teC $hu% $prmb $hh:$mm:$ss @ $tow ms - " APRS_COMMENT, 87 | // " B$bu $teC $hu% $prmb - " APRS_COMMENT, 88 | // " B$bu $loc12 $hh:$mm:$ss - " APRS_COMMENT, 89 | // " $loc12 - " APRS_COMMENT, 90 | // " $teC $hu% $prmb PC $pc RI $ri uR/h - " APRS_COMMENT, 91 | " " APRS_COMMENT, 92 | NULL 93 | }; 94 | 95 | /** 96 | * CATS mode comment messages. 97 | * The maximum CATS comment length supported by RS41ng is about 100 characters. 98 | * The CATS standard allows for up to 255 characters. 99 | */ 100 | char *cats_comment_templates[] = { 101 | // "$dc $gu $sv $lat $lon - $hh:$mm:$ss @ $tow ms", 102 | // "T:$teC H:$hu% P:$prmb - " CATS_COMMENT, 103 | // "T:$teC H:$hu% P:$prmb PC:$pc RI:$ri uR/h - " CATS_COMMENT, 104 | CATS_COMMENT, 105 | NULL 106 | }; 107 | 108 | /** 109 | * FSQ mode comment message templates. 110 | * Maximum length: 130 characters. 111 | */ 112 | char *fsq_comment_templates[] = { 113 | // "TEST $loc6 $altm $tiC", 114 | // " $lat $lon, $alt m, $cl m/s, $gs km/h, $he deg - " FSQ_COMMENT, 115 | // " $loc12, $teC $hu% $prmb $hh:$mm:$ss @ $tow ms - " FSQ_COMMENT, 116 | NULL 117 | }; 118 | 119 | /** 120 | * FTx/JTxx mode message templates. 121 | * Maximum length: 13 characters allowed by the protocols. 122 | */ 123 | char *ftjt_message_templates[] = { 124 | // "$cs $loc4", 125 | // "$loc12", 126 | // "$altm $cl", 127 | // "$bvmV $tiC", 128 | // "$hu% $prmb", 129 | NULL 130 | }; 131 | -------------------------------------------------------------------------------- /src/config_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_INTERNAL_H 2 | #define __CONFIG_INTERNAL_H 3 | 4 | #define GPS_SERIAL_PORT_BAUD_RATE 38400 5 | 6 | // The external serial port baud rate must be higher than the GPS serial port baud rate (38400) 7 | #define EXTERNAL_SERIAL_PORT_BAUD_RATE 115200 8 | 9 | #define RADIO_PAYLOAD_MAX_LENGTH 320 10 | #define RADIO_SYMBOL_DATA_MAX_LENGTH 512 11 | #define RADIO_PAYLOAD_MESSAGE_MAX_LENGTH 128 12 | 13 | // PARIS: 50 dot durations, 20 WPM -> 60ms per unit 14 | #define MORSE_WPM_TO_SYMBOL_RATE(wpm) (1000 / (60 * 20 / wpm)) 15 | 16 | // Experimental fast frequency change routine for Si5351, not tested 17 | #define SI5351_FAST_ENABLE false 18 | 19 | #include 20 | 21 | extern bool leds_enabled; 22 | extern bool gps_nmea_output_enabled; 23 | extern bool bmp280_enabled; 24 | extern bool radsens_enabled; 25 | extern bool si5351_enabled; 26 | extern bool pulse_counter_enabled; 27 | 28 | extern volatile bool system_initialized; 29 | 30 | extern char *cw_message_templates[]; 31 | extern char *pip_message_templates[]; 32 | extern char *aprs_comment_templates[]; 33 | extern char *cats_comment_templates[]; 34 | extern char *fsq_comment_templates[]; 35 | extern char *ftjt_message_templates[]; 36 | 37 | void set_green_led(bool enabled); 38 | void set_red_led(bool enabled); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/drivers/bmp280/bmp280.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * Ciastkolog.pl (https://github.com/ciastkolog) 4 | * Copyright (c) 2016 sheinz (https://github.com/sheinz) 5 | */ 6 | #ifndef __BMP280_H__ 7 | #define __BMP280_H__ 8 | 9 | #include 10 | #include 11 | #include "hal/i2c.h" 12 | 13 | /** 14 | * BMP280 or BME280 address is 0x77 if SDO pin is high, and is 0x76 if 15 | * SDO pin is low. 16 | */ 17 | 18 | #define BMP280_I2C_ADDRESS_0 0x76 19 | #define BMP280_I2C_ADDRESS_1 0x77 20 | 21 | #define BMP280_CHIP_ID 0x58 /* BMP280 has chip-id 0x58 */ 22 | #define BME280_CHIP_ID 0x60 /* BME280 has chip-id 0x60 */ 23 | 24 | /** 25 | * Mode of BMP280 module operation. 26 | * Forced - Measurement is initiated by user. 27 | * Normal - Continues measurement. 28 | */ 29 | typedef enum { 30 | BMP280_MODE_SLEEP = 0, 31 | BMP280_MODE_FORCED = 1, 32 | BMP280_MODE_NORMAL = 3 33 | } BMP280_Mode; 34 | 35 | typedef enum { 36 | BMP280_FILTER_OFF = 0, 37 | BMP280_FILTER_2 = 1, 38 | BMP280_FILTER_4 = 2, 39 | BMP280_FILTER_8 = 3, 40 | BMP280_FILTER_16 = 4 41 | } BMP280_Filter; 42 | 43 | /** 44 | * Pressure oversampling settings 45 | */ 46 | typedef enum { 47 | BMP280_SKIPPED = 0, /* no measurement */ 48 | BMP280_ULTRA_LOW_POWER = 1, /* oversampling x1 */ 49 | BMP280_LOW_POWER = 2, /* oversampling x2 */ 50 | BMP280_STANDARD = 3, /* oversampling x4 */ 51 | BMP280_HIGH_RES = 4, /* oversampling x8 */ 52 | BMP280_ULTRA_HIGH_RES = 5 /* oversampling x16 */ 53 | } BMP280_Oversampling; 54 | 55 | /** 56 | * Stand by time between measurements in normal mode 57 | */ 58 | typedef enum { 59 | BMP280_STANDBY_05 = 0, /* stand by time 0.5ms */ 60 | BMP280_STANDBY_62 = 1, /* stand by time 62.5ms */ 61 | BMP280_STANDBY_125 = 2, /* stand by time 125ms */ 62 | BMP280_STANDBY_250 = 3, /* stand by time 250ms */ 63 | BMP280_STANDBY_500 = 4, /* stand by time 500ms */ 64 | BMP280_STANDBY_1000 = 5, /* stand by time 1s */ 65 | BMP280_STANDBY_2000 = 6, /* stand by time 2s BMP280, 10ms BME280 */ 66 | BMP280_STANDBY_4000 = 7, /* stand by time 4s BMP280, 20ms BME280 */ 67 | } BMP280_StandbyTime; 68 | 69 | /** 70 | * Configuration parameters for BMP280 module. 71 | * Use function bmp280_init_default_params to use default configuration. 72 | */ 73 | typedef struct { 74 | BMP280_Mode mode; 75 | BMP280_Filter filter; 76 | BMP280_Oversampling oversampling_pressure; 77 | BMP280_Oversampling oversampling_temperature; 78 | BMP280_Oversampling oversampling_humidity; 79 | BMP280_StandbyTime standby; 80 | } bmp280_params_t; 81 | 82 | 83 | typedef struct _bmp280 { 84 | uint16_t dig_T1; 85 | int16_t dig_T2; 86 | int16_t dig_T3; 87 | uint16_t dig_P1; 88 | int16_t dig_P2; 89 | int16_t dig_P3; 90 | int16_t dig_P4; 91 | int16_t dig_P5; 92 | int16_t dig_P6; 93 | int16_t dig_P7; 94 | int16_t dig_P8; 95 | int16_t dig_P9; 96 | 97 | /* Humidity compensation for BME280 */ 98 | uint8_t dig_H1; 99 | int16_t dig_H2; 100 | uint8_t dig_H3; 101 | int16_t dig_H4; 102 | int16_t dig_H5; 103 | int8_t dig_H6; 104 | 105 | uint16_t addr; 106 | 107 | i2c_port *port; 108 | 109 | bmp280_params_t params; 110 | 111 | uint8_t id; /* Chip ID */ 112 | 113 | } bmp280; 114 | 115 | /** 116 | * Initialize default parameters. 117 | * Default configuration: 118 | * mode: NORAML 119 | * filter: OFF 120 | * oversampling: x4 121 | * standby time: 250ms 122 | */ 123 | void bmp280_init_default_params(bmp280_params_t *params); 124 | 125 | /** 126 | * Initialize BMP280 module, probes for the device, soft resets the device, 127 | * reads the calibration constants, and configures the device using the supplied 128 | * parameters. Returns true on success otherwise false. 129 | * 130 | * The I2C address is assumed to have been initialized in the dev, and 131 | * may be either BMP280_I2C_ADDRESS_0 or BMP280_I2C_ADDRESS_1. If the I2C 132 | * address is unknown then try initializing each in turn. 133 | * 134 | * This may be called again to soft reset the device and initialize it again. 135 | */ 136 | bool bmp280_init(bmp280 *dev, bmp280_params_t *params); 137 | 138 | /** 139 | * Start measurement in forced mode. 140 | * The module remains in forced mode after this call. 141 | * Do not call this method in normal mode. 142 | */ 143 | bool bmp280_force_measurement(bmp280 *dev); 144 | 145 | /** 146 | * Check if BMP280 is busy with measuring temperature/pressure. 147 | * Return true if BMP280 is busy. 148 | */ 149 | bool bmp280_is_measuring(bmp280 *dev); 150 | 151 | /** 152 | * Read compensated temperature and pressure data: 153 | * 154 | * Temperature in degrees Celsius times 100. 155 | * 156 | * Pressure in Pascals in fixed point 24 bit integer 8 bit fraction format. 157 | * 158 | * Humidity is optional and only read for the BME280, in percent relative 159 | * humidity as a fixed point 22 bit interger and 10 bit fraction format. 160 | */ 161 | bool bmp280_read_fixed(bmp280 *dev, int32_t *temperature, uint32_t *pressure, uint32_t *humidity); 162 | 163 | /** 164 | * Read compensated temperature and pressure data: 165 | * Temperature in degrees Celsius. 166 | * Pressure in Pascals. 167 | * Humidity is optional and only read for the BME280, in percent relative 168 | * humidity. 169 | */ 170 | bool bmp280_read_float(bmp280 *dev, float *temperature, float *pressure, float *humidity); 171 | 172 | 173 | #endif // __BMP280_H__ 174 | -------------------------------------------------------------------------------- /src/drivers/pulse_counter/pulse_counter.c: -------------------------------------------------------------------------------- 1 | #include "pulse_counter.h" 2 | #include "stm32f10x_exti.h" 3 | #include "stm32f10x_gpio.h" 4 | #include "stm32f10x.h" 5 | #include "misc.h" 6 | #include "config.h" 7 | 8 | // The pulse count will wrap to zero at 65535 as it is stored as a 16-bit unsigned integer value 9 | uint16_t pulse_count = 0; 10 | 11 | void pulse_counter_init(int pin_mode, int edge) 12 | { 13 | // Initialize pin PB11 with optional internal pull-up resistor 14 | GPIO_InitTypeDef gpio_init; 15 | gpio_init.GPIO_Pin = GPIO_Pin_11; 16 | gpio_init.GPIO_Mode = (pin_mode == PULSE_COUNTER_PIN_MODE_INTERNAL_PULL_UP) 17 | ? GPIO_Mode_IPU : 18 | ((pin_mode == PULSE_COUNTER_PIN_MODE_INTERNAL_PULL_DOWN) 19 | ? GPIO_Mode_IPD 20 | : GPIO_Mode_IN_FLOATING); 21 | gpio_init.GPIO_Speed = GPIO_Speed_10MHz; 22 | GPIO_Init(GPIOB, &gpio_init); 23 | 24 | // PB11 is connected to interrupt line 11, set trigger on the configured edge and enable the interrupt 25 | EXTI_InitTypeDef exti_init; 26 | exti_init.EXTI_Line = EXTI_Line11; 27 | exti_init.EXTI_Mode = EXTI_Mode_Interrupt; 28 | exti_init.EXTI_Trigger = (edge == PULSE_COUNTER_INTERRUPT_EDGE_FALLING) 29 | ? EXTI_Trigger_Falling 30 | : EXTI_Trigger_Rising; 31 | exti_init.EXTI_LineCmd = ENABLE; 32 | EXTI_Init(&exti_init); 33 | 34 | // Attach interrupt line to port B 35 | GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11); 36 | 37 | // PB11 is connected to EXTI_Line11, which has EXTI15_10_IRQn vector. Use priority 0 for now. 38 | NVIC_InitTypeDef NVIC_InitStruct; 39 | NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; 40 | NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; 41 | NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00; 42 | NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; 43 | NVIC_Init(&NVIC_InitStruct); 44 | } 45 | 46 | uint16_t pulse_counter_get_count() 47 | { 48 | return pulse_count; 49 | } 50 | 51 | void EXTI15_10_IRQHandler(void) 52 | { 53 | pulse_count = pulse_count + 1; 54 | EXTI_ClearITPendingBit(EXTI_Line11); 55 | } 56 | -------------------------------------------------------------------------------- /src/drivers/pulse_counter/pulse_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef __PULSE_COUNTER_H 2 | #define __PULSE_COUNTER_H 3 | 4 | #include 5 | #include 6 | 7 | void pulse_counter_init(int pin_mode, int edge); 8 | uint16_t pulse_counter_get_count(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/drivers/radsens/radsens.h: -------------------------------------------------------------------------------- 1 | /** 2 | * RadSens - universal dosimeter-radiometer module 3 | * 4 | * Driver code adapted from https://github.com/climateguard/RadSens 5 | * Original license GPL 3.0. 6 | */ 7 | 8 | #ifndef __RADSENS_H 9 | #define __RADSENS_H 10 | 11 | #include 12 | 13 | #include "hal/i2c.h" 14 | 15 | #define RS_REG_COUNT 21 16 | 17 | // Default RadSens I2C device address 18 | #define RS_DEFAULT_I2C_ADDRESS 0x66 19 | 20 | // Device id, default value: 0x7D 21 | // Size: 8 bit 22 | #define RS_REG_DEVICE_ID 0x00 23 | 24 | // Firmware version 25 | // Size: 8 bit 26 | #define RS_REG_FIRMWARE_VER 0x01 27 | 28 | // Radiation intensity (dynamic period T < 123 sec) 29 | // Size: 24 bit 30 | #define RS_REG_RAD_INTENSITY_DYNAMIC 0x03 31 | 32 | // Radiation intensity (static period T = 500 sec) 33 | // Size: 24 bit 34 | #define RS_REG_RAD_INTENSITY_STATIC 0x06 35 | 36 | // Contains the accumulated number of pulses registered by the module since the last I2C data reading. 37 | // The value is reset each time it is read. Allows you to process directly the pulses from the Geiger counter 38 | // and implement other algorithms. The value is updated when each pulse is registered. 39 | // Size: 16 bit 40 | #define RS_REG_PULSE_COUNTER 0x09 41 | 42 | // This register is used to change the device address when multiple devices need to be connected 43 | // to the same line at the same time. By default, it contains the value 0x66. At the end of recording, the new 44 | // value is stored in the non-volatile memory of the microcontroller. 45 | // Size: 8 bit 46 | // Access: R/W 47 | #define RS_REG_DEVICE_ADDRESS 0x10 48 | 49 | // Control register for a high-voltage voltage Converter. By default, it is in the enabled state. 50 | // To enable the HV generator, write 1 to the register, and 0 to disable it. If you try to write other 51 | // values, the command is ignored. 52 | // Size: 8 bit 53 | // Access: R/W 54 | #define RS_REG_HV_GENERATOR 0x11 55 | 56 | // Contains the value coefficient used for calculating the radiation intensity. 57 | // If necessary (for example, when installing a different type of counter), the necessary sensitivity value in 58 | // imp/MKR is entered in the register. The default value is 105 imp/MKR. At the end of 59 | // recording, the new value is stored in the non-volatile memory of the microcontroller. 60 | // Size: 16 bit 61 | // Access: R/W 62 | #define RS_REG_SENSITIVITY 0x12 63 | 64 | // Control register for an indication diode. By default, it is in the enabled state. To enable the indication, 65 | // write 1 to the register, and 0 to disable it. If you try to write other values, the command is ignored. 66 | // Size: 8 bit 67 | // Access: R/W 68 | #define RS_REG_LED_CONTROL 0x14 69 | 70 | class RadSens { 71 | private: 72 | i2c_port *_port; 73 | uint8_t _sensor_address; 74 | uint8_t _chip_id = 0; 75 | uint8_t _firmware_ver = 0; 76 | uint32_t _pulse_count = 0; 77 | 78 | bool i2c_read(uint8_t addr, uint8_t *dest, uint8_t num); 79 | bool i2c_write(uint8_t reg, uint8_t data); 80 | 81 | bool updatePulses(); 82 | 83 | public: 84 | RadSens(i2c_port *port, uint8_t sensor_address); 85 | 86 | ~RadSens(); 87 | 88 | bool init(); 89 | uint8_t getChipId(); 90 | uint8_t getFirmwareVersion(); 91 | float getRadIntensityDynamic(); 92 | float getRadIntensityStatic(); 93 | int32_t getNumberOfPulses(); 94 | uint8_t getSensorAddress(); 95 | bool getHVGeneratorState(); 96 | bool getLedState(); 97 | int16_t getSensitivity(); 98 | bool setHVGeneratorState(bool state); 99 | bool setSensitivity(uint16_t sens); 100 | bool setLedState(bool state); 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/drivers/si4032/si4032.h: -------------------------------------------------------------------------------- 1 | #ifndef __SI4032_H 2 | #define __SI4032_H 3 | 4 | #include 5 | #include 6 | 7 | typedef enum _si4032_modulation_type { 8 | SI4032_MODULATION_TYPE_NONE = 0, 9 | SI4032_MODULATION_TYPE_OOK, 10 | SI4032_MODULATION_TYPE_FSK, 11 | SI4032_MODULATION_TYPE_FIFO_FSK, 12 | } si4032_modulation_type; 13 | 14 | void si4032_soft_reset(); 15 | void si4032_enable_tx(); 16 | void si4032_inhibit_tx(); 17 | void si4032_disable_tx(); 18 | uint16_t si4032_start_tx(uint8_t *data, int len); 19 | uint16_t si4032_refill_buffer(uint8_t *data, int len, bool *overflow); 20 | int si4032_wait_for_tx_complete(int timeout_ms); 21 | void si4032_use_direct_mode(bool use); 22 | void si4032_set_tx_frequency(float frequency_mhz); 23 | void si4032_set_data_rate(const uint32_t rate_bps); 24 | void si4032_set_tx_power(uint8_t power); 25 | void si4032_set_frequency_offset(uint16_t offset); 26 | void si4032_set_frequency_offset_small(uint8_t offset); 27 | void si4032_set_frequency_deviation(uint8_t deviation); 28 | void si4032_set_modulation_type(si4032_modulation_type type); 29 | int32_t si4032_read_temperature_celsius_100(); 30 | void si4032_set_sdi_pin(bool high); 31 | void si4032_use_sdi_pin(bool use); 32 | void si4032_init(); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/drivers/si4063/si4063.h: -------------------------------------------------------------------------------- 1 | #ifndef __SI4063_H 2 | #define __SI4063_H 3 | 4 | #include 5 | #include 6 | 7 | typedef enum _si4063_modulation_type { 8 | SI4063_MODULATION_TYPE_CW = 0, 9 | SI4063_MODULATION_TYPE_OOK, 10 | SI4063_MODULATION_TYPE_FSK, 11 | SI4063_MODULATION_TYPE_FIFO_FSK, 12 | } si4063_modulation_type; 13 | 14 | void si4063_enable_tx(); 15 | void si4063_inhibit_tx(); 16 | void si4063_disable_tx(); 17 | uint16_t si4063_start_tx(uint8_t *data, int len); 18 | uint16_t si4063_refill_buffer(uint8_t *data, int len); 19 | int si4063_wait_for_tx_complete(int timeout_ms); 20 | bool si4063_fifo_underflow(); 21 | void si4063_set_tx_frequency(uint32_t frequency_hz); 22 | void si4063_set_data_rate(const uint32_t rate_bps); 23 | void si4063_set_tx_power(uint8_t power); 24 | void si4063_set_frequency_offset(uint16_t offset); 25 | void si4063_set_frequency_deviation(uint32_t deviation); 26 | void si4063_set_modulation_type(si4063_modulation_type type); 27 | int32_t si4063_read_temperature_celsius_100(); 28 | void si4063_set_direct_mode_pin(bool high); 29 | int si4063_init(); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/drivers/ubxg6010/ubxg6010.h: -------------------------------------------------------------------------------- 1 | #ifndef __UBXG6010_H 2 | #define __UBXG6010_H 3 | 4 | #include 5 | 6 | #include "src/gps.h" 7 | 8 | bool ubxg6010_init(); 9 | 10 | bool ubxg6010_enable_power_save_mode(); 11 | 12 | void ubxg6010_request_gpstime(); 13 | 14 | bool ubxg6010_get_current_gps_data(gps_data *data); 15 | 16 | void ubxg6010_handle_incoming_byte(uint8_t data); 17 | 18 | void ubxg6010_reset_parser(); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/gpio.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPIO_H 2 | #define __GPIO_H 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | 10 | // GPIO definitions for devices we use 11 | 12 | #if defined (RS41) 13 | 14 | #define BANK_SHUTDOWN GPIOA 15 | #define PIN_SHUTDOWN GPIO_Pin_12 16 | 17 | #define BANK_VOLTAGE GPIOA 18 | #define PIN_VOLTAGE GPIO_Pin_5 19 | #define ADC_VOLTAGE ADC1 20 | #define CHANNEL_VOLTAGE ADC_Channel_5 21 | 22 | #define BANK_BUTTON GPIOA 23 | #define PIN_BUTTON GPIO_Pin_6 24 | #define ADC_BUTTON ADC1 25 | #define CHANNEL_BUTTON ADC_Channel_6 26 | 27 | #define BANK_RED_LED GPIOB 28 | #define PIN_RED_LED GPIO_Pin_8 29 | 30 | #define BANK_GREEN_LED GPIOB 31 | #define PIN_GREEN_LED GPIO_Pin_7 32 | 33 | #define BANK_MOSI GPIOB 34 | #define PIN_MOSI GPIO_Pin_15 35 | #define BANK_SCK GPIOB 36 | #define PIN_SCK GPIO_Pin_13 37 | #define BANK_MISO GPIOB 38 | #define PIN_MISO GPIO_Pin_14 39 | #define APBPERIPHERAL_SPI RCC_APB1Periph_SPI2 40 | #define PERIPHERAL_SPI SPI2 41 | #define RCC_SPIPeriphClockCmd RCC_APB1PeriphClockCmd 42 | 43 | #define PIN_USART_TX GPIO_Pin_9 44 | #define BANK_USART_TX GPIOA 45 | #define PIN_USART_RX GPIO_Pin_10 46 | #define BANK_USART_RX GPIOA 47 | #define USART_IRQ USART1_IRQn 48 | #define USART_IT USART1 49 | #define APBPERIPHERAL_USART RCC_APB2Periph_USART1 50 | #define USART_IRQ_HANDLER USART1_IRQHandler 51 | 52 | #elif defined (DFM17) 53 | 54 | #define BANK_SHUTDOWN GPIOC 55 | #define PIN_SHUTDOWN GPIO_Pin_0 56 | 57 | #define BANK_VOLTAGE GPIOA // Needs confirmation 58 | #define PIN_VOLTAGE GPIO_Pin_0 // Needs confirmation 59 | #define ADC_VOLTAGE ADC1 // Needs confirmation 60 | #define CHANNEL_VOLTAGE ADC_Channel_0 // Needs confirmation 61 | 62 | #define BANK_BUTTON GPIOC 63 | #define PIN_BUTTON GPIO_Pin_8 64 | // No ADC available on the GPIOC, so we have to use digital reads/writes for the button 65 | 66 | #define BANK_RED_LED GPIOB 67 | #define PIN_RED_LED GPIO_Pin_12 68 | 69 | #define BANK_GREEN_LED GPIOC 70 | #define PIN_GREEN_LED GPIO_Pin_6 71 | 72 | #define BANK_YELLOW_LED GPIOC 73 | #define PIN_YELLOW_LED GPIO_Pin_7 74 | 75 | #define BANK_MOSI GPIOA 76 | #define PIN_MOSI GPIO_Pin_7 77 | #define BANK_SCK GPIOA 78 | #define PIN_SCK GPIO_Pin_5 79 | #define BANK_MISO GPIOA 80 | #define PIN_MISO GPIO_Pin_6 81 | #define APBPERIPHERAL_SPI RCC_APB2Periph_SPI1 82 | #define PERIPHERAL_SPI SPI1 83 | #define RCC_SPIPeriphClockCmd RCC_APB2PeriphClockCmd 84 | 85 | #define PIN_USART_TX GPIO_Pin_2 86 | #define BANK_USART_TX GPIOA 87 | #define PIN_USART_RX GPIO_Pin_3 88 | #define BANK_USART_RX GPIOA 89 | #define USART_IRQ USART2_IRQn 90 | #define USART_IT USART2 91 | #define APBPERIPHERAL_USART RCC_APB1Periph_USART2 92 | #define USART_IRQ_HANDLER USART2_IRQHandler 93 | 94 | #else 95 | Compiler error. You must define RS41 or DFM17. 96 | #endif // RS41 or DFM17 97 | 98 | // Hardware Sanity Check 99 | 100 | #if defined (RS41) && defined (DFM17) 101 | Compiler error. You must define RS41 or DFM17 but not both. 102 | #endif 103 | 104 | #endif // __GPIO_H 105 | -------------------------------------------------------------------------------- /src/gps.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPS_H 2 | #define __GPS_H 3 | 4 | #include 5 | #include 6 | #include "config.h" 7 | 8 | // Acquisition state: The receiver actively searches for and acquires signals. 9 | // Maximum power consumption. Can also mean that power saving is not turned on. 10 | #define POWER_SAFE_MODE_STATE_ACQUISITION 0 11 | // Tracking state: The receiver continuously tracks and downloads data. Less power consumption than in Acquisition state. 12 | #define POWER_SAFE_MODE_STATE_TRACKING 1 13 | // POT state: The receiver repeatedly loops through a sequence of tracking (TRK), calculating the position fix 14 | // (Calc), and entering an idle period (Idle). No new signals are acquired and no data is downloaded. Much less 15 | // power consumption than in Tracking state. 16 | #define POWER_SAFE_MODE_STATE_POWER_OPTIMIZED_TRACKING 2 17 | // Inactive state: Most parts of the receiver are switched off. 18 | #define POWER_SAFE_MODE_STATE_INACTIVE 3 19 | 20 | #define GPS_IS_POWER_SAVING_ACTIVE(gps_data) (gps_data.power_safe_mode_state != POWER_SAFE_MODE_STATE_ACQUISITION) 21 | 22 | #define GPS_FIX_NO_FIX 0 23 | #define GPS_FIX_DEAD_RECKONING_ONLY 1 24 | #define GPS_FIX_2D 2 25 | #define GPS_FIX_3D 3 26 | #define GPS_FIX_GPS_AND_DEAD_RECKONING 4 27 | #define GPS_FIX_TIME_ONLY 5 28 | 29 | #if GPS_REQUIRE_3D_FIX 30 | #define GPS_HAS_FIX(gps_data) ((gps_data).fix_ok && ((gps_data).fix == GPS_FIX_3D)) 31 | #else 32 | #define GPS_HAS_FIX(gps_data) ((gps_data).fix_ok && ((gps_data).fix == GPS_FIX_2D || (gps_data).fix == GPS_FIX_3D)) 33 | #endif 34 | 35 | typedef struct _gps_data { 36 | bool updated; 37 | 38 | uint32_t time_of_week_millis; 39 | int16_t week; 40 | uint16_t year; 41 | uint8_t month; 42 | uint8_t day; 43 | uint8_t seconds; 44 | uint8_t minutes; 45 | uint8_t hours; 46 | int8_t leap_seconds; 47 | 48 | int32_t latitude_degrees_1000000; 49 | int32_t longitude_degrees_1000000; 50 | int32_t altitude_mm; 51 | uint32_t ground_speed_cm_per_second; 52 | int32_t heading_degrees_100000; 53 | int32_t climb_cm_per_second; 54 | uint8_t satellites_visible; 55 | uint8_t fix; 56 | bool fix_ok; 57 | uint16_t ok_packets; 58 | uint16_t bad_packets; 59 | 60 | uint8_t power_safe_mode_state; 61 | uint16_t position_dilution_of_precision; // pDOP 62 | } gps_data; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/hal/clock_calibration.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #ifdef DFM17 4 | 5 | #include "stm32f10x_exti.h" 6 | #include "stm32f10x_gpio.h" 7 | #include "stm32f10x.h" 8 | #include "stm32f10x_rcc.h" 9 | #include "misc.h" 10 | #include "system.h" 11 | #include "millis.h" 12 | #include "clock_calibration.h" 13 | 14 | // The HSI (internal oscillator) trim register mask, copied from stm_lib/src/stm32f10x_rcc.c 15 | #define CR_HSITRIM_Mask ((uint32_t)0xFFFFFF07) 16 | 17 | // Register definition for reading the HSI current trim out of the Calibration Register (CR). 18 | // Resulting value will be between 0-31. 19 | #define CURRENT_TRIM ((RCC->CR & ~CR_HSITRIM_Mask) >>3) 20 | 21 | /** 22 | * On the DFM-17, GPIO PB8 is wired to the GPS Timepulse. We take advantage of this to do a 23 | * processor speed calibration. HSITRIM[4:0] allows for 32 values to adjust the HSI clock 24 | * speed. The center (16) value is "neutral". Each trim value above or below 16 adjusts 25 | * the clock by approximately 40kHZ (0.5% of the 8MHZ clock speed) (per AN2868). 26 | * 0.5% is about 5ms per second, so if we detect that we're off by more than 5 milliseconds between time pulses, 27 | * we will suggest a recalibration. The "trim_suggestion" variable is a static that will be maintained 28 | * by the time pulse IRQ and can be used at any time it's convenient to adjust the clock speed. 29 | */ 30 | 31 | // Defaults, will be set it in the init routine below. 32 | int trim_suggestion = 16; 33 | int trim_current = 16; 34 | 35 | uint32_t old_millis = 0; 36 | uint16_t calibration_change_count = 0; 37 | 38 | bool calibration_indicator_state = true; 39 | 40 | uint8_t clock_calibration_get_trim() 41 | { 42 | return CURRENT_TRIM; 43 | } 44 | 45 | uint16_t clock_calibration_get_change_count() 46 | { 47 | return calibration_change_count; 48 | } 49 | 50 | void clock_calibration_adjust() 51 | { 52 | if (trim_suggestion == trim_current) { 53 | return; 54 | } 55 | 56 | RCC_AdjustHSICalibrationValue(trim_suggestion); 57 | trim_current = trim_suggestion; 58 | 59 | calibration_change_count++; 60 | 61 | calibration_indicator_state = !calibration_indicator_state; 62 | system_set_yellow_led(calibration_indicator_state); 63 | } 64 | 65 | void timepulse_init() 66 | { 67 | // Initialize pin PB8 as floating input 68 | GPIO_InitTypeDef gpio_init; 69 | gpio_init.GPIO_Pin = GPIO_Pin_8; 70 | gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; 71 | gpio_init.GPIO_Speed = GPIO_Speed_10MHz; 72 | GPIO_Init(GPIOB, &gpio_init); 73 | 74 | // PB8 is connected to interrupt line 8, set trigger on the configured edge and enable the interrupt 75 | EXTI_InitTypeDef exti_init; 76 | exti_init.EXTI_Line = EXTI_Line8; 77 | exti_init.EXTI_Mode = EXTI_Mode_Interrupt; 78 | exti_init.EXTI_Trigger = EXTI_Trigger_Rising; 79 | exti_init.EXTI_LineCmd = ENABLE; 80 | EXTI_Init(&exti_init); 81 | 82 | // Attach interrupt line to port B 83 | GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource8); 84 | 85 | // PB8 is connected to EXTI_Line8, which has EXTI9_5_IRQn vector. Use priority 0 for now. 86 | NVIC_InitTypeDef NVIC_InitStruct; 87 | NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn; 88 | NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; 89 | NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; 90 | NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; 91 | NVIC_Init(&NVIC_InitStruct); 92 | 93 | // Pull the current calibration to start 94 | trim_current = CURRENT_TRIM; 95 | trim_suggestion = trim_current; 96 | 97 | // Set the yellow LED to help identify calibration changes 98 | system_set_yellow_led(calibration_indicator_state); 99 | } 100 | 101 | // This handler is (at present) only being used for the GPS time pulse interrupt, 102 | // so we shouldn't need to do additional testing for the cause of the interrupt. 103 | 104 | void EXTI9_5_IRQHandler(void) 105 | { 106 | uint32_t current_millis = millis(); 107 | 108 | EXTI_ClearITPendingBit(EXTI_Line8); 109 | 110 | if (old_millis == 0) { 111 | // First timepulse. Just store millis. 112 | old_millis = current_millis; 113 | return; 114 | } 115 | 116 | if (current_millis < old_millis) { 117 | // Milliseconds value wrapped to zero. Wait for the next interrupt. 118 | return; 119 | } 120 | 121 | // Calculate milliseconds since last timepulse. Ideally there were 1000. 122 | uint32_t millis_delta = current_millis - old_millis; 123 | old_millis = current_millis; 124 | 125 | // If too few clicks, speed up clock. If too many, slow down. 126 | int delta = (int) (1000 - millis_delta) / 5; 127 | 128 | // Take one step at a time in case we had a bad clock tick 129 | if (delta > 1) { 130 | delta = 1; 131 | } 132 | if (delta < -1) { 133 | delta = -1; 134 | } 135 | 136 | // Don't allow calibration suggestion to go out of range 137 | if (((delta + trim_current) >= 0) && 138 | ((delta + trim_current <= 31))) { 139 | // If the delta makes sense, apply to the suggestion. Otherwise, skip. 140 | trim_suggestion = trim_current + delta; 141 | } 142 | } 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/hal/clock_calibration.h: -------------------------------------------------------------------------------- 1 | #ifndef __CLOCK_CALIBRATION_H 2 | #define __CLOCK_CALIBRATION_H 3 | 4 | #include "config.h" 5 | 6 | #ifdef DFM17 7 | 8 | extern void timepulse_init(); 9 | extern uint8_t clock_calibration_get_trim(); 10 | extern uint16_t clock_calibration_get_change_count(); 11 | extern void clock_calibration_adjust(); 12 | 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/hal/cmsis_boot/stm32f10x.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelnousiainen/RS41ng/d04e3ca961f06f900f403327e2a24f519337086c/src/hal/cmsis_boot/stm32f10x.h -------------------------------------------------------------------------------- /src/hal/cmsis_boot/stm32f10x_conf.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file RTC/Calendar/stm32f10x_conf.h 4 | * @author MCD Application Team 5 | * @version V3.4.0 6 | * @date 10/15/2010 7 | * @brief Library configuration file. 8 | ****************************************************************************** 9 | * @copy 10 | * 11 | * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS 12 | * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE 13 | * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY 14 | * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING 15 | * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE 16 | * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. 17 | * 18 | *

© COPYRIGHT 2010 STMicroelectronics

19 | */ 20 | 21 | /* Define to prevent recursive inclusion -------------------------------------*/ 22 | #ifndef __STM32F10x_CONF_H 23 | #define __STM32F10x_CONF_H 24 | 25 | /* Includes ------------------------------------------------------------------*/ 26 | /* Uncomment the line below to enable peripheral header file inclusion */ 27 | /* #include "stm32f10x_adc.h" */ 28 | /* #include "stm32f10x_bkp.h" */ 29 | /* #include "stm32f10x_can.h" */ 30 | /* #include "stm32f10x_cec.h" */ 31 | /* #include "stm32f10x_crc.h" */ 32 | /* #include "stm32f10x_dac.h" */ 33 | /* #include "stm32f10x_dbgmcu.h" */ 34 | /* #include "stm32f10x_dma.h" */ 35 | /* #include "stm32f10x_exti.h" */ 36 | /* #include "stm32f10x_flash.h" */ 37 | /* #include "stm32f10x_fsmc.h" */ 38 | /* #include "stm32f10x_gpio.h" */ 39 | /* #include "stm32f10x_i2c.h" */ 40 | /* #include "stm32f10x_iwdg.h" */ 41 | /* #include "stm32f10x_pwr.h" */ 42 | /* #include "stm32f10x_rcc.h" */ 43 | /* #include "stm32f10x_rtc.h" */ 44 | /* #include "stm32f10x_sdio.h" */ 45 | /* #include "stm32f10x_spi.h" */ 46 | /* #include "stm32f10x_tim.h" */ 47 | /* #include "stm32f10x_usart.h" */ 48 | /* #include "stm32f10x_wwdg.h" */ 49 | /* #include "misc.h" */ /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ 50 | 51 | 52 | /* Exported types ------------------------------------------------------------*/ 53 | /* Exported constants --------------------------------------------------------*/ 54 | /* Uncomment the line below to expanse the "assert_param" macro in the 55 | Standard Peripheral Library drivers code */ 56 | /* #define USE_FULL_ASSERT 1 */ 57 | 58 | /* Exported macro ------------------------------------------------------------*/ 59 | #ifdef USE_FULL_ASSERT 60 | 61 | /** 62 | * @brief The assert_param macro is used for function's parameters check. 63 | * @param expr: If expr is false, it calls assert_failed function 64 | * which reports the name of the source file and the source 65 | * line number of the call that failed. 66 | * If expr is true, it returns no value. 67 | * @retval None 68 | */ 69 | #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) 70 | /* Exported functions ------------------------------------------------------- */ 71 | void assert_failed(uint8_t* file, uint32_t line); 72 | #else 73 | #define assert_param(expr) ((void)0) 74 | #endif /* USE_FULL_ASSERT */ 75 | 76 | #endif /* __STM32F10x_CONF_H */ 77 | 78 | /******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/ 79 | -------------------------------------------------------------------------------- /src/hal/cmsis_boot/system_stm32f10x.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file system_stm32f10x.h 4 | * @author MCD Application Team 5 | * @version V3.5.0 6 | * @date 11-March-2011 7 | * @brief CMSIS Cortex-M3 Device Peripheral Access Layer System Header File. 8 | ****************************************************************************** 9 | * @attention 10 | * 11 | * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS 12 | * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE 13 | * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY 14 | * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING 15 | * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE 16 | * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. 17 | * 18 | *

© COPYRIGHT 2011 STMicroelectronics

19 | ****************************************************************************** 20 | */ 21 | 22 | /** @addtogroup CMSIS 23 | * @{ 24 | */ 25 | 26 | /** @addtogroup stm32f10x_system 27 | * @{ 28 | */ 29 | 30 | /** 31 | * @brief Define to prevent recursive inclusion 32 | */ 33 | #ifndef __SYSTEM_STM32F10X_H 34 | #define __SYSTEM_STM32F10X_H 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /** @addtogroup STM32F10x_System_Includes 41 | * @{ 42 | */ 43 | 44 | /** 45 | * @} 46 | */ 47 | 48 | 49 | /** @addtogroup STM32F10x_System_Exported_types 50 | * @{ 51 | */ 52 | 53 | extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ 54 | 55 | /** 56 | * @} 57 | */ 58 | 59 | /** @addtogroup STM32F10x_System_Exported_Constants 60 | * @{ 61 | */ 62 | 63 | /** 64 | * @} 65 | */ 66 | 67 | /** @addtogroup STM32F10x_System_Exported_Macros 68 | * @{ 69 | */ 70 | 71 | /** 72 | * @} 73 | */ 74 | 75 | /** @addtogroup STM32F10x_System_Exported_Functions 76 | * @{ 77 | */ 78 | 79 | extern void SystemInit(void); 80 | extern void SystemCoreClockUpdate(void); 81 | /** 82 | * @} 83 | */ 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | 89 | #endif /*__SYSTEM_STM32F10X_H */ 90 | 91 | /** 92 | * @} 93 | */ 94 | 95 | /** 96 | * @} 97 | */ 98 | /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ 99 | -------------------------------------------------------------------------------- /src/hal/datatimer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "datatimer.h" 6 | 7 | void (*system_handle_data_timer_tick)() = NULL; 8 | 9 | void data_timer_init(uint32_t baud_rate) 10 | { 11 | // Timer frequency = TIM_CLK/(TIM_PSC+1)/(TIM_ARR + 1) 12 | // TIM_CLK = 13 | // TIM_PSC = Prescaler 14 | // TIM_ARR = Period 15 | 16 | TIM_DeInit(TIM2); 17 | 18 | TIM_TimeBaseInitTypeDef tim_init; 19 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 20 | RCC_APB2PeriphResetCmd(RCC_APB1Periph_TIM2, DISABLE); 21 | 22 | // The data timer assumes a 24 MHz clock source 23 | tim_init.TIM_Prescaler = 24 - 1; // tick every 1/1000000 s 24 | tim_init.TIM_CounterMode = TIM_CounterMode_Up; 25 | tim_init.TIM_Period = (uint16_t) ((1000000 / baud_rate) - 1); 26 | tim_init.TIM_ClockDivision = TIM_CKD_DIV1; 27 | tim_init.TIM_RepetitionCounter = 0; 28 | 29 | TIM_TimeBaseInit(TIM2, &tim_init); 30 | 31 | // No interrupts necessary for data timer, as it is only used for triggering DMA transfers 32 | TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 33 | TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); 34 | 35 | NVIC_InitTypeDef nvic_init; 36 | nvic_init.NVIC_IRQChannel = TIM2_IRQn; 37 | nvic_init.NVIC_IRQChannelPreemptionPriority = 2; 38 | nvic_init.NVIC_IRQChannelSubPriority = 2; 39 | nvic_init.NVIC_IRQChannelCmd = ENABLE; 40 | NVIC_Init(&nvic_init); 41 | 42 | TIM_Cmd(TIM2, ENABLE); 43 | } 44 | 45 | void data_timer_uninit() 46 | { 47 | TIM_Cmd(TIM2, DISABLE); 48 | 49 | NVIC_InitTypeDef nvic_init; 50 | nvic_init.NVIC_IRQChannel = TIM2_IRQn; 51 | nvic_init.NVIC_IRQChannelPreemptionPriority = 2; 52 | nvic_init.NVIC_IRQChannelSubPriority = 2; 53 | nvic_init.NVIC_IRQChannelCmd = DISABLE; 54 | NVIC_Init(&nvic_init); 55 | 56 | TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); 57 | TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 58 | } 59 | 60 | void TIM2_IRQHandler(void) 61 | { 62 | if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { 63 | TIM_ClearITPendingBit(TIM2, TIM_IT_Update); 64 | system_handle_data_timer_tick(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/hal/datatimer.h: -------------------------------------------------------------------------------- 1 | #ifndef __DATATIMER_H 2 | #define __DATATIMER_H 3 | 4 | #include 5 | #include 6 | 7 | void data_timer_init(uint32_t baud_rate); 8 | void data_timer_uninit(); 9 | 10 | extern void (*system_handle_data_timer_tick)(); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/hal/delay.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "delay.h" 9 | 10 | volatile bool done; 11 | 12 | void delay_init() 13 | { 14 | TIM_DeInit(TIM3); 15 | 16 | TIM_TimeBaseInitTypeDef tim_init; 17 | 18 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 19 | RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM3, DISABLE); 20 | 21 | // The delay timer assumes a 24 MHz clock source 22 | tim_init.TIM_Prescaler = 24 - 1; 23 | tim_init.TIM_CounterMode = TIM_CounterMode_Up; 24 | tim_init.TIM_Period = 0; 25 | tim_init.TIM_ClockDivision = TIM_CKD_DIV1; 26 | tim_init.TIM_RepetitionCounter = 0; 27 | TIM_TimeBaseInit(TIM3, &tim_init); 28 | 29 | TIM_ClearITPendingBit(TIM3, TIM_IT_Update); 30 | TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); 31 | 32 | NVIC_InitTypeDef nvic_init; 33 | nvic_init.NVIC_IRQChannel = TIM3_IRQn; 34 | nvic_init.NVIC_IRQChannelPreemptionPriority = 1; 35 | nvic_init.NVIC_IRQChannelSubPriority = 1; 36 | nvic_init.NVIC_IRQChannelCmd = ENABLE; 37 | NVIC_Init(&nvic_init); 38 | 39 | TIM_Cmd(TIM3, DISABLE); 40 | } 41 | 42 | void delay_us(uint16_t us) 43 | { 44 | TIM_Cmd(TIM3, DISABLE); 45 | TIM_SetAutoreload(TIM3, us); 46 | TIM_SetCounter(TIM3, 0); 47 | TIM_Cmd(TIM3, ENABLE); 48 | done = false; 49 | while (!done) {} 50 | 51 | TIM_Cmd(TIM3, DISABLE); 52 | } 53 | 54 | inline void delay_ms(uint32_t ms) 55 | { 56 | while (ms-- > 0) { 57 | delay_us(1000); 58 | } 59 | } 60 | 61 | void TIM3_IRQHandler(void) 62 | { 63 | if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { 64 | TIM_ClearITPendingBit(TIM3, TIM_IT_Update); 65 | done = true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/hal/delay.h: -------------------------------------------------------------------------------- 1 | #ifndef __DELAY_H 2 | #define __DELAY_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void delay_init(); 11 | 12 | void delay_us(uint16_t us); 13 | 14 | void delay_ms(uint32_t ms); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/hal/hal.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAL_H 2 | #define __HAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define HAL_OK 0 9 | 10 | #define HAL_ERROR -1 11 | #define HAL_ERROR_TIMEOUT -2 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/hal/i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAL_I2C_H 2 | #define __HAL_I2C_H 3 | 4 | #define I2C_PORT I2C2 5 | #define I2C_PORT_RCC_PERIPH RCC_APB1Periph_I2C2 6 | #define I2C_GPIO GPIOB 7 | 8 | // PB10: I2C2_SCL/USART3_TX 9 | #define I2C_PIN_SCL GPIO_Pin_10 10 | // PB11: I2C2_SDA/USART3_RX 11 | #define I2C_PIN_SDA GPIO_Pin_11 12 | 13 | #include "hal.h" 14 | 15 | typedef struct _i2c_port i2c_port; 16 | 17 | extern i2c_port DEFAULT_I2C_PORT; 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | void i2c_init(uint32_t clock_speed); 24 | void i2c_uninit(); 25 | int i2c_read_bytes(struct _i2c_port *port, uint8_t address, uint8_t reg, uint8_t size, uint8_t *data); 26 | int i2c_read_byte(struct _i2c_port *port, uint8_t address, uint8_t reg, uint8_t *data); 27 | int i2c_write_bytes(struct _i2c_port *port, uint8_t address, uint8_t reg, uint8_t size, uint8_t *data); 28 | int i2c_write_byte(struct _i2c_port *port, uint8_t address, uint8_t reg, uint8_t data); 29 | 30 | #ifdef __cplusplus 31 | }; 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/hal/millis.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "src/hal/millis.h" 6 | 7 | static uint32_t millis_counter; 8 | 9 | void millis_timer_init(void) 10 | { 11 | TIM_DeInit(TIM7); 12 | 13 | TIM_TimeBaseInitTypeDef tim_init; 14 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); 15 | RCC_APB2PeriphResetCmd(RCC_APB1Periph_TIM7, DISABLE); 16 | 17 | // The data timer assumes a 24 MHz clock source 18 | tim_init.TIM_Prescaler = 24 - 1; // tick every 1/1000000 s 19 | tim_init.TIM_CounterMode = TIM_CounterMode_Up; 20 | tim_init.TIM_Period = (uint16_t) (1000 - 1); // set up period of 1 millisecond 21 | tim_init.TIM_ClockDivision = TIM_CKD_DIV1; 22 | tim_init.TIM_RepetitionCounter = 0; 23 | 24 | TIM_TimeBaseInit(TIM7, &tim_init); 25 | 26 | TIM_ClearITPendingBit(TIM7, TIM_IT_Update); 27 | TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE); 28 | 29 | NVIC_InitTypeDef nvic_init; 30 | nvic_init.NVIC_IRQChannel = TIM7_IRQn; 31 | nvic_init.NVIC_IRQChannelPreemptionPriority = 0; 32 | nvic_init.NVIC_IRQChannelSubPriority = 1; 33 | nvic_init.NVIC_IRQChannelCmd = ENABLE; 34 | NVIC_Init(&nvic_init); 35 | 36 | TIM_Cmd(TIM7, ENABLE); 37 | } 38 | 39 | void millis_timer_uninit() 40 | { 41 | TIM_Cmd(TIM7, DISABLE); 42 | 43 | NVIC_InitTypeDef nvic_init; 44 | nvic_init.NVIC_IRQChannel = TIM7_IRQn; 45 | nvic_init.NVIC_IRQChannelPreemptionPriority = 0; 46 | nvic_init.NVIC_IRQChannelSubPriority = 1; 47 | nvic_init.NVIC_IRQChannelCmd = DISABLE; 48 | NVIC_Init(&nvic_init); 49 | 50 | TIM_ITConfig(TIM7, TIM_IT_Update, DISABLE); 51 | TIM_ClearITPendingBit(TIM7, TIM_IT_Update); 52 | } 53 | 54 | void TIM7_IRQHandler(void) 55 | { 56 | if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET) { 57 | TIM_ClearITPendingBit(TIM7, TIM_IT_Update); 58 | millis_counter++; 59 | } 60 | } 61 | 62 | uint32_t millis(void) 63 | { 64 | return millis_counter; 65 | } 66 | -------------------------------------------------------------------------------- /src/hal/millis.h: -------------------------------------------------------------------------------- 1 | #ifndef __MILLIS_H 2 | #define __MILLIS_H 3 | 4 | #include 5 | 6 | extern void millis_timer_init(void); 7 | extern void millis_timer_uninit(); 8 | 9 | extern uint32_t millis(); 10 | 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /src/hal/pwm.h: -------------------------------------------------------------------------------- 1 | #ifndef __PWM_H 2 | #define __PWM_H 3 | 4 | #include 5 | #include 6 | 7 | #define PWM_TIMER_DMA_BUFFER_SIZE 256 8 | 9 | void pwm_data_timer_init(); 10 | void pwm_data_timer_dma_request_enable(bool enabled); 11 | void pwm_data_timer_uninit(); 12 | 13 | void pwm_timer_init(uint32_t frequency_hz_100); 14 | void pwm_timer_pwm_enable(bool enabled); 15 | void pwm_timer_use(bool use); 16 | void pwm_timer_uninit(); 17 | uint16_t pwm_calculate_period(uint32_t frequency_hz_100); 18 | void pwm_timer_set_frequency(uint32_t frequency_hz_100); 19 | 20 | void pwm_dma_init(); 21 | void pwm_dma_interrupt_enable(bool enabled); 22 | void pwm_dma_start(); 23 | void pwm_dma_stop(); 24 | 25 | extern uint16_t (*pwm_handle_dma_transfer_half)(uint16_t buffer_size, uint16_t *buffer); 26 | extern uint16_t (*pwm_handle_dma_transfer_full)(uint16_t buffer_size, uint16_t *buffer); 27 | 28 | extern uint16_t pwm_timer_dma_buffer[PWM_TIMER_DMA_BUFFER_SIZE]; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/hal/spi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "spi.h" 6 | #include "gpio.h" 7 | 8 | void spi_init() 9 | { 10 | GPIO_InitTypeDef gpio_init; 11 | 12 | // SCK 13 | gpio_init.GPIO_Pin = PIN_SCK; 14 | gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; 15 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 16 | GPIO_Init(BANK_SCK, &gpio_init); 17 | 18 | // MOSI 19 | gpio_init.GPIO_Pin = PIN_MOSI; 20 | gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; 21 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 22 | GPIO_Init(BANK_MOSI, &gpio_init); 23 | 24 | // MISO 25 | gpio_init.GPIO_Pin = PIN_MISO; 26 | #ifdef RS41 27 | gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; 28 | #endif 29 | #ifdef DFM17 30 | gpio_init.GPIO_Mode = GPIO_Mode_IPU; 31 | #endif 32 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 33 | GPIO_Init(BANK_MISO, &gpio_init); 34 | 35 | RCC_SPIPeriphClockCmd(APBPERIPHERAL_SPI, ENABLE); 36 | 37 | SPI_InitTypeDef spi_init; 38 | SPI_StructInit(&spi_init); 39 | 40 | spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; 41 | spi_init.SPI_Mode = SPI_Mode_Master; 42 | #ifdef RS41 43 | spi_init.SPI_DataSize = SPI_DataSize_16b; 44 | #endif 45 | #ifdef DFM17 46 | spi_init.SPI_DataSize = SPI_DataSize_8b; 47 | #endif 48 | spi_init.SPI_CPOL = SPI_CPOL_Low; 49 | spi_init.SPI_CPHA = SPI_CPHA_1Edge; 50 | #ifdef RS41 51 | spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; 52 | #endif 53 | #ifdef DFM17 54 | spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; 55 | #endif 56 | spi_init.SPI_FirstBit = SPI_FirstBit_MSB; 57 | #ifdef RS41 58 | spi_init.SPI_CRCPolynomial = 7; 59 | #endif 60 | #ifdef DFM17 61 | spi_init.SPI_CRCPolynomial = 10; 62 | spi_init.SPI_NSS = SPI_NSS_Soft; 63 | #endif 64 | SPI_Init(PERIPHERAL_SPI, &spi_init); 65 | 66 | #ifdef RS41 67 | SPI_SSOutputCmd(PERIPHERAL_SPI, ENABLE); 68 | #endif 69 | #ifdef DFM17 70 | SPI_CalculateCRC(PERIPHERAL_SPI, DISABLE); 71 | #endif 72 | 73 | SPI_Cmd(PERIPHERAL_SPI, ENABLE); 74 | #ifdef RS41 75 | // TODO: Why is this call even here? 76 | SPI_Init(PERIPHERAL_SPI, &spi_init); 77 | #endif 78 | } 79 | 80 | void spi_uninit() 81 | { 82 | SPI_I2S_DeInit(PERIPHERAL_SPI); 83 | SPI_Cmd(PERIPHERAL_SPI, DISABLE); 84 | SPI_SSOutputCmd(PERIPHERAL_SPI, DISABLE); 85 | RCC_SPIPeriphClockCmd(APBPERIPHERAL_SPI, DISABLE); 86 | 87 | GPIO_InitTypeDef gpio_init; 88 | 89 | gpio_init.GPIO_Pin = PIN_MISO; 90 | gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; 91 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 92 | GPIO_Init(BANK_MISO, &gpio_init); 93 | 94 | gpio_init.GPIO_Pin = PIN_MOSI; 95 | gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; // was: GPIO_Mode_Out_PP; // GPIO_Mode_AF_PP 96 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 97 | GPIO_Init(BANK_MOSI, &gpio_init); 98 | } 99 | 100 | void spi_send(uint16_t data) 101 | { 102 | // Wait for TX buffer 103 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_TXE) == RESET); 104 | SPI_I2S_SendData(PERIPHERAL_SPI, data); 105 | #ifdef DFM17 106 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_TXE) == RESET); 107 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_BSY) == SET); 108 | 109 | // Reset the overrun error by reading the data and status registers 110 | // NOTE: It seems this sequence is required to make Si4063 SPI communication work on DFM17 radiosondes 111 | SPI_I2S_ReceiveData(PERIPHERAL_SPI); 112 | SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_OVR); 113 | #endif 114 | } 115 | 116 | uint8_t spi_receive() 117 | { 118 | // Wait for data in RX buffer 119 | #ifdef DFM17 120 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_BSY) == SET); 121 | #endif 122 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_RXNE) == RESET); 123 | return (uint8_t) SPI_I2S_ReceiveData(PERIPHERAL_SPI); 124 | } 125 | 126 | uint8_t spi_read() 127 | { 128 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_BSY) == SET); 129 | // Send dummy data to read in bidirectional mode 130 | SPI_I2S_SendData(PERIPHERAL_SPI, 0xFF); 131 | // Wait for data in RX buffer 132 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_RXNE) == RESET); 133 | return (uint8_t) SPI_I2S_ReceiveData(PERIPHERAL_SPI); 134 | } 135 | 136 | void spi_set_chip_select(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, bool select) 137 | { 138 | if (select) { 139 | GPIO_ResetBits(gpio_cs, pin_cs); 140 | } else { 141 | GPIO_SetBits(gpio_cs, pin_cs); 142 | } 143 | } 144 | 145 | uint8_t spi_send_and_receive(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, uint16_t data) { 146 | GPIO_ResetBits(gpio_cs, pin_cs); 147 | 148 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_TXE) == RESET); 149 | SPI_I2S_SendData(PERIPHERAL_SPI, data); 150 | 151 | while (SPI_I2S_GetFlagStatus(PERIPHERAL_SPI, SPI_I2S_FLAG_RXNE) == RESET); 152 | GPIO_SetBits(gpio_cs, pin_cs); 153 | return (uint8_t) SPI_I2S_ReceiveData(PERIPHERAL_SPI); 154 | } 155 | -------------------------------------------------------------------------------- /src/hal/spi.h: -------------------------------------------------------------------------------- 1 | #ifndef __SPI_H 2 | #define __SPI_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | void spi_init(); 9 | 10 | void spi_uninit(); 11 | 12 | void spi_send(uint16_t data); 13 | 14 | uint8_t spi_receive(); 15 | 16 | uint8_t spi_read(); 17 | 18 | void spi_set_chip_select(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, bool select); 19 | 20 | uint8_t spi_send_and_receive(GPIO_TypeDef *gpio_cs, uint16_t pin_cs, uint16_t data); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/hal/stm_lib/inc/stm32f10x_pwr.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32f10x_pwr.h 4 | * @author MCD Application Team 5 | * @version V3.5.0 6 | * @date 11-March-2011 7 | * @brief This file contains all the functions prototypes for the PWR firmware 8 | * library. 9 | ****************************************************************************** 10 | * @attention 11 | * 12 | * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS 13 | * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE 14 | * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY 15 | * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING 16 | * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE 17 | * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. 18 | * 19 | *

© COPYRIGHT 2011 STMicroelectronics

20 | ****************************************************************************** 21 | */ 22 | 23 | /* Define to prevent recursive inclusion -------------------------------------*/ 24 | #ifndef __STM32F10x_PWR_H 25 | #define __STM32F10x_PWR_H 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /* Includes ------------------------------------------------------------------*/ 32 | #include "stm32f10x.h" 33 | 34 | /** @addtogroup STM32F10x_StdPeriph_Driver 35 | * @{ 36 | */ 37 | 38 | /** @addtogroup PWR 39 | * @{ 40 | */ 41 | 42 | /** @defgroup PWR_Exported_Types 43 | * @{ 44 | */ 45 | 46 | /** 47 | * @} 48 | */ 49 | 50 | /** @defgroup PWR_Exported_Constants 51 | * @{ 52 | */ 53 | 54 | /** @defgroup PVD_detection_level 55 | * @{ 56 | */ 57 | 58 | #define PWR_PVDLevel_2V2 ((uint32_t)0x00000000) 59 | #define PWR_PVDLevel_2V3 ((uint32_t)0x00000020) 60 | #define PWR_PVDLevel_2V4 ((uint32_t)0x00000040) 61 | #define PWR_PVDLevel_2V5 ((uint32_t)0x00000060) 62 | #define PWR_PVDLevel_2V6 ((uint32_t)0x00000080) 63 | #define PWR_PVDLevel_2V7 ((uint32_t)0x000000A0) 64 | #define PWR_PVDLevel_2V8 ((uint32_t)0x000000C0) 65 | #define PWR_PVDLevel_2V9 ((uint32_t)0x000000E0) 66 | #define IS_PWR_PVD_LEVEL(LEVEL) (((LEVEL) == PWR_PVDLevel_2V2) || ((LEVEL) == PWR_PVDLevel_2V3)|| \ 67 | ((LEVEL) == PWR_PVDLevel_2V4) || ((LEVEL) == PWR_PVDLevel_2V5)|| \ 68 | ((LEVEL) == PWR_PVDLevel_2V6) || ((LEVEL) == PWR_PVDLevel_2V7)|| \ 69 | ((LEVEL) == PWR_PVDLevel_2V8) || ((LEVEL) == PWR_PVDLevel_2V9)) 70 | /** 71 | * @} 72 | */ 73 | 74 | /** @defgroup Regulator_state_is_STOP_mode 75 | * @{ 76 | */ 77 | 78 | #define PWR_Regulator_ON ((uint32_t)0x00000000) 79 | #define PWR_Regulator_LowPower ((uint32_t)0x00000001) 80 | #define IS_PWR_REGULATOR(REGULATOR) (((REGULATOR) == PWR_Regulator_ON) || \ 81 | ((REGULATOR) == PWR_Regulator_LowPower)) 82 | /** 83 | * @} 84 | */ 85 | 86 | /** @defgroup STOP_mode_entry 87 | * @{ 88 | */ 89 | 90 | #define PWR_STOPEntry_WFI ((uint8_t)0x01) 91 | #define PWR_STOPEntry_WFE ((uint8_t)0x02) 92 | #define IS_PWR_STOP_ENTRY(ENTRY) (((ENTRY) == PWR_STOPEntry_WFI) || ((ENTRY) == PWR_STOPEntry_WFE)) 93 | 94 | /** 95 | * @} 96 | */ 97 | 98 | /** @defgroup PWR_Flag 99 | * @{ 100 | */ 101 | 102 | #define PWR_FLAG_WU ((uint32_t)0x00000001) 103 | #define PWR_FLAG_SB ((uint32_t)0x00000002) 104 | #define PWR_FLAG_PVDO ((uint32_t)0x00000004) 105 | #define IS_PWR_GET_FLAG(FLAG) (((FLAG) == PWR_FLAG_WU) || ((FLAG) == PWR_FLAG_SB) || \ 106 | ((FLAG) == PWR_FLAG_PVDO)) 107 | 108 | #define IS_PWR_CLEAR_FLAG(FLAG) (((FLAG) == PWR_FLAG_WU) || ((FLAG) == PWR_FLAG_SB)) 109 | /** 110 | * @} 111 | */ 112 | 113 | /** 114 | * @} 115 | */ 116 | 117 | /** @defgroup PWR_Exported_Macros 118 | * @{ 119 | */ 120 | 121 | /** 122 | * @} 123 | */ 124 | 125 | /** @defgroup PWR_Exported_Functions 126 | * @{ 127 | */ 128 | 129 | void PWR_DeInit(void); 130 | void PWR_BackupAccessCmd(FunctionalState NewState); 131 | void PWR_PVDCmd(FunctionalState NewState); 132 | void PWR_PVDLevelConfig(uint32_t PWR_PVDLevel); 133 | void PWR_WakeUpPinCmd(FunctionalState NewState); 134 | void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry); 135 | void PWR_EnterSTANDBYMode(void); 136 | FlagStatus PWR_GetFlagStatus(uint32_t PWR_FLAG); 137 | void PWR_ClearFlag(uint32_t PWR_FLAG); 138 | 139 | #ifdef __cplusplus 140 | } 141 | #endif 142 | 143 | #endif /* __STM32F10x_PWR_H */ 144 | /** 145 | * @} 146 | */ 147 | 148 | /** 149 | * @} 150 | */ 151 | 152 | /** 153 | * @} 154 | */ 155 | 156 | /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ 157 | -------------------------------------------------------------------------------- /src/hal/stm_lib/src/stm32f10x_flash.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelnousiainen/RS41ng/d04e3ca961f06f900f403327e2a24f519337086c/src/hal/stm_lib/src/stm32f10x_flash.c -------------------------------------------------------------------------------- /src/hal/stm_lib/src/stm32f10x_usart.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikaelnousiainen/RS41ng/d04e3ca961f06f900f403327e2a24f519337086c/src/hal/stm_lib/src/stm32f10x_usart.c -------------------------------------------------------------------------------- /src/hal/system.h: -------------------------------------------------------------------------------- 1 | #ifndef __HAL_SYSTEM_H 2 | #define __HAL_SYSTEM_H 3 | 4 | #include "config.h" 5 | #include "hal.h" 6 | 7 | #define SYSTEM_SCHEDULER_TIMER_TICKS_PER_SECOND 10000 8 | 9 | void system_init(); 10 | void system_shutdown(); 11 | uint32_t system_get_tick(); 12 | void system_disable_tick(); 13 | void system_enable_tick(); 14 | void system_disable_irq(); 15 | void system_enable_irq(); 16 | void system_set_green_led(bool enabled); 17 | void system_set_red_led(bool enabled); 18 | #ifdef DFM17 19 | void system_set_yellow_led(bool enabled); 20 | #endif 21 | uint16_t system_get_battery_voltage_millivolts(); 22 | uint16_t system_get_button_adc_value(); 23 | 24 | extern void (*system_handle_timer_tick)(); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/hal/usart_ext.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "usart_ext.h" 6 | 7 | // USART3 is the external serial port, which shares TX/RX pins with the external I²C bus. 8 | 9 | void usart_ext_init(uint32_t baud_rate) 10 | { 11 | GPIO_InitTypeDef gpio_init; 12 | 13 | // USART3 TX 14 | gpio_init.GPIO_Pin = GPIO_Pin_10; 15 | gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; 16 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 17 | GPIO_Init(GPIOB, &gpio_init); 18 | 19 | // USART3 RX 20 | gpio_init.GPIO_Pin = GPIO_Pin_11; 21 | gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; 22 | GPIO_Init(GPIOB, &gpio_init); 23 | 24 | NVIC_DisableIRQ(USART3_IRQn); 25 | USART_Cmd(USART3, DISABLE); 26 | 27 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); 28 | 29 | USART_InitTypeDef usart_init; 30 | usart_init.USART_BaudRate = baud_rate; 31 | usart_init.USART_WordLength = USART_WordLength_8b; 32 | usart_init.USART_StopBits = USART_StopBits_1; 33 | usart_init.USART_Parity = USART_Parity_No; 34 | usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 35 | usart_init.USART_Mode = USART_Mode_Tx; // Enable only transmit for now | USART_Mode_Rx; 36 | USART_Init(USART3, &usart_init); 37 | 38 | USART_Cmd(USART3, ENABLE); 39 | } 40 | 41 | void usart_ext_uninit() 42 | { 43 | USART_Cmd(USART3, DISABLE); 44 | USART_DeInit(USART3); 45 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, DISABLE); 46 | } 47 | 48 | void usart_ext_enable(bool enabled) 49 | { 50 | USART_Cmd(USART3, enabled ? ENABLE : DISABLE); 51 | } 52 | 53 | void usart_ext_send_byte(uint8_t data) 54 | { 55 | while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) {} 56 | USART_SendData(USART3, data); 57 | while (USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET) {} 58 | } 59 | -------------------------------------------------------------------------------- /src/hal/usart_ext.h: -------------------------------------------------------------------------------- 1 | #ifndef __USART_EXT_H 2 | #define __USART_EXT_H 3 | 4 | #include 5 | #include 6 | 7 | void usart_ext_init(uint32_t baud_rate); 8 | void usart_ext_uninit(); 9 | void usart_ext_enable(bool enabled); 10 | void usart_ext_send_byte(uint8_t data); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/hal/usart_gps.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "usart_gps.h" 9 | #include "gpio.h" 10 | 11 | void (*usart_gps_handle_incoming_byte)(uint8_t data) = NULL; 12 | 13 | void usart_gps_init(uint32_t baud_rate, bool enable_irq) 14 | { 15 | GPIO_InitTypeDef gpio_init; 16 | 17 | // USART TX 18 | gpio_init.GPIO_Pin = PIN_USART_TX; 19 | gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; 20 | gpio_init.GPIO_Speed = GPIO_Speed_50MHz; 21 | GPIO_Init(BANK_USART_TX, &gpio_init); 22 | 23 | // USART RX 24 | gpio_init.GPIO_Pin = PIN_USART_RX; 25 | gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; 26 | GPIO_Init(BANK_USART_RX, &gpio_init); 27 | 28 | NVIC_DisableIRQ(USART_IRQ); 29 | USART_ITConfig(USART_IT, USART_IT_RXNE, DISABLE); 30 | USART_ClearITPendingBit(USART_IT, USART_IT_RXNE); 31 | USART_ClearITPendingBit(USART_IT, USART_IT_ORE); 32 | 33 | USART_Cmd(USART_IT, DISABLE); 34 | 35 | #if defined(RS41) 36 | RCC_APB2PeriphClockCmd(APBPERIPHERAL_USART, ENABLE); 37 | #elif defined(DFM17) 38 | RCC_APB1PeriphClockCmd(APBPERIPHERAL_USART, ENABLE); 39 | #endif 40 | 41 | USART_InitTypeDef usart_init; 42 | usart_init.USART_BaudRate = baud_rate; 43 | usart_init.USART_WordLength = USART_WordLength_8b; 44 | usart_init.USART_StopBits = USART_StopBits_1; 45 | usart_init.USART_Parity = USART_Parity_No; 46 | usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 47 | usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; 48 | USART_Init(USART_IT, &usart_init); 49 | 50 | NVIC_InitTypeDef nvic_init; 51 | nvic_init.NVIC_IRQChannel = USART_IRQ; 52 | nvic_init.NVIC_IRQChannelPreemptionPriority = 3; 53 | nvic_init.NVIC_IRQChannelSubPriority = 2; 54 | nvic_init.NVIC_IRQChannelCmd = ENABLE; 55 | NVIC_Init(&nvic_init); 56 | 57 | USART_Cmd(USART_IT, ENABLE); 58 | if (enable_irq) { 59 | USART_ITConfig(USART_IT, USART_IT_RXNE, ENABLE); 60 | NVIC_EnableIRQ(USART_IRQ); 61 | } 62 | } 63 | 64 | static void usart_gps_enable_irq(bool enabled) { 65 | if (enabled) { 66 | USART_ITConfig(USART_IT, USART_IT_RXNE, ENABLE); 67 | NVIC_EnableIRQ(USART_IRQ); 68 | } else { 69 | NVIC_DisableIRQ(USART_IRQ); 70 | USART_ITConfig(USART_IT, USART_IT_RXNE, DISABLE); 71 | } 72 | USART_ClearITPendingBit(USART_IT, USART_IT_RXNE); 73 | USART_ClearITPendingBit(USART_IT, USART_IT_ORE); 74 | } 75 | 76 | void usart_gps_uninit() 77 | { 78 | usart_gps_enable_irq(false); 79 | USART_Cmd(USART_IT, DISABLE); 80 | USART_DeInit(USART_IT); 81 | #if defined(RS41) 82 | RCC_APB2PeriphClockCmd(APBPERIPHERAL_USART, DISABLE); 83 | #elif defined(DFM17) 84 | RCC_APB1PeriphClockCmd(APBPERIPHERAL_USART, DISABLE); 85 | #endif 86 | } 87 | 88 | void usart_gps_enable(bool enabled) 89 | { 90 | usart_gps_enable_irq(enabled); 91 | USART_Cmd(USART_IT, enabled ? ENABLE : DISABLE); 92 | } 93 | 94 | void usart_gps_send_byte(uint8_t data) 95 | { 96 | while (USART_GetFlagStatus(USART_IT, USART_FLAG_TC) == RESET) {} 97 | USART_SendData(USART_IT, data); 98 | while (USART_GetFlagStatus(USART_IT, USART_FLAG_TC) == RESET) {} 99 | } 100 | 101 | void USART_IRQ_HANDLER(void) 102 | { 103 | if (USART_GetITStatus(USART_IT, USART_IT_RXNE) != RESET) { 104 | USART_ClearITPendingBit(USART_IT, USART_IT_RXNE); 105 | uint8_t data = (uint8_t) USART_ReceiveData(USART_IT); 106 | usart_gps_handle_incoming_byte(data); 107 | } else if (USART_GetITStatus(USART_IT, USART_IT_ORE) != RESET) { 108 | USART_ClearITPendingBit(USART_IT, USART_IT_ORE); 109 | USART_ReceiveData(USART_IT); 110 | } else { 111 | USART_ReceiveData(USART_IT); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/hal/usart_gps.h: -------------------------------------------------------------------------------- 1 | #ifndef __USART_GPS_H 2 | #define __USART_GPS_H 3 | 4 | #include 5 | #include 6 | 7 | void usart_gps_init(uint32_t baud_rate, bool enable_irq); 8 | void usart_gps_uninit(); 9 | void usart_gps_enable(bool enabled); 10 | void usart_gps_send_byte(uint8_t data); 11 | 12 | extern void (*usart_gps_handle_incoming_byte)(uint8_t data); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/locator.c: -------------------------------------------------------------------------------- 1 | // Based on HamLib's locator routines 2 | // OK1TE 2018-10 3 | 4 | #include "locator.h" 5 | 6 | const static uint8_t loc_char_range[] = {18, 10, 24, 10, 24, 10}; 7 | const float precision = 1E+7f; 8 | 9 | void locator_from_lonlat(int32_t longitude, int32_t latitude, uint8_t pair_count, char *locator) 10 | { 11 | for (uint8_t x_or_y = 0; x_or_y < 2; ++x_or_y) { 12 | float ordinate = ((x_or_y == 0) ? (longitude / 2.0f) / precision : latitude / precision) + 90; 13 | uint32_t divisions = 1; 14 | 15 | for (uint8_t pair = 0; pair < pair_count; pair++) { 16 | divisions *= loc_char_range[pair]; 17 | const float square_size = 180.0f / divisions; 18 | 19 | uint8_t locvalue = (uint8_t) (ordinate / square_size); 20 | ordinate -= square_size * (float) locvalue; 21 | locvalue += (loc_char_range[pair] == 10) ? '0' : 'A'; 22 | locator[pair * 2 + x_or_y] = locvalue; 23 | } 24 | } 25 | 26 | locator[pair_count * 2] = 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/locator.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOCATOR_H 2 | #define __LOCATOR_H 3 | 4 | #include 5 | 6 | void locator_from_lonlat(int32_t longitude, int32_t latitude, uint8_t pair_count, char *locator); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | void log_bytes(int length, char *data) 4 | { 5 | for (int i = 0; i < length; i++) { 6 | char c = data[i]; 7 | if (c >= 0x20 && c <= 0x7e) { 8 | log_info("%c", c); 9 | } else { 10 | log_info(" [%02X] ", c); 11 | } 12 | } 13 | } 14 | 15 | void log_bytes_hex(int length, char *data) 16 | { 17 | for (int i = 0; i < length; i++) { 18 | log_info("%02X ", data[i]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_H 2 | #define __LOG_H 3 | 4 | #include "config.h" 5 | 6 | #if defined(SEMIHOSTING_ENABLE) && defined(LOGGING_ENABLE) 7 | 8 | #include 9 | 10 | #define log_error printf 11 | #define log_warn printf 12 | #define log_info printf 13 | #define log_debug(...) 14 | #define log_trace(...) 15 | 16 | #else 17 | 18 | #define log_error(...) 19 | #define log_warn(...) 20 | #define log_info(...) 21 | #define log_debug(...) 22 | #define log_trace(...) 23 | 24 | #endif 25 | 26 | void log_bytes(int length, char *data); 27 | void log_bytes_hex(int length, char *data); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/payload.h: -------------------------------------------------------------------------------- 1 | #ifndef __PAYLOAD_H 2 | #define __PAYLOAD_H 3 | 4 | #include 5 | #include 6 | 7 | #include "telemetry.h" 8 | 9 | typedef struct _payload_encoder { 10 | uint16_t (*encode)(uint8_t *payload, uint16_t length, telemetry_data *data, char *message); 11 | } payload_encoder; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/radio.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_H 2 | #define __RADIO_H 3 | 4 | void radio_init(); 5 | void radio_handle_timer_tick(); 6 | void radio_handle_data_timer_tick(); 7 | void radio_handle_main_loop(); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/radio_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_INTERNAL_H 2 | #define __RADIO_INTERNAL_H 3 | 4 | #include 5 | 6 | #include "payload.h" 7 | #include "codecs/jtencode/jtencode.h" 8 | #include "codecs/fsk/fsk.h" 9 | 10 | typedef enum _radio_type { 11 | RADIO_TYPE_SI4032 = 1, 12 | RADIO_TYPE_SI5351, 13 | RADIO_TYPE_SI4063, 14 | } radio_type; 15 | 16 | typedef enum _radio_data_mode { 17 | RADIO_DATA_MODE_CW = 1, 18 | RADIO_DATA_MODE_PIP, 19 | RADIO_DATA_MODE_RTTY, 20 | RADIO_DATA_MODE_APRS_1200, 21 | RADIO_DATA_MODE_HORUS_V1, 22 | RADIO_DATA_MODE_HORUS_V2, 23 | RADIO_DATA_MODE_WSPR, 24 | RADIO_DATA_MODE_FT8, 25 | RADIO_DATA_MODE_JT65, 26 | RADIO_DATA_MODE_JT4, 27 | RADIO_DATA_MODE_JT9, 28 | RADIO_DATA_MODE_FSQ_2, 29 | RADIO_DATA_MODE_FSQ_3, 30 | RADIO_DATA_MODE_FSQ_4_5, 31 | RADIO_DATA_MODE_FSQ_6, 32 | RADIO_DATA_MODE_CATS, 33 | } radio_data_mode; 34 | 35 | typedef struct _radio_transmit_entry { 36 | bool enabled; 37 | bool end; 38 | 39 | radio_type radio_type; 40 | radio_data_mode data_mode; 41 | 42 | uint16_t time_sync_seconds; 43 | uint16_t time_sync_seconds_offset; 44 | 45 | uint32_t frequency; 46 | uint8_t tx_power; 47 | uint32_t symbol_rate; 48 | 49 | char **messages; 50 | uint8_t current_message_index; 51 | uint8_t message_count; 52 | 53 | uint8_t current_transmit_index; 54 | uint8_t transmit_count; 55 | 56 | payload_encoder *payload_encoder; 57 | fsk_encoder_api *fsk_encoder_api; 58 | 59 | fsk_encoder fsk_encoder; 60 | } radio_transmit_entry; 61 | 62 | typedef struct _radio_module_state { 63 | volatile bool radio_transmission_active; 64 | volatile bool radio_transmission_finished; 65 | 66 | volatile bool radio_transmit_next_symbol_flag; 67 | 68 | volatile uint32_t radio_symbol_count_interrupt; 69 | volatile uint32_t radio_symbol_count_loop; 70 | 71 | volatile bool radio_dma_transfer_active; 72 | volatile bool radio_manual_transmit_active; 73 | volatile bool radio_interrupt_transmit_active; 74 | volatile bool radio_fifo_transmit_active; 75 | 76 | fsk_tone *radio_current_fsk_tones; 77 | int8_t radio_current_fsk_tone_count; 78 | uint32_t radio_current_tone_spacing_hz_100; 79 | 80 | uint32_t radio_current_symbol_rate; 81 | uint32_t radio_current_symbol_delay_ms_100; 82 | } radio_module_state; 83 | 84 | extern radio_transmit_entry *radio_current_transmit_entry; 85 | extern radio_module_state radio_shared_state; 86 | extern uint32_t precalculated_pwm_periods[]; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/radio_payload_aprs_position.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "codecs/ax25/ax25.h" 4 | #include "codecs/aprs/aprs_position.h" 5 | #include "config.h" 6 | #include "telemetry.h" 7 | #include "log.h" 8 | #include "radio_payload_aprs_position.h" 9 | 10 | uint16_t radio_aprs_position_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 11 | { 12 | uint8_t aprs_packet[RADIO_PAYLOAD_MAX_LENGTH]; 13 | 14 | aprs_generate_position(aprs_packet, sizeof(aprs_packet), telemetry_data, 15 | APRS_SYMBOL_TABLE, APRS_SYMBOL, false, message); 16 | 17 | log_debug("APRS packet: %s\n", aprs_packet); 18 | 19 | return ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS, 20 | (char *) aprs_packet, length, payload); 21 | } 22 | 23 | payload_encoder radio_aprs_position_payload_encoder = { 24 | .encode = radio_aprs_position_encode, 25 | }; 26 | -------------------------------------------------------------------------------- /src/radio_payload_aprs_position.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_APRS_POSITION_H 2 | #define __RADIO_PAYLOAD_APRS_POSITION_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_aprs_position_payload_encoder; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/radio_payload_aprs_weather.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "codecs/ax25/ax25.h" 4 | #include "codecs/aprs/aprs_weather.h" 5 | #include "config.h" 6 | #include "telemetry.h" 7 | #include "log.h" 8 | #include "radio_payload_aprs_weather.h" 9 | 10 | uint16_t radio_aprs_weather_report_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 11 | { 12 | uint8_t aprs_packet[RADIO_PAYLOAD_MAX_LENGTH]; 13 | 14 | aprs_generate_weather_report(aprs_packet, sizeof(aprs_packet), telemetry_data,true, message); 15 | 16 | log_debug("APRS packet: %s\n", aprs_packet); 17 | 18 | return ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS, 19 | (char *) aprs_packet, length, payload); 20 | } 21 | 22 | payload_encoder radio_aprs_weather_report_payload_encoder = { 23 | .encode = radio_aprs_weather_report_encode, 24 | }; 25 | -------------------------------------------------------------------------------- /src/radio_payload_aprs_weather.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_APRS_WEATHER_H 2 | #define __RADIO_PAYLOAD_APRS_WEATHER_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_aprs_weather_report_payload_encoder; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/radio_payload_cats.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "config.h" 5 | #include "telemetry.h" 6 | #include "payload.h" 7 | #include "log.h" 8 | #include "codecs/cats/cats.h" 9 | #include "codecs/cats/whisker.h" 10 | 11 | #define CATS_PREAMBLE_BYTE 0x55 12 | #define CATS_PREAMBLE_LENGTH 4 13 | #define CATS_SYNC_WORD 0xABCDEF12 14 | #define CATS_SYNC_WORD_LENGTH 4 15 | 16 | uint16_t radio_cats_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 17 | { 18 | uint8_t *cur = payload; 19 | 20 | // preamble 21 | for (int i = 0; i < CATS_PREAMBLE_LENGTH; i++) { // 4 22 | *(cur++) = CATS_PREAMBLE_BYTE; 23 | } 24 | 25 | // sync word 26 | for (int i = CATS_SYNC_WORD_LENGTH - 1; i >= 0; i--) { // 4 27 | *(cur++) = (CATS_SYNC_WORD >> (i * 8)); 28 | } 29 | 30 | uint8_t *data = malloc(length); 31 | cats_packet packet = cats_create(data); 32 | cats_append_identification_whisker(&packet, CATS_CALLSIGN, CATS_SSID, CATS_ICON); // 11 33 | cats_append_comment_whisker(&packet, message); // 102 34 | 35 | if (GPS_HAS_FIX(telemetry_data->gps) && 36 | (telemetry_data->gps.latitude_degrees_1000000 != 0 || 37 | telemetry_data->gps.longitude_degrees_1000000 != 0)) { 38 | cats_append_gps_whisker(&packet, telemetry_data->gps); // 16 39 | } 40 | 41 | cats_append_node_info_whisker(&packet, telemetry_data); // 16 42 | 43 | size_t len = cats_fully_encode(packet, cur); 44 | log_info("CATS packet length: %ld\n", len + CATS_PREAMBLE_LENGTH + CATS_SYNC_WORD_LENGTH); 45 | free(data); 46 | 47 | return (uint16_t)(CATS_PREAMBLE_LENGTH + CATS_SYNC_WORD_LENGTH + len); 48 | } 49 | 50 | payload_encoder radio_cats_payload_encoder = { 51 | .encode = radio_cats_encode, 52 | }; 53 | -------------------------------------------------------------------------------- /src/radio_payload_cats.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_CATS_H 2 | #define __RADIO_PAYLOAD_CATS_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_cats_payload_encoder; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/radio_payload_cw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "radio_payload_cw.h" 4 | 5 | uint16_t radio_cw_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 6 | { 7 | return snprintf((char *) payload, length, "%s", message); 8 | } 9 | 10 | payload_encoder radio_cw_payload_encoder = { 11 | .encode = radio_cw_encode, 12 | }; 13 | -------------------------------------------------------------------------------- /src/radio_payload_cw.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_CW_H 2 | #define __RADIO_PAYLOAD_CW_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_cw_payload_encoder; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/radio_payload_fsq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "radio_payload_fsq.h" 4 | 5 | uint16_t radio_fsq_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 6 | { 7 | return snprintf((char *) payload, length, "%s", message); 8 | } 9 | 10 | payload_encoder radio_fsq_payload_encoder = { 11 | .encode = radio_fsq_encode, 12 | }; 13 | -------------------------------------------------------------------------------- /src/radio_payload_fsq.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_FSQ_H 2 | #define __RADIO_PAYLOAD_FSQ_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_fsq_payload_encoder; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/radio_payload_horus_v1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "codecs/horus/horus_packet_v1.h" 4 | #include "codecs/horus/horus_l2.h" 5 | #include "config.h" 6 | #include "telemetry.h" 7 | #include "radio_payload_horus_v1.h" 8 | 9 | #ifdef SEMIHOSTING_ENABLE 10 | #include "log.h" 11 | #endif 12 | 13 | #define HORUS_V1_PREAMBLE_BYTE 0x1B 14 | 15 | uint16_t radio_horus_v1_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 16 | { 17 | horus_packet_v1 horus_packet; 18 | 19 | size_t packet_length = horus_packet_v1_create((uint8_t *) &horus_packet, sizeof(horus_packet), 20 | telemetry_data, HORUS_V1_PAYLOAD_ID); 21 | 22 | #ifdef SEMIHOSTING_ENABLE 23 | log_info("Horus V1 packet: "); 24 | log_bytes_hex((int) packet_length, (char *) &horus_packet); 25 | log_info("\n"); 26 | #endif 27 | 28 | // Preamble to help the decoder lock-on after a quiet period. 29 | for (int i = 0; i < HORUS_V1_PREAMBLE_LENGTH; i++) { 30 | payload[i] = HORUS_V1_PREAMBLE_BYTE; 31 | } 32 | 33 | // Encode the packet, and write into the mfsk buffer. 34 | int encoded_length = horus_l2_encode_tx_packet( 35 | (unsigned char *) payload + HORUS_V1_PREAMBLE_LENGTH, 36 | (unsigned char *) &horus_packet, (int) packet_length); 37 | 38 | return encoded_length + HORUS_V1_PREAMBLE_LENGTH; 39 | } 40 | 41 | uint16_t radio_horus_v1_idle_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 42 | { 43 | // Use the preamble for idle tones during continuous transmit mode 44 | for (int i = 0; i < HORUS_V1_IDLE_PREAMBLE_LENGTH; i++) { 45 | payload[i] = HORUS_V1_PREAMBLE_BYTE; 46 | } 47 | 48 | return HORUS_V1_IDLE_PREAMBLE_LENGTH; 49 | } 50 | 51 | payload_encoder radio_horus_v1_payload_encoder = { 52 | .encode = radio_horus_v1_encode, 53 | }; 54 | 55 | payload_encoder radio_horus_v1_idle_encoder = { 56 | .encode = radio_horus_v1_idle_encode, 57 | }; 58 | -------------------------------------------------------------------------------- /src/radio_payload_horus_v1.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_HORUS_V1_H 2 | #define __RADIO_PAYLOAD_HORUS_V1_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_horus_v1_payload_encoder; 7 | extern payload_encoder radio_horus_v1_idle_encoder; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/radio_payload_horus_v2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "codecs/horus/horus_packet_v2.h" 4 | #include "codecs/horus/horus_l2.h" 5 | #include "config.h" 6 | #include "telemetry.h" 7 | #include "radio_payload_horus_v2.h" 8 | 9 | #ifdef SEMIHOSTING_ENABLE 10 | #include "log.h" 11 | #endif 12 | 13 | #define HORUS_V2_PREAMBLE_BYTE 0xE4 14 | 15 | uint16_t radio_horus_v2_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 16 | { 17 | horus_packet_v2 horus_packet; 18 | 19 | size_t packet_length = horus_packet_v2_create((uint8_t *) &horus_packet, sizeof(horus_packet), 20 | telemetry_data, HORUS_V2_PAYLOAD_ID); 21 | 22 | #ifdef SEMIHOSTING_ENABLE 23 | log_info("Horus V2 packet: "); 24 | log_bytes_hex((int) packet_length, (char *) &horus_packet); 25 | log_info("\n"); 26 | #endif 27 | 28 | // Preamble to help the decoder lock-on after a quiet period. 29 | for (int i = 0; i < HORUS_V2_PREAMBLE_LENGTH; i++) { 30 | payload[i] = HORUS_V2_PREAMBLE_BYTE; 31 | } 32 | 33 | // Encode the packet, and write into the mfsk buffer. 34 | int encoded_length = horus_l2_encode_tx_packet( 35 | (unsigned char *) payload + HORUS_V2_PREAMBLE_LENGTH, 36 | (unsigned char *) &horus_packet, (int) packet_length); 37 | 38 | return encoded_length + HORUS_V2_PREAMBLE_LENGTH; 39 | } 40 | 41 | uint16_t radio_horus_v2_idle_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 42 | { 43 | // Use the preamble for idle tones during continuous transmit mode 44 | for (int i = 0; i < HORUS_V2_IDLE_PREAMBLE_LENGTH; i++) { 45 | payload[i] = HORUS_V2_PREAMBLE_BYTE; 46 | } 47 | 48 | return HORUS_V2_IDLE_PREAMBLE_LENGTH; 49 | } 50 | 51 | payload_encoder radio_horus_v2_payload_encoder = { 52 | .encode = radio_horus_v2_encode, 53 | }; 54 | 55 | payload_encoder radio_horus_v2_idle_encoder = { 56 | .encode = radio_horus_v2_idle_encode, 57 | }; 58 | -------------------------------------------------------------------------------- /src/radio_payload_horus_v2.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_HORUS_V2_H 2 | #define __RADIO_PAYLOAD_HORUS_V2_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_horus_v2_payload_encoder; 7 | extern payload_encoder radio_horus_v2_idle_encoder; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/radio_payload_jtencode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "radio_payload_jtencode.h" 4 | 5 | uint16_t radio_ft8_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 6 | { 7 | return snprintf((char *) payload, length, "%s", message); 8 | } 9 | 10 | payload_encoder radio_ft8_payload_encoder = { 11 | .encode = radio_ft8_encode, 12 | }; 13 | 14 | uint16_t radio_jt9_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 15 | { 16 | return snprintf((char *) payload, length, "%s", message); 17 | } 18 | 19 | payload_encoder radio_jt9_payload_encoder = { 20 | .encode = radio_jt9_encode, 21 | }; 22 | 23 | uint16_t radio_jt4_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 24 | { 25 | return snprintf((char *) payload, length, "%s", message); 26 | } 27 | 28 | payload_encoder radio_jt4_payload_encoder = { 29 | .encode = radio_jt4_encode, 30 | }; 31 | 32 | uint16_t radio_jt65_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 33 | { 34 | return snprintf((char *) payload, length, "%s", message); 35 | } 36 | 37 | payload_encoder radio_jt65_payload_encoder = { 38 | .encode = radio_jt65_encode, 39 | }; 40 | -------------------------------------------------------------------------------- /src/radio_payload_jtencode.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_JTENCODE_H 2 | #define __RADIO_PAYLOAD_JTENCODE_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_ft8_payload_encoder; 7 | extern payload_encoder radio_jt9_payload_encoder; 8 | extern payload_encoder radio_jt4_payload_encoder; 9 | extern payload_encoder radio_jt65_payload_encoder; 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/radio_payload_wspr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "telemetry.h" 3 | #include "radio_payload_wspr.h" 4 | 5 | uint16_t radio_wspr_encode(uint8_t *payload, uint16_t length, telemetry_data *telemetry_data, char *message) 6 | { 7 | // Not used for WSPR 8 | return 0; 9 | } 10 | 11 | payload_encoder radio_wspr_payload_encoder = { 12 | .encode = radio_wspr_encode, 13 | }; 14 | -------------------------------------------------------------------------------- /src/radio_payload_wspr.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_PAYLOAD_WSPR_H 2 | #define __RADIO_PAYLOAD_WSPR_H 3 | 4 | #include "payload.h" 5 | 6 | extern payload_encoder radio_wspr_payload_encoder; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/radio_si4032.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_SI4032_H 2 | #define __RADIO_SI4032_H 3 | 4 | #include "radio_internal.h" 5 | 6 | bool radio_start_transmit_si4032(radio_transmit_entry *entry, radio_module_state *shared_state); 7 | bool radio_transmit_symbol_si4032(radio_transmit_entry *entry, radio_module_state *shared_state); 8 | void radio_handle_main_loop_si4032(radio_transmit_entry *entry, radio_module_state *shared_state); 9 | void radio_handle_data_timer_si4032(); 10 | bool radio_stop_transmit_si4032(radio_transmit_entry *entry, radio_module_state *shared_state); 11 | void radio_init_si4032(); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/radio_si4063.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_SI4063_H 2 | #define __RADIO_SI4063_H 3 | #ifdef DFM17 4 | #include "radio_internal.h" 5 | 6 | bool radio_start_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state); 7 | bool radio_transmit_symbol_si4063(radio_transmit_entry *entry, radio_module_state *shared_state); 8 | void radio_handle_main_loop_si4063(radio_transmit_entry *entry, radio_module_state *shared_state); 9 | void radio_handle_data_timer_si4063(); 10 | bool radio_stop_transmit_si4063(radio_transmit_entry *entry, radio_module_state *shared_state); 11 | void radio_init_si4063(); 12 | #endif 13 | #endif 14 | -------------------------------------------------------------------------------- /src/radio_si5351.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADIO_SI5351_H 2 | #define __RADIO_SI5351_H 3 | 4 | #include "radio_internal.h" 5 | 6 | bool radio_start_transmit_si5351(radio_transmit_entry *entry, radio_module_state *shared_state); 7 | bool radio_transmit_symbol_si5351(radio_transmit_entry *entry, radio_module_state *shared_state); 8 | void radio_handle_main_loop_si5351(radio_transmit_entry *entry, radio_module_state *shared_state); 9 | void radio_handle_data_timer_si5351(); 10 | bool radio_stop_transmit_si5351(radio_transmit_entry *entry, radio_module_state *shared_state); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/radsens_handler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "drivers/radsens/radsens.h" 3 | #include "radsens_handler.h" 4 | #include "hal/delay.h" 5 | #include "log.h" 6 | 7 | RadSens *radsens = NULL; 8 | 9 | static bool radsens_initialization_required = true; 10 | 11 | static bool radsens_handler_init_sensor(); 12 | 13 | bool radsens_handler_init() 14 | { 15 | if (radsens == NULL) { 16 | radsens = new RadSens(&DEFAULT_I2C_PORT, SENSOR_RADSENS_I2C_ADDRESS); 17 | } 18 | return radsens_handler_init_sensor(); 19 | } 20 | 21 | static bool radsens_handler_init_sensor() 22 | { 23 | uint16_t sensitivity; 24 | 25 | bool success = radsens->init(); 26 | radsens_initialization_required = !success; 27 | if (!success) { 28 | log_error("RadSens init failed\n"); 29 | return false; 30 | } 31 | 32 | success = radsens->getChipId() == SENSOR_RADSENS_CHIP_ID; 33 | radsens_initialization_required = !success; 34 | if (!success) { 35 | log_error("RadSens invalid chip ID\n"); 36 | return false; 37 | } 38 | 39 | sensitivity = radsens->getSensitivity(); 40 | success = sensitivity > 0; 41 | radsens_initialization_required = !success; 42 | if (!success) { 43 | log_error("RadSens get sensitivity failed\n"); 44 | return false; 45 | } 46 | log_info("RadSens initial sensitivity: %d\n", sensitivity); 47 | 48 | // Give the sensor some time to start up 49 | delay_ms(50); 50 | 51 | success = radsens->setLedState(true); 52 | radsens_initialization_required = !success; 53 | if (!success) { 54 | log_error("RadSens enable LED failed\n"); 55 | return false; 56 | } 57 | 58 | delay_ms(200); 59 | 60 | #if defined(SENSOR_RADSENS_SENSITIVITY) 61 | success = radsens->setSensitivity(SENSOR_RADSENS_SENSITIVITY); 62 | radsens_initialization_required = !success; 63 | if (!success) { 64 | log_error("RadSens set sensitivity failed\n"); 65 | return false; 66 | } 67 | 68 | sensitivity = radsens->getSensitivity(); 69 | success = sensitivity > 0; 70 | radsens_initialization_required = !success; 71 | if (!success) { 72 | log_error("RadSens get sensitivity failed\n"); 73 | return false; 74 | } 75 | log_info("RadSens final sensitivity: %d\n", sensitivity); 76 | #endif 77 | 78 | success = radsens->setHVGeneratorState(true); 79 | radsens_initialization_required = !success; 80 | if (!success) { 81 | log_error("RadSens HV generator enable failed\n"); 82 | return false; 83 | } 84 | 85 | delay_ms(50); 86 | 87 | bool enabled = radsens->getHVGeneratorState(); 88 | radsens_initialization_required = !enabled; 89 | if (!enabled) { 90 | log_error("RadSens HV generator is not enabled\n"); 91 | return false; 92 | } 93 | 94 | delay_ms(50); 95 | 96 | radsens->getNumberOfPulses(); 97 | 98 | return success; 99 | } 100 | 101 | bool radsens_read(uint16_t *pulse_count, float *dynamic_intensity, float *static_intensity) 102 | { 103 | float radiation_intensity_static; 104 | float radiation_intensity_dynamic; 105 | uint32_t radiation_pulse_counter; 106 | 107 | if (static_intensity) { 108 | radiation_intensity_static = radsens->getRadIntensityStatic(); 109 | if (radiation_intensity_static < 0) { 110 | radsens_initialization_required = true; 111 | log_error("Failed to read RadSens static radiation intensity\n"); 112 | return false; 113 | } 114 | *static_intensity = radiation_intensity_static; 115 | } 116 | 117 | if (dynamic_intensity) { 118 | radiation_intensity_dynamic = radsens->getRadIntensityDynamic(); 119 | if (radiation_intensity_dynamic < 0) { 120 | radsens_initialization_required = true; 121 | log_error("Failed to read RadSens dynamic radiation intensity\n"); 122 | return false; 123 | } 124 | *dynamic_intensity = radiation_intensity_dynamic; 125 | } 126 | 127 | if (pulse_count) { 128 | radiation_pulse_counter = radsens->getNumberOfPulses(); 129 | if (radiation_pulse_counter < 0) { 130 | radsens_initialization_required = true; 131 | log_error("Failed to read RadSens pulse counter\n"); 132 | return false; 133 | } 134 | *pulse_count = (uint16_t) (radiation_pulse_counter % 0x10000); 135 | } 136 | 137 | // log_info("PC: %d RI: %d\n", radiation_pulse_counter, (int) radiation_intensity_dynamic); 138 | 139 | return true; 140 | } 141 | 142 | bool radsens_read_telemetry(telemetry_data *data) 143 | { 144 | bool success; 145 | 146 | if (radsens_initialization_required) { 147 | log_info("RadSens re-init\n"); 148 | success = radsens_handler_init_sensor(); 149 | log_info("RadSens re-init: %d\n", success); 150 | if (!success) { 151 | // Pulse counter does not need to be zeroed 152 | data->radiation_intensity_uR_h = 0; 153 | return false; 154 | } 155 | } 156 | 157 | success = radsens_read(&data->pulse_count, &data->radiation_intensity_uR_h, NULL); 158 | 159 | if (!success) { 160 | log_info("RadSens re-init\n"); 161 | success = radsens_handler_init_sensor(); 162 | log_info("RadSens re-init: %d\n", success); 163 | 164 | if (success) { 165 | success = radsens_read(&data->pulse_count, &data->radiation_intensity_uR_h, NULL); 166 | } else { 167 | // Pulse counter does not need to be zeroed 168 | data->radiation_intensity_uR_h = 0; 169 | } 170 | } 171 | 172 | radsens_initialization_required = !success; 173 | 174 | return success; 175 | } 176 | -------------------------------------------------------------------------------- /src/radsens_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef __RADSENS_HANDLER_H 2 | #define __RADSENS_HANDLER_H 3 | 4 | #include 5 | #include "telemetry.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | bool radsens_handler_init(); 12 | bool radsens_read(uint16_t *pulse_count, float *dynamic_intensity, float *static_intensity); 13 | bool radsens_read_telemetry(telemetry_data *data); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/si5351_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #if SI5351_FAST_ENABLE 4 | #include "drivers/si5351fast/si5351mcu.h" 5 | #else 6 | #include "drivers/si5351/si5351.h" 7 | #endif 8 | #include "si5351_handler.h" 9 | 10 | #if SI5351_FAST_ENABLE 11 | Si5351mcu si5351_fast; 12 | #else 13 | Si5351 *si5351; 14 | #endif 15 | 16 | #if SI5351_FAST_ENABLE 17 | bool si5351_handler_init() 18 | { 19 | si5351_fast.init(&DEFAULT_I2C_PORT, SIADDR); 20 | return true; 21 | } 22 | 23 | bool si5351_set_frequency(si5351_clock_id clock, uint64_t frequency_hz_100) 24 | { 25 | si5351_fast.setFreq((uint8_t) clock, frequency_hz_100 / 100L); 26 | return true; 27 | } 28 | 29 | void si5351_output_enable(si5351_clock_id clock, bool enabled) 30 | { 31 | if (enabled) { 32 | si5351_fast.enable((uint8_t) clock); 33 | } else { 34 | si5351_fast.disable((uint8_t) clock); 35 | } 36 | } 37 | 38 | void si5351_set_drive_strength(si5351_clock_id clock, uint8_t drive) 39 | { 40 | int si5351_drive; 41 | 42 | switch (drive) { 43 | case 0: 44 | si5351_drive = SIOUT_2mA; 45 | break; 46 | case 1: 47 | si5351_drive = SIOUT_4mA; 48 | break; 49 | case 2: 50 | si5351_drive = SIOUT_6mA; 51 | break; 52 | case 3: 53 | si5351_drive = SIOUT_8mA; 54 | break; 55 | default: 56 | si5351_drive = SIOUT_2mA; 57 | } 58 | 59 | si5351_fast.setPower(si5351_drive, (uint8_t) clock); 60 | } 61 | #else 62 | bool si5351_handler_init() 63 | { 64 | si5351 = new Si5351(&DEFAULT_I2C_PORT); 65 | 66 | // Change the 2nd parameter in init if using a reference oscillator other than 25 MHz 67 | bool si5351_found = si5351->init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); 68 | if (!si5351_found) { 69 | return si5351_found; 70 | } 71 | return si5351_found; 72 | } 73 | 74 | bool si5351_set_frequency(si5351_clock_id clock, uint64_t frequency_hz_100) 75 | { 76 | return si5351->set_freq(frequency_hz_100, (enum si5351_clock) clock) == 0; 77 | } 78 | 79 | void si5351_output_enable(si5351_clock_id clock, bool enabled) 80 | { 81 | si5351->output_enable((enum si5351_clock) clock, enabled ? 1 : 0); 82 | } 83 | 84 | void si5351_set_drive_strength(si5351_clock_id clock, uint8_t drive) 85 | { 86 | si5351_drive si5351_drive; 87 | 88 | switch (drive) { 89 | case 0: 90 | si5351_drive = SI5351_DRIVE_2MA; 91 | break; 92 | case 1: 93 | si5351_drive = SI5351_DRIVE_4MA; 94 | break; 95 | case 2: 96 | si5351_drive = SI5351_DRIVE_6MA; 97 | break; 98 | case 3: 99 | si5351_drive = SI5351_DRIVE_8MA; 100 | break; 101 | default: 102 | si5351_drive = SI5351_DRIVE_2MA; 103 | } 104 | 105 | si5351->drive_strength((enum si5351_clock) clock, si5351_drive); 106 | } 107 | #endif 108 | -------------------------------------------------------------------------------- /src/si5351_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef __SI5351_HANDLER_H 2 | #define __SI5351_HANDLER_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | typedef enum _si5351_clock_id { 12 | SI5351_CLOCK_CLK0, 13 | SI5351_CLOCK_CLK1, 14 | SI5351_CLOCK_CLK2, 15 | SI5351_CLOCK_CLK3, 16 | SI5351_CLOCK_CLK4, 17 | SI5351_CLOCK_CLK5, 18 | SI5351_CLOCK_CLK6, 19 | SI5351_CLOCK_CLK7, 20 | } si5351_clock_id; 21 | 22 | bool si5351_handler_init(); 23 | bool si5351_set_frequency(si5351_clock_id clock, uint64_t frequency_hz_100); 24 | void si5351_output_enable(si5351_clock_id clock, bool enabled); 25 | void si5351_set_drive_strength(si5351_clock_id clock, uint8_t drive); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/si5351_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hal/delay.h" 4 | #include "drivers/si5351/si5351.h" 5 | #include "si5351_test.h" 6 | 7 | int si5351_test() 8 | { 9 | Si5351 si5351 = Si5351(&DEFAULT_I2C_PORT); 10 | 11 | printf("Si5351 init\n"); 12 | bool i2c_found = si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0); 13 | if (!i2c_found) { 14 | printf("Si5351 not found\n"); 15 | } 16 | 17 | printf("Si5351 set freq 1\n"); 18 | 19 | // Set CLK0 to output 14 MHz 20 | si5351.set_freq(1400000000ULL, SI5351_CLK0); 21 | 22 | // Set CLK1 to output 175 MHz 23 | printf("Si5351 set ms source\n"); 24 | //si5351.set_ms_source(SI5351_CLK1, SI5351_PLLB); 25 | 26 | printf("Si5351 set freq 2\n"); 27 | //si5351.set_freq_manual(17500000000ULL, 70000000000ULL, SI5351_CLK1); 28 | 29 | printf("Si5351 update status\n"); 30 | // Query a status update and wait a bit to let the Si5351 populate the 31 | // status flags correctly. 32 | si5351.update_status(); 33 | 34 | delay_ms(500); 35 | 36 | printf("Loop\n"); 37 | 38 | int counter = 0; 39 | while (true) { 40 | si5351.update_status(); 41 | printf("SYS_INIT: %d, LOL_A: %d, LOL_B: %d, LOS: %d, REVID: %d\n", 42 | si5351.dev_status.SYS_INIT, si5351.dev_status.LOL_A, si5351.dev_status.LOL_B, 43 | si5351.dev_status.LOS, si5351.dev_status.REVID); 44 | delay_ms(1000); 45 | si5351.set_freq(1400000000ULL + counter * 10 * 100, SI5351_CLK0); 46 | //si5351.set_freq_manual(17500000000ULL + counter * 10, 70000000000ULL, SI5351_CLK1); 47 | counter++; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/si5351_test.h: -------------------------------------------------------------------------------- 1 | #ifndef __SI5351_TEST_H 2 | #define __SI5351_TEST_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int si5351_test(); 9 | 10 | #ifdef __cplusplus 11 | }; 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Copy string src to buffer dst of size dsize. At most dsize-1 24 | * chars will be copied. Always NUL terminates (unless dsize == 0). 25 | * Returns strlen(src); if retval >= dsize, truncation occurred. 26 | */ 27 | size_t 28 | strlcpy(char *dst, const char *src, size_t dsize) 29 | { 30 | const char *osrc = src; 31 | size_t nleft = dsize; 32 | 33 | /* Copy as many bytes as will fit. */ 34 | if (nleft != 0) { 35 | while (--nleft != 0) { 36 | if ((*dst++ = *src++) == '\0') 37 | break; 38 | } 39 | } 40 | 41 | /* Not enough room in dst, add NUL and traverse rest of src. */ 42 | if (nleft == 0) { 43 | if (dsize != 0) 44 | *dst = '\0'; /* NUL-terminate dst */ 45 | while (*src++) 46 | ; 47 | } 48 | 49 | return(src - osrc - 1); /* count does not include NUL */ 50 | } 51 | -------------------------------------------------------------------------------- /src/strlcpy.h: -------------------------------------------------------------------------------- 1 | #ifndef __STRLCPY_H 2 | #define __STRLCPY_H 3 | 4 | #include 5 | 6 | size_t strlcpy(char *dst, const char *src, size_t siz); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/syscalls/semihosting.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define OPENOCD_SYS_WRITEC 0x03 4 | #define OPENOCD_SYS_WRITE0 0x04 5 | #define OPENOCD_SYS_WRITE 0x05 6 | 7 | /** 8 | * TODO: 9 | * I've used the following code to check for a connected debugger in the past with an STM32F4xx series MCU 10 | * (when the only choice was the StdPeriph library -- perhaps this has changed with HAL/LL, but the hardware 11 | * register and corresponding bit is obviously the same): 12 | * if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) 13 | * { 14 | * // Debugger is connected 15 | * } 16 | */ 17 | 18 | void openocd_send_command(int command, void *message) 19 | { 20 | 21 | asm("mov r0, %[cmd];" 22 | "mov r1, %[msg];" 23 | "bkpt #0xAB" 24 | : 25 | : [cmd] "r"(command), [msg] "r"(message) 26 | : "r0", "r1", "memory"); 27 | } 28 | 29 | void openocd_write(int fd, int length, char *data) 30 | { 31 | uint32_t message[] = {fd, (uint32_t) data, length}; 32 | openocd_send_command(OPENOCD_SYS_WRITE, message); 33 | } 34 | 35 | void openocd_print_string(char *string) 36 | { 37 | uint32_t message[] = {(uint32_t) string}; 38 | openocd_send_command(OPENOCD_SYS_WRITE0, message); 39 | } 40 | 41 | void openocd_print_char(char c) 42 | { 43 | uint32_t message[] = {(uint32_t) &c}; 44 | openocd_send_command(OPENOCD_SYS_WRITEC, message); 45 | } 46 | -------------------------------------------------------------------------------- /src/syscalls/semihosting.h: -------------------------------------------------------------------------------- 1 | #ifndef __SEMIHOSTING_H 2 | #define __SEMIHOSTING_H 3 | 4 | void openocd_send_command(int command, void *message); 5 | void openocd_write(int fd, int length, char *data); 6 | void openocd_print_string(char *string); 7 | void openocd_print_char(char c); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/syscalls/syscalls.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************//***** 2 | * @file stdio.c 3 | * @brief Implementation of newlib syscall 4 | ********************************************************************************/ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "log.h" 11 | #include "semihosting.h" 12 | 13 | #undef errno 14 | extern int errno; 15 | extern int _end; 16 | extern int __HeapLimit; 17 | 18 | /*This function is used for handle heap option*/ 19 | __attribute__ ((used)) 20 | caddr_t _sbrk ( int incr ) 21 | { 22 | static unsigned char *heap = NULL; 23 | unsigned char *prev_heap; 24 | 25 | if (heap == NULL) { 26 | heap = (unsigned char *)&_end; 27 | } 28 | prev_heap = heap; 29 | 30 | if (heap + incr > (unsigned char *) &__HeapLimit) { 31 | #ifdef SEMIHOSTING_ENABLE 32 | extern void abort(void); 33 | openocd_write(1, 15, "Out of memory!\n"); 34 | abort(); 35 | #else 36 | errno = ENOMEM; 37 | return (caddr_t) -1; 38 | #endif 39 | } 40 | // Need to align heap to word boundary, else will get 41 | // hard faults on Cortex-M0. So we assume that heap starts on 42 | // word boundary, hence make sure we always add a multiple of 43 | // 4 to it. 44 | incr = (incr + 7) & (~7); // align value to 8 45 | heap += incr; 46 | 47 | return (caddr_t) prev_heap; 48 | } 49 | 50 | __attribute__ ((used)) 51 | int link(char *old, char *new) 52 | { 53 | return -1; 54 | } 55 | 56 | __attribute__ ((used)) 57 | int _close(int file) 58 | { 59 | return -1; 60 | } 61 | 62 | __attribute__ ((used)) 63 | int _fstat(int file, struct stat *st) 64 | { 65 | st->st_mode = S_IFCHR; 66 | return 0; 67 | } 68 | 69 | __attribute__ ((used)) 70 | int _isatty(int file) 71 | { 72 | return 1; 73 | } 74 | 75 | __attribute__ ((used)) 76 | int _lseek(int file, int ptr, int dir) 77 | { 78 | return 0; 79 | } 80 | 81 | /*Low layer read(input) function*/ 82 | __attribute__ ((used)) 83 | int _read(int file, char *ptr, int len) 84 | { 85 | 86 | #if 0 87 | //user code example 88 | int i; 89 | (void)file; 90 | 91 | for(i = 0; i < len; i++) 92 | { 93 | // UART_GetChar is user's basic input function 94 | *ptr++ = UART_GetChar(); 95 | } 96 | 97 | #endif 98 | 99 | return len; 100 | } 101 | 102 | 103 | /*Low layer write(output) function*/ 104 | __attribute__ ((used)) 105 | int _write(int file, char *ptr, int len) 106 | { 107 | 108 | #ifdef SEMIHOSTING_ENABLE 109 | openocd_write(file, len, ptr); 110 | #endif 111 | 112 | #if 0 113 | //user code example 114 | 115 | int i; 116 | (void)file; 117 | 118 | for(i = 0; i < len; i++) 119 | { 120 | // UART_PutChar is user's basic output function 121 | UART_PutChar(*ptr++); 122 | } 123 | #endif 124 | 125 | return len; 126 | } 127 | 128 | __attribute__ ((used)) 129 | void abort(void) 130 | { 131 | /* Abort called */ 132 | while(1); 133 | } 134 | 135 | /* --------------------------------- End Of File ------------------------------ */ 136 | -------------------------------------------------------------------------------- /src/telemetry.c: -------------------------------------------------------------------------------- 1 | #include "telemetry.h" 2 | #include "hal/system.h" 3 | #include "drivers/ubxg6010/ubxg6010.h" 4 | #include "drivers/pulse_counter/pulse_counter.h" 5 | #include "bmp280_handler.h" 6 | #include "radsens_handler.h" 7 | #include "locator.h" 8 | #include "config.h" 9 | #include "log.h" 10 | 11 | #ifdef RS41 12 | #include "drivers/si4032/si4032.h" 13 | #endif 14 | #ifdef DFM17 15 | #include "hal/clock_calibration.h" 16 | #include "drivers/si4063/si4063.h" 17 | #endif 18 | 19 | // Initialize leap seconds with a known good value 20 | int8_t gps_time_leap_seconds = GPS_TIME_LEAP_SECONDS; 21 | 22 | static bool gps_power_saving_enabled = false; 23 | 24 | void telemetry_collect(telemetry_data *data) 25 | { 26 | log_info("Collecting telemetry...\n"); 27 | 28 | data->button_adc_value = system_get_button_adc_value(); 29 | data->battery_voltage_millivolts = system_get_battery_voltage_millivolts(); 30 | #ifdef RS41 31 | data->internal_temperature_celsius_100 = si4032_read_temperature_celsius_100(); 32 | #endif 33 | #ifdef DFM17 34 | data->internal_temperature_celsius_100 = si4063_read_temperature_celsius_100(); 35 | #endif 36 | 37 | if (bmp280_enabled) { 38 | bmp280_read_telemetry(data); 39 | } 40 | 41 | if (radsens_enabled) { 42 | radsens_read_telemetry(data); 43 | } 44 | 45 | if (pulse_counter_enabled) { 46 | data->pulse_count = pulse_counter_get_count(); 47 | } 48 | 49 | ubxg6010_get_current_gps_data(&data->gps); 50 | 51 | if (GPS_HAS_FIX(data->gps)) { 52 | // If we have a good fix, we can enter power-saving mode 53 | if ((data->gps.satellites_visible >= 6) && !gps_power_saving_enabled) { 54 | #if GPS_POWER_SAVING_ENABLE 55 | ubxg6010_enable_power_save_mode(); 56 | gps_power_saving_enabled = true; 57 | #endif 58 | } 59 | 60 | // If we get the number of leap seconds from GPS data, use it 61 | if (data->gps.leap_seconds > 0) { 62 | gps_time_leap_seconds = data->gps.leap_seconds; 63 | } 64 | } else { 65 | // Zero out position data if we don't have a valid GPS fix. 66 | // This is done to avoid transmitting invalid position information. 67 | data->gps.latitude_degrees_1000000 = 0; 68 | data->gps.longitude_degrees_1000000 = 0; 69 | data->gps.altitude_mm = 0; 70 | data->gps.ground_speed_cm_per_second = 0; 71 | data->gps.heading_degrees_100000 = 0; 72 | data->gps.climb_cm_per_second = 0; 73 | } 74 | 75 | #ifdef DFM17 76 | data->clock_calibration_trim = clock_calibration_get_trim(); 77 | data->clock_calibration_count = clock_calibration_get_change_count(); 78 | #endif 79 | 80 | locator_from_lonlat(data->gps.longitude_degrees_1000000, data->gps.latitude_degrees_1000000, 81 | LOCATOR_PAIR_COUNT_FULL, data->locator); 82 | 83 | data->data_counter++; 84 | 85 | log_info("Telemetry collected!\n"); 86 | } 87 | -------------------------------------------------------------------------------- /src/telemetry.h: -------------------------------------------------------------------------------- 1 | #ifndef __TELEMETRY_H 2 | #define __TELEMETRY_H 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | #include "gps.h" 9 | 10 | typedef struct _telemetry_data { 11 | uint16_t data_counter; 12 | 13 | uint16_t battery_voltage_millivolts; 14 | uint16_t button_adc_value; 15 | int32_t internal_temperature_celsius_100; 16 | 17 | int32_t temperature_celsius_100; 18 | uint32_t pressure_mbar_100; 19 | uint32_t humidity_percentage_100; 20 | uint16_t pulse_count; 21 | float radiation_intensity_uR_h; 22 | 23 | gps_data gps; 24 | 25 | char locator[LOCATOR_PAIR_COUNT_FULL * 2 + 1]; 26 | 27 | int clock_calibration_trim; 28 | uint16_t clock_calibration_count; 29 | } telemetry_data; 30 | 31 | void telemetry_collect(telemetry_data *data); 32 | 33 | extern int8_t gps_time_leap_seconds; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/template.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "utils.h" 4 | #include "config.h" 5 | #include "strlcpy.h" 6 | #include "template.h" 7 | 8 | size_t template_replace(char *dest, size_t dest_len, char *src, telemetry_data *data) 9 | { 10 | char *temp = malloc(dest_len); 11 | if (temp == NULL) { 12 | return 0; 13 | } 14 | 15 | char replacement[32]; 16 | 17 | strlcpy(replacement, CALLSIGN, sizeof(replacement)); 18 | strlcpy(temp, src, dest_len); 19 | str_replace(dest, dest_len, temp, "$cs", replacement); 20 | 21 | strlcpy(replacement, data->locator, 4 + 1); 22 | strlcpy(temp, dest, dest_len); 23 | str_replace(dest, dest_len, temp, "$loc4", replacement); 24 | 25 | strlcpy(replacement, data->locator, 6 + 1); 26 | strlcpy(temp, dest, dest_len); 27 | str_replace(dest, dest_len, temp, "$loc6", replacement); 28 | 29 | strlcpy(replacement, data->locator, 8 + 1); 30 | strlcpy(temp, dest, dest_len); 31 | str_replace(dest, dest_len, temp, "$loc8", replacement); 32 | 33 | strlcpy(replacement, data->locator, 12 + 1); 34 | strlcpy(temp, dest, dest_len); 35 | str_replace(dest, dest_len, temp, "$loc12", replacement); 36 | 37 | snprintf(replacement, sizeof(replacement), "%d", data->battery_voltage_millivolts); 38 | strlcpy(temp, dest, dest_len); 39 | str_replace(dest, dest_len, temp, "$bv", replacement); 40 | 41 | snprintf(replacement, sizeof(replacement), "%d", data->button_adc_value); 42 | strlcpy(temp, dest, dest_len); 43 | str_replace(dest, dest_len, temp, "$bu", replacement); 44 | 45 | snprintf(replacement, sizeof(replacement), "%d", (int) data->temperature_celsius_100 / 100); 46 | strlcpy(temp, dest, dest_len); 47 | str_replace(dest, dest_len, temp, "$te", replacement); 48 | 49 | snprintf(replacement, sizeof(replacement), "%d", (int) data->internal_temperature_celsius_100 / 100); 50 | strlcpy(temp, dest, dest_len); 51 | str_replace(dest, dest_len, temp, "$ti", replacement); 52 | 53 | snprintf(replacement, sizeof(replacement), "%d", (int) data->humidity_percentage_100 / 100); 54 | strlcpy(temp, dest, dest_len); 55 | str_replace(dest, dest_len, temp, "$hu", replacement); 56 | 57 | snprintf(replacement, sizeof(replacement), "%d", (int) data->pressure_mbar_100 / 100); 58 | strlcpy(temp, dest, dest_len); 59 | str_replace(dest, dest_len, temp, "$pr", replacement); 60 | 61 | snprintf(replacement, sizeof(replacement), "%u", (unsigned int) data->gps.time_of_week_millis); 62 | strlcpy(temp, dest, dest_len); 63 | str_replace(dest, dest_len, temp, "$tow", replacement); 64 | 65 | snprintf(replacement, sizeof(replacement), "%02d", data->gps.hours); 66 | strlcpy(temp, dest, dest_len); 67 | str_replace(dest, dest_len, temp, "$hh", replacement); 68 | 69 | snprintf(replacement, sizeof(replacement), "%02d", data->gps.minutes); 70 | strlcpy(temp, dest, dest_len); 71 | str_replace(dest, dest_len, temp, "$mm", replacement); 72 | 73 | snprintf(replacement, sizeof(replacement), "%02d", data->gps.seconds); 74 | strlcpy(temp, dest, dest_len); 75 | str_replace(dest, dest_len, temp, "$ss", replacement); 76 | 77 | snprintf(replacement, sizeof(replacement), "%d", data->gps.satellites_visible); 78 | strlcpy(temp, dest, dest_len); 79 | str_replace(dest, dest_len, temp, "$sv", replacement); 80 | 81 | snprintf(replacement, sizeof(replacement), "%05d", (int) data->gps.latitude_degrees_1000000 / 10000); 82 | strlcpy(temp, dest, dest_len); 83 | str_replace(dest, dest_len, temp, "$lat", replacement); 84 | 85 | snprintf(replacement, sizeof(replacement), "%05d", (int) data->gps.longitude_degrees_1000000 / 10000); 86 | strlcpy(temp, dest, dest_len); 87 | str_replace(dest, dest_len, temp, "$lon", replacement); 88 | 89 | snprintf(replacement, sizeof(replacement), "%d", (int) data->gps.altitude_mm / 1000); 90 | strlcpy(temp, dest, dest_len); 91 | str_replace(dest, dest_len, temp, "$alt", replacement); 92 | 93 | snprintf(replacement, sizeof(replacement), "%d", 94 | (int) ((float) data->gps.ground_speed_cm_per_second * 3.6f / 100.0f)); 95 | strlcpy(temp, dest, dest_len); 96 | str_replace(dest, dest_len, temp, "$gs", replacement); 97 | 98 | snprintf(replacement, sizeof(replacement), "%d", (int) data->gps.climb_cm_per_second / 100); 99 | strlcpy(temp, dest, dest_len); 100 | str_replace(dest, dest_len, temp, "$cl", replacement); 101 | 102 | snprintf(replacement, sizeof(replacement), "%03d", (int) data->gps.heading_degrees_100000 / 100000); 103 | strlcpy(temp, dest, dest_len); 104 | size_t len = str_replace(dest, dest_len, temp, "$he", replacement); 105 | 106 | snprintf(replacement, sizeof(replacement), "%d", (int) data->pulse_count); 107 | strlcpy(temp, dest, dest_len); 108 | str_replace(dest, dest_len, temp, "$pc", replacement); 109 | 110 | snprintf(replacement, sizeof(replacement), "%d", (int) data->radiation_intensity_uR_h); 111 | strlcpy(temp, dest, dest_len); 112 | str_replace(dest, dest_len, temp, "$ri", replacement); 113 | 114 | snprintf(replacement, sizeof(replacement), "%d", (int) data->data_counter); 115 | strlcpy(temp, dest, dest_len); 116 | str_replace(dest, dest_len, temp, "$dc", replacement); 117 | 118 | snprintf(replacement, sizeof(replacement), "%d", (int) data->gps.updated); 119 | strlcpy(temp, dest, dest_len); 120 | str_replace(dest, dest_len, temp, "$gu", replacement); 121 | 122 | snprintf(replacement, sizeof(replacement), "%d", (int) data->clock_calibration_trim); 123 | strlcpy(temp, dest, dest_len); 124 | str_replace(dest, dest_len, temp, "$ct", replacement); 125 | 126 | snprintf(replacement, sizeof(replacement), "%d", (int) data->clock_calibration_count); 127 | strlcpy(temp, dest, dest_len); 128 | str_replace(dest, dest_len, temp, "$cc", replacement); 129 | 130 | free(temp); 131 | 132 | return len; 133 | } 134 | -------------------------------------------------------------------------------- /src/template.h: -------------------------------------------------------------------------------- 1 | #ifndef __TEMPLATE_H 2 | #define __TEMPLATE_H 3 | 4 | #include 5 | #include "telemetry.h" 6 | 7 | size_t template_replace(char *dest, size_t dest_len, char *src, telemetry_data *data); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | size_t str_replace(char *dest, size_t dest_len, char *orig, char *rep, char *with) 4 | { 5 | char *ins; // the next insert point 6 | char *tmp; // varies 7 | size_t len_rep; // length of rep (the string to remove) 8 | size_t len_with; // length of with (the string to replace rep with) 9 | size_t len_front; // distance between rep and end of last rep 10 | size_t count; // number of replacements 11 | 12 | // sanity checks and initialization 13 | if (!orig || !rep) { 14 | return 0; 15 | } 16 | len_rep = strlen(rep); 17 | if (len_rep == 0) { 18 | return 0; // empty rep causes infinite loop during count 19 | } 20 | if (!with) { 21 | with = ""; 22 | } 23 | len_with = strlen(with); 24 | 25 | // count the number of replacements needed 26 | ins = orig; 27 | for (count = 0; (tmp = strstr(ins, rep)); ++count) { 28 | ins = tmp + len_rep; 29 | } 30 | 31 | size_t required_len = strlen(orig) + (len_with - len_rep) * count + 1; 32 | if (dest_len < required_len) { 33 | return 0; 34 | } 35 | 36 | tmp = dest; 37 | 38 | // first time through the loop, all the variable are set correctly 39 | // from here on, 40 | // tmp points to the end of the result string 41 | // ins points to the next occurrence of rep in orig 42 | // orig points to the remainder of orig after "end of rep" 43 | while (count--) { 44 | ins = strstr(orig, rep); 45 | len_front = ins - orig; 46 | tmp = strncpy(tmp, orig, len_front) + len_front; 47 | tmp = strcpy(tmp, with) + len_with; 48 | orig += len_front + len_rep; // move to next "end of rep" 49 | } 50 | 51 | strcpy(tmp, orig); 52 | 53 | return required_len - 1; 54 | } 55 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __STRING_H 2 | #define __STRING_H 3 | 4 | #include 5 | 6 | size_t str_replace(char *dest, size_t dest_len, char *orig, char *rep, char *with); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | 3 | #SET(CMAKE_SYSTEM_NAME Linux) 4 | #SET(CMAKE_SYSTEM_VERSION 1) 5 | 6 | project(RS41ng_test C CXX) 7 | 8 | set(BINARY ${CMAKE_PROJECT_NAME}) 9 | 10 | SET(CMAKE_C_FLAGS "${COMMON_FLAGS} -std=gnu99") 11 | 12 | file(GLOB_RECURSE USER_SOURCES "../src/config.c" "../src/codecs/*.c" "../src/template.c" "../src/utils.c" "../src/strlcpy.c") 13 | file(GLOB_RECURSE USER_SOURCES_CXX "../src/codecs/*.cpp") 14 | file(GLOB_RECURSE USER_HEADERS "../src/codecs/*.h" "../src/template.h" "../src/utils.h" "../src/config.h" "../src/strlcpy.h") 15 | 16 | file(GLOB_RECURSE TEST_SOURCES "*.c") 17 | file(GLOB_RECURSE TEST_SOURCES_CXX "*.cpp") 18 | file(GLOB_RECURSE TEST_HEADERS "*.h") 19 | 20 | set(SOURCES ${TEST_SOURCES}) 21 | 22 | add_executable(${BINARY} ${TEST_SOURCES} ${USER_SOURCES}) 23 | 24 | add_test(NAME ${BINARY} COMMAND ${BINARY}) 25 | -------------------------------------------------------------------------------- /tests/bell_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "codecs/bell/bell.h" 5 | #include "codecs/aprs/aprs_position.h" 6 | #include "codecs/ax25/ax25.h" 7 | #include "telemetry.h" 8 | #include "config.h" 9 | 10 | int main2(void) 11 | { 12 | fsk_encoder fsk_encoder; 13 | 14 | char *gg = "gasfa"; 15 | char test[100]; 16 | snprintf(test, sizeof(test), "%3$s %4$s %2$s\n", "1fd", gg, "3aa", "4ab"); 17 | printf("%s\n", test); 18 | 19 | bell_encoder_new(&fsk_encoder, 1200, 0, bell202_tones); 20 | 21 | telemetry_data telemetry; 22 | memset(&telemetry, 0, sizeof(telemetry_data)); 23 | 24 | char aprs_comment[256]; 25 | snprintf(aprs_comment, sizeof(aprs_comment), 26 | APRS_COMMENT, 27 | telemetry.locator, 28 | telemetry.temperature_celsius_100 / 100, 29 | telemetry.humidity_percentage_100 / 100, 30 | telemetry.pressure_mbar_100 / 100, 31 | telemetry.gps.time_of_week_millis, 32 | telemetry.gps.hours, telemetry.gps.minutes, telemetry.gps.seconds); 33 | 34 | uint8_t aprs_packet[256]; 35 | size_t aprs_length = aprs_generate_position( 36 | aprs_packet, sizeof(aprs_packet), &telemetry, APRS_SYMBOL_TABLE, APRS_SYMBOL, false, aprs_comment); 37 | 38 | uint8_t payload[256]; 39 | size_t payload_length = ax25_encode_packet_aprs(APRS_CALLSIGN, APRS_SSID, APRS_DESTINATION, APRS_DESTINATION_SSID, APRS_RELAYS, 40 | (char *) aprs_packet, aprs_length, payload); 41 | 42 | printf("Full payload length: %ld\n", payload_length); 43 | 44 | for (int i = 0; i < payload_length; i++) { 45 | uint8_t c = payload[i]; 46 | if (c >= 0x20 && c <= 0x7e) { 47 | printf("%c", c); 48 | } else { 49 | printf(" [%02X] ", c); 50 | } 51 | } 52 | 53 | printf("\n"); 54 | 55 | bell_encoder_set_data(&fsk_encoder, sizeof(payload), payload); 56 | 57 | size_t index = 0; 58 | while (true) { 59 | int8_t tone = bell_encoder_next_tone(&fsk_encoder); 60 | if (tone < 0) { 61 | break; 62 | } 63 | 64 | printf("%d ", tone); 65 | 66 | index++; 67 | 68 | if (index % 8 == 0) { 69 | printf("\n"); 70 | } 71 | }; 72 | 73 | bell_encoder_destroy(&fsk_encoder); 74 | } 75 | -------------------------------------------------------------------------------- /tests/morse_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "codecs/morse/morse.h" 5 | #include "config.h" 6 | 7 | int main4(void) 8 | { 9 | fsk_encoder morse; 10 | 11 | printf("%d\n", MORSE_WPM_TO_SYMBOL_RATE(20)); 12 | printf("%d\n", MORSE_WPM_TO_SYMBOL_RATE(15)); 13 | printf("%d\n", MORSE_WPM_TO_SYMBOL_RATE(10)); 14 | 15 | char *input = "TEST T"; 16 | 17 | morse_encoder_new(&morse, 25); 18 | 19 | morse_encoder_set_data(&morse, strlen(input), (uint8_t *) input); 20 | 21 | int8_t tone_index; 22 | 23 | while ((tone_index = morse_encoder_next_tone(&morse)) >= 0) { 24 | printf("CW: %d\n", tone_index); 25 | } 26 | 27 | morse_encoder_destroy(&morse); 28 | } 29 | -------------------------------------------------------------------------------- /tests/template_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "strlcpy.h" 4 | #include "template.h" 5 | 6 | int main(void) 7 | { 8 | char *source = "DE $cs: $bv $loc6, $hh:$mm:$ss, $tow, Ti$ti Te$te $hu $pr"; 9 | char dest[512]; 10 | telemetry_data data; 11 | 12 | data.battery_voltage_millivolts = 3247; 13 | data.internal_temperature_celsius_100 = -7 * 100; 14 | data.temperature_celsius_100 = 24 * 100; 15 | data.humidity_percentage_100 = 68 * 100; 16 | data.pressure_mbar_100 = 1023 * 100; 17 | strlcpy(data.locator, "KP21FA35jk45", sizeof(data.locator)); 18 | 19 | data.gps.time_of_week_millis = 110022330; 20 | data.gps.hours = 18; 21 | data.gps.minutes = 33; 22 | data.gps.seconds = 51; 23 | 24 | template_replace(dest, sizeof(dest), source, &data); 25 | 26 | printf("%s\n", dest); 27 | 28 | printf("%03d\n", data.internal_temperature_celsius_100 / 100); 29 | } 30 | --------------------------------------------------------------------------------