コード例 #1
0
        /// <summary>
        /// Processes a 2B "RadioText" frame type 0b00101
        /// </summary>
        /// <param name="frame"></param>
        private void ProcessFramePayload_RadioTextB(RDSFrame frame)
        {
            //Read the header flags
            bool flagAb = 1 == ((frame.b >> (HEADER_OFFSET - 0)) & 0b0000000000000001);

            //Get the index and multiply it by 2, as that is the spacing
            int addressIndex = (frame.b & 0b0000000000001111) * 2;

            //Check if we should reset
            if (flagAb != _rtLastAbFlag && addressIndex == 0)
            {
                //Clear buffer
                for (int i = 0; i < rtBuffer.Length; i++)
                {
                    rtBuffer[i] = (char)0x00;
                }

                //Update state
                _rtLastAbFlag = flagAb;

                //Send event
                OnRtBufferCleared?.Invoke(this);
            }

            //Write all
            rtBuffer[addressIndex + 0] = (char)((frame.d >> 8) & 0x00FF);
            rtBuffer[addressIndex + 1] = (char)((frame.d >> 0) & 0x00FF);

            //Search this string to see if we reached the end
            int endIndex = -1;

            for (int i = addressIndex; i < addressIndex + 4; i++)
            {
                if (rtBuffer[i] == (char)0x0A)
                {
                    endIndex = i;
                    break;
                }
            }

            //Send buffer updated events
            OnRtBufferUpdated?.Invoke(this, rtBuffer);

            //Handle ending if we did
            if (endIndex != -1)
            {
                //Convert to string and update state
                rtText     = new string(rtBuffer, 0, endIndex);
                rtComplete = true;

                //Send event
                OnRtTextUpdated?.Invoke(this, rtText);
            }
        }
コード例 #2
0
        public void ProcessFrame(RDSFrame frame)
        {
            //Get the PI code from the first block. We flip this because that's how it's familiar
            ushort piCode = (ushort)((frame.a >> 8) | (frame.a << 8));

            //Decode header data that will always exist
            byte groupTypeCode    = (byte)((frame.b >> (16 - 5)) & 0b0000000000011111);
            bool trafficSupported = 1 == ((frame.b >> (16 - 6)) & 0b0000000000000001);
            byte programTypeCode  = (byte)((frame.b >> (16 - 11)) & 0b0000000000011111);

            //Send updated events for these
            if (this.piCode != piCode)
            {
                this.piCode = piCode;
                OnPiCodeUpdated?.Invoke(this, piCode);
            }
            if (this.trafficSupported != trafficSupported)
            {
                this.trafficSupported = trafficSupported;
                OnTrafficSupportedUpdated?.Invoke(this, trafficSupported);
            }
            if (this.programTypeCode != programTypeCode)
            {
                this.programTypeCode = programTypeCode;
                OnProgramTypeUpdated?.Invoke(this, programTypeCode);
            }

            //We'll now handle decoding of this frame
            switch (groupTypeCode)
            {
            case 0b00000: ProcessFramePayload_BasicInfo(frame); break;

            case 0b00001: ProcessFramePayload_BasicInfo(frame); break;

            case 0b00100: ProcessFramePayload_RadioTextA(frame); break;

            case 0b00101: ProcessFramePayload_RadioTextB(frame); break;

            case 0b01000: ProcessFramePayload_ClockTime(frame); break;
            }
        }
コード例 #3
0
        /// <summary>
        /// Processes 0A and 0B "basic tuning and switching" frame types 0b00000 and 0b00001
        /// </summary>
        private void ProcessFramePayload_BasicInfo(RDSFrame frame)
        {
            //Read the header flags
            bool flagTa = 1 == ((frame.b >> (HEADER_OFFSET - 0)) & 0b0000000000000001);
            bool flagMs = 1 == ((frame.b >> (HEADER_OFFSET - 1)) & 0b0000000000000001);
            bool flagDi = 1 == ((frame.b >> (HEADER_OFFSET - 2)) & 0b0000000000000001);

            //Get the code bits. These are just used for the text for the most part just for the PS name
            byte decoderControlCode = (byte)((frame.b >> (HEADER_OFFSET - 4)) & 0b0000000000000011);

            //Get the two PS code characters
            char psA = (char)((frame.d >> 8) & 0x00FF);
            char psB = (char)((frame.d >> 0) & 0x00FF);

            //Write to the PS buffer
            psBuffer[(decoderControlCode * 2) + 0] = psA;
            psBuffer[(decoderControlCode * 2) + 1] = psB;

            //Set flags for the characters updated if they're in sequential order
            if (decoderControlCode == psUpdatedFrameIndex)
            {
                psUpdatedFrameIndex++;
            }

            //Check if we've completed it
            if (psUpdatedFrameIndex == 4)
            {
                //Completed!
                //Update state
                psName              = new string(psBuffer);
                psComplete          = true;
                psUpdatedFrameIndex = 0;

                //Fire events
                OnPsNameUpdated?.Invoke(this, psName);
            }

            //Send updated events
            OnPsBufferUpdated?.Invoke(this, psBuffer);
        }
コード例 #4
0
        /// <summary>
        /// Processes a 4A "Clock-time and date" frame 0b01000
        /// </summary>
        private void ProcessFramePayload_ClockTime(RDSFrame frame)
        {
            //Read julian day code
            uint dayCode = (uint)((frame.c >> 1) & 0b0111111111111111);

            dayCode |= (uint)(((frame.b >> (HEADER_OFFSET - 4)) & 0b0000000000000011) << 15);

            //Decode day code according to Annex G of the linked document
            //This is in UTC
            int year  = (int)((dayCode - 15078.2f) / 365.25f);
            int month = (int)((dayCode - 14956.1f - (int)(year * 365.25f)) / 30.6001f);
            int day   = (int)dayCode - 14956 - ((int)(year * 365.25f)) - ((int)(month * 30.6001f));
            int k;

            if (month == 14 || month == 15)
            {
                k = 1;
            }
            else
            {
                k = 0;
            }
            year  += 1900 + k;
            month -= 1 - (k * 12);

            //Get the hour
            int localHour = ((frame.d >> 12) & 0b0000000000001111);

            localHour |= ((frame.c & 0b0000000000000001) << 4);

            //Get the minute
            int localMinute = ((frame.d >> 6) & 0b0000000000111111);

            //Get the local time offset, in half hours. Also calculate the sign
            int localOffsetParts = (frame.d & 0b0000000000011111);

            if ((frame.d & 0b0000000000100000) != 0)
            {
                localOffsetParts = -localOffsetParts;
            }

            DateTime time;
            TimeSpan localOffset;
            DateTime localTime;

            try
            {
                //Create a UTC DateTime from the UTC portion
                time = new DateTime(year, month, day, 0, 0, 0, 0, DateTimeKind.Local);

                //Convert the local hour and minute to a TimeSpan we'll save
                localOffset = new TimeSpan(0, 30 * localOffsetParts, 0);

                //Convert to local time
                localTime = time.AddMinutes((localHour * 60) + (localOffsetParts * 30) + localMinute);
            } catch
            {
                //Ignore. If this was a corrupted RDS frame, this would've thrown an exception
                return;
            }

            //Set
            this.timeLast     = localTime;
            this.timeOffset   = localOffset;
            this.timeComplete = true;

            //Send event
            OnTimeUpdated?.Invoke(this, localTime, localOffset);
        }
コード例 #5
0
        /// <summary>
        /// Processes a 2A "RadioText" frame type 0b00100
        /// </summary>
        /// <param name="frame"></param>
        private void ProcessFramePayload_RadioTextA(RDSFrame frame)
        {
            //Read the header flags
            bool flagAb = 1 == ((frame.b >> (HEADER_OFFSET - 0)) & 0b0000000000000001);

            //Get the index and multiply it by 4, as that is the spacing
            int addressIndex = (frame.b & 0b0000000000001111) * 4;

            //Check if we should reset
            if (flagAb != _rtLastAbFlag && addressIndex == 0)
            {
                //Clear buffer
                for (int i = 0; i < rtBuffer.Length; i++)
                {
                    rtBuffer[i] = (char)0x00;
                }

                //Update state
                _rtLastAbFlag = flagAb;

                //Send event
                OnRtBufferCleared?.Invoke(this);
            }

            //Write all
            rtBuffer[addressIndex + 0] = (char)((frame.c >> 8) & 0x00FF);
            rtBuffer[addressIndex + 1] = (char)((frame.c >> 0) & 0x00FF);
            rtBuffer[addressIndex + 2] = (char)((frame.d >> 8) & 0x00FF);
            rtBuffer[addressIndex + 3] = (char)((frame.d >> 0) & 0x00FF);

            //Search this string to see if we reached the end
            int endIndex = -1;

            for (int i = addressIndex; i < addressIndex + 4; i++)
            {
                //Official spec claims that the character 0x0A should be used to indicate the end, but some stations in my area don't do that.
                //We also check \r for this reason. Cumulus Media stations are using \r instead.
                if (rtBuffer[i] == (char)0x0A || rtBuffer[i] == '\r')
                {
                    endIndex = i;
                    break;
                }
            }

            //Send buffer updated events
            OnRtBufferUpdated?.Invoke(this, rtBuffer);

            //Handle ending if we did
            if (endIndex != -1)
            {
                //Make sure this is really the end. We should have no null characters up to this point
                for (int i = 0; i < endIndex; i++)
                {
                    if (rtBuffer[i] == (char)0x00)
                    {
                        return;
                    }
                }

                //Convert to string and update state
                rtText     = new string(rtBuffer, 0, endIndex);
                rtComplete = true;

                //Send event
                OnRtTextUpdated?.Invoke(this, rtText);
            }
        }