/******************************************************************************
 FileName............: Support.pico
 Project.............:
 Author(s)...........: MM
 Version.............: 1.03
-------------------------------------------------------------------------------
  Support/generic functions.

  Copyright (C) 2003-2006  M.Majoor

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

-------------------------------------------------------------------------------

 Version   Date   Comment
  1.00   20051119 - Initial release
  1.01   20051207 - Confirmation "Typhoon TV SAT DVB-S CARD CI" card working
                    using CardTerratecDvbS setting (thanks to Sally Twomey)
  1.02   20051227 - 'TunerSet' and 'TunerSetFine' always use a positive
                    tuner frequency to allow for possible calculated negative
                    frequencies (which happens with C-band settings).
                  - '*' wildcard added for satellite identifier
                  - '*' wildcard added for polarity
                  - Global vars 'TransponderXX' removed were they were
                    also define locally (e.g. TransponderSet)
  1.03   20060318 - 'I2cWriteMasked' no longer masks the data itself (which
                    is now just ORed, instead of first masked)
                  - Added 'ReadRemoteControl'
******************************************************************************/

/*-----------------------------------------------------------------------------
  Include files
-----------------------------------------------------------------------------*/
<<Driver>>

var:TransponderDefinitions "" !TransponderDefinitions
var:TransponderSatellite   "" !TransponderSatellite
var:TransponderFrequency    0 !TransponderFrequency
var:TransponderSymbolRate   0 !TransponderSymbolRate
var:TransponderPolarity    "" !TransponderPolarity
var:TransponderFec          0 !TransponderFec

var:RemoteRepeat        false !RemoteRepeat                /* Last received repeat flag */
var:RemoteCommand           0 !RemoteCommand               /* Last received command */
var:RemoteDevice            0 !RemoteDevice                /* Last received device */

var:Options                 0 !Options                     /* Options */
                                                           /* %.......1 = use fine tuning */
var:OptionFineTuning        1 !OptionFineTuning


/*-----------------------------------------------------------------------------
  Params  : <Card>      Card number
                        Negative numbers give the available scripts ('suggestion')
  Returns : <Flag>      True if can be controlled by script
            <Result>    Identification string (empty if nothing identified)
                           Identifier, Hardware, Tuner, Type, Manufacturer, Name
                         or script suggestion
                        String is empty if out of range
            [<Script>]  Script to use (only if Flag is true)

  Descript: Return identification of indicated card.
  Notes   : A valid identification does -NOT- mean that the card is supported,
            not even if the Script result is valid (the script might be in it's
            initial testing phase)
-----------------------------------------------------------------------------*/
function:CardIdentify()
{
  $C1 "CardIdentify" logmessage
  var:Req         0 !Req
  var:Subsys      0 !Subsys
  var:Name       "" !Name
  var:Type       "" !Type
  var:Card      " " !Card
  var:Man        "" !Man
  var:Hw "SAA7146A" !Hw
  var:Tuner      "" !Tuner

  dup !Req
  _cardsubsys !Subsys
  ?Subsys $0000110A == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "Siemens C                            " !Name }
  ?Subsys $000013C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium S                  " !Name }
  ?Subsys $000113C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium T                  " !Name }
  ?Subsys $000213C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium C                  " !Name }
  ?Subsys $000313C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "Hauppauge WinTV-Nexus                " !Name }
  ?Subsys $000413C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "Galaxis DVB PC-Sat-Karte             " !Name }
  ?Subsys $000513C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "Technisat SkyStar 1                  " !Name }
  ?Subsys $000613C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium S                  " !Name }
  ?Subsys $000813C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium T                  " !Name }
  ?Subsys $000913C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium C                  " !Name }
  ?Subsys $000A13C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium C                  " !Name }

  ?Subsys $00101894 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "KNC        " !Man "TV Station DVB-S                     " !Name }
  ?Subsys $00111894 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "KNC        " !Man "TV Station DVB-S+                    " !Name }
  ?Subsys $00201894 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "KNC        " !Man "TV Station DVB-C                     " !Name }
  ?Subsys $00211894 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "KNC        " !Man "TV Station DVB-C+                    " !Name }
  ?Subsys $00301894 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "KNC        " !Man "TV Station DVB-T                     " !Name }
  ?Subsys $00311894 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "KNC        " !Man "TV Station DVB-T+                    " !Name }

  ?Subsys $100213C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline premium S                  " !Name }

  ?Subsys $100313C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100413C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100513C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100913C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100A13C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100B13C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100C13C2 == if { "DVB-S" !Type "CardNova1       " !Card "Alps BSRU6       " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100D13C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100E13C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $100F13C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $101013C2 == if { "DVB-C" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $101113C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $101213C2 == if { "DVB-T" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $101613C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }
  ?Subsys $101713C2 == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Technotrend" !Man "TT-PCline budget Adapter             " !Name }

  ?Subsys $1154153B == if { "DVB-S" !Type "CardTerratecDvbS" !Card "Philips SU1278   " !Tuner "Terratec   " !Man "Cinergy 1200 DVB-S                   " !Name }
  ?Subsys $1155153B == if { "DVB-S" !Type "                " !Card "                 " !Tuner "Terratec   " !Man "Cinergy 1200 DVB-S                   " !Name }
  ?Subsys $1156153B == if { "DVB-C" !Type "CardTerratecDvbC" !Card "Philips CU1216   " !Tuner "Terratec   " !Man "Cinergy 1200 DVB-C                   " !Name }
  ?Subsys $1157153B == if { "DVB-T" !Type "CardTerratecDvbT" !Card "Philips TU1216   " !Tuner "Terratec   " !Man "Cinergy 1200 DVB-T                   " !Name }

  ?Subsys $210313D0 == if { "DVB-S" !Type "CardSkystar26c  " !Card "Samsung TBMU24112" !Tuner "Technisat  " !Man "Skystar 2                            " !Name "FLEXCOP " !Hw }

  ?Subsys $4F561131 == if { "DVB-S" !Type "CardTerratecDvbS" !Card "Philips SU1278   " !Tuner "Typhoon    " !Man "TV SAT DVB-S CARD CI                 " !Name }

  /* Available choices */
  0 ?Req < if { "" !Type 0 !Subsys "-------" !Hw }
  ?Req -1           == if { "DVB-S" !Type "CardNova1       " !Card "Alps BSRU6       " !Tuner "           " !Man "Synth=$C2 Reset=GPIO2 Pol=OP0 LNB=OP1" !Name }
  ?Req -2           == if { "DVB-S" !Type "CardNova2       " !Card "Philips SU1278   " !Tuner "           " !Man "Synth=$C0 Reset=GPIO2 Pol=OP0 LNB=OP1" !Name }
  ?Req -3           == if { "DVB-S" !Type "CardNova3       " !Card "Alps BSBE1       " !Tuner "           " !Man "Synth=$C2 Reset=GPIO2 Pol=OP0 LNB=OP1" !Name }
  ?Req -4           == if { "DVB-S" !Type "CardTerratecDvbS" !Card "Philips SU1278   " !Tuner "           " !Man "Synth=$C2 Reset=-     Pol=OP1 LNB=-  " !Name }
  ?Req -5           == if { "DVB-S" !Type "CardSkyStar26c  " !Card "Samsung TBMU24112" !Tuner "           " !Man "Skystar 2  Rev:2.6C                  " !Name }
  ?Req -6           == if { "DVB-T" !Type "CardTerratecDvbT" !Card "Philips TU1216   " !Tuner "           " !Man "Synth=$C2                            " !Name }
  ?Req -7           == if { "DVB-C" !Type "CardTerratecDvbC" !Card "Philips CU1216   " !Tuner "           " !Man "Synth=$C0                            " !Name }

  "00000000" ?Subsys ashex & 8 right "$" swap & ", " & ?Hw & ", " & ?Tuner & ", " &  ?Type & ", " & ?Man & ", " & ?Name &
  ?Type "" == if { drop "" }
  ?Card 1 left " " != if { ?Card swap true } else { false }

  $E1 " CardIdentify<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : <Address>   Base addres of I2C
            <Register>  Register
            <Data>      Data
            <Mask>      Mask (bits to change are set)
  Returns : <Flag>      True if success

  Descript: Write to I2C register by modifying it using a mask and data.
  Notes   :
-----------------------------------------------------------------------------*/
function:I2cWriteMasked()
{
  $C4 "I2cWriteMasked" logmessage
  var:Address 0 !Address
  var:Reg     0 !Reg
  var:Data    0 !Data
  var:Mask    0 !Mask

  !Address !Reg !Data !Mask                                /* Save parameters */
  1 ?Reg ?Address I2cRead if                               /* Get original contents */
  { drop ?Mask not and                                     /* Reset masked   bits of read data */
    ?Data
    or 1 ?Reg ?Address I2cWrite                            /* Write it */
  }
  else
  { false }

  $E4 " I2cWriteMasked<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Major>   Major version
            <Minor>   Minor version
            <Build>   Build data ($YYYYMMDD format if displayed hexadecimal)
            <Device>  Device name

  Descript: Get version information (of active card).
  Notes   : Used by the application to setup for the correct hardware.
-----------------------------------------------------------------------------*/
function:HardwareVersion()
{
  $C3 "HardwareVersion" logmessage

  GetVersion

  $E3 " HardwareVersion<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Information>  Informative string

  Descript: Get information of selected hardware (of active card).
  Notes   : Used by the application for informative purposes.
-----------------------------------------------------------------------------*/
function:HardwareInfo()
{
  $C3 "HardwareInfo" logmessage

  ?Card CardIdentify if
    { swap ", " & swap & }
  else
    { "Unknown, " swap & }

  $E3 " HardwareInfo<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : <Card>  Card to select
  Returns : <Flag>  Result

  Descript: Select the indicated card (of active driver).
  Notes   : Used by application to select a specific card.
-----------------------------------------------------------------------------*/
function:HardwareSelectCard()
{
  $C3 "HardwareSelectCard" logmessage

  SelectCard

  $E3 " HardwareSelectCard<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Cards>  Total number of cards

  Descript: Get number of all cards in system (of both SAA7146/FLEXCOP).
  Notes   :
-----------------------------------------------------------------------------*/
function:HardwareAllCards()
{
  $C3 "HardwareAllCards" logmessage

  _?saa7146a if
  { GetCards _flexcop GetCards + _saa7146a }
  else
  { _?flexcop if
    { GetCards _saa7146a GetCards + _flexcop }
    else
    { 0 }
  }

  $E3 " HardwareAllCards<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Flag>      True if data available
            [<Command>] New command received (if Flag is true)
            [<Device>]  New device received (if Flag is true)

  Descript: Read remote control and return data if new data received.
  Notes   :
-----------------------------------------------------------------------------*/
function:ReadRemoteControl()
{
  $C3 "ReadRemoteControl" logmessage
  var:Valid   false !Valid
  var:Repeat  false !Repeat
  var:Command     0 !Command
  var:Device      0 !Device

  _?saa7146a if
  { ?Saa7146aRps0Program -1 == if
    { Saa7146aGetAuxData if
      { drop !Valid !Repeat !Command !Device               /* Save received data (old or new) */
        ?Valid if
        { ?Repeat  ?RemoteRepeat  !=                       /* Check for any changed data == new data */
          ?Command ?RemoteCommand != or
          ?Device  ?RemoteDevice  != or if
          { ?Repeat  !RemoteRepeat                         /* New data -> save last received data */
            ?Command !RemoteCommand
            ?Device  !RemoteDevice
            ?Device ?Command true                          /* Return new data */
          }
          else
          { false }
        }
        else { false }
      }
      else { false }
    }
    else { false }
  }
  else
  { false }

  $E3 " ReadRemoteControl<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : <Text>  Text with VDR command ('{command}' section)
                    (e.g. "t V W15 [E0 10 38 F0] W15 T")
  Returns : <Flag>  True if success

  Descript: Send tuner command
  Notes   : Used by the application to set the tuner manually.
-----------------------------------------------------------------------------*/
function:TunerCommand()
{
  $C2 "TunerCommand" logmessage

  /* We add the dummy VDR selection part to the command and pass it on       */
  "0 0 V 0 " swap & TunerDiSEqCVdr

  $E2 " TunerCommand<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : <Filename>  Filename
  Returns : -

  Descript: Dump registers of the tuner to a file.
  Notes   : Debug, max 255 registers
-----------------------------------------------------------------------------*/
function:TunerDumpRegisters()
{
  $C2 "TunerDumpRegisters" logmessage
  var:FileHandle  0 !FileHandle
  var:Index       0 !Index

  openwfile dup !FileHandle while                          /* Create file for writing */
  {
    1 ?Index ?CDemodAddr I2cRead if
    { drop
      "00" ?Index ashex & 2 right "$" swap & " = " &
      swap ashex "00" swap & 2 right "$" swap & &
      ?FileHandle writefilestr drop
      10 13 2 ?FileHandle writefile drop
      ++Index ?Index $100 >
    }
    else
    { false }
    ;
  }
  ?FileHandle closefile

  $E2 " TunerDumpRegisters<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Percentage>  Power percentage (0..100) of full range.
                          0% if some error was detected

  Descript: Get power indication.
  Notes   : Used by the application to get a power indication.
-----------------------------------------------------------------------------*/
function:TransponderPower()
{
  $C3 "TransponderPower" logmessage

  TunerSignalPower

  $E3 " TransponderPower<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Snr>  Signal/Noise percentage (0..100) of full range.
                   0% if some error was detected

  Descript: Get signal indication.
  Notes   : Used by the application to get a signal indication.
-----------------------------------------------------------------------------*/
function:TransponderNoise()
{
  $C3 "TransponderNoise" logmessage

  TunerSignalNoise

  $E3 " TransponderNoise<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Status>  (LinuxDVB API style)
                      $01  Found something above the noise level
                      $02  Found a DVB signal
                      $04  FEC is stable
                      $08  Found sync bytes
                      $10  Everything's working... (has lock)

  Descript: Get status.
  Notes   : Used by the application to get a status indication.
-----------------------------------------------------------------------------*/
function:TransponderStatus()
{
  $C3 "TransponderStatus" logmessage

  $00                                            /* Default status           */
  TransponderPower 10 <= if
  { $01 or }                                     /* > 10% means some signal  */
  TunerSignalState or

  $E3 " TransponderStatus<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : -
  Returns : <Satellite>   Satellite identifier (text)
            <Frequency>   Frequency (kHz)
            <SymbolRate>  Symbol rate (kS)
            <Polarity>    'H', 'V', '-' (text)
            <FEC>         00 = auto
                          12 = 1/2
                          34 = 3/4 etc

  Descript: Get information on current transponder (settings).
  Notes   : Used by the application to get a settings indication.
            Only really applicable when auto tuning is done in script and
            the application needs to be aware of this.
-----------------------------------------------------------------------------*/
function:TransponderInfo()
{
  $C3 "TransponderInfo" logmessage

  ?TransponderFec
  ?TransponderPolarity
  ?TransponderSymbolRate
  ?TransponderFrequency
  ?TransponderSatellite

  $E3 " TransponderInfo<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : <Text>  Text with VDR commands
  Returns : <Flag>  True if success

  Descript: Handle VDR commands.
  Notes   : Commands are processed until there are no more commands or an error
            is detected, which means that incorrect command sequences are
            carried out until the incorrect part is processed.
            DiSEqC configuration for VDR
              Format:   satellite slof polarization lof {command}
                            |       |        |       |      |
                            |       |        |       |     'DiSEqC' command (optional)
                            |       |        |      Local Oscillator Frequency
                            |       |       H/V/- polarization
                            |      Switch frequency
                           Satellite identifier
              The optional {command} is made up of the following
                t         Tone off
                T         Tone on
                v         Voltage low (13V)
                V         Voltage high (18V)
                A         Mini A (burst A)
                B         Mini B (burst B)
                Wxx       Wait xx milliseconds
                W xx      Wait xx milliseconds
                [ xx yy ] Hex code sequence, eg [ E0 10 38 F0 ]
                [xx yy ]  Hex code sequence, eg [E0 10 38 F0 ]
                [xx yy]   Hex code sequence, eg [E0 10 38 F0]
                [ xx yy]  Hex code sequence, eg [ E0 10 38 F0]
            Example:
            S19.2E  11700 V  9750  t v W15 [E0 10 38 F0] W15 A W15 t
            This function only handles the {command} part here. All other
            information is bypassed.
-----------------------------------------------------------------------------*/
function:TunerDiSEqCVdr()
{
  $81 "TunerDiSEqCVdr" logmessage
  var:DiSEqCList        "" !DiSEqCList
  var:DiSEqCCommand     "" !DiSEqCCommand
  var:DiSEqCTemp         0 !DiSEqCTemp
  var:DiSEqCAdding   false !DiSEqCAdding
  var:DiSEqCVdrError false !DiSEqCVdrError

  true while                                               /* Replace all occurences of double spaces into single spaces */
  { "  " " " replacetext
    dup "  " 0 posstr 0 <
  }
  " " split                                                /* Create string list */
  dup count[] 4 < if                                       /* Check if correct length */
  { 4 repeat { 0 delete[] ; }                              /* Remove unused data */
    !DiSEqCList                                            /* Save list */
    false !DiSEqCAdding                                    /* Not adding to [ ] list */
    true while                                             /* Go through list */
    { ?DiSEqCList first[]                                  /* Get first item */
      ?DiSEqCList 0 delete[] !DiSEqCList                   /* Remove first item */
      ?DiSEqCAdding if
      { dup "]" == if                                      /* If within a [ ] section */
        { drop                                             /* End of [ ] section */
          false !DiSEqCAdding                              /* No longer adding to list */
          ?DiSEqCCommand count[] 0 < if                    /* Check for something to do*/
          { ?DiSEqCCommand count[] dup !DiSEqCTemp
            repeat
            { ?DiSEqCCommand last[]                        /* Get last string */
              ?DiSEqCCommand dup count[] -1 +              /* And remove it too */
              delete[] !DiSEqCCommand
              "$" swap & asinteger                         /* Convert as hexadecimal */
            }
            ?DiSEqCTemp 3 TunerDiSEqC                      /* Send DiSEqC data */
            not if { true !DiSEqCVdrError }
          }
        }
        else
        { dup 1 right
          "]" == if
          {                                                /* 'xx]' -> 'xx ]' */
            dup length -1 + left                           /* Remove last character */
            "]"  append[]
            ?DiSEqCList count[] repeat
            { ?DiSEqCList first[]                          /* Get first item */
              append[]                                     /* Add to new list */
              ?DiSEqCList 0 delete[]                       /* Remove first item */
              !DiSEqCList
            }
            !DiSEqCList
          }
          else
          { ?DiSEqCCommand swap append[]                   /* Add to stringlist */
            !DiSEqCCommand
          }
        }
      }
      else
      { dup
        "[" == if { "" !DiSEqCCommand                      /* DiSEqC section, clear command list */
                    true !DiSEqCAdding                     /* Adding to list */
                    drop ""                                /* Let other checks fail */
                  }
        dup
        "t" == if { false Tuner22kHz not if                /* Tone off (low band) */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "T" == if { true Tuner22kHz not if                 /* Tone on (high band) */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "v" == if { 13 TunerPower not if                   /* 13V (vertical polarity) */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "V" == if { 18 TunerPower not if                   /* 18V (horizontal polarity) */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "A" == if { 1 TunerDiSEqC not if                   /* Burst A */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "a" == if { 1 TunerDiSEqC not if                   /* Burst A */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "B" == if { 2 TunerDiSEqC not if                   /* Burst B */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "b" == if { 2 TunerDiSEqC not if                   /* Burst B */
                    { true !DiSEqCVdrError }
                    drop ""
                  }
        dup
        "W" == if { drop                                   /* Delay (ms) */
                    ?DiSEqCList first[]
                    ?DiSEqCList 0 delete[] !DiSEqCList
                    asinteger
                    1000 *
                    $82 "TunerDiSEqCVdr - W delay" logmessage
                    delay
                    ""
                  }
        /* Handle commands which need to be separated here */
        dup 1 left
        "W" == if {                                        /* 'Wxx' -> 'W xx' */
                    99 2 mid                               /* Remove 1st character */
                    "" "W"  append[]                       /* New string list */
                    swap append[]
                    ?DiSEqCList count[] repeat
                    {
                      ?DiSEqCList first[]                  /* Get first item */
                      append[]                             /* Add to new list */
                      ?DiSEqCList 0 delete[]               /* Remove first item */
                      !DiSEqCList
                    }
                    !DiSEqCList
                    ""
                  }
        dup 1 left
        "[" == if {                                        /* '[xx' -> '[ xx' */
                    99 2 mid                               /* Remove 1st character */
                    "" "["  append[]                       /* New string list */
                    swap append[]
                    ?DiSEqCList count[] repeat
                    { ?DiSEqCList first[]                  /* Get first item */
                      append[]                             /* Add to new list */
                      ?DiSEqCList 0 delete[]               /* Remove first item */
                      !DiSEqCList
                    }
                    !DiSEqCList
                    ""
                  }
        /* Check for any unhandled data which indicates an error */
        "" != if  { true !DiSEqCVdrError }
      }
      ?DiSEqCVdrError if { "" !DiSEqCList }                /* An error clears the list */
      ?DiSEqCList count[] 0 !=                             /* Continue until list empty */
    }
    ?DiSEqCAdding if { true !DiSEqCVdrError }              /* If [ ] section not closed */
  }
  else
  { drop }
  ?DiSEqCVdrError not

  $A1 " TunerDiSEqCVdr<" logmessage
}


/*-----------------------------------------------------------------------------
  Params  : <Satellite>   Satellite identifier (text)
            <Frequency>   Frequency (kHz)
            <SymbolRate>  Symbol rate (kS)
            <Polarity>    'H', 'V', '-' (text)
            <FEC/QAM>     00 = auto
                          12 = FEC 1/2
                          34 = FEC 3/4 etc
  Returns : <Flag>  True if success

  Descript: Set transponder.
  Notes   : Used by the application to set a transponder.
-----------------------------------------------------------------------------*/
function:TransponderSet()
{
  $80 "TransponderSet" logmessage
  var:TransponderSelection   "" !TransponderSelection
  var:TransponderIndex        0 !TransponderIndex
  var:TransponderMatch    false !TransponderMatch
  var:TransponderError    false !TransponderError

  true  !TransponderError
  false !TransponderMatch
  lowercase !TransponderSatellite                /* Satellite to lowercase   */
  !TransponderFrequency
  !TransponderSymbolRate
  lowercase !TransponderPolarity                 /* Polarity to lowercase    */
  !TransponderFec

  /* First search in the list of DiSEqCCommands for the satellite identifier */
  "" !TransponderSelection
  0 !TransponderIndex
  true while
  {
    ?TransponderDefinitions ?TransponderIndex pick[]       /* Get item       */
    true while
    {
      "  " " " replacetext
      dup "  " 0 posstr 0 <
    }
    " " split                                    /* Create string list       */
    !TransponderSelection
    ++TransponderIndex
    ?TransponderSelection 0 pick[] lowercase     /* Get satellite part       */
    dup "*" == swap                              /* Check for wildcard       */
    ?TransponderSatellite == or if               /* Or direct match          */
    {
      /* If we have a satellite match then also check frequency (note the    */
      /* conversion from MHz to kHz)                                         */
      ?TransponderFrequency ?TransponderSelection 1 pick[] asinteger 1000 *
      > if
      {
        ?TransponderSelection 2 pick[] lowercase /* Get polarity part        */
        dup "*" == swap                          /* Check for wildcard       */
        ?TransponderPolarity == or if            /* Or direct match          */
        {
          /* We have a match, pass the found string on to the DiSEqC part    */
          /* and to the tuning part.                                         */
          ?TransponderSelection TunerDiSEqCVdr if
          {
            /* DiSEqC command executed correctly, now set the tuner for which*/
            /* we have to calculate the correct tuner frequency according to */
            /* the LF setting.                                               */
            ?TransponderFec
            ?TransponderFrequency ?TransponderSelection 3 pick[] asinteger
            1000 * -
            ?TransponderSymbolRate swap
            ?Options ?OptionFineTuning and 0 != if
            { abs TunerSetFine }
            else
            { abs TunerSet }
            dup !TransponderMatch
            not !TransponderError
          }
        }
      }
    }
    /* We end the loop if we have a match or are at the end of the list      */
    ?TransponderMatch
    ?TransponderDefinitions count[] ?TransponderIndex >= or not
  }
  ?TransponderError not

  $A0 " TransponderSet<" logmessage
}