libv4l2rds: support for decoding RDS tuning information
cover libv4l2rds: support for decoding RDS tuning information
This set of patches adds support to decode information about other networks and radio stations which can submitted as a part of the RDS and RDS-TMC data stream.
RDS-EON: Enhanced Other Network information that can be used to update the information stored in the receiver about programme service other than the one received. Alternative frequencies, the PS name, Traffic Programme and Traffic Announcement identification as well as Programme Type and Programme Item Number information can be transmitted for each other service.
RDS-TMC-Tuning: Detailed information about other stations that carry the same RDS-TMC service that can be used by terminals to tune directly to a new station at boundaries of a transmitter's coverage.
RFC PATCH 1/4 libv4l2rds: added support to decode RDS-EON information
--- lib/include/libv4l2rds.h | 35 +++++++- lib/libv4l2rds/libv4l2rds.c | 190 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 208 insertions(+), 17 deletions(-)
lib/include/libv4l2rds.h +31 -4
@@ -50,6 +50,9 @@ extern "C" {* Additional data is limited to 112 bit, and the smallest * optional tuple has a size of 4 bit (4 bit identifier + * 0 bits of data) */ +#define MAX_EON_CNT 20/* Maximal number of entries in the EON table (for storing +* information about other radio stations, broadcasted +* by the current station) */ /* Define Constants for the possible types of RDS information * used to address the relevant bit in the valid_fields bitmask */
@@ -69,7 +72,9 @@ extern "C" {#define V4L2_RDS_LC 0x2000/* Language Code */ #define V4L2_RDS_TMC_SG 0x4000/* RDS-TMC single group */ #define V4L2_RDS_TMC_MG 0x8000/* RDS-TMC multi group */ -#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ +#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ +#define V4L2_RDS_EON 0x20000/* Enhanced Other Network Info */ +#define V4L2_RDS_LSF 0x40000/* Linkage information */ /* Define Constants for the state of the RDS decoding process * used to address the relevant bit in the decode_information bitmask */
@@ -84,9 +89,10 @@ extern "C" {#define V4L2_RDS_FLAG_STATIC_PTY 0x08/* TMC related codes -* used to extract TMC fields from RDS groups */ -#define V4L2_TMC_TUNING_INFO 0x08 -#define V4L2_TMC_SINGLE_GROUP 0x04 +* used to extract TMC fields from RDS-TMC groups +* see ISO 14819-1:2003, Figure 2 - RDS-TMC single-grp full message structure */ +#define V4L2_TMC_TUNING_INFO 0x10/* Bit 4 indicates Tuning Info / User msg */ +#define V4L2_TMC_SINGLE_GROUP 0x08/* Bit 3 indicates Single / Multi-group msg */ /* struct to encapsulate one complete RDS group */ /* This structure is used internally to store data until a complete RDS
@@ -149,6 +155,26 @@ struct v4l2_rds_af_set {uint32_t af[MAX_AF_CNT ];/* AFs defined in Hz */ }; +/* struct to encapsulate one entry in the EON table (Enhanced Other Network) */ +struct v4l2_rds_eon { +uint32_t valid_fields; +uint16_t pi; +uint8_t ps[9 ]; +uint8_t pty; +bool ta; +bool tp; +uint16_t lsf;/* Linkage Set Number */ +struct v4l2_rds_af_set af; +}; + +/* struct to encapsulate a table of EON information */ +struct v4l2_rds_eon_set { +uint8_t size;/* size of the table */ +uint8_t index;/* current position in the table */ +struct v4l2_rds_eon eon[MAX_EON_CNT ];/* Information about other +* radio channels */ +}; +/* struct to encapsulate an additional data field in a TMC message */ struct v4l2_tmc_additional {uint8_t label;
@@ -236,6 +262,7 @@ struct v4l2_rds {struct v4l2_rds_statistics rds_statistics;struct v4l2_rds_oda_set rds_oda;/* Open Data Services */ struct v4l2_rds_af_set rds_af;/* Alternative Frequencies */ +struct v4l2_rds_eon_set rds_eon;/* EON information */ struct v4l2_rds_tmc tmc;/* TMC information */ };
lib/libv4l2rds/libv4l2rds.c +177 -13
234 lines not on this page · raw message
Signature · 1 line
1.7.10.4
Re: [RFC PATCH 1/4] libv4l2rds: added support to decode RDS-EON information
Hi Konke!
Thank you very much for your work on this! I do have a number of comments, but nothing serious:
Quoted 11 lines · On Tue May 7 2013 18:24:20 Konke Radlow wrote:
Signed-off-by Konke Radlow <[email protected]>--- lib/include/libv4l2rds.h | 35 +++++++- lib/libv4l2rds/libv4l2rds.c | 190 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 208 insertions(+), 17 deletions(-)lib/include/libv4l2rds.h +0
-0
lib/include/libv4l2rds.h extern "C" { · line 55
Quoted 6 lines
lib/include/libv4l2rds.h +3
-0@@ -50,6 +50,9 @@ extern "C" {* Additional data is limited to 112 bit, and the smallest * optional tuple has a size of 4 bit (4 bit identifier + * 0 bits of data) */ +#define MAX_EON_CNT 20/* Maximal number of entries in the EON table (for storing +* information about other radio stations, broadcasted +* by the current station) */
How did you derive this number? Is this from the standard or is this just a reasonable estimate? This should be documented.
Quoted 68 lines
lib/include/libv4l2rds.h +28
-4/* Define Constants for the possible types of RDS information * used to address the relevant bit in the valid_fields bitmask */ @@ -69,7 +72,9 @@ extern "C" {#define V4L2_RDS_LC 0x2000/* Language Code */ #define V4L2_RDS_TMC_SG 0x4000/* RDS-TMC single group */ #define V4L2_RDS_TMC_MG 0x8000/* RDS-TMC multi group */ -#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ +#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ +#define V4L2_RDS_EON 0x20000/* Enhanced Other Network Info */ +#define V4L2_RDS_LSF 0x40000/* Linkage information */ /* Define Constants for the state of the RDS decoding process * used to address the relevant bit in the decode_information bitmask */ @@ -84,9 +89,10 @@ extern "C" {#define V4L2_RDS_FLAG_STATIC_PTY 0x08/* TMC related codes -* used to extract TMC fields from RDS groups */ -#define V4L2_TMC_TUNING_INFO 0x08 -#define V4L2_TMC_SINGLE_GROUP 0x04 +* used to extract TMC fields from RDS-TMC groups +* see ISO 14819-1:2003, Figure 2 - RDS-TMC single-grp full message structure */ +#define V4L2_TMC_TUNING_INFO 0x10/* Bit 4 indicates Tuning Info / User msg */ +#define V4L2_TMC_SINGLE_GROUP 0x08/* Bit 3 indicates Single / Multi-group msg */ /* struct to encapsulate one complete RDS group */ /* This structure is used internally to store data until a complete RDS @@ -149,6 +155,26 @@ struct v4l2_rds_af_set {uint32_t af[MAX_AF_CNT ];/* AFs defined in Hz */ }; +/* struct to encapsulate one entry in the EON table (Enhanced Other Network) */ +struct v4l2_rds_eon { +uint32_t valid_fields; +uint16_t pi; +uint8_t ps[9 ]; +uint8_t pty; +bool ta; +bool tp; +uint16_t lsf;/* Linkage Set Number */ +struct v4l2_rds_af_set af; +}; + +/* struct to encapsulate a table of EON information */ +struct v4l2_rds_eon_set { +uint8_t size;/* size of the table */ +uint8_t index;/* current position in the table */ +struct v4l2_rds_eon eon[MAX_EON_CNT ];/* Information about other +* radio channels */ +}; +/* struct to encapsulate an additional data field in a TMC message */ struct v4l2_tmc_additional {uint8_t label;@@ -236,6 +262,7 @@ struct v4l2_rds {struct v4l2_rds_statistics rds_statistics;struct v4l2_rds_oda_set rds_oda;/* Open Data Services */ struct v4l2_rds_af_set rds_af;/* Alternative Frequencies */ +struct v4l2_rds_eon_set rds_eon;/* EON information */ struct v4l2_rds_tmc tmc;/* TMC information */ };lib/libv4l2rds/libv4l2rds.c +0
-0
lib/libv4l2rds/libv4l2rds.c enum rds_state { · line 96
Quoted 5 lines
lib/libv4l2rds/libv4l2rds.c +2
-0@@ -92,6 +92,9 @@ enum rds_state { RDS_C_RECEIVED, }; +/* function declarations to prevent the need to move large code blocks */ +static uint32_t rds_decode_af (uint8_t af,bool is_vhf);
It is better to have a separate patch that just moves code around and nothing else, than to add a forward declaration. I never like those, except when dealing with circular references.
Quoted 29 lines
lib/libv4l2rds/libv4l2rds.c +4
-12+static inline uint8_t set_bit (uint8_t input,uint8_t bitmask,bool bitvalue) {return bitvalue ? input | bitmask : input & ~bitmask;@@ -455,20 +458,11 @@ static bool rds_add_oda(struct rds_private_state *priv_state, struct v4l2_rds_od/* add a new AF to the list, if it doesn't exist yet */ static bool rds_add_af_to_list (struct v4l2_rds_af_set *af_set,uint8_t af,bool is_vhf) { -uint32_t freq =0 ; - -/* AF0 -> "Not to be used" */ -if (af ==0 ) +/* convert the frequency to Hz, skip on errors */ +uint32_t freq =rds_decode_af (af, is_vhf); +if (freq ==0 )return false; -/* calculate the AF values in HZ */ -if (is_vhf) - freq =87500000 + af *100000 ; -else if (freq <=15 ) - freq =152000 + af *9000 ; -else - freq =531000 + af *9000 ; -/* prevent buffer overflows */ if (af_set->size >=MAX_AF_CNT || af_set->size >= af_set->announced_af)return false;
lib/libv4l2rds/libv4l2rds.c static bool rds_add_ps(struct rds_private_state *priv_state, uint8_t pos, uint8_ · line 543
Quoted 7 lines
lib/libv4l2rds/libv4l2rds.c +4
-0@@ -543,6 +537,42 @@ static bool rds_add_ps(struct rds_private_state *priv_state, uint8_t pos, uint8_return true; } +/* checks if an entry for the given PI already exists and returns the index +* of that entry if so. Else it adds a new entry to the EON table and returns +* the index of the new field */ +static uint8_t rds_add_eon_entry (struct rds_private_state *priv_state,uint16_t pi) {
Newline before '{'.
Quoted 5 lines
lib/libv4l2rds/libv4l2rds.c +5
-0+struct v4l2_rds *handle = &priv_state->handle; +uint8_t index = handle->rds_eon.index; +uint8_t size = handle->rds_eon.size; + +/* check if there's an entry for the given PI key */
lib/libv4l2rds/libv4l2rds.c static bool rds_add_ps(struct rds_private_state *priv_state, uint8_t pos, uint8_ · line 558
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+for (int i =0 ; i < handle->rds_eon.size; i++) { +if (handle->rds_eon.eon[i].pi == pi) { +return i; + } + } +/* if the the maximum table size is reached, overwrite old +* entries, starting at the oldest one = 0 */ + handle->rds_eon.eon[index].pi = pi; + handle->rds_eon.eon[index].valid_fields |=V4L2_RDS_PI ; + handle->rds_eon.index = (index+1 <MAX_EON_CNT )? (index+1 ) :0 ;
Space before '?'
lib/libv4l2rds/libv4l2rds.c static bool rds_add_ps(struct rds_private_state *priv_state, uint8_t pos, uint8_ · line 559
Quoted 1 line
lib/libv4l2rds/libv4l2rds.c +1
-0+ handle->rds_eon.size = (size+1 <=MAX_EON_CNT )? (size+1 ) :MAX_EON_CNT ;
ditto.
lib/libv4l2rds/libv4l2rds.c static bool rds_add_ps(struct rds_private_state *priv_state, uint8_t pos, uint8_ · line 564
Quoted 5 lines
lib/libv4l2rds/libv4l2rds.c +5
-0+return index; +} + +/* checks if an entry for the given PI already exists */ +static bool rds_check_eon_entry (struct rds_private_state *priv_state,uint16_t pi) {
'{' on next line.
Quoted 53 lines
lib/libv4l2rds/libv4l2rds.c +34
-1+struct v4l2_rds *handle = &priv_state->handle; + +/* check if there's an entry for the given PI key */ +for (int i =0 ; i <= handle->rds_eon.size; i++) { +if (handle->rds_eon.eon[i].pi == pi) { +return true; + } + } +return false; +} +/* group of functions to decode successfully received RDS groups into * easily accessible data fields * @@ -790,6 +820,29 @@ static uint32_t rds_decode_group3(struct rds_private_state *priv_state)return updated_fields; } +/* decodes the RDS radio frequency representation into Hz +* @af: 8-bit AF value as transmitted in RDS groups +* @is_vhf: boolean value defining which conversion table to use +* @return: frequency in Hz, 0 in case of wrong input values */ +static uint32_t rds_decode_af (uint8_t af,bool is_vhf) { +uint32_t freq =0 ; + +/* AF = 0 => "not to be used" +* AF >= 205 => special meanings */ +if (af ==0 || af >=205 ) +return 0 ; + +/* calculate the AF values in HZ */ +if (is_vhf) + freq =87500000 + af *100000 ; +else if (freq <=15 ) + freq =152000 + af *9000 ; +else + freq =531000 + af *9000 ; + +return freq; +} +/* decodes the RDS date/time representation into a standard c representation * that can be used with c-library functions */ static time_t rds_decode_mjd (const struct rds_private_state *priv_state)@@ -880,7 +933,6 @@ static uint32_t rds_decode_group4(struct rds_private_state *priv_state) handle->time =rds_decode_mjd (priv_state); updated_fields |=V4L2_RDS_TIME ; handle->valid_fields |=V4L2_RDS_TIME ; -printf ("\nLIB: time_t: %ld" , handle->time);return updated_fields; }
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group10(struct rds_private_state *priv_state) · line 1037
Quoted 5 lines
lib/libv4l2rds/libv4l2rds.c +2
-0@@ -981,6 +1033,117 @@ static uint32_t rds_decode_group10(struct rds_private_state *priv_state)return updated_fields; } +/* group 14: EON (Enhanced Other Network) information */ +static uint32_t rds_decode_group14 (struct rds_private_state * priv_state) {
'{' on next line. Please check any other such occurances.
Quoted 4 lines
lib/libv4l2rds/libv4l2rds.c +4
-0+struct v4l2_rds *handle = &priv_state->handle; +struct v4l2_rds_group *grp = &priv_state->rds_group; +uint32_t updated_fields =0 ; +uint16_t pi_on;
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group10(struct rds_private_state *priv_state) · line 1051
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+uint16_t lsf_on; +uint8_t variant_code; +uint8_t eon_index; +uint8_t pty_on; +bool tp_on, ta_on; +bool new_a = false, new_b = false; + +if (grp->group_version !='A' ) { +return 0 ; + }
No '{' '}' around a block with a single statement.
Quoted 9 lines
lib/libv4l2rds/libv4l2rds.c +9
-0+ +/* bits 0-3 of group b contain the variant code */ + variant_code = grp->data_b_lsb &0x0f ; + +/* group d contains the PI code of the ON (Other Network) */ + pi_on = (grp->data_d_msb <<8 ) | grp->data_d_lsb; + +/* bit 4 of group b contains the TP status of the ON*/ + tp_on = grp->data_b_lsb &0x10 ;
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group10(struct rds_private_state *priv_state) · line 1070
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+if (rds_check_eon_entry (priv_state, pi_on)) { +/* if there's an entry for this PI(ON) update the TP field */ + eon_index =rds_add_eon_entry (priv_state, pi_on); + handle->rds_eon.eon[eon_index].tp = tp_on; + handle->rds_eon.eon[eon_index].valid_fields |=V4L2_RDS_TP ; + updated_fields |=V4L2_RDS_EON ; + } + +/* perform group variant dependent decoding */ +if ((variant_code >=5 && variant_code <=11 ) || variant_code >=14 ) {
Space before '11'.
Quoted 11 lines
lib/libv4l2rds/libv4l2rds.c +11
-0+/* 5-9 = mapped FM frequencies -> unsupported +* 10-11 = unallocated +* 14 = PIN(ON) -> unsupported (unused RDS feature) +* 15 = reserved for broadcasters use */ +return updated_fields; + } +/* PS Name */ +else if (variant_code <4 ) { + eon_index =rds_add_eon_entry (priv_state, pi_on); + handle->rds_eon.eon[eon_index].ps[variant_code*2 ] = grp->data_c_msb; + handle->rds_eon.eon[eon_index].ps[variant_code*2 +1 ] = grp->data_c_lsb;
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group10(struct rds_private_state *priv_state) · line 1091
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+ handle->rds_eon.eon[eon_index].valid_fields |=V4L2_RDS_PS ; + updated_fields |=V4L2_RDS_EON ; + } +/* Alternative frequencies */ +else if (variant_code ==4 ) { +uint8_t c_msb = grp->data_c_msb; +uint8_t c_lsb = grp->data_c_lsb; + eon_index =rds_add_eon_entry (priv_state, pi_on); + +/* 224..249: announcement of AF count (224=0, 249=25)*/
Space before '*/'
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group10(struct rds_private_state *priv_state) · line 1093
Quoted 2 lines
lib/libv4l2rds/libv4l2rds.c +2
-0+if (c_msb >=224 && c_msb <=249 ) + handle->rds_eon.eon[eon_index].af.announced_af = c_msb -224 ;
Incorrect indentation, this line needs an additional TAB.
I would also suggest using a temp variable pointing to &handle->rds_eon.eon[eon_index]. That should make it more readable.
Quoted 11 lines
lib/libv4l2rds/libv4l2rds.c +11
-0+/* check if the data represents an AF (for 1 =< val <= 204 the +* value represents an AF) */ +if (c_msb <205 ) + new_a =rds_add_af_to_list (&handle->rds_eon.eon[eon_index].af, + grp->data_c_msb, true); +if (c_lsb <205 ) + new_b =rds_add_af_to_list (&handle->rds_eon.eon[eon_index].af, + grp->data_c_lsb, true); +/* check if one of the frequencies was previously unknown */ +if (new_a || new_b) { + handle->rds_eon.eon[eon_index].valid_fields |=V4L2_RDS_AF ;
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group10(struct rds_private_state *priv_state) · line 1114
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+ updated_fields |=V4L2_RDS_EON ; + } + } +/* Linkage information */ +else if (variant_code ==12 ) { + eon_index =rds_add_eon_entry (priv_state, pi_on); +/* group c contains the lsf code */ + lsf_on = (grp->data_c_msb <<8 ) | grp->data_c_lsb; +/* check if the lsf code is already known */ + new_a = (handle->rds_eon.eon[eon_index].lsf == lsf_on) ? false : true;
Just use:
new_a = (handle->rds_eon.eon[eon_index].lsf != lsf_on);
Ditto elsewhere.
Quoted 37 lines
lib/libv4l2rds/libv4l2rds.c +32
-0+if (new_a) { + handle->rds_eon.eon[eon_index].lsf = lsf_on; + handle->rds_eon.eon[eon_index].valid_fields |=V4L2_RDS_LSF ; + updated_fields |=V4L2_RDS_EON ; + } + } +/* PTY(ON) and TA(ON) */ +else if (variant_code ==13 ) { + eon_index =rds_add_eon_entry (priv_state, pi_on); +/* bits 15-10 of group c contain the PTY(ON) */ + pty_on = grp->data_c_msb >>3 ; +/* bit 0 of group c contains the TA code */ + ta_on = grp->data_c_lsb &0x01 ; +/* check if the data is new */ + new_a = (handle->rds_eon.eon[eon_index].pty == pty_on) ? false : true; +if (new_a) { + handle->rds_eon.eon[eon_index].pty = pty_on; + handle->rds_eon.eon[eon_index].valid_fields |=V4L2_RDS_PTY ; + } + new_b = (handle->rds_eon.eon[eon_index].ta == ta_on) ? false : true; + handle->rds_eon.eon[eon_index].ta = ta_on; + handle->rds_eon.eon[eon_index].valid_fields |=V4L2_RDS_TA ; +if (new_a || new_b) + updated_fields |=V4L2_RDS_EON ; + } +/* set valid field for EON data, if EON table contains entries */ +if (handle->rds_eon.size >0 ) + handle->valid_fields |=V4L2_RDS_EON ; + +return updated_fields; +} +typedef uint32_t (*decode_group_func )(struct rds_private_state *);/* array of function pointers to contain all group specific decoding functions */
lib/libv4l2rds/libv4l2rds.c static const decode_group_func decode_group[16] = { · line 1161
Quoted 7 lines
lib/libv4l2rds/libv4l2rds.c +1
-0@@ -992,6 +1155,7 @@ static const decode_group_func decode_group[16] = { [4] = rds_decode_group4, [8] = rds_decode_group8, [10] = rds_decode_group10, + [14] = rds_decode_group14 };static uint32_t rds_decode_group (struct rds_private_state *priv_state)
Regards,
Hans
RFC PATCH 2/4 rds-ctl.cpp: added functionality to print RDS-EON information
In reply to Konke Radlow--- utils/rds-ctl/rds-ctl.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
utils/rds-ctl/rds-ctl.cpp +29 -0
@@ -550,6 +550,33 @@ static void print_rds_af(const struct v4l2_rds_af_set *af_set) } } +static void print_rds_eon(const struct v4l2_rds_eon_set *eon_set) +{ + int counter = 0; + + printf("\n\nEnhanced Other Network information: %u channels", eon_set->size); + for (int i = 0; i < eon_set->size; i++, counter++) { + if (eon_set->eon[i].valid_fields & V4L2_RDS_PI) + printf("\nPI(ON %02i) = %04x", i, eon_set->eon[i].pi); + if (eon_set->eon[i].valid_fields & V4L2_RDS_PS) + printf("\nPS(ON %02i) = %s", i, eon_set->eon[i].ps); + if (eon_set->eon[i].valid_fields & V4L2_RDS_PTY) + printf("\nPTY(ON %02i) = %0u", i, eon_set->eon[i].pty); + if (eon_set->eon[i].valid_fields & V4L2_RDS_LSF) + printf("\nLSF(ON %02i) = %0u", i, eon_set->eon[i].lsf); + if (eon_set->eon[i].valid_fields & V4L2_RDS_AF) + printf("\nPTY(ON %02i) = %0u", i, eon_set->eon[i].pty); + if (eon_set->eon[i].valid_fields & V4L2_RDS_TP) + printf("\nTP(ON %02i): %s" ,i ,eon_set->eon[i].tp? "yes":"no"); + if (eon_set->eon[i].valid_fields & V4L2_RDS_TA) + printf("\nTA(ON %02i): %s",i ,eon_set->eon[i].tp? "yes":"no"); + if (eon_set->eon[i].valid_fields & V4L2_RDS_AF) { + printf("\nAF(ON %02i): size=%i", i, eon_set->eon[i].af.size); + print_rds_af(&(eon_set->eon[i].af)); + } + } +} + static void print_rds_pi(const struct v4l2_rds *handle) { printf("\nArea Coverage: %s", v4l2_rds_get_coverage_str(handle));
@@ -662,6 +689,8 @@ static void read_rds_from_fd(const int fd) /* try to receive and decode RDS data */ read_rds(rds_handle, fd, params.wait_limit); + if (rds_handle->valid_fields & V4L2_RDS_EON) + print_rds_eon(&rds_handle->rds_eon); print_rds_statistics(&rds_handle->rds_statistics); v4l2_rds_destroy(rds_handle);
Signature · 1 line
1.7.10.4
Re: [RFC PATCH 2/4] rds-ctl.cpp: added functionality to print RDS-EON information
Quoted 21 lines · On Tue May 7 2013 18:24:21 Konke Radlow wrote:
Signed-off-by Konke Radlow <[email protected]>--- utils/rds-ctl/rds-ctl.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)utils/rds-ctl/rds-ctl.cpp +8
-0@@ -550,6 +550,33 @@ static void print_rds_af(const struct v4l2_rds_af_set *af_set) } } +static void print_rds_eon(const struct v4l2_rds_eon_set *eon_set) +{ + int counter = 0; + + printf("\n\nEnhanced Other Network information: %u channels", eon_set->size); + for (int i = 0; i < eon_set->size; i++, counter++) { + if (eon_set->eon[i].valid_fields & V4L2_RDS_PI) + printf("\nPI(ON %02i) = %04x", i, eon_set->eon[i].pi);
utils/rds-ctl/rds-ctl.cpp static void print_rds_af(const struct v4l2_rds_af_set *af_set) · line 570
Quoted 10 lines
utils/rds-ctl/rds-ctl.cpp +10
-0+ if (eon_set->eon[i].valid_fields & V4L2_RDS_PS) + printf("\nPS(ON %02i) = %s", i, eon_set->eon[i].ps); + if (eon_set->eon[i].valid_fields & V4L2_RDS_PTY) + printf("\nPTY(ON %02i) = %0u", i, eon_set->eon[i].pty); + if (eon_set->eon[i].valid_fields & V4L2_RDS_LSF) + printf("\nLSF(ON %02i) = %0u", i, eon_set->eon[i].lsf); + if (eon_set->eon[i].valid_fields & V4L2_RDS_AF) + printf("\nPTY(ON %02i) = %0u", i, eon_set->eon[i].pty); + if (eon_set->eon[i].valid_fields & V4L2_RDS_TP) + printf("\nTP(ON %02i): %s" ,i ,eon_set->eon[i].tp? "yes":"no");
Spacing is a bit messy. This is better:
printf("\nTP(ON %02i): %s", i, eon_set->eon[i].tp ? "yes" : "no");utils/rds-ctl/rds-ctl.cpp static void print_rds_af(const struct v4l2_rds_af_set *af_set) · line 572
Quoted 2 lines
utils/rds-ctl/rds-ctl.cpp +2
-0+ if (eon_set->eon[i].valid_fields & V4L2_RDS_TA) + printf("\nTA(ON %02i): %s",i ,eon_set->eon[i].tp? "yes":"no");
Ditto:
printf("\nTA(ON %02i): %s", i, eon_set->eon[i].tp ? "yes" : "no");Quoted 12 lines
utils/rds-ctl/rds-ctl.cpp +7
-0+ if (eon_set->eon[i].valid_fields & V4L2_RDS_AF) { + printf("\nAF(ON %02i): size=%i", i, eon_set->eon[i].af.size); + print_rds_af(&(eon_set->eon[i].af)); + } + } +} + static void print_rds_pi(const struct v4l2_rds *handle) { printf("\nArea Coverage: %s", v4l2_rds_get_coverage_str(handle));
utils/rds-ctl/rds-ctl.cpp static void read_rds_from_fd(const int fd) · line 696
Quoted 8 lines
utils/rds-ctl/rds-ctl.cpp +2
-0@@ -662,6 +689,8 @@ static void read_rds_from_fd(const int fd) /* try to receive and decode RDS data */ read_rds(rds_handle, fd, params.wait_limit); + if (rds_handle->valid_fields & V4L2_RDS_EON) + print_rds_eon(&rds_handle->rds_eon); print_rds_statistics(&rds_handle->rds_statistics); v4l2_rds_destroy(rds_handle);
Regards,
Hans
RFC PATCH 3/4 libv4l2rds: added support to decode RDS-TMC tuning information
In reply to Konke Radlow--- lib/include/libv4l2rds.h | 39 +++++++++++ lib/libv4l2rds/libv4l2rds.c | 159 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 4 deletions(-)
lib/include/libv4l2rds.h +39 -0
@@ -50,6 +50,10 @@ extern "C" {* Additional data is limited to 112 bit, and the smallest * optional tuple has a size of 4 bit (4 bit identifier + * 0 bits of data) */ +#define MAX_TMC_ALT_STATIONS 32/* defined by ISO 14819-1:2003, 7.5.3.3 */ +#define MAX_TMC_AF_CNT 4/* limit for the numbers of AFs stored per alternative TMC +* station. This value is not defined by the standard, but based on observation +* of real-world RDS-TMC streams */ #define MAX_EON_CNT 20/* Maximal number of entries in the EON table (for storing * information about other radio stations, broadcasted * by the current station) */
@@ -75,6 +79,7 @@ extern "C" {#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ #define V4L2_RDS_EON 0x20000/* Enhanced Other Network Info */ #define V4L2_RDS_LSF 0x40000/* Linkage information */ +#define V4L2_RDS_TMC_TUNING 0x80000/* RDS-TMC tuning information */ /* Define Constants for the state of the RDS decoding process * used to address the relevant bit in the decode_information bitmask */
@@ -175,6 +180,37 @@ struct v4l2_rds_eon_set {* radio channels */ }; +/* struct to encapsulate alternative frequencies (AFs) for RDS-TMC stations. +* AFs listed in af[] can be used unconditionally. +* AFs listed in mapped_af[n] should only be used if the current +* tuner frequency matches the value in mapped_af_tuning[n] */ +struct v4l2_tmc_alt_freq { +uint8_t af_size;/* number of known AFs */ +uint8_t af_index; +uint8_t mapped_af_size;/* number of mapped AFs */ +uint8_t mapped_af_index; +uint32_t af[MAX_TMC_AF_CNT ];/* AFs defined in Hz */ +uint32_t mapped_af[MAX_TMC_AF_CNT ];/* mapped AFs defined in Hz */ +uint32_t mapped_af_tuning[MAX_TMC_AF_CNT ];/* mapped AFs defined in Hz */ +}; + +/* struct to encapsulate information about stations carrying RDS-TMC services */ +struct v4l2_tmc_station { +uint16_t pi; +uint8_t ltn;/* database-ID of ON */ +uint8_t msg;/* msg parameters of ON */ +uint8_t sid;/* service-ID of ON */ +struct v4l2_tmc_alt_freq afi; +}; + +/* struct to encapsulate tuning information for TMC */ +struct v4l2_tmc_tuning { +uint8_t station_cnt;/* number of announced alternative stations */ +uint8_t index; +struct v4l2_tmc_station station[MAX_TMC_ALT_STATIONS ];/* information +* about other stations carrying the same RDS-TMC service */ +}; +/* struct to encapsulate an additional data field in a TMC message */ struct v4l2_tmc_additional {uint8_t label;
@@ -225,6 +261,9 @@ struct v4l2_rds_tmc {uint8_t t_d;/* delay time (only if mode = enhanced */ uint8_t spn[9 ];/* service provider name */ struct v4l2_rds_tmc_msg tmc_msg; + +/* tuning information for alternative service providers */ +struct v4l2_tmc_tuning tuning; };/* struct to encapsulate state and RDS information for current decoding process */
lib/libv4l2rds/libv4l2rds.c +155 -4
187 lines not on this page · raw message
Signature · 1 line
1.7.10.4
Re: [RFC PATCH 3/4] libv4l2rds: added support to decode RDS-TMC tuning information
Quoted 11 lines · On Tue May 7 2013 18:24:22 Konke Radlow wrote:
Signed-off-by Konke Radlow <[email protected]>--- lib/include/libv4l2rds.h | 39 +++++++++++ lib/libv4l2rds/libv4l2rds.c | 159 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 4 deletions(-)lib/include/libv4l2rds.h +0
-0
lib/include/libv4l2rds.h extern "C" { · line 56
Quoted 7 lines
lib/include/libv4l2rds.h +4
-0@@ -50,6 +50,10 @@ extern "C" {* Additional data is limited to 112 bit, and the smallest * optional tuple has a size of 4 bit (4 bit identifier + * 0 bits of data) */ +#define MAX_TMC_ALT_STATIONS 32/* defined by ISO 14819-1:2003, 7.5.3.3 */ +#define MAX_TMC_AF_CNT 4/* limit for the numbers of AFs stored per alternative TMC +* station. This value is not defined by the standard, but based on observation +* of real-world RDS-TMC streams */
Could you clarify this a bit more? E.g. what is the maximum number of AFs you have seen in practice?
Quoted 64 lines
lib/include/libv4l2rds.h +35
-0#define MAX_EON_CNT 20/* Maximal number of entries in the EON table (for storing * information about other radio stations, broadcasted * by the current station) */ @@ -75,6 +79,7 @@ extern "C" {#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ #define V4L2_RDS_EON 0x20000/* Enhanced Other Network Info */ #define V4L2_RDS_LSF 0x40000/* Linkage information */ +#define V4L2_RDS_TMC_TUNING 0x80000/* RDS-TMC tuning information */ /* Define Constants for the state of the RDS decoding process * used to address the relevant bit in the decode_information bitmask */ @@ -175,6 +180,37 @@ struct v4l2_rds_eon_set {* radio channels */ }; +/* struct to encapsulate alternative frequencies (AFs) for RDS-TMC stations. +* AFs listed in af[] can be used unconditionally. +* AFs listed in mapped_af[n] should only be used if the current +* tuner frequency matches the value in mapped_af_tuning[n] */ +struct v4l2_tmc_alt_freq { +uint8_t af_size;/* number of known AFs */ +uint8_t af_index; +uint8_t mapped_af_size;/* number of mapped AFs */ +uint8_t mapped_af_index; +uint32_t af[MAX_TMC_AF_CNT ];/* AFs defined in Hz */ +uint32_t mapped_af[MAX_TMC_AF_CNT ];/* mapped AFs defined in Hz */ +uint32_t mapped_af_tuning[MAX_TMC_AF_CNT ];/* mapped AFs defined in Hz */ +}; + +/* struct to encapsulate information about stations carrying RDS-TMC services */ +struct v4l2_tmc_station { +uint16_t pi; +uint8_t ltn;/* database-ID of ON */ +uint8_t msg;/* msg parameters of ON */ +uint8_t sid;/* service-ID of ON */ +struct v4l2_tmc_alt_freq afi; +}; + +/* struct to encapsulate tuning information for TMC */ +struct v4l2_tmc_tuning { +uint8_t station_cnt;/* number of announced alternative stations */ +uint8_t index; +struct v4l2_tmc_station station[MAX_TMC_ALT_STATIONS ];/* information +* about other stations carrying the same RDS-TMC service */ +}; +/* struct to encapsulate an additional data field in a TMC message */ struct v4l2_tmc_additional {uint8_t label;@@ -225,6 +261,9 @@ struct v4l2_rds_tmc {uint8_t t_d;/* delay time (only if mode = enhanced */ uint8_t spn[9 ];/* service provider name */ struct v4l2_rds_tmc_msg tmc_msg; + +/* tuning information for alternative service providers */ +struct v4l2_tmc_tuning tuning; };/* struct to encapsulate state and RDS information for current decoding process */ lib/libv4l2rds/libv4l2rds.c +0
-0
lib/libv4l2rds/libv4l2rds.c enum rds_state { · line 98
Quoted 6 lines
lib/libv4l2rds/libv4l2rds.c +2
-0@@ -93,7 +93,9 @@ enum rds_state { };/* function declarations to prevent the need to move large code blocks */ +static int rds_add_tmc_station (struct rds_private_state *priv_state,uint16_t pi);static uint32_t rds_decode_af (uint8_t af,bool is_vhf); +static bool rds_add_tmc_af (struct rds_private_state *priv_state);
Same comment as previously: just have a separate patch that moves code around.
Quoted 32 lines
lib/libv4l2rds/libv4l2rds.c +25
-0static inline uint8_t set_bit (uint8_t input,uint8_t bitmask,bool bitvalue) {@@ -437,6 +439,59 @@ static uint32_t rds_decode_tmc_multi_group(struct rds_private_state *priv_state)return V4L2_RDS_TMC_MG ; } +/* decode the RDS-TMC tuning information that is contained in type 8A groups +* (variants 4 to 9) that announce the presence alternative transmitters +* providing the same RDS-TMC service */ +static uint32_t rds_decode_tmc_tuning (struct rds_private_state *priv_state) +{ +struct v4l2_rds_group *group = &priv_state->rds_group; +struct v4l2_rds_tmc *tmc = &priv_state->handle.tmc; +uint8_t variant_code = group->data_b_lsb &0x0f ; +uint16_t pi_on = (group->data_d_msb <<8 ) | group->data_d_lsb; +uint8_t index; + +/* variants 4 and 5 carry the service provider name */ +if (variant_code >=4 && variant_code <=5 ) { +int offset =4 * (variant_code -4 ); + tmc->spn[0 + offset] = group->data_c_msb; + tmc->spn[1 + offset] = group->data_c_lsb; + tmc->spn[2 + offset] = group->data_d_msb; + tmc->spn[3 + offset] = group->data_d_lsb; + +/* variant 6 provides specific frequencies for the same RDS-TMC service +* on a network with a different PI code */ +/* variant 7 provides mapped frequency pair information which should only +* be used if the terminal is tuned to the tuning frequency */ + }else if (variant_code ==6 || variant_code ==7 ) { +rds_add_tmc_af (priv_state);
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_tmc_multi_group(struct rds_private_state *priv_state) · line 476
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+ +/* variant 8 indicates up to 2 PI codes of adjacent networks carrying +* the same RDS-TMC service on all transmitters of the network */ + }else if (variant_code ==8 ) { +uint16_t pi_on_2 = (group->data_c_msb <<8 ) | group->data_c_lsb; + +/* try to add both transmitted PI codes to the table */ +rds_add_tmc_station (priv_state, pi_on); +/* PI = 0 is used as a filler code */ +if (pi_on_2 !=0 )rds_add_tmc_station (priv_state, pi_on_2);
Newline before rds_add_tmc_station().
Quoted 34 lines
lib/libv4l2rds/libv4l2rds.c +27
-0+ +/* variant 9 provides PI codes of other networks with different system +* parameters */ + }else if (variant_code ==9 ) { + index =rds_add_tmc_station (priv_state, pi_on); + +/* bits 0 - 5 contain the service-ID of the ON */ + tmc->tuning.station[index].sid = group->data_c_lsb &0x3F ; +/* bits 6-10 contain the msg parameters of the ON */ + tmc->tuning.station[index].msg = (group->data_c_msb &0x03 ) <<2 ; + tmc->tuning.station[index].msg |= (group->data_c_lsb >>6 ) &0x03 ; +/* bits 11-15 contain the database-ID of the ON */ + tmc->tuning.station[index].ltn = group->data_c_msb >>2 ; + } + +return V4L2_RDS_TMC_TUNING ; +} +static bool rds_add_oda (struct rds_private_state *priv_state,struct v4l2_rds_oda oda) {struct v4l2_rds *handle = &priv_state->handle;@@ -516,6 +571,103 @@ static bool rds_add_af(struct rds_private_state *priv_state)return updated_af; } +/* checks if an entry for the given PI already exists and returns the index +* of that entry if so. Else it adds a new entry to the TMC-Tuning table and returns +* the index of the new field */ +static int rds_add_tmc_station (struct rds_private_state *priv_state,uint16_t pi) +{ +struct v4l2_tmc_tuning *tuning = &priv_state->handle.tmc.tuning; +uint8_t index = tuning->index; +uint8_t size = tuning->station_cnt; +
lib/libv4l2rds/libv4l2rds.c static bool rds_add_af(struct rds_private_state *priv_state) · line 592
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+/* check if there's an entry for the given PI key */ +for (int i =0 ; i < tuning->station_cnt; i++) { +if (tuning->station[i].pi == pi) { +return i; + } + } +/* if the the maximum table size is reached, overwrite old +* entries, starting at the oldest one = 0 */ + tuning->station[index].pi = pi; + tuning->index = (index+1 <MAX_TMC_ALT_STATIONS )? (index+1 ) :0 ;
Space before '?'
lib/libv4l2rds/libv4l2rds.c static bool rds_add_af(struct rds_private_state *priv_state) · line 593
Quoted 1 line
lib/libv4l2rds/libv4l2rds.c +1
-0+ tuning->station_cnt = (size+1 <=MAX_TMC_ALT_STATIONS )? (size+1 ) :MAX_TMC_ALT_STATIONS ;
Ditto. You do this elsewhere as well, please check :-)
Quoted 88 lines
lib/libv4l2rds/libv4l2rds.c +78
-2+return index; +} + +/* tries to add new AFs to the relevant entry in the list of RDS-TMC providers */ +static bool rds_add_tmc_af (struct rds_private_state *priv_state) +{ +struct v4l2_rds_group *grp = &priv_state->rds_group; +struct v4l2_tmc_alt_freq *afi; +uint16_t pi_on = grp->data_d_msb <<8 | grp->data_d_lsb; +uint8_t variant = grp->data_b_lsb &0x0f ; +uint8_t station_index =rds_add_tmc_station (priv_state, pi_on); +uint8_t af_index; +uint8_t mapped_af_index; +uint32_t freq_a =rds_decode_af (grp->data_c_msb, true); +uint32_t freq_b =rds_decode_af (grp->data_c_lsb, true); + + afi = &priv_state->handle.tmc.tuning.station[station_index].afi; + af_index = afi->af_index; + mapped_af_index = afi->mapped_af_index; + +/* specific frequencies */ +if (variant ==6 ) { +/* compare the new AFs to the stored ones, reset them to 0 if the AFs are +* already known */ +for (int i =0 ; i < afi->af_size; i++) { + freq_a = (freq_a == afi->af[i])?0 : freq_a; + freq_b = (freq_b == afi->af[i])?0 : freq_b; + } +/* return early if there is nothing to do */ +if (freq_a ==0 && freq_b ==0 ) +return false; + +/* add the new AFs if they were previously unknown */ +if (freq_a !=0 ) { + afi->af[af_index] = freq_a; + af_index = (af_index+1 <MAX_TMC_AF_CNT )? af_index+1 :0 ; + afi->af_size++; + } +if (freq_b !=0 ) { + afi->af[af_index] = freq_b; + af_index = (af_index+1 <MAX_TMC_AF_CNT )? af_index+1 :0 ; + afi->af_size++; + } +/* update the information in the handle */ + afi->af_index = af_index; +if (afi->af_size >=MAX_TMC_AF_CNT ) + afi->af_size =MAX_TMC_AF_CNT ; + +return true; + } + +/* mapped frequency pair */ +else if (variant ==7 ) { +/* check the if there's already a frequency mapped to the new tuning +* frequency, update the mapped frequency in this case */ +for (int i =0 ; i < afi->mapped_af_size; i++) { +if (freq_a == afi->mapped_af_tuning[i]) + afi->mapped_af[i] = freq_b; +return true; + } +/* new pair is unknown, add it to the list */ +if (freq_a !=0 && freq_b !=0 ) { + mapped_af_index = (mapped_af_index+1 >=MAX_TMC_AF_CNT )?0 : mapped_af_index +1 ; + afi->mapped_af[mapped_af_index] = freq_b; + afi->mapped_af_tuning[mapped_af_index] = freq_a; + afi->mapped_af_size++; + } +/* update the information in the handle */ + afi->mapped_af_index = mapped_af_index; +if (afi->mapped_af_size >=MAX_TMC_AF_CNT ) + afi->mapped_af_size =MAX_TMC_AF_CNT ; + +return true; + } +return false; +} +/* adds one char of the ps name to temporal storage, the value is validated * if it is received twice in a row * @pos: position of the char within the PS name (0..7) @@ -968,13 +1120,12 @@ static uint32_t rds_decode_group8(struct rds_private_state *priv_state) !(grp->data_b_lsb & V4L2_TMC_TUNING_INFO)) {return rds_decode_tmc_multi_group (priv_state); } -/* -> tuning information message, defined for variants 4..9, submitted -* in bits 0-3 of block 2 */ +/* -> tuning information message, defined for variants 4..9,
lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group8(struct rds_private_state *priv_state) · line 1131
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +2
-2+* submitted in bits 0-3 of block 2 */ tuning_variant = grp->data_b_lsb &0x0f ;if ((grp->data_b_lsb &V4L2_TMC_TUNING_INFO ) && tuning_variant >=4 && tuning_variant <=9 ) { -/* TODO: Implement tuning information decoding */ -return 0 ; +return rds_decode_tmc_tuning (priv_state); }return 0 ;
I suggest that we update V4L2_RDS_VERSION to 2.
Unless I am mistaken we are now complete with regards to the RDS/TMC functionality and we can release the library officially.
It would still be nice if rds-ctl could decode the TMC messages to human readable text, though...
Regards,
Hans
Re: [RFC PATCH 3/4] libv4l2rds: added support to decode RDS-TMC tuning information
Hi Hans,
Thank you for your comments, they're very appreciated as always :)
I will integrate the proposals and submit an updated version towards the end of this week.
To my best knowledge I would say that the functionality of RDS/TMC as defined by the standards is completely implemented now.
And I agree that human readable output for the TMC messages would be nice to have, but I'm quite busy with my thesis atm and will not make any promises ;)
Cheers,
Konke
Quoted 302 lines · Hans Verkuil
Quoted 11 lines · On Tue May 7 2013 18:24:22 Konke Radlow wrote:
Signed-off-by Konke Radlow <[email protected]>--- lib/include/libv4l2rds.h | 39 +++++++++++ lib/libv4l2rds/libv4l2rds.c | 159 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 194 insertions(+), 4 deletions(-)lib/include/libv4l2rds.h +0
-0lib/include/libv4l2rds.h extern "C" { · line 56
Quoted 7 lines
lib/include/libv4l2rds.h +4
-0@@ -50,6 +50,10 @@ extern "C" {* Additional data is limited to 112 bit, and the smallest * optional tuple has a size of 4 bit (4 bit identifier + * 0 bits of data) */ +#define MAX_TMC_ALT_STATIONS 32/* defined by ISO 14819-1:2003, 7.5.3.3 */ +#define MAX_TMC_AF_CNT 4/* limit for the numbers of AFs stored per alternative TMC +* station. This value is not defined by the standard, but based on observation +* of real-world RDS-TMC streams */ Could you clarify this a bit more? E.g. what is the maximum number of AFs you have seen in practice?
Quoted 64 lines
lib/include/libv4l2rds.h +35
-0#define MAX_EON_CNT 20/* Maximal number of entries in the EON table (for storing * information about other radio stations, broadcasted * by the current station) */ @@ -75,6 +79,7 @@ extern "C" {#define V4L2_RDS_TMC_SYS 0x10000/* RDS-TMC system information */ #define V4L2_RDS_EON 0x20000/* Enhanced Other Network Info */ #define V4L2_RDS_LSF 0x40000/* Linkage information */ +#define V4L2_RDS_TMC_TUNING 0x80000/* RDS-TMC tuning information */ /* Define Constants for the state of the RDS decoding process * used to address the relevant bit in the decode_information bitmask */ @@ -175,6 +180,37 @@ struct v4l2_rds_eon_set {* radio channels */ }; +/* struct to encapsulate alternative frequencies (AFs) for RDS-TMC stations. +* AFs listed in af[] can be used unconditionally. +* AFs listed in mapped_af[n] should only be used if the current +* tuner frequency matches the value in mapped_af_tuning[n] */ +struct v4l2_tmc_alt_freq { +uint8_t af_size;/* number of known AFs */ +uint8_t af_index; +uint8_t mapped_af_size;/* number of mapped AFs */ +uint8_t mapped_af_index; +uint32_t af[MAX_TMC_AF_CNT ];/* AFs defined in Hz */ +uint32_t mapped_af[MAX_TMC_AF_CNT ];/* mapped AFs defined in Hz */ +uint32_t mapped_af_tuning[MAX_TMC_AF_CNT ];/* mapped AFs defined in Hz */ +}; + +/* struct to encapsulate information about stations carrying RDS-TMC services */ +struct v4l2_tmc_station { +uint16_t pi; +uint8_t ltn;/* database-ID of ON */ +uint8_t msg;/* msg parameters of ON */ +uint8_t sid;/* service-ID of ON */ +struct v4l2_tmc_alt_freq afi; +}; + +/* struct to encapsulate tuning information for TMC */ +struct v4l2_tmc_tuning { +uint8_t station_cnt;/* number of announced alternative stations */ +uint8_t index; +struct v4l2_tmc_station station[MAX_TMC_ALT_STATIONS ];/* information +* about other stations carrying the same RDS-TMC service */ +}; +/* struct to encapsulate an additional data field in a TMC message */ struct v4l2_tmc_additional {uint8_t label;@@ -225,6 +261,9 @@ struct v4l2_rds_tmc {uint8_t t_d;/* delay time (only if mode = enhanced */ uint8_t spn[9 ];/* service provider name */ struct v4l2_rds_tmc_msg tmc_msg; + +/* tuning information for alternative service providers */ +struct v4l2_tmc_tuning tuning; };/* struct to encapsulate state and RDS information for current decoding process */ lib/libv4l2rds/libv4l2rds.c +0
-0lib/libv4l2rds/libv4l2rds.c enum rds_state { · line 98
Quoted 6 lines
lib/libv4l2rds/libv4l2rds.c +2
-0@@ -93,7 +93,9 @@ enum rds_state { };/* function declarations to prevent the need to move large code blocks */ +static int rds_add_tmc_station (struct rds_private_state *priv_state,uint16_t pi);static uint32_t rds_decode_af (uint8_t af,bool is_vhf); +static bool rds_add_tmc_af (struct rds_private_state *priv_state);Same comment as previously: just have a separate patch that moves code around.
Quoted 32 lines
lib/libv4l2rds/libv4l2rds.c +25
-0static inline uint8_t set_bit (uint8_t input,uint8_t bitmask,bool bitvalue) {@@ -437,6 +439,59 @@ static uint32_t rds_decode_tmc_multi_group(struct rds_private_state *priv_state)return V4L2_RDS_TMC_MG ; } +/* decode the RDS-TMC tuning information that is contained in type 8A groups +* (variants 4 to 9) that announce the presence alternative transmitters +* providing the same RDS-TMC service */ +static uint32_t rds_decode_tmc_tuning (struct rds_private_state *priv_state) +{ +struct v4l2_rds_group *group = &priv_state->rds_group; +struct v4l2_rds_tmc *tmc = &priv_state->handle.tmc; +uint8_t variant_code = group->data_b_lsb &0x0f ; +uint16_t pi_on = (group->data_d_msb <<8 ) | group->data_d_lsb; +uint8_t index; + +/* variants 4 and 5 carry the service provider name */ +if (variant_code >=4 && variant_code <=5 ) { +int offset =4 * (variant_code -4 ); + tmc->spn[0 + offset] = group->data_c_msb; + tmc->spn[1 + offset] = group->data_c_lsb; + tmc->spn[2 + offset] = group->data_d_msb; + tmc->spn[3 + offset] = group->data_d_lsb; + +/* variant 6 provides specific frequencies for the same RDS-TMC service +* on a network with a different PI code */ +/* variant 7 provides mapped frequency pair information which should only +* be used if the terminal is tuned to the tuning frequency */ + }else if (variant_code ==6 || variant_code ==7 ) { +rds_add_tmc_af (priv_state);lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_tmc_multi_group(struct rds_private_state *priv_state) · line 476
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+ +/* variant 8 indicates up to 2 PI codes of adjacent networks carrying +* the same RDS-TMC service on all transmitters of the network */ + }else if (variant_code ==8 ) { +uint16_t pi_on_2 = (group->data_c_msb <<8 ) | group->data_c_lsb; + +/* try to add both transmitted PI codes to the table */ +rds_add_tmc_station (priv_state, pi_on); +/* PI = 0 is used as a filler code */ +if (pi_on_2 !=0 )rds_add_tmc_station (priv_state, pi_on_2);Newline before
rds_add_tmc_station().Quoted 34 lines
lib/libv4l2rds/libv4l2rds.c +27
-0+ +/* variant 9 provides PI codes of other networks with different system +* parameters */ + }else if (variant_code ==9 ) { + index =rds_add_tmc_station (priv_state, pi_on); + +/* bits 0 - 5 contain the service-ID of the ON */ + tmc->tuning.station[index].sid = group->data_c_lsb &0x3F ; +/* bits 6-10 contain the msg parameters of the ON */ + tmc->tuning.station[index].msg = (group->data_c_msb &0x03 ) <<2 ; + tmc->tuning.station[index].msg |= (group->data_c_lsb >>6 ) &0x03 ; +/* bits 11-15 contain the database-ID of the ON */ + tmc->tuning.station[index].ltn = group->data_c_msb >>2 ; + } + +return V4L2_RDS_TMC_TUNING ; +} +static bool rds_add_oda (struct rds_private_state *priv_state,struct v4l2_rds_oda oda) {struct v4l2_rds *handle = &priv_state->handle;@@ -516,6 +571,103 @@ static bool rds_add_af(struct rds_private_state *priv_state)return updated_af; } +/* checks if an entry for the given PI already exists and returns the index +* of that entry if so. Else it adds a new entry to the TMC-Tuning table and returns +* the index of the new field */ +static int rds_add_tmc_station (struct rds_private_state *priv_state,uint16_t pi) +{ +struct v4l2_tmc_tuning *tuning = &priv_state->handle.tmc.tuning; +uint8_t index = tuning->index; +uint8_t size = tuning->station_cnt; +lib/libv4l2rds/libv4l2rds.c static bool rds_add_af(struct rds_private_state *priv_state) · line 592
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +10
-0+/* check if there's an entry for the given PI key */ +for (int i =0 ; i < tuning->station_cnt; i++) { +if (tuning->station[i].pi == pi) { +return i; + } + } +/* if the the maximum table size is reached, overwrite old +* entries, starting at the oldest one = 0 */ + tuning->station[index].pi = pi; + tuning->index = (index+1 <MAX_TMC_ALT_STATIONS )? (index+1 ) :0 ;Space before '?'
lib/libv4l2rds/libv4l2rds.c static bool rds_add_af(struct rds_private_state *priv_state) · line 593
Quoted 1 line
lib/libv4l2rds/libv4l2rds.c +1
-0+ tuning->station_cnt = (size+1 <=MAX_TMC_ALT_STATIONS )? (size+1 ) :MAX_TMC_ALT_STATIONS ;Ditto. You do this elsewhere as well, please check :-)
Quoted 88 lines
lib/libv4l2rds/libv4l2rds.c +78
-2+return index; +} + +/* tries to add new AFs to the relevant entry in the list of RDS-TMC providers */ +static bool rds_add_tmc_af (struct rds_private_state *priv_state) +{ +struct v4l2_rds_group *grp = &priv_state->rds_group; +struct v4l2_tmc_alt_freq *afi; +uint16_t pi_on = grp->data_d_msb <<8 | grp->data_d_lsb; +uint8_t variant = grp->data_b_lsb &0x0f ; +uint8_t station_index =rds_add_tmc_station (priv_state, pi_on); +uint8_t af_index; +uint8_t mapped_af_index; +uint32_t freq_a =rds_decode_af (grp->data_c_msb, true); +uint32_t freq_b =rds_decode_af (grp->data_c_lsb, true); + + afi = &priv_state->handle.tmc.tuning.station[station_index].afi; + af_index = afi->af_index; + mapped_af_index = afi->mapped_af_index; + +/* specific frequencies */ +if (variant ==6 ) { +/* compare the new AFs to the stored ones, reset them to 0 if the AFs are +* already known */ +for (int i =0 ; i < afi->af_size; i++) { + freq_a = (freq_a == afi->af[i])?0 : freq_a; + freq_b = (freq_b == afi->af[i])?0 : freq_b; + } +/* return early if there is nothing to do */ +if (freq_a ==0 && freq_b ==0 ) +return false; + +/* add the new AFs if they were previously unknown */ +if (freq_a !=0 ) { + afi->af[af_index] = freq_a; + af_index = (af_index+1 <MAX_TMC_AF_CNT )? af_index+1 :0 ; + afi->af_size++; + } +if (freq_b !=0 ) { + afi->af[af_index] = freq_b; + af_index = (af_index+1 <MAX_TMC_AF_CNT )? af_index+1 :0 ; + afi->af_size++; + } +/* update the information in the handle */ + afi->af_index = af_index; +if (afi->af_size >=MAX_TMC_AF_CNT ) + afi->af_size =MAX_TMC_AF_CNT ; + +return true; + } + +/* mapped frequency pair */ +else if (variant ==7 ) { +/* check the if there's already a frequency mapped to the new tuning +* frequency, update the mapped frequency in this case */ +for (int i =0 ; i < afi->mapped_af_size; i++) { +if (freq_a == afi->mapped_af_tuning[i]) + afi->mapped_af[i] = freq_b; +return true; + } +/* new pair is unknown, add it to the list */ +if (freq_a !=0 && freq_b !=0 ) { + mapped_af_index = (mapped_af_index+1 >=MAX_TMC_AF_CNT )?0 : mapped_af_index +1 ; + afi->mapped_af[mapped_af_index] = freq_b; + afi->mapped_af_tuning[mapped_af_index] = freq_a; + afi->mapped_af_size++; + } +/* update the information in the handle */ + afi->mapped_af_index = mapped_af_index; +if (afi->mapped_af_size >=MAX_TMC_AF_CNT ) + afi->mapped_af_size =MAX_TMC_AF_CNT ; + +return true; + } +return false; +} +/* adds one char of the ps name to temporal storage, the value is validated * if it is received twice in a row * @pos: position of the char within the PS name (0..7) @@ -968,13 +1120,12 @@ static uint32_t rds_decode_group8(struct rds_private_state *priv_state) !(grp->data_b_lsb & V4L2_TMC_TUNING_INFO)) {return rds_decode_tmc_multi_group (priv_state); } -/* -> tuning information message, defined for variants 4..9, submitted -* in bits 0-3 of block 2 */ +/* -> tuning information message, defined for variants 4..9, lib/libv4l2rds/libv4l2rds.c static uint32_t rds_decode_group8(struct rds_private_state *priv_state) · line 1131
Quoted 10 lines
lib/libv4l2rds/libv4l2rds.c +2
-2+* submitted in bits 0-3 of block 2 */ tuning_variant = grp->data_b_lsb &0x0f ;if ((grp->data_b_lsb &V4L2_TMC_TUNING_INFO ) && tuning_variant >=4 && tuning_variant <=9 ) { -/* TODO: Implement tuning information decoding */ -return 0 ; +return rds_decode_tmc_tuning (priv_state); }return 0 ;I suggest that we update
V4L2_RDS_VERSIONto 2.Unless I am mistaken we are now complete with regards to the RDS/TMC functionality and we can release the library officially.
It would still be nice if rds-ctl could decode the TMC messages to human readable text, though...
Regards,
Hans
RFC PATCH 4/4 rds-ctl.cpp: added functionality to print RDS-TMC tuning information
In reply to Konke Radlow--- utils/rds-ctl/rds-ctl.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-)
utils/rds-ctl/rds-ctl.cpp +27 -1
@@ -517,6 +517,30 @@ static void print_rds_tmc(const struct v4l2_rds *handle, uint32_t updated_fields } } +static void print_rds_tmc_tuning(const struct v4l2_rds *handle, uint32_t updated_fields) +{ + const struct v4l2_tmc_tuning *tuning = &handle->tmc.tuning; + const struct v4l2_tmc_station *station; + + if (updated_fields & V4L2_RDS_TMC_TUNING) { + printf("\nTMC Service provider: %s, %u alternative stations\n", handle->tmc.spn, tuning->station_cnt); + for (int i = 0; i < tuning->station_cnt; i++) { + station = &tuning->station[i]; + printf("PI(ON %02u) = %04x, AFs: %u, mapped AFs: %u \n", i, station->pi, + station->afi.af_size, station->afi.mapped_af_size); + for (int j = 0; j < station->afi.af_size; j++) + printf(" AF%02d: %.1fMHz\n", j, station->afi.af[j] / 1000000.0); + for (int k = 0; k < station->afi.mapped_af_size; k++) + printf(" m_AF%02d: %.1fMHz => %.1fMHz\n", k, + station->afi.mapped_af_tuning[k] / 1000000.0, + station->afi.mapped_af[k] / 1000000.0); + if (station->ltn != 0 || station->msg != 0 || station-> sid != 0) + printf(" ltn: %02x, msg: %02x, sid: %02x\n", station->ltn, + station->msg, station->sid); + } + } +} + static void print_rds_statistics(const struct v4l2_rds_statistics *statistics) { printf("\n\nRDS Statistics: \n");
@@ -636,8 +660,10 @@ static void print_rds_data(const struct v4l2_rds *handle, uint32_t updated_field print_rds_af(&handle->rds_af); if (params.options[OptPrintBlock]) printf("\n"); - if (params.options[OptTMC]) + if (params.options[OptTMC]) { print_rds_tmc(handle, updated_fields); + print_rds_tmc_tuning(handle, updated_fields); + } } static void read_rds(struct v4l2_rds *handle, const int fd, const int wait_limit)
Signature · 1 line
1.7.10.4