radioapp/radiouiengine/src/radiostation.cpp
branchRCL_3
changeset 20 93c594350b9a
parent 19 cce62ebc198e
equal deleted inserted replaced
19:cce62ebc198e 20:93c594350b9a
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 // System includes
       
    19 #include <QTime>
       
    20 
       
    21 // User includes
       
    22 #include "radiostation.h"
       
    23 #include "radiostation_p.h"
       
    24 #include "radiologger.h"
       
    25 #include "radio_global.h"
       
    26 
       
    27 // Constants
       
    28 const QLatin1String HTML_TAG_START( "<font color='cyan'><u>" );
       
    29 const QLatin1String HTML_TAG_END( "</u></font>" );
       
    30 
       
    31 const uint LAST_CALLSIGN_CHAR_CODE = 25;
       
    32 const uint THREE_LETTER_CALLSIGN_COUNT = 72;
       
    33 const uint KXXX_CALLSIGN_PI_FIRST = 0x1000;
       
    34 const uint WXXX_CALLSIGN_PI_FIRST = 0x54A8;
       
    35 const uint WXXX_CALLSIGN_PI_LAST = 0x994F;
       
    36 const uint XXX_CALLSIGN_PI_FIRST = 0x9950;
       
    37 const uint XXX_CALLSIGN_PI_LAST = 0x99B9;
       
    38 
       
    39 const char* CALLSIGN_TABLE[THREE_LETTER_CALLSIGN_COUNT] =
       
    40    {"KBW", "KCY", "KDB", "KDF", "KEX", "KFH","KFI","KGA","KGB",
       
    41     "KGO", "KGU", "KGW", "KGY", "KHQ", "KID", "KIT", "KJR", "KLO",
       
    42     "KLZ", "KMA", "KMJ", "KNX", "KOA", "KOB", "KOY", "KPQ", "KQV",
       
    43     "KSD", "KSL", "KUJ", "KUT", "KVI", "KWG", "KXL", "KXO", "KYW",
       
    44     "WBT", "WBZ", "WDZ", "WEW", "WGH", "WGL", "WGN", "WGR", "WGY",
       
    45     "WHA", "WHB", "WHK", "WHO", "WHP", "WIL", "WIP", "WIS", "WJR",
       
    46     "WJW", "WJZ", "WKY", "WLS", "WLW", "WMC", "WMT", "WOC", "WOI",
       
    47     "WOL", "WOR", "WOW", "WRC", "WRR", "WSB", "WSM", "WWJ", "WWL"};
       
    48 
       
    49 const uint PI_CODE_TABLE[THREE_LETTER_CALLSIGN_COUNT] =
       
    50    {0x99A5, 0x99A6, 0x9990, 0x99A7, 0x9950, 0x9951, 0x9952, 0x9953,
       
    51     0x9991, 0x9954, 0x9955, 0x9956, 0x9957, 0x99AA, 0x9958, 0x9959,
       
    52     0x995A, 0x995B, 0x995C, 0x995D, 0x995E, 0x995F, 0x9960, 0x99AB,
       
    53     0x9992, 0x9993, 0x9964, 0x9994, 0x9965, 0x9966, 0x9995, 0x9967,
       
    54     0x9968, 0x9996, 0x9997, 0x996B, 0x9999, 0x996D, 0x996E, 0x996F,
       
    55     0x999A, 0x9971, 0x9972, 0x9973, 0x999B, 0x9975, 0x9976, 0x9977,
       
    56     0x9978, 0x999C, 0x999D, 0x997A, 0x99B3, 0x997B, 0x99B4, 0x99B5,
       
    57     0x997C, 0x997D, 0x997E, 0x999E, 0x999F, 0x9981, 0x99A0, 0x9983,
       
    58     0x9984, 0x99A1, 0x99B9, 0x99A2, 0x99A3, 0x99A4, 0x9988, 0x9989};
       
    59 
       
    60 const uint DISABLE_LOCAL_AREA_COVERAGE_MASK = 0x0800;
       
    61 
       
    62 const int PS_NAME_CHANGE_THRESHOLD_SECONDS = 10;
       
    63 
       
    64 // Macros to help protect shared null and manual station instances to help debugging
       
    65 // Enabled in emulator or win32 builds by default
       
    66 #if defined __WINS__ || defined BUILD_WIN32
       
    67 #   define PROTECT_SHAREDNULL_AND_MANUALSTATION
       
    68 #endif
       
    69 
       
    70 #ifdef PROTECT_SHAREDNULL_AND_MANUALSTATION
       
    71 #   define ASSERT_SHARED_NULL_IS_INTACT \
       
    72         Q_ASSERT_X( mData->mPresetIndex != SharedNull, "RadioStation", "Shared null modified illegally!" );
       
    73 #   define ASSERT_MANUAL_STATION \
       
    74         Q_ASSERT_X( mData->mPresetIndex != ManualStation, "RadioStation", "Illegally modifying manual station" );
       
    75 #else
       
    76 #   define ASSERT_SHARED_NULL_IS_INTACT
       
    77 #   define ASSERT_MANUAL_STATION
       
    78 #endif // PROTECT_SHAREDNULL_AND_MANUALSTATION
       
    79 
       
    80 /**
       
    81  * Static shared data instance that is used by all default-constructed RadioStation instances
       
    82  */
       
    83 static RadioStationPrivate shared_null( RadioStation::SharedNull, 0 );
       
    84 
       
    85 /*!
       
    86  *
       
    87  */
       
    88 QString RadioStation::parseFrequency( uint frequency )
       
    89 {
       
    90     QString freqString;
       
    91     freqString.sprintf( "%.1f", qreal( frequency ) / FREQUENCY_MULTIPLIER );
       
    92     return freqString;
       
    93 }
       
    94 
       
    95 /*!
       
    96  *
       
    97  */
       
    98 RadioStation::RadioStation() :
       
    99     mData( &shared_null )
       
   100 {
       
   101 }
       
   102 
       
   103 /*!
       
   104  *
       
   105  */
       
   106 RadioStation::RadioStation( const RadioStation& other ) :
       
   107     mData( other.mData )
       
   108 {
       
   109 }
       
   110 
       
   111 /*!
       
   112  *
       
   113  */
       
   114 RadioStation::RadioStation( int presetIndex, uint frequency ) :
       
   115     mData( new RadioStationPrivate( presetIndex, frequency ) )
       
   116 {
       
   117 }
       
   118 
       
   119 /*!
       
   120  *
       
   121  */
       
   122 RadioStation::~RadioStation()
       
   123 {
       
   124 }
       
   125 
       
   126 /*!
       
   127  *
       
   128  */
       
   129 RadioStation& RadioStation::operator=( const RadioStation& other )
       
   130 {
       
   131     mData = other.mData;
       
   132     return *this;
       
   133 }
       
   134 
       
   135 /*!
       
   136  *
       
   137  */
       
   138 void RadioStation::reset()
       
   139 {
       
   140     mData = &shared_null;
       
   141     ASSERT_SHARED_NULL_IS_INTACT
       
   142 }
       
   143 
       
   144 /*!
       
   145  *
       
   146  */
       
   147 void RadioStation::setChangeFlags( RadioStation::Change flags )
       
   148 {
       
   149     if ( mData->mChangeFlags != flags ) {
       
   150         mData->mChangeFlags = flags;
       
   151     }
       
   152 }
       
   153 
       
   154 /*!
       
   155  *
       
   156  */
       
   157 void RadioStation::setPresetIndex( int presetIndex )
       
   158 {
       
   159     if ( mData->mPresetIndex != presetIndex ) {
       
   160         ASSERT_MANUAL_STATION
       
   161         mData->mPresetIndex = presetIndex;
       
   162         mData->mChangeFlags |= RadioStation::PersistentDataChanged;
       
   163     }
       
   164 }
       
   165 
       
   166 /*!
       
   167  *
       
   168  */
       
   169 void RadioStation::setFrequency( uint frequency )
       
   170 {
       
   171     if ( mData->mFrequency != frequency ) {
       
   172         mData->mFrequency = frequency;
       
   173         mData->mChangeFlags |= RadioStation::PersistentDataChanged;
       
   174         ASSERT_SHARED_NULL_IS_INTACT
       
   175     }
       
   176 }
       
   177 
       
   178 /*!
       
   179  * Sets the preset name
       
   180  */
       
   181 void RadioStation::setName( const QString& name )
       
   182 {
       
   183     // Name emptiness is checked because this name setter is used by incoming RDS PS name
       
   184     // and empty names should be ignored
       
   185     if ( !name.isEmpty() && !mData->isRenamedByUser() && mData->mName.compare( name ) != 0 ) {
       
   186         mData->mName = name.trimmed();
       
   187         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged;
       
   188         mData->setStationHasSentRds( true );
       
   189 
       
   190         // Save the time when PS name changed and if the last change was too close to the current time
       
   191         // change the PS type to dynamic if it has already been incorrectly determined to be static.
       
   192         QTime previousChange = mData->mLastPsNameChangeTime;
       
   193         mData->mLastPsNameChangeTime = QTime::currentTime();
       
   194         if ( previousChange.isValid() && mData->mPsType == RadioStation::Static &&
       
   195              previousChange.secsTo( mData->mLastPsNameChangeTime ) < PS_NAME_CHANGE_THRESHOLD_SECONDS ) {
       
   196             LOG( "Station changed PS name too often. PS type changed to Dynamic" );
       
   197             mData->mPsType = RadioStation::Dynamic;
       
   198             mData->mDynamicPsText = mData->mName;
       
   199             mData->mName = "";
       
   200             mData->mChangeFlags |= RadioStation::PsTypeChanged | RadioStation::DynamicPsChanged;
       
   201             mData->setCallSignCheckDone( false );
       
   202         }
       
   203 
       
   204         //TODO: This is a temporary thing to see some URL. Remove this
       
   205         if ( !mData->mName.isEmpty() ) {
       
   206             QString url = mData->mName.toLower().remove( " " );
       
   207             mData->mUrl = "http://www." + url + ".fi";
       
   208         } else {
       
   209             mData->mUrl = "";
       
   210         }
       
   211         mData->mChangeFlags |= RadioStation::UrlChanged;
       
   212         ASSERT_SHARED_NULL_IS_INTACT
       
   213     }
       
   214 }
       
   215 
       
   216 /*!
       
   217  *
       
   218  */
       
   219 void RadioStation::setGenre( const int genre )
       
   220 {
       
   221     if ( mData->mGenre != genre ) {
       
   222         mData->mGenre = genre;
       
   223         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::GenreChanged;
       
   224 
       
   225         // Note that setStationHasSentRds() is not set when genre or pi code arrives.
       
   226 
       
   227         ASSERT_SHARED_NULL_IS_INTACT
       
   228     }
       
   229 }
       
   230 
       
   231 /*!
       
   232  *
       
   233  */
       
   234 void RadioStation::setUrl( const QString& url )
       
   235 {
       
   236     if ( mData->mUrl.compare( url ) != 0 ) {
       
   237         mData->mUrl = url;
       
   238         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::UrlChanged;
       
   239         mData->setStationHasSentRds( true );
       
   240         ASSERT_SHARED_NULL_IS_INTACT
       
   241     }
       
   242 }
       
   243 
       
   244 /*!
       
   245  * Sets the PI code
       
   246  * @return true if code was changed, false if not
       
   247  */
       
   248 bool RadioStation::setPiCode( int piCode, RadioRegion::Region region )
       
   249 {
       
   250     LOG_FORMAT( "RadioStation::setPiCode new PI: %d", piCode );
       
   251     // toggling local area coverage bit code must not be interpreted as new PI code
       
   252     if ( region != RadioRegion::America ) {
       
   253         piCode &= ~DISABLE_LOCAL_AREA_COVERAGE_MASK;
       
   254     }
       
   255 
       
   256     LOG_FORMAT( "stored PI: %d", mData->mPiCode );
       
   257     LOG_FORMAT( "call sign check done: %d", mData->isCallSignCheckDone() );
       
   258     //prevent executing the below code when unnessesary
       
   259     if ( mData->mPiCode != piCode || !mData->isCallSignCheckDone() ) {
       
   260         mData->mPiCode = piCode;
       
   261         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::PiCodeChanged;
       
   262         // Note that setStationHasSentRds() is not set when genre or pi code arrives.
       
   263 
       
   264         // call sign not calculated for clear channel stations
       
   265 		//TODO: Remove magic numbers
       
   266         if ( ( (mData->mPiCode & 0xF000 ) >> 12 ) == 0x1 ) {
       
   267             LOG( "Clear channel station" );
       
   268             mData->setCallSignCheckDone( true );
       
   269         } else if ( region == RadioRegion::America && mData->mName.isEmpty() && !isRenamed() ) {
       
   270             LOG( "Calculate call sign" );
       
   271             mData->mName = piCodeToCallSign( mData->mPiCode );
       
   272             mData->mChangeFlags |= RadioStation::NameChanged;
       
   273         }
       
   274 
       
   275         if ( mData->mChangeFlags.testFlag( RadioStation::PsTypeChanged ) ) {
       
   276             LOG( "Call sign check done" );
       
   277             mData->setCallSignCheckDone( true );
       
   278         }
       
   279 
       
   280         ASSERT_SHARED_NULL_IS_INTACT
       
   281         return true;
       
   282     }
       
   283     return false;
       
   284 }
       
   285 
       
   286 /*!
       
   287  *
       
   288  */
       
   289 void RadioStation::setPsType( PsType psType )
       
   290 {
       
   291     if ( mData->mPsType != psType ) {
       
   292         mData->mPsType = psType;
       
   293         mData->mChangeFlags |= RadioStation::PsTypeChanged;
       
   294         ASSERT_SHARED_NULL_IS_INTACT
       
   295     }
       
   296 }
       
   297 
       
   298 /*!
       
   299  *
       
   300  */
       
   301 void RadioStation::setRadioText( const QString& radioText )
       
   302 {
       
   303     if ( mData->mRadioText.compare( radioText ) != 0 ) {
       
   304         mData->mRadioText = radioText.isEmpty() ? "" : radioText.trimmed();
       
   305         mData->mChangeFlags |= RadioStation::RadioTextChanged;
       
   306         mData->setStationHasSentRds( true );
       
   307         ASSERT_SHARED_NULL_IS_INTACT
       
   308     }
       
   309 }
       
   310 
       
   311 /*!
       
   312  *
       
   313  */
       
   314 void RadioStation::setRadioTextPlus( const int rtPlusClass, const QString& rtPlusItem )
       
   315 {
       
   316     if ( !mData->mRadioText.isEmpty() &&
       
   317          !rtPlusItem.isEmpty() &&
       
   318          ( rtPlusClass == RtPlus::Artist || rtPlusClass == RtPlus::Title || rtPlusClass == RtPlus::Homepage) )
       
   319     {
       
   320         // Url is saved to its own variable and it is not highlighted from the radiotext
       
   321         if ( rtPlusClass == RtPlus::Homepage ) {
       
   322             setUrl( rtPlusItem );
       
   323             return;
       
   324         }
       
   325 
       
   326         const QString replacement = HTML_TAG_START + rtPlusItem + HTML_TAG_END;
       
   327 
       
   328         mData->mRadioText.replace( rtPlusItem, replacement );
       
   329         mData->mChangeFlags |= RadioStation::RadioTextChanged;
       
   330     }
       
   331 }
       
   332 
       
   333 /*!
       
   334  *
       
   335  */
       
   336 void RadioStation::setDynamicPsText( const QString& dynamicPsText )
       
   337 {
       
   338     if ( mData->mDynamicPsText.compare( dynamicPsText ) != 0 ) {
       
   339         mData->mDynamicPsText = dynamicPsText;
       
   340         mData->mChangeFlags |= RadioStation::DynamicPsChanged;
       
   341         ASSERT_SHARED_NULL_IS_INTACT
       
   342     }
       
   343 }
       
   344 
       
   345 /*!
       
   346  *
       
   347  */
       
   348 bool RadioStation::isValid() const
       
   349 {
       
   350     return mData->mFrequency > 0 && ( isType( ManualStation ) || mData->mPresetIndex >= 0 );
       
   351 }
       
   352 
       
   353 /*!
       
   354  *
       
   355  */
       
   356 QString RadioStation::name() const
       
   357 {
       
   358     return mData->mName.isEmpty() ? "" : mData->mName;
       
   359 }
       
   360 
       
   361 /*!
       
   362  *
       
   363  */
       
   364 void RadioStation::setUserDefinedName( const QString& name )
       
   365 {
       
   366     // We don't check for name emptiness because this setter is used also to remove the renaming
       
   367     // of a station by setting an empty name
       
   368     if ( mData->mName.compare( name ) != 0 ) {
       
   369         mData->mName = name;
       
   370         mData->setRenamedByUser( !name.isEmpty() );
       
   371         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged;
       
   372     }
       
   373 }
       
   374 
       
   375 /*!
       
   376  *
       
   377  */
       
   378 bool RadioStation::isRenamed() const
       
   379 {
       
   380     return mData->isRenamedByUser();
       
   381 }
       
   382 
       
   383 /*!
       
   384  *
       
   385  */
       
   386 int RadioStation::genre() const
       
   387 {
       
   388     return mData->mGenre;
       
   389 }
       
   390 
       
   391 /*!
       
   392  *
       
   393  */
       
   394 QString RadioStation::frequencyString() const
       
   395 {
       
   396     return parseFrequency( mData->mFrequency );
       
   397 }
       
   398 
       
   399 /*!
       
   400  *
       
   401  */
       
   402 uint RadioStation::frequency() const
       
   403 {
       
   404     return mData->mFrequency;
       
   405 }
       
   406 
       
   407 /*!
       
   408  *
       
   409  */
       
   410 int RadioStation::presetIndex() const
       
   411 {
       
   412     return mData->mPresetIndex;
       
   413 }
       
   414 
       
   415 /*!
       
   416  *
       
   417  */
       
   418 void RadioStation::setFavorite( bool favorite )
       
   419 {
       
   420     if ( isFavorite() != favorite ) {
       
   421         favorite ? setType( RadioStation::Favorite ) : unsetType( RadioStation::Favorite );
       
   422         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::FavoriteChanged;
       
   423         ASSERT_SHARED_NULL_IS_INTACT
       
   424     }
       
   425 }
       
   426 
       
   427 /*!
       
   428  *
       
   429  */
       
   430 bool RadioStation::isFavorite() const
       
   431 {
       
   432     return mData->mType.testFlag( RadioStation::Favorite );
       
   433 }
       
   434 
       
   435 /*!
       
   436  *
       
   437  */
       
   438 QString RadioStation::url() const
       
   439 {
       
   440     return mData->mUrl;
       
   441 }
       
   442 
       
   443 /*!
       
   444  *
       
   445  */
       
   446 int RadioStation::piCode() const
       
   447 {
       
   448     return mData->mPiCode;
       
   449 }
       
   450 
       
   451 /*!
       
   452  *
       
   453  */
       
   454 void RadioStation::setType( RadioStation::Type type )
       
   455 {
       
   456     if ( !isType( type ) ) {
       
   457 
       
   458         // Check if favorite-status changed
       
   459         if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) {
       
   460             mData->mChangeFlags |= RadioStation::FavoriteChanged;
       
   461         }
       
   462 
       
   463         mData->mType |= type;
       
   464         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged;
       
   465         ASSERT_SHARED_NULL_IS_INTACT
       
   466     }
       
   467 }
       
   468 
       
   469 /*!
       
   470  *
       
   471  */
       
   472 void RadioStation::unsetType( RadioStation::Type type )
       
   473 {
       
   474     if ( isType( type ) ) {
       
   475 
       
   476         // Check if favorite-status changed
       
   477         if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) {
       
   478             mData->mChangeFlags |= RadioStation::FavoriteChanged;
       
   479         }
       
   480 
       
   481         mData->mType &= ~type;
       
   482         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged;
       
   483         ASSERT_SHARED_NULL_IS_INTACT
       
   484     }
       
   485 }
       
   486 
       
   487 /*!
       
   488  *
       
   489  */
       
   490 bool RadioStation::isType( RadioStation::Type type ) const
       
   491 {
       
   492     return ( mData->mType & type ) == type;
       
   493 }
       
   494 
       
   495 /*!
       
   496  *
       
   497  */
       
   498 RadioStation::PsType RadioStation::psType() const
       
   499 {
       
   500     return mData->mPsType;
       
   501 }
       
   502 
       
   503 /*!
       
   504  *
       
   505  */
       
   506 QString RadioStation::radioText() const
       
   507 {
       
   508     return mData->mRadioText;
       
   509 }
       
   510 
       
   511 /*!
       
   512  *
       
   513  */
       
   514 QString RadioStation::dynamicPsText() const
       
   515 {
       
   516     return mData->mDynamicPsText;
       
   517 }
       
   518 
       
   519 /*!
       
   520  *
       
   521  */
       
   522 RadioStation::Change RadioStation::changeFlags() const
       
   523 {
       
   524     return mData->mChangeFlags;
       
   525 }
       
   526 
       
   527 /*!
       
   528  *
       
   529  */
       
   530 bool RadioStation::hasDataChanged( RadioStation::Change flags ) const
       
   531 {
       
   532     return ( mData->mChangeFlags & flags ) == flags;
       
   533 }
       
   534 
       
   535 /*!
       
   536  *
       
   537  */
       
   538 bool RadioStation::hasChanged() const
       
   539 {
       
   540     return mData->mChangeFlags != RadioStation::NoChange;
       
   541 }
       
   542 
       
   543 /*!
       
   544  *
       
   545  */
       
   546 void RadioStation::resetChangeFlags()
       
   547 {
       
   548     if ( mData->mChangeFlags != RadioStation::NoChange ) {
       
   549         mData->mChangeFlags = RadioStation::NoChange;
       
   550     }
       
   551 }
       
   552 
       
   553 /*!
       
   554  *
       
   555  */
       
   556 bool RadioStation::hasSentRds() const
       
   557 {
       
   558     return mData->hasStationSentRds();
       
   559 }
       
   560 
       
   561 /*!
       
   562  *
       
   563  */
       
   564  QString RadioStation::piCodeToCallSign( uint programmeIdentification )
       
   565  {
       
   566     QString callSign;
       
   567 
       
   568     LOG_FORMAT( "RadioStation::piCodeToCallSign PI: %d", programmeIdentification );
       
   569     // call signs beginning with 'K'
       
   570     if ( ( programmeIdentification>=KXXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification < WXXX_CALLSIGN_PI_FIRST ) ) {
       
   571         callSign += "K";
       
   572         callSign += iterateCallSign( KXXX_CALLSIGN_PI_FIRST, programmeIdentification );
       
   573     }
       
   574     // call signs beginning with 'W'
       
   575     else if ( ( programmeIdentification >= WXXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification <= WXXX_CALLSIGN_PI_LAST ) ) {
       
   576         callSign += "W";
       
   577         callSign += iterateCallSign( WXXX_CALLSIGN_PI_FIRST, programmeIdentification );
       
   578     }
       
   579     // 3 letter only call signs
       
   580     else if ( ( programmeIdentification >= XXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification <= XXX_CALLSIGN_PI_LAST ) ) {
       
   581         callSign += callSignString( programmeIdentification );
       
   582     } else {
       
   583         LOG( "RadioStation::piCodeToCallSign - Unhandled else" );
       
   584     }
       
   585 
       
   586     LOG_FORMAT( "RadioStation::piCodeToCallSign, call sign: %s", GETSTRING(callSign) );
       
   587 
       
   588     return callSign;
       
   589 }
       
   590 
       
   591 /*!
       
   592  *
       
   593  */
       
   594 QString RadioStation::iterateCallSign( int piBase, int programmeIdentification )
       
   595 {
       
   596     QString callSign;
       
   597     LOG_FORMAT( "RadioStation::iterateCallSign base: %d", piBase );
       
   598 
       
   599     int sum = 0;
       
   600     int i = 0;
       
   601     while( sum < programmeIdentification ) {
       
   602         ++i;
       
   603         sum = piBase + i * 676 + 0 + 0;
       
   604     }
       
   605     callSign += callSignChar( i - 1 );
       
   606 
       
   607     int tmpSum( sum - 676 );
       
   608     sum -= 676;
       
   609     i = 0;
       
   610     while( sum <= programmeIdentification ) {
       
   611         ++i;
       
   612         sum = tmpSum + 0 + i * 26 + 0;
       
   613     }
       
   614     callSign += callSignChar( i - 1 );
       
   615 
       
   616     tmpSum = sum - 26;
       
   617     sum -= 26;
       
   618     i = 0;
       
   619     while( sum <= programmeIdentification ) {
       
   620         ++i;
       
   621         sum = tmpSum + 0 + 0 + i;
       
   622     }
       
   623     callSign += callSignChar( i - 1 );
       
   624 
       
   625     return callSign;
       
   626     }
       
   627 
       
   628 /*!
       
   629  *
       
   630  */
       
   631 QString RadioStation::callSignString( uint programmeIdentification )
       
   632 {
       
   633     for ( uint i = 0; i < THREE_LETTER_CALLSIGN_COUNT; ++i ) {
       
   634         if( PI_CODE_TABLE[i] == programmeIdentification ) {
       
   635             return CALLSIGN_TABLE[i];
       
   636         }
       
   637     }
       
   638 
       
   639     LOG_FORMAT( "RadioStation::callSignString, Not found PI: %d", programmeIdentification );
       
   640 
       
   641     return QString( "????" );
       
   642 }
       
   643 
       
   644 /*!
       
   645  *
       
   646  */
       
   647 char RadioStation::callSignChar( uint decimalValue )
       
   648 {
       
   649     LOG_FORMAT( "RadioStation::callSignChar A+: %d", decimalValue );
       
   650     if ( decimalValue <= LAST_CALLSIGN_CHAR_CODE ) {
       
   651         return static_cast<char>( 'A' + decimalValue );
       
   652     }
       
   653     return '?';
       
   654 }
       
   655 
       
   656 /**
       
   657  * Checks if the class is detached from implicitly shared data
       
   658  */
       
   659 bool RadioStation::isDetached() const
       
   660 {
       
   661     return mData->ref == 1;
       
   662 }