diff --git a/Makefile b/Makefile index 0c3e3ed..8a932b7 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ ENABLE_FEAT_F4HWN_RESET_CHANNEL ?= 0 ENABLE_FEAT_F4HWN_PMR ?= 0 ENABLE_FEAT_F4HWN_GMRS_FRS_MURS ?= 0 ENABLE_FEAT_F4HWN_CA ?= 1 +ENABLE_REGA ?= 1 # ---- DEBUGGING ---- ENABLE_AM_FIX_SHOW_DATA ?= 0 @@ -135,6 +136,9 @@ OBJS += app/app.o OBJS += app/chFrScanner.o OBJS += app/common.o OBJS += app/dtmf.o +ifeq ($(ENABLE_REGA),1) + OBJS += app/rega.o +endif ifeq ($(ENABLE_FLASHLIGHT),1) OBJS += app/flashlight.o endif @@ -399,6 +403,9 @@ endif ifeq ($(ENABLE_DTMF_CALLING),1) CFLAGS += -DENABLE_DTMF_CALLING endif +ifeq ($(ENABLE_REGA),1) + CFLAGS += -DENABLE_REGA +endif ifeq ($(ENABLE_AGC_SHOW_DATA),1) CFLAGS += -DENABLE_AGC_SHOW_DATA endif diff --git a/app/action.c b/app/action.c index 08a3540..95b8d9e 100644 --- a/app/action.c +++ b/app/action.c @@ -42,6 +42,9 @@ #include "settings.h" #include "ui/inputbox.h" #include "ui/ui.h" +#ifdef ENABLE_REGA + #include "app/rega.h" +#endif #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #include "screenshot.h" @@ -118,6 +121,10 @@ void (*action_opt_table[])(void) = { #else [ACTION_OPT_RXMODE] = &FUNCTION_NOP, #endif +#ifdef ENABLE_REGA + [ACTION_OPT_REGA_ALARM] = &ACTION_RegaAlarm, + [ACTION_OPT_REGA_TEST] = &ACTION_RegaTest, +#endif }; static_assert(ARRAY_SIZE(action_opt_table) == ACTION_OPT_LEN); diff --git a/app/app.c b/app/app.c index afbad11..8c8623c 100644 --- a/app/app.c +++ b/app/app.c @@ -96,8 +96,12 @@ void (*ProcessKeysFunctions[])(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) #endif }; +#ifdef ENABLE_REGA +// This is a hack for REGA as I need a special display element only for it with no key +static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM-1); +#else static_assert(ARRAY_SIZE(ProcessKeysFunctions) == DISPLAY_N_ELEM); - +#endif static void CheckForIncoming(void) diff --git a/app/rega.c b/app/rega.c new file mode 100644 index 0000000..c01470d --- /dev/null +++ b/app/rega.c @@ -0,0 +1,187 @@ +/* + * REGA Alam and Test + * ================== + * + * This bit of code is to implement the REGA test and alarm functions. + * This function sends out a fixed ZVEI tone sequence for testing or alarm purposes. + * Further information: https://www.rega.ch/en/our-missions/sites-and-infrastructure/emergency-radio + * + * There are two REGA ACTIONS: Test and Alarm. + * The Test action sends out a fixed ZVEI tone sequence for testing purposes. + * The Alarm action sends out a fixed ZVEI tone sequence for alarm purposes. + * + * These actions can be assigned to a key in the settings menu. + * + * The Test/Alarm function will perform the following actions: + * - Set the radio to transmit mode + * - Wait 100ms to allow the radio to switch to transmit mode and stabilize the tramsmitter + * - Send out the ZVEI tone sequence + * - Wait 100ms to allow the radio to finish transmitting + * - Set the radio back to receive mode + * + * The ZVEI tone squence for alarm is: 21414 + * The ZVEI tone squence for test is: 21301 + * + * To save space the two tone sequences are stored as precalculated register values. + * This avoids the need to calculate the register values at runtime. + * They are hardcoded as array of 32bit integers in the rega.h file. + * + * Copyright 2025 Markus Bärtschi + * https://github.com/markusb + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "rega.h" +#include "radio.h" +#include "action.h" +#include "misc.h" +#include "settings.h" +#include "functions.h" +#include "driver/bk4819.h" +#include "driver/st7565.h" +#include "driver/system.h" +#include "driver/gpio.h" +#include "bsp/dp32g030/gpio.h" +#include "ui/helper.h" +#include "ui/ui.h" + + +const uint16_t rega_alarm_tones[5] = { + 1160, // 2 1160Hz + 1060, // 1 1060Hz + 1400, // 4 1400Hz + 1060, // 1 1060Hz + 1400, // 4 1400Hz +}; +const uint16_t rega_test_tones[5] = { + 1160, // 2 1160Hz + 1060, // 1 1060Hz + 1260, // 3 1260Hz + 2400, // 0 2400Hz + 1060, // 1 1060Hz +}; + +// Global variable to pass the message to the display +char rega_message[16]; + +// Transmit the ZVEI tone sequence for alarm +void ACTION_RegaAlarm() +{ + const char message[16] = "REGA Alarm"; + REGA_TransmitZvei(rega_alarm_tones,message); +} + +// Tranmit the ZVEI tone sequence for test +void ACTION_RegaTest() +{ + const char message[16] = "REGA Test"; + REGA_TransmitZvei(rega_test_tones,message); +} + +// Display the REGA message on the screen +void UI_DisplayREGA() +{ + UI_DisplayClear(); + UI_DisplayPopup(rega_message); + ST7565_BlitFullScreen(); +} + +// Transmit a ZVEI tone sequence on the REGA frequency +// Configures the radio on VFO A with the required parameters +// tones: array of 5 ZVEI tones +// message: message to display on the screen +void REGA_TransmitZvei(const uint16_t tones[],const char message[]) +{ + // Copy the message text + strncpy(rega_message,message,16); + // Trigger the display + gScreenToDisplay = DISPLAY_REGA; + + // Flash the green LED twice + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true); + SYSTEM_DelayMs(70); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false); + SYSTEM_DelayMs(50); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true); + SYSTEM_DelayMs(70); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false); + + // Select VFO A + gEeprom.TX_VFO = 0; + gEeprom.ScreenChannel[gEeprom.TX_VFO] = gEeprom.FreqChannel[gEeprom.TX_VFO]; + gRxVfo = gTxVfo; + gRequestSaveVFO = true; + gVfoConfigureMode = VFO_CONFIGURE_RELOAD; + + // Set the REGA frequency, no offset + gTxVfo->freq_config_RX.Frequency = REGA_FREQUENCY; + gTxVfo->freq_config_TX = gTxVfo->freq_config_RX; + gTxVfo->TX_OFFSET_FREQUENCY = 0; + + // Set the modulation to FM narrow + gTxVfo->Modulation = MODULATION_FM; + gTxVfo->CHANNEL_BANDWIDTH = BK4819_FILTER_BW_NARROW; + + // Set Tx Squelch Tone + gTxVfo->freq_config_TX.CodeType = CODE_TYPE_CONTINUOUS_TONE; + gTxVfo->freq_config_TX.Code = REGA_CTCSS_FREQ_INDEX; + BK4819_SetCTCSSFrequency(CTCSS_Options[gTxVfo->freq_config_TX.Code]); + // Turn Rx squelch tone off + gTxVfo->freq_config_RX.CodeType = CODE_TYPE_OFF; + + // Set the transmit power to high + gTxVfo->OUTPUT_POWER = OUTPUT_POWER_HIGH; + + // Lock the keyboard + gEeprom.KEY_LOCK = true; + + gRequestSaveChannel = 1; + gRequestSaveSettings = true; + + // Configure the receiver + BK4819_RX_TurnOn(); + + // Set the radio to transmit mode + RADIO_PrepareCssTX(); + FUNCTION_Select(FUNCTION_TRANSMIT); + + // Wait to allow the radio to switch to transmit mode and stabilize the transmitter + SYSTEM_DelayMs(ZVEI_PRE_LENGTH_MS); + + // Send out the ZVEI2 tone sequence + for (int i = 0; i < ZVEI_NUM_TONES; i++) + { + BK4819_PlaySingleTone(tones[i], ZVEI_TONE_LENGTH_MS, 100, true); + SYSTEM_DelayMs(ZVEI_PAUSE_LENGTH_MS); + } + + // Wait to allow the radio to finish transmitting + SYSTEM_DelayMs(ZVEI_POST_LENGTH_MS); + + // Flash the green LED twice + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true); + SYSTEM_DelayMs(70); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false); + SYSTEM_DelayMs(70); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true); + SYSTEM_DelayMs(70); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false); + + // Display the normal screen + gRequestDisplayScreen = DISPLAY_MAIN; +} diff --git a/app/rega.h b/app/rega.h new file mode 100644 index 0000000..68f53c1 --- /dev/null +++ b/app/rega.h @@ -0,0 +1,65 @@ +/* + * REGA Alam and Test + * ================== + * + * This function sends out a fixed ZVEI tone sequence for testing or alarm purposes for the Alpine REGA alarm channel. + * Further information: https://www.rega.ch/en/our-missions/sites-and-infrastructure/emergency-radio + * + * There are two REGA ACTIONS: Test and Alarm. + * The Test action sends out a fixed ZVEI tone sequence for testing purposes. + * The Alarm action sends out a fixed ZVEI tone sequence for alarm purposes. + * + * These actions can be assigned to a key in the settings menu. + * + * The Test/Alarm function will perform the following actions: + * - Set the radio configuration to the REGA frequency, CTCSS, FM, full power etc. + * - Start transmitting + * - Wait 100ms to allow the radio to switch to transmit mode and stabilize the transmitter + * - Send out the ZVEI tone sequence + * - Wait 100ms to allow the radio to finish transmitting + * - Set the radio back to receive mode + * + * The REGA frequency is 161.300 Mhz + * + * The ZVEI tone squence for alarm is: 21414 + * The ZVEI tone squence for test is: 21301 + * + * The two tone sequences are hardcoded in two arrays + * + * Copyright 2025 Markus Bärtschi + * + * https://github.com/markusb + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef REGA_H +#define REGA_H + +#include + +#define ZVEI_NUM_TONES 5 +#define ZVEI_TONE_LENGTH_MS 70 // 70 +#define ZVEI_PAUSE_LENGTH_MS 10 // 10 +#define ZVEI_PRE_LENGTH_MS 300 +#define ZVEI_POST_LENGTH_MS 100 +#define REGA_CTCSS_FREQ_INDEX 18 // dcs.c: CTCSS_Options[18] = 1230 +#define REGA_FREQUENCY 16130000 // 43370000 16130000 + +void ACTION_RegaAlarm(void); +void ACTION_RegaTest(void); +void UI_DisplayREGA(void); +void REGA_TransmitZvei(const uint16_t[], const char[]); + +#endif diff --git a/settings.h b/settings.h index 8947379..b09816a 100644 --- a/settings.h +++ b/settings.h @@ -121,6 +121,10 @@ enum ACTION_OPT_t { ACTION_OPT_POWER_HIGH, ACTION_OPT_REMOVE_OFFSET, #endif +#endif +#ifdef ENABLE_REGA + ACTION_OPT_REGA_ALARM, + ACTION_OPT_REGA_TEST, #endif ACTION_OPT_LEN }; diff --git a/ui/menu.c b/ui/menu.c index f78a007..867577f 100644 --- a/ui/menu.c +++ b/ui/menu.c @@ -438,6 +438,10 @@ const t_sidefunction gSubMenu_SIDEFUNCTIONS[] = #endif #ifdef ENABLE_TX1750 {"1750Hz", ACTION_OPT_1750}, +#endif +#ifdef ENABLE_REGA + {"REGA\nALARM", ACTION_OPT_REGA_ALARM}, + {"REGA\nTEST", ACTION_OPT_REGA_TEST}, #endif {"LOCK\nKEYPAD", ACTION_OPT_KEYLOCK}, {"VFO A\nVFO B", ACTION_OPT_A_B}, diff --git a/ui/ui.c b/ui/ui.c index aaa7961..f690628 100644 --- a/ui/ui.c +++ b/ui/ui.c @@ -30,6 +30,9 @@ #ifdef ENABLE_FMRADIO #include "ui/fmradio.h" #endif +#ifdef ENABLE_REGA + #include "app/rega.h" +#endif #include "ui/inputbox.h" #include "ui/main.h" #include "ui/menu.h" @@ -57,6 +60,10 @@ void (*UI_DisplayFunctions[])(void) = { #ifdef ENABLE_AIRCOPY [DISPLAY_AIRCOPY] = &UI_DisplayAircopy, #endif + +#ifdef ENABLE_REGA + [DISPLAY_REGA] = &UI_DisplayREGA, +#endif }; static_assert(ARRAY_SIZE(UI_DisplayFunctions) == DISPLAY_N_ELEM); diff --git a/ui/ui.h b/ui/ui.h index ce4d35f..0f1fea4 100644 --- a/ui/ui.h +++ b/ui/ui.h @@ -34,6 +34,10 @@ enum GUI_DisplayType_t DISPLAY_AIRCOPY, #endif +#ifdef ENABLE_REGA + DISPLAY_REGA, +#endif + DISPLAY_N_ELEM, DISPLAY_INVALID = 0xFFu };