Skip to content

Commit 3e1ab49

Browse files
committed
Initialize RTC from RDS clock-time
1 parent b531011 commit 3e1ab49

4 files changed

Lines changed: 270 additions & 29 deletions

File tree

ch32v305/src/demod/rds.cpp

Lines changed: 170 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "demod/rds.h"
22

3+
#include "hw/rtc.h"
34
#include "utils/dsp.h"
45

56
#include <array>
@@ -44,6 +45,14 @@ constexpr size_t kMaxBasebandSamplesPerSymbol =
4445
(kSymbolPhaseModulus + kSymbolPhaseIncrement - 1U) / kSymbolPhaseIncrement;
4546
constexpr size_t kGroupQueueCapacity = 16U;
4647
constexpr size_t kGroupQueueMask = kGroupQueueCapacity - 1U;
48+
constexpr uint32_t kUnixEpochMjd = 40587U;
49+
constexpr uint32_t kMaximumMjd = 99999U;
50+
constexpr uint32_t kSecondsPerMinute = 60U;
51+
constexpr uint32_t kMinutesPerHour = 60U;
52+
constexpr uint32_t kHoursPerDay = 24U;
53+
constexpr uint32_t kSecondsPerHour = kMinutesPerHour * kSecondsPerMinute;
54+
constexpr uint32_t kSecondsPerDay = kHoursPerDay * kSecondsPerHour;
55+
constexpr uint8_t kMaximumLocalOffsetHalfHours = 24U;
4756
constexpr size_t kCorrectableBurstCount =
4857
26U + 25U + (24U * 2U) + (23U * 4U) + (22U * 8U);
4958
constexpr int64_t kMixerMagnitudeBound =
@@ -449,21 +458,178 @@ class AcquisitionChannel
449458
struct Group
450459
{
451460
std::array<uint16_t, 4U> words{};
452-
uint8_t corrected_mask = 0U;
453461
};
454462

463+
struct ClockTime
464+
{
465+
uint32_t mjd = 0U;
466+
uint32_t utc_seconds = 0U;
467+
int16_t local_offset_minutes = 0;
468+
uint8_t hour = 0U;
469+
uint8_t minute = 0U;
470+
bool valid = false;
471+
};
472+
473+
constexpr ClockTime decode_clock_time(uint16_t block_b,
474+
uint16_t block_c,
475+
uint16_t block_d)
476+
{
477+
if((block_b & 0xF800U) != 0x4000U)
478+
{
479+
return {};
480+
}
481+
482+
uint32_t const mjd =
483+
(static_cast<uint32_t>(block_b & 0x0003U) << 15U) |
484+
(static_cast<uint32_t>(block_c) >> 1U);
485+
uint8_t const hour = static_cast<uint8_t>(
486+
((block_c & 0x0001U) << 4U) | ((block_d >> 12U) & 0x000FU));
487+
uint8_t const minute = static_cast<uint8_t>((block_d >> 6U) & 0x003FU);
488+
uint8_t const offset_half_hours = static_cast<uint8_t>(block_d & 0x001FU);
489+
490+
if((mjd < kUnixEpochMjd) || (mjd > kMaximumMjd) ||
491+
(hour >= kHoursPerDay) || (minute >= kMinutesPerHour) ||
492+
(offset_half_hours > kMaximumLocalOffsetHalfHours))
493+
{
494+
return {};
495+
}
496+
497+
uint64_t const utc_seconds =
498+
static_cast<uint64_t>(mjd - kUnixEpochMjd) * kSecondsPerDay +
499+
static_cast<uint64_t>(hour) * kSecondsPerHour +
500+
static_cast<uint64_t>(minute) * kSecondsPerMinute;
501+
if(utc_seconds > std::numeric_limits<uint32_t>::max())
502+
{
503+
return {};
504+
}
505+
506+
int16_t local_offset_minutes =
507+
static_cast<int16_t>(offset_half_hours * 30U);
508+
if((block_d & 0x0020U) != 0U)
509+
{
510+
local_offset_minutes = static_cast<int16_t>(-local_offset_minutes);
511+
}
512+
513+
return {
514+
mjd,
515+
static_cast<uint32_t>(utc_seconds),
516+
local_offset_minutes,
517+
hour,
518+
minute,
519+
true,
520+
};
521+
}
522+
523+
constexpr uint16_t make_clock_time_block_b(uint32_t mjd)
524+
{
525+
return static_cast<uint16_t>(0x4000U | ((mjd >> 15U) & 0x0003U));
526+
}
527+
528+
constexpr uint16_t make_clock_time_block_c(uint32_t mjd, uint8_t hour)
529+
{
530+
return static_cast<uint16_t>(((mjd & 0x7FFFU) << 1U) |
531+
((hour >> 4U) & 0x0001U));
532+
}
533+
534+
constexpr uint16_t make_clock_time_block_d(uint8_t hour,
535+
uint8_t minute,
536+
bool negative_offset,
537+
uint8_t offset_half_hours)
538+
{
539+
return static_cast<uint16_t>(
540+
((hour & 0x0FU) << 12U) |
541+
((minute & 0x3FU) << 6U) |
542+
(negative_offset ? 0x0020U : 0U) |
543+
(offset_half_hours & 0x1FU));
544+
}
545+
546+
constexpr ClockTime kUnixEpochClockTime = decode_clock_time(
547+
make_clock_time_block_b(kUnixEpochMjd),
548+
make_clock_time_block_c(kUnixEpochMjd, 0U),
549+
make_clock_time_block_d(0U, 0U, false, 0U));
550+
static_assert(kUnixEpochClockTime.valid);
551+
static_assert(kUnixEpochClockTime.utc_seconds == 0U);
552+
553+
constexpr ClockTime kBoundaryClockTime = decode_clock_time(
554+
make_clock_time_block_b(65535U),
555+
make_clock_time_block_c(65535U, 23U),
556+
make_clock_time_block_d(23U, 59U, true, 1U));
557+
static_assert(kBoundaryClockTime.valid);
558+
static_assert(kBoundaryClockTime.mjd == 65535U);
559+
static_assert(kBoundaryClockTime.hour == 23U);
560+
static_assert(kBoundaryClockTime.minute == 59U);
561+
static_assert(kBoundaryClockTime.local_offset_minutes == -30);
562+
563+
static_assert(!decode_clock_time(
564+
make_clock_time_block_b(0U),
565+
make_clock_time_block_c(0U, 0U),
566+
make_clock_time_block_d(0U, 0U, false, 0U)).valid);
567+
static_assert(!decode_clock_time(
568+
make_clock_time_block_b(kUnixEpochMjd),
569+
make_clock_time_block_c(kUnixEpochMjd, 24U),
570+
make_clock_time_block_d(24U, 0U, false, 0U)).valid);
571+
static_assert(!decode_clock_time(
572+
make_clock_time_block_b(kUnixEpochMjd),
573+
make_clock_time_block_c(kUnixEpochMjd, 0U),
574+
make_clock_time_block_d(0U, 60U, false, 0U)).valid);
575+
static_assert(!decode_clock_time(
576+
make_clock_time_block_b(kUnixEpochMjd),
577+
make_clock_time_block_c(kUnixEpochMjd, 0U),
578+
make_clock_time_block_d(0U, 0U, false, 25U)).valid);
579+
static_assert(!decode_clock_time(
580+
make_clock_time_block_b(kMaximumMjd),
581+
make_clock_time_block_c(kMaximumMjd, 0U),
582+
make_clock_time_block_d(0U, 0U, false, 0U)).valid);
583+
static_assert(!decode_clock_time(
584+
static_cast<uint16_t>(
585+
make_clock_time_block_b(kUnixEpochMjd) | 0x0800U),
586+
make_clock_time_block_c(kUnixEpochMjd, 0U),
587+
make_clock_time_block_d(0U, 0U, false, 0U)).valid);
588+
455589
static std::array<Group, kGroupQueueCapacity> s_group_queue{};
456590
static std::atomic<uint32_t> s_group_head{0U};
457591
static std::atomic<uint32_t> s_group_tail{0U};
458-
static std::atomic<uint32_t> s_group_dropped{0U};
592+
593+
static void process_clock_time(const Group &group)
594+
{
595+
uint16_t const block_b = group.words[1U];
596+
if((block_b & 0xF800U) != 0x4000U)
597+
{
598+
return;
599+
}
600+
601+
ClockTime const clock_time =
602+
decode_clock_time(block_b, group.words[2U], group.words[3U]);
603+
if(!clock_time.valid)
604+
{
605+
std::printf("RDS: CT invalid B=%04X C=%04X D=%04X\r\n",
606+
static_cast<unsigned int>(block_b),
607+
static_cast<unsigned int>(group.words[2U]),
608+
static_cast<unsigned int>(group.words[3U]));
609+
return;
610+
}
611+
612+
rtc_set_utc(clock_time.utc_seconds, clock_time.local_offset_minutes);
613+
614+
int const offset = static_cast<int>(clock_time.local_offset_minutes);
615+
unsigned int const offset_magnitude =
616+
static_cast<unsigned int>((offset < 0) ? -offset : offset);
617+
std::printf("RDS: CT MJD=%lu UTC=%02u:%02u LTO=%c%02u:%02u RTC=%lu\r\n",
618+
static_cast<unsigned long>(clock_time.mjd),
619+
static_cast<unsigned int>(clock_time.hour),
620+
static_cast<unsigned int>(clock_time.minute),
621+
(offset < 0) ? '-' : '+',
622+
offset_magnitude / 60U,
623+
offset_magnitude % 60U,
624+
static_cast<unsigned long>(clock_time.utc_seconds));
625+
}
459626

460627
static void enqueue_group(const Group &group)
461628
{
462629
uint32_t const head = s_group_head.load(std::memory_order_relaxed);
463630
uint32_t const tail = s_group_tail.load(std::memory_order_acquire);
464631
if((head - tail) >= kGroupQueueCapacity)
465632
{
466-
s_group_dropped.fetch_add(1U, std::memory_order_relaxed);
467633
return;
468634
}
469635

@@ -639,11 +805,6 @@ class LinkDecoder
639805
if(result.valid)
640806
{
641807
group.words[position] = result.information;
642-
if(result.corrected)
643-
{
644-
group.corrected_mask =
645-
static_cast<uint8_t>(group.corrected_mask | (1U << position));
646-
}
647808
}
648809
}
649810

@@ -796,36 +957,16 @@ void rds_reset()
796957

797958
s_group_head.store(0U, std::memory_order_relaxed);
798959
s_group_tail.store(0U, std::memory_order_relaxed);
799-
s_group_dropped.store(0U, std::memory_order_relaxed);
800960
}
801961

802962
void rds_poll()
803963
{
804-
uint32_t const dropped = s_group_dropped.exchange(0U, std::memory_order_relaxed);
805-
if(dropped != 0U)
806-
{
807-
std::printf("RDS: dropped %lu group(s)\r\n",
808-
static_cast<unsigned long>(dropped));
809-
}
810-
811964
uint32_t tail = s_group_tail.load(std::memory_order_relaxed);
812965
uint32_t const head = s_group_head.load(std::memory_order_acquire);
813966
while(tail != head)
814967
{
815968
Group const group = s_group_queue[tail & kGroupQueueMask];
816-
uint16_t const block_b = group.words[1U];
817-
unsigned int const type = static_cast<unsigned int>((block_b >> 12U) & 0x0FU);
818-
char const version = ((block_b & 0x0800U) != 0U) ? 'B' : 'A';
819-
820-
std::printf("RDS: PI=%04X TYPE=%u%c A=%04X B=%04X C=%04X D=%04X CORR=%X\r\n",
821-
static_cast<unsigned int>(group.words[0U]),
822-
type,
823-
version,
824-
static_cast<unsigned int>(group.words[0U]),
825-
static_cast<unsigned int>(group.words[1U]),
826-
static_cast<unsigned int>(group.words[2U]),
827-
static_cast<unsigned int>(group.words[3U]),
828-
static_cast<unsigned int>(group.corrected_mask));
969+
process_clock_time(group);
829970
++tail;
830971
s_group_tail.store(tail, std::memory_order_release);
831972
}

ch32v305/src/hw/rtc.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include "rtc.h"
2+
3+
#include "ch32v30x_pwr.h"
4+
#include "ch32v30x_rcc.h"
5+
#include "ch32v30x_rtc.h"
6+
7+
#include <stddef.h>
8+
9+
#define RTC_HSE_DIVIDER 128U
10+
#define RTC_CLOCK_HZ (HSE_VALUE / RTC_HSE_DIVIDER)
11+
#define RTC_PRESCALER (RTC_CLOCK_HZ - 1U)
12+
#define RTC_MAX_PRESCALER 0x000FFFFFU
13+
14+
static_assert((HSE_VALUE % RTC_HSE_DIVIDER) == 0U,
15+
"HSE must be exactly divisible by the RTC clock divider");
16+
static_assert(RTC_CLOCK_HZ > 0U, "RTC clock must be nonzero");
17+
static_assert(RTC_PRESCALER <= RTC_MAX_PRESCALER,
18+
"RTC prescaler does not fit in the hardware register");
19+
20+
static bool s_synchronized;
21+
static int16_t s_local_offset_minutes;
22+
23+
static void rtc_backup_access(FunctionalState state)
24+
{
25+
PWR_BackupAccessCmd(state);
26+
}
27+
28+
void rtc_init(void)
29+
{
30+
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
31+
rtc_backup_access(ENABLE);
32+
33+
if((RCC->BDCTLR & RCC_RTCSEL) != RCC_RTCSEL_HSE)
34+
{
35+
RCC_BackupResetCmd(ENABLE);
36+
RCC_BackupResetCmd(DISABLE);
37+
RCC_RTCCLKConfig(RCC_RTCCLKSource_HSE_Div128);
38+
}
39+
40+
RCC_RTCCLKCmd(ENABLE);
41+
RTC_WaitForSynchro();
42+
RTC_WaitForLastTask();
43+
RTC_SetPrescaler(RTC_PRESCALER);
44+
RTC_WaitForLastTask();
45+
46+
rtc_backup_access(DISABLE);
47+
s_local_offset_minutes = 0;
48+
s_synchronized = false;
49+
}
50+
51+
void rtc_set_utc(uint32_t utc_seconds, int16_t local_offset_minutes)
52+
{
53+
rtc_backup_access(ENABLE);
54+
RTC_WaitForLastTask();
55+
RTC_SetCounter(utc_seconds);
56+
RTC_WaitForLastTask();
57+
rtc_backup_access(DISABLE);
58+
59+
s_local_offset_minutes = local_offset_minutes;
60+
s_synchronized = true;
61+
}
62+
63+
bool rtc_get(uint32_t *utc_seconds, int16_t *local_offset_minutes)
64+
{
65+
if(!s_synchronized)
66+
{
67+
return false;
68+
}
69+
70+
if(utc_seconds != NULL)
71+
{
72+
*utc_seconds = RTC_GetCounter();
73+
}
74+
if(local_offset_minutes != NULL)
75+
{
76+
*local_offset_minutes = s_local_offset_minutes;
77+
}
78+
return true;
79+
}

ch32v305/src/hw/rtc.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef RTC_H
2+
#define RTC_H
3+
4+
#include <stdbool.h>
5+
#include <stdint.h>
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
void rtc_init(void);
12+
void rtc_set_utc(uint32_t utc_seconds, int16_t local_offset_minutes);
13+
bool rtc_get(uint32_t *utc_seconds, int16_t *local_offset_minutes);
14+
15+
#ifdef __cplusplus
16+
}
17+
#endif
18+
19+
#endif

ch32v305/src/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extern "C" {
3434
#include "hw/usb.h"
3535
#include "hw/watchdog.h"
3636
#include "hw/adc.h"
37+
#include "hw/rtc.h"
3738
#include "hw/trng.h"
3839
#include "feature/blinky/blinky.h"
3940
#include "demod/demod.h"
@@ -288,6 +289,7 @@ int main(void)
288289

289290
SystemCoreClockUpdate();
290291
Delay_Init();
292+
rtc_init();
291293
detect_hardware_rev();
292294

293295
usb_hw_init();

0 commit comments

Comments
 (0)