/*************************************************************************/
/**
\file hpirds.h

RDS analysis functions

Copyright (C) 1997-2019 AudioScience, Inc. All rights reserved.

This software is provided 'as-is', without any express or implied warranty.
In no event will AudioScience Inc. be held liable for any damages arising
from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This copyright notice and list of conditions may not be altered or removed
   from any source distribution.

AudioScience, Inc. <support@audioscience.com>

( This license is GPL compatible see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses )

*/

#ifndef _HPI_RDS_H_
#define _HPI_RDS_H_

/** \addtogroup mixer_controls
@{
*/
/** \defgroup rds RDS analysis functions

\section rdsIntro HPIRDS Introduction

The RDS analysis functions outlined below constitute an extendable set of functions
that can be used to extract RDS information from a "raw" RDS bitstream. The idea is that
after creating an HPIRDS object, groups of RDS bits are sent to the HPIRDS object for
decoding. The bits are decoded into the supported RDS bit fields and characters. RDS strings
are built internally in the HPIRDS object and may be retrieved at any time.

The user does not have to do anything at all with the raw RDS data except to pass it from the
tuner to the analyzer. New RDS data is output at a rate of one data block every 87 ms. The AudioScience
ASI8920 has on-board buffering that can hold up to 2 seconds of RDS data per tuner.

\image html rds_buffers.png

The recommended practice is for an application to poll each tuner every second and extract
and analyze all the RDS data in the data buffer.

The following steps should be performed to analyze RDS data.

\subsection rdsStartup Startup

Create an RDS analyzer using HPI_RDS_Create(). One analyzer should be created per stream of
RDS data to analyze. This implies that HPI_RDS_Create() would be called on a per tuner basis.

Below is a code snippet to create an HPIRDS object.

\code
	HPI_RDS_HANDLE h;
	h=HPI_RDS_Create(HPI_RDS_DATATYPE_RBDS);
\endcode

\subsection rdsRunning Running

The following code sequence should be performed every second.

-# Obtain RDS data from the tuner and call HPI_RDS_AnalyzeGroup(). This processes the RDS data and
fills out internal data structures.
-# Display or log RDS fields of interest by calling HPI_RDS_Get_XXXX() functions.
-# Repeat 1 until a call to HPI_Tuner_GetRDS() returns HPI_ERROR_BUFFER_EMPTY. If HPI_Tuner_GetRDS() succeeds it will return 0.

The following code shows how to call the analyze function and then shows how to query various
RDS fields.

\code
	enum eHPI_RDS_errors eRDSerror;
	int nGroupType;
	char cGroupVersion;
	char *szPS;
	char *szRT;
	char *szPTY;
	unsigned short nPI,nPTY;
	char rds[12];
	HW16 wHE=0;

	while(1)	// run the following loop until all data has been read
	{
		// Use HPI Tuner call to fill in rds structure with valid bits from the tuner.
		wHE = HPI_Tuner_GetRDS(hSubSys, hTunerControl, rds);
		if(wHE)
			break;	// break out of the loop if there is no data available

		// Call analyze function to analyze the RDS bits.
		eRDSerror = HPI_RDS_AnalyzeGroup(h, rds, sizeof(rds));
		if(eRDSerror!=HPI_RDS_ERROR_NOERROR)
			printf("HPIRDS error #%d\n",(int)eRDSerror);

		// Retrieve current RDS settings.
		nGroupType = HPI_RDS_Get_GroupType(h);
		cGroupVersion = HPI_RDS_Get_GroupVersion(h);
		szPS = HPI_RDS_Get_PS(h);	// call HPI_RDS_Get_PS_Ready() to check if a new or updated PS text is 'ready'.
		szRT = HPI_RDS_Get_RT(h);	// call HPI_RDS_Get_RT_Ready() to check if a new or updated radio text is 'ready'.
		nPI = HPI_RDS_Get_PI(h);
		nPTY = HPI_RDS_Get_PTY(h);
		szPTY = HPI_RDS_Get_PTY_Text(h);
	}
\endcode

\subsection rdsShutdown Shutdown

Destroy the RDS object by calling HPI_RDS_Delete().

\section rdsErrors HPIRDS Error handling

Occasionally there may be errors in received RDS blocks. The error counts for each of the 4 16-bit RDS blocks is
appended to the end of the RDS data that is returned from a call to HPI_Tuner_GetRDS(). The HPI_RDS_AnalyzeGroup()
function checks the error counts of each block before processing RDS data from that block. Error counts less than 6 indicate that there were
correctable bit errors in the data. An error count of 6+ indicates there are non correctable errors and in that case
a call to HPI_RDS_AnalyzeGroup() will return HPI_RDS_ERROR_BLOCK_DATA. Even if HPI_RDS_AnalyzeGroup() returns HPI_RDS_ERROR_BLOCK_DATA
blocks with correctable errors have been processed the internal state of the decoder updated accordingly.

The impact of RDS errors depends on the type of group in which the error occurs. Sometimes blocks with correctable errors result in
temporary incorrect text or RDS metadata. When this occurs, the next RDS data group of the same type carrying correct information
will rectify the output of the decoder.

\section rdsFields HPIRDS Supported fields

\subsection supportPS PS

Program service name. Sometimes called "Dynamic" Program Service. The name of the station. This is an 8 character name
(plus 1 character for the zero terminator) returned by a call to HPI_RDS_Get_PS().

\subsection supportRT RT

Radio text, which typically shows the name of the song. This is a 64 character string (plus 1 character for the zero terminator) returned
by a call to HPI_RDS_Get_RT().

\section rdsGroups HPIRDS Supported groups

This section lists the group numbers and types currently supported by this code. As different group types become a requirement,
they can be added to the code in hpirds.c. The list below is obviously a subset of the total of 16 groups that
could potentially be supported. Whenever an attempt is made to analyze an unsupported group, the error \ref HPI_RDS_ERROR_UNKNOWN_GROUP
will be returned.

Note that we have concatinated the
number (0-15) and the type (A or B) together in the list below.

<table border=1 cellspacing=0 cellpadding=5>
<tr>
<td><p><b>Group</b></p></td>
<td><p><b>Description</b></p></td>
</tr>
<tr>
<td>0A</td>
<td>Basic tuning and switching information</td>
</tr>
<tr>
<td>0B</td>
<td>Basic tuning and switching information</td>
</tr>
<tr>
<td>2A</td>
<td>Radio text</td>
</tr>
</table>

@{
*/

#include "hpi.h"

#ifdef __cplusplus
extern "C" {
#endif				// __cplusplus

struct HPI_RDS_DATA;	///< RDS data structure.
typedef struct HPI_RDS_DATA *HPI_RDS_HANDLE;	///< Handle to an RDS data structure.
typedef char *HPI_RDS_STRING;	///< Pointer to a string.

/** Raw RDS data. This data structure is used to pass data into the RDS
  * analysis routines. The terms "group" and "block", used below, are as defined in the EDS
  * specification. It makes the following assumptions.<br>
<ul>
<li>Data in the group is error free.</li>
<li>The data is organized as a single group, containing 4 blocks, each of 16 bits.
</ul>
  */
typedef struct {
		unsigned short g[4];	///< The bits stored in an array of 4 16 bit blocks.
	} HPI_RDS_GROUP;

/** Error codes returned from HPI_RDS_AnalyzeGroup().
  */
enum eHPI_RDS_errors {
		HPI_RDS_ERROR_NOERROR = 0,	///< No error.
		HPI_RDS_ERROR_UNKNOWN_GROUP = 1,/**< Unknown group. The RDS data belonged to a
		group that the RDS analyzer does not currently know how to analyze. To fix this
		error, code for handling additional groups should be added to hpirds.c. This error does
		not imply that RDS is not working properly. It just means that the station transmitted
		an RDS group of a type that the HPI_RDS_AnanlyzeGroup() function does not know how to
		handle.*/
		HPI_RDS_ERROR_INVALID_DATASIZE = 2,	///< Invalid datasize.
		HPI_RDS_ERROR_BLOCK_DATA = 3,	///< Unrecoverable data error in one of the data blocks.
		HPI_RDS_ERROR_HANDLE = 4	///< Bad object handle.
	};


/*--------------------------------------------------------------------------------------*/
/** Create an HPIRDS instance. One of these should be created per tuner in the system.
  * \return Returns an allocated HPIRDS handle.
  */
HPI_API (HPI_RDS_HANDLE) HPI_RDS_Create(
	enum eHPI_RDS_type eType	///< The type of bitstream thsi module will handle.
);

/*--------------------------------------------------------------------------------------*/
/** Delete an HPIRDS instance.
  */
HPI_API_VOID(void) HPI_RDS_Delete(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Clears internal HPIRDS data structures. Applications should call this after changing
  * the tuner frequency.
  */
HPI_API_VOID(void) HPI_RDS_Clear(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);

/*--------------------------------------------------------------------------------------*/
/** Analyze a block of RDS data. This function should be called with a group of
  * RDS data. Here a group refers to an array of 4 16-bit unsigned shorts that
  * contain a complete Group of RDS bits. This function analyzes the passed in
  * data and fills in various internal fields that can then be extracted with
  * calls to HPI_RDS_Get_xxxx() functions.
  *
  * \param h A previously allocated HPIRDS handle.
  * \param pData Pointer to raw RDS data. This data structure is used to pass data into the RDS
  * analysis routines. The terms "group" and "block", used below, are as defined in the EDS
  * speficiation. It makes the following assumptions.<br>
<ul>
<li>Data in the pData array is error free.</li>
<li>The data is organized as a single group, containing 4 blocks, each of 16 bits, making a total of 8 bytes.
</ul>
  * \param nDataSize The size of the bitstream data array. For RDS and RDBS this should be 8 bytes.
  * \return Returns an error code. See \ref eHPI_RDS_errors.
  */
HPI_API (enum eHPI_RDS_errors) HPI_RDS_AnalyzeGroup(
	HPI_RDS_HANDLE h,
	const char *pData,
	const unsigned int nDataSize
);
/*--------------------------------------------------------------------------------------*/
/** Get error counts per block. This function should be called if HPI_RDS_AnalyzeGroup()
  * returns HPI_RDS_ERROR_BLOCK_DATA and the client wants to konw exactly which of the 4
  * RDS blocks contained un-correctable errors. It can also be called anytime after a call
  * HPI_RDS_AnalyzeGroup() to monitor error counts.
  * \return Returns an error code.
  */
HPI_API (enum eHPI_RDS_errors) HPI_RDS_Get_BlockErrorCounts(
	HPI_RDS_HANDLE h,	///< A previously allocated HPIRDS handle.
	unsigned int *uBlock0,	///< Error count for Block0. 0xff indicates uncorrectable errors.
	unsigned int *uBlock1,	///< Error count for Block1. 0xff indicates uncorrectable errors.
	unsigned int *uBlock2,	///< Error count for Block2. 0xff indicates uncorrectable errors.
	unsigned int *uBlock3	///< Error count for Block3. 0xff indicates uncorrectable errors.
);
/*--------------------------------------------------------------------------------------*/
/** Get the group type. The group type is a number between 0 and 15 and indicates
  * the type of information that the last group processed contains.
  * \return The returns group type.
  */
HPI_API (unsigned int) HPI_RDS_Get_GroupType(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Get RDS type.
  * \return Returns the type of bitstream this module handles.
  */
HPI_API (enum eHPI_RDS_type) HPI_RDS_Get_RdsType(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Set RDS type.
  * \return Returns the previous value of bitstream type.
  */
HPI_API (enum eHPI_RDS_type) HPI_RDS_Set_RdsType(
	HPI_RDS_HANDLE h,	///< A previously allocated HPIRDS handle.
	enum eHPI_RDS_type eType	///< The type of bitstream this module will handle.
);
/*--------------------------------------------------------------------------------------*/
/** Get the group verion.
  * \return The returns group version returned is either 'A' or 'B'.
  */
HPI_API (char) HPI_RDS_Get_GroupVersion(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Get PS (program service) name ready flag. This is set to 1 when a new complete PS string data
  * is available or has been updated. Reading the PS string using HPI_RDS_Get_PS() resets this flag.
  * \return A boolean value. 1 indicates new PS data is available, 0 indicates no new PS data.
  */
HPI_API (unsigned int) HPI_RDS_Get_PS_Ready(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Get PS (program service) name. This is the name of the station and can be up to 8
  * characters long (plus 1 character for the zero terminator). Calling this function resets the PS_Ready flag.
  * \return A pointer to the zero-terminated read-only PS string.
  */
HPI_API (HPI_RDS_STRING) HPI_RDS_Get_PS(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Sets the RT (Radio Test) good character threshold.
  * Sometimes "bad" characters can end up in the RT string buffer. This variable defines how
  * many times each each character in the buffer should be received before the RT buffer is
  * considered good. An independant count is maintained for each character in the buffer.
  * The count reset to one each time a change in character is detected.
  */
HPI_API_VOID (void) HPI_RDS_Set_RT_Threshold(
	HPI_RDS_HANDLE h,	///< A previously allocated HPIRDS handle.
	unsigned int nCount	/**< Number of times each character must be received in an RT
				message before the message is considered "good". By defaul this
				number is set to 1.
				*/
);
/*--------------------------------------------------------------------------------------*/
/** Get RT (Radio Text) ready flag. This is set to 1 when a new complete RT string data
  * is available or has been updated. Reading the RT string using HPI_RDS_Get_RT() resets this flag.
  * \return A boolean value. 1 indicates new RT data is available, 0 indicates no new RT data.
  */
HPI_API (unsigned int) HPI_RDS_Get_RT_Ready(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);
/*--------------------------------------------------------------------------------------*/
/** Get RT (Radio Text). This is typically the title of the playing song and can be up to
  * 64 characters long (plus 1 character for the zero terminator). Calling this function resets the RT_Ready flag.
  * \return A pointer to the zero-terminated read-only RT string.
  */
HPI_API (HPI_RDS_STRING) HPI_RDS_Get_RT(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);

/*--------------------------------------------------------------------------------------*/
/** Get PI (Program Identification). PI is a two byte number which identifies the country,
  * coverage area and service. It is not normally intended for display. A change in PI code
  * causes the initialisation  of all RDS data as it indicates that the radio has been retuned.
  * This application also facilitates the display of the current PI code.
  *
  * \return A pointer to the RT string.
  */
HPI_API (unsigned short) HPI_RDS_Get_PI(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);

/*--------------------------------------------------------------------------------------*/
/** Get PTY (Program Type). PTY is a 5-bit number which indicates the current program type.
 * Examples include "no programme type", "Current affairs"
 * and "Pop music". See below table.

<table border=1 cellspacing=0 cellpadding=5>
<tr>
<td><p><b>PTY code</b></p></td>
<td><p><b>RDS Program type (EU)</b></p></td>
<td><p><b>RBDS Program type (USA)</b></p></td>
</tr>
<tr><td>0</td><td>No program type or undefined</td><td>No program type or undefined</td></tr>
<tr><td>1</td><td>News</td><td>News</td></tr>
<tr><td>2</td><td>Current affairs</td><td>Information</td></tr>
<tr><td>3</td><td>Information</td><td>Sports</td></tr>
<tr><td>4</td><td>Sport</td><td>Talk</td></tr>
<tr><td>5</td><td>Education</td><td>Rock</td></tr>
<tr><td>6</td><td>Drama</td><td>Classic Rock</td></tr>
<tr><td>7</td><td>Culture</td><td>Adult Hits</td></tr>
<tr><td>8</td><td>Science</td><td>Soft Rock</td></tr>
<tr><td>9</td><td>Varied</td><td>Top 40</td></tr>
<tr><td>10</td><td>Pop Music</td><td>Country</td></tr>
<tr><td>11</td><td>Rock Music</td><td>Oldies</td></tr>
<tr><td>12</td><td>M.O.R. Music</td><td>Soft</td></tr>
<tr><td>13</td><td>Light classical</td><td>Nostalgia</td></tr>
<tr><td>14</td><td>Serious classical</td><td>Jazz</td></tr>
<tr><td>15</td><td>Other Music</td><td>Classical</td></tr>
<tr><td>16</td><td>Weather</td><td>Rhythm and Blues</td></tr>
<tr><td>17</td><td>Finance</td><td>Soft Rhythm and Blues</td></tr>
<tr><td>18</td><td>Childrens programmes</td><td>Language</td></tr>
<tr><td>19</td><td>Social Affairs</td><td>Religious Music</td></tr>
<tr><td>20</td><td>Religion</td><td>Religious Talk</td></tr>
<tr><td>21</td><td>Phone In</td><td>Personality</td></tr>
<tr><td>22</td><td>Travel</td><td>Public</td></tr>
<tr><td>23</td><td>Leisure</td><td>College</td></tr>
<tr><td>24</td><td>Jazz Music</td><td>Unassigned</td></tr>
<tr><td>25</td><td>Country Music</td><td>Unassigned</td></tr>
<tr><td>26</td><td>National Music</td><td>Unassigned</td></tr>
<tr><td>27</td><td>Oldies Music</td><td>Unassigned</td></tr>
<tr><td>28</td><td>Folk Music</td><td>Unassigned</td></tr>
<tr><td>29</td><td>Documentary</td><td>Weather</td></tr>
<tr><td>30</td><td>Alarm Test</td><td>Emergency Test</td></tr>
<tr><td>31</td><td>Alarm</td><td>Emergency</td></tr>
</table>

  * \return A pointer to the PTY string.
  */
HPI_API (uint8_t) HPI_RDS_Get_PTY(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);

/*--------------------------------------------------------------------------------------*/
/** Get PTY (Program Type) text given PTY number and data type.
  * \return Pointer to the zero-terminated read-only Program Type string.
  */
HPI_API (HPI_RDS_STRING) HPI_RDS_Get_PTY_Translate(
	enum eHPI_RDS_type eType, ///< Data type.
	unsigned int uPTY	///< PTY code.
);
/*--------------------------------------------------------------------------------------*/
/** Get PTY string. Translates the Program Type code returned by HPI_RDS_Get_PTY() into a
  * string.
  *
  * \return Pointer to the zero-terminated read-only Program Type string.
  */
HPI_API (HPI_RDS_STRING) HPI_RDS_Get_PTY_Text(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);

/*--------------------------------------------------------------------------------------*/
/** Get TP (Traffic Program). The traffic program code indicates that the station will
  * broadcast traffic announcements from time to time.
  *
  * \return Either 1 or 0.
  */
HPI_API (unsigned int) HPI_RDS_Get_TP(
	HPI_RDS_HANDLE h	///< A previously allocated HPIRDS handle.
);

/*@}*/
/*@}*/
#ifdef __cplusplus
}
#endif				// __cplusplus
#endif				// #ifndef _HPI_RDS_H_
