From 843c465c73dccd0356b560d10f76c61a8822c9a2 Mon Sep 17 00:00:00 2001 From: furrtek Date: Thu, 8 Dec 2016 21:08:53 +0100 Subject: [PATCH] RDS radiotext and time group generators --- firmware/application/rds.cpp | 112 +++++++++++++++++++++++--------- firmware/application/rds.hpp | 20 +++++- firmware/application/ui_rds.cpp | 15 +++-- firmware/application/ui_rds.hpp | 4 ++ 4 files changed, 111 insertions(+), 40 deletions(-) diff --git a/firmware/application/rds.cpp b/firmware/application/rds.cpp index e2c37a6b..2cf4ec2c 100644 --- a/firmware/application/rds.cpp +++ b/firmware/application/rds.cpp @@ -24,19 +24,24 @@ #include "portapack_shared_memory.hpp" +// RDS infos: +// One group = 4 blocks = 4 * 26 bits +// One block = 26 bits (16 bits data + 10 bits checkword) +// Sent MSB first + namespace rds { -uint32_t makeblock(uint32_t blockdata, uint16_t offset) { +uint32_t makeblock(uint32_t data, uint16_t offset) { uint16_t CRC = 0; - uint8_t doinv; + uint8_t bit; for (uint8_t i = 0; i < 16; i++) { - doinv = (((blockdata << i) & 0x8000) >> 15) ^ (CRC >> 9); - if (doinv) CRC ^= 0b0011011100; - CRC = ((CRC << 1) | doinv) & 0x3FF; + bit = (((data << i) & 0x8000) >> 15) ^ (CRC >> 9); + if (bit) CRC ^= 0b0011011100; + CRC = ((CRC << 1) | bit) & 0x3FF; } - return (blockdata << 10) | (CRC ^ offset); + return (data << 10) | (CRC ^ offset); } // Todo: @@ -52,35 +57,50 @@ uint8_t b2b(const bool in) { return 0; } -void make_0B_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, +// Type 0B groups are like 0A groups but without alternative frequency data +void make_0B_group(uint32_t blocks[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, const bool MS, const bool DI, const uint8_t C, const char * chars) { - - group[0] = PI_code; - group[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | (PTY << 5) | (b2b(TA) << 4) | (b2b(MS) << 3) | (b2b(DI) << 2) | (C & 3); - group[2] = PI_code; - group[3] = (chars[0] << 8) | chars[1]; + blocks[0] = PI_code; + blocks[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | (b2b(TA) << 4) | (b2b(MS) << 3) | (b2b(DI) << 2) | (C & 3); + blocks[2] = PI_code; + blocks[3] = (chars[0] << 8) | chars[1]; } -void make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, +// For RadioText, up to 64 chars with 2A, 32 chars with 2B +void make_2A_group(uint32_t blocks[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, const uint8_t segment, const char * chars) { - - group[0] = PI_code; - group[1] = (0x0 << 12) | (1 << 11) | (b2b(TP) << 10) | (PTY << 5) | (b2b(AB) << 4) | (segment & 15); - group[2] = (chars[0] << 8) | chars[1]; - group[3] = (chars[2] << 8) | chars[3]; + blocks[0] = PI_code; + blocks[1] = (0x2 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | (b2b(AB) << 4) | (segment & 15); + blocks[2] = (chars[0] << 8) | chars[1]; + blocks[3] = (chars[2] << 8) | chars[3]; } -uint16_t gen_PSN(const char * psname, const uint8_t pty) { +// Time and date - usually one message per minute - Month: 1~12 - Day: 1~31 - Hour/Minute: 0~59 - Local offset: -12/+12 from UTC +void make_4A_group(uint32_t blocks[], const uint16_t PI_code, const bool TP, const uint8_t PTY, + const uint16_t year, const uint8_t month, const uint8_t day, + const uint8_t hour, const uint8_t minute, const int8_t local_offset) { + uint32_t L = 0; + uint32_t day_code; + + if ((month == 1) || (month == 2)) L = 1; + + day_code = 14956 + day + (uint32_t)((float)(year - L) * 365.25) + uint16_t(((month + 1) * L * 12) * 30.6001); + + blocks[0] = PI_code; + blocks[1] = (0x4 << 12) | (0 << 11) | (b2b(TP) << 10) | ((PTY & 0x1F) << 5) | ((day_code & 0x18000) >> 15); + blocks[2] = ((day_code & 0x7FFF) << 1) | (hour >> 4); + blocks[3] = ((hour & 15) << 12) | ((minute & 0x3F) << 6) | (local_offset & 0x3F); +} + +uint16_t gen_PSN(const char * psname, const RDS_flags * rds_flags) { uint8_t c; uint32_t group[4][4] = { 0 }; // 4 groups with 2 PSN characters in each - make_0B_group(&group[0][0], 0xF849, true, pty, false, true, false, 0, &psname[0]); - make_0B_group(&group[1][0], 0xF849, true, pty, false, true, false, 1, &psname[2]); - make_0B_group(&group[2][0], 0xF849, true, pty, false, true, false, 2, &psname[4]); - make_0B_group(&group[3][0], 0xF849, true, pty, false, true, false, 3, &psname[6]); + for (c = 0; c < 4; c++) + make_0B_group(&group[c][0], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, rds_flags->TA, rds_flags->MS, rds_flags->DI, c, &psname[c * 2]); - // Generate checkbits for all blocks + // Generate checkbits for each block of each group for (c = 0; c < 4; c++) { group[c][0] = makeblock(group[c][0], RDS_OFFSET_A); group[c][1] = makeblock(group[c][1], RDS_OFFSET_B); @@ -90,36 +110,38 @@ uint16_t gen_PSN(const char * psname, const uint8_t pty) { uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data; + // Copy to tx_data for baseband for (c = 0; c < 4 * 4; c++) tx_data_u32[c] = group[c >> 2][c & 3]; return 4 * 4 * 26; } -uint16_t gen_RadioText(const char * radiotext, const uint8_t pty) { +uint16_t gen_RadioText(const char * text, const bool AB, const RDS_flags * rds_flags) { size_t c, i; uint32_t * group; char radiotext_buffer[65] = { 0 }; uint8_t rtlen, groups; - strcpy(radiotext_buffer, radiotext); + strncpy(radiotext_buffer, text, 64); rtlen = strlen(radiotext_buffer); - radiotext_buffer[rtlen] = 0x0D; - - // Pad to multiple of 4 + // Pad message with spaces to a multiple of 4 characters while (rtlen & 3) radiotext_buffer[rtlen++] = ' '; + // End message with Carriage Return, as required + if (rtlen < 64) radiotext_buffer[rtlen] = 0x0D; + groups = rtlen >> 2; // 4 characters per group - group = (uint32_t*)chHeapAlloc(0x0, 4 * groups * sizeof(uint32_t)); + group = (uint32_t*)chHeapAlloc(0, 4 * groups * sizeof(uint32_t)); for (c = 0; c < groups; c++) - make_2A_group(&group[c << 2], 0xF849, true, pty, false, c, &radiotext_buffer[c * 4]); + make_2A_group(&group[c * 4], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, AB, c, &radiotext_buffer[c * 4]); - // Generate checkbits + // Generate checkbits for each block of each group for (c = 0; c < groups; c++) { i = c * 4; group[i + 0] = makeblock(group[i + 0], RDS_OFFSET_A); @@ -130,10 +152,36 @@ uint16_t gen_RadioText(const char * radiotext, const uint8_t pty) { uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data; + // Copy to tx_data for baseband for (c = 0; c < (groups * 4); c++) tx_data_u32[c] = group[c]; + chHeapFree(group); + return groups * 4 * 26; } +uint16_t gen_ClockTime(const RDS_flags * rds_flags, + const uint16_t year, const uint8_t month, const uint8_t day, + const uint8_t hour, const uint8_t minute, const int8_t local_offset) { + uint8_t c; + uint32_t group[4] = { 0 }; + + make_4A_group(&group[0], rds_flags->PI_code, rds_flags->TP, rds_flags->PTY, year, month, day, hour, minute, local_offset); + + // Generate checkbits for each block + group[0] = makeblock(group[0], RDS_OFFSET_A); + group[1] = makeblock(group[1], RDS_OFFSET_B); + group[2] = makeblock(group[2], RDS_OFFSET_C); + group[3] = makeblock(group[3], RDS_OFFSET_D); + + uint32_t * tx_data_u32 = (uint32_t*)shared_memory.tx_data; + + // Copy to tx_data for baseband + for (c = 0; c < 4; c++) + tx_data_u32[c] = group[c]; + + return 4 * 26; +} + } /* namespace rds */ diff --git a/firmware/application/rds.hpp b/firmware/application/rds.hpp index f5fcc7f9..35b891e5 100644 --- a/firmware/application/rds.hpp +++ b/firmware/application/rds.hpp @@ -35,14 +35,30 @@ namespace rds { #define RDS_OFFSET_Cp 0b1101010000 #define RDS_OFFSET_D 0b0110110100 +struct RDS_flags { + uint16_t PI_code; + bool TP; + uint8_t PTY; + bool TA; + bool MS; + bool DI; +}; + uint32_t makeblock(uint32_t blockdata, uint16_t offset); uint8_t b2b(const bool in); void make_0B_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool TA, const bool MS, const bool DI, const uint8_t C, const char * chars); void make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB, const uint8_t segment, const char * chars); -uint16_t gen_PSN(const char * psname, const uint8_t pty); -uint16_t gen_RadioText(const char * radiotext, const uint8_t pty); +void make_4A_group(uint32_t blocks[], const uint16_t PI_code, const bool TP, const uint8_t PTY, + const uint16_t year, const uint8_t month, const uint8_t day, + const uint8_t hour, const uint8_t minute, const int8_t local_offset); + +uint16_t gen_PSN(const char * psname, const RDS_flags * rds_flags); +uint16_t gen_RadioText(const char * text, const bool AB, const RDS_flags * rds_flags); +uint16_t gen_ClockTime(const RDS_flags * rds_flags, + const uint16_t year, const uint8_t month, const uint8_t day, + const uint8_t hour, const uint8_t minute, const int8_t local_offset); } /* namespace rds */ diff --git a/firmware/application/ui_rds.cpp b/firmware/application/ui_rds.cpp index c0273a75..d623bb72 100644 --- a/firmware/application/ui_rds.cpp +++ b/firmware/application/ui_rds.cpp @@ -22,7 +22,6 @@ #include "ui_rds.hpp" -#include "rds.hpp" #include "portapack.hpp" #include "baseband_api.hpp" #include "portapack_shared_memory.hpp" @@ -115,24 +114,28 @@ RDSView::RDSView(NavigationView& nav) { options_countrycode.set_selected_index(18); // France options_coverage.set_selected_index(0); // Local - button_editpsn.on_select = [this,&nav](Button&){ + options_pty.on_change = [this](size_t, int32_t v) { + rds_flags.PTY = v; + }; + + button_editpsn.on_select = [this,&nav](Button&) { textentry(nav, PSN, 8); }; - button_txpsn.on_select = [this](Button&){ + button_txpsn.on_select = [this](Button&) { if (txing) { button_txpsn.set_text("PSN"); button_txradiotext.set_text("Radiotext"); transmitter_model.disable(); txing = false; } else { - message_length = gen_PSN(PSN, options_pty.selected_index()); + message_length = gen_PSN(PSN, &rds_flags); button_txpsn.set_text("STOP"); txing = true; start_tx(); } }; - button_editradiotext.on_select = [this,&nav](Button&){ + button_editradiotext.on_select = [this, &nav](Button&){ textentry(nav, RadioText, 24); }; button_txradiotext.on_select = [this](Button&){ @@ -142,7 +145,7 @@ RDSView::RDSView(NavigationView& nav) { transmitter_model.disable(); txing = false; } else { - message_length = gen_RadioText(RadioText, options_pty.selected_index()); + message_length = gen_RadioText(RadioText, 0, &rds_flags); button_txradiotext.set_text("STOP"); txing = true; start_tx(); diff --git a/firmware/application/ui_rds.hpp b/firmware/application/ui_rds.hpp index 78e2afe9..b8cee9d2 100644 --- a/firmware/application/ui_rds.hpp +++ b/firmware/application/ui_rds.hpp @@ -27,6 +27,9 @@ #include "ui_receiver.hpp" #include "ui_textentry.hpp" #include "message.hpp" +#include "rds.hpp" + +using namespace rds; namespace ui { @@ -44,6 +47,7 @@ private: char PSN[9]; char RadioText[25]; bool txing = false; + RDS_flags rds_flags; uint16_t message_length;