/ adafruit_si4713.py
adafruit_si4713.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 Tony DiCola for Adafruit Industries
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_si4713`
 24  ====================================================
 25  
 26  CircuitPython module for the SI4713 RDS FM transmitter.  See
 27  examples/simpletest.py for a demo of the usage.  Based on the Arduino library
 28  at: https://github.com/adafruit/Adafruit-Si4713-Library/
 29  
 30  * Author(s): Tony DiCola
 31  """
 32  import time
 33  
 34  from micropython import const
 35  import ustruct
 36  
 37  import adafruit_bus_device.i2c_device as i2c_device
 38  
 39  
 40  __version__ = "0.0.0-auto.0"
 41  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SI4713.git"
 42  
 43  
 44  #pylint: disable=bad-whitespace
 45  # Internal constants:
 46  _SI4710_ADDR0                           = const(0x11) # if SEN is = const(low)
 47  _SI4710_ADDR1                           = const(0x63) # if SEN is high, default
 48  _SI4710_STATUS_CTS                      = const(0x80)
 49  _SI4710_CMD_POWER_UP                    = const(0x01)
 50  _SI4710_CMD_GET_REV                     = const(0x10)
 51  _SI4710_CMD_POWER_DOWN                  = const(0x11)
 52  _SI4710_CMD_SET_PROPERTY                = const(0x12)
 53  _SI4710_CMD_GET_PROPERTY                = const(0x13)
 54  _SI4710_CMD_GET_INT_STATUS              = const(0x14)
 55  _SI4710_CMD_PATCH_ARGS                  = const(0x15)
 56  _SI4710_CMD_PATCH_DATA                  = const(0x16)
 57  _SI4710_CMD_TX_TUNE_FREQ                = const(0x30)
 58  _SI4710_CMD_TX_TUNE_POWER               = const(0x31)
 59  _SI4710_CMD_TX_TUNE_MEASURE             = const(0x32)
 60  _SI4710_CMD_TX_TUNE_STATUS              = const(0x33)
 61  _SI4710_CMD_TX_ASQ_STATUS               = const(0x34)
 62  _SI4710_CMD_TX_RDS_BUFF                 = const(0x35)
 63  _SI4710_CMD_TX_RDS_PS                   = const(0x36)
 64  _SI4710_CMD_TX_AGC_OVERRIDE             = const(0x48)
 65  _SI4710_CMD_GPO_CTL                     = const(0x80)
 66  _SI4710_CMD_GPO_SET                     = const(0x81)
 67  _SI4713_PROP_GPO_IEN                    = const(0x0001)
 68  _SI4713_PROP_DIGITAL_INPUT_FORMAT       = const(0x0101)
 69  _SI4713_PROP_DIGITAL_INPUT_SAMPLE_RATE  = const(0x0103)
 70  _SI4713_PROP_REFCLK_FREQ                = const(0x0201)
 71  _SI4713_PROP_REFCLK_PRESCALE            = const(0x0202)
 72  _SI4713_PROP_TX_COMPONENT_ENABLE        = const(0x2100)
 73  _SI4713_PROP_TX_AUDIO_DEVIATION         = const(0x2101)
 74  _SI4713_PROP_TX_PILOT_DEVIATION         = const(0x2102)
 75  _SI4713_PROP_TX_RDS_DEVIATION           = const(0x2103)
 76  _SI4713_PROP_TX_LINE_LEVEL_INPUT_LEVEL  = const(0x2104)
 77  _SI4713_PROP_TX_LINE_INPUT_MUTE         = const(0x2105)
 78  _SI4713_PROP_TX_PREEMPHASIS             = const(0x2106)
 79  _SI4713_PROP_TX_PILOT_FREQUENCY         = const(0x2107)
 80  _SI4713_PROP_TX_ACOMP_ENABLE            = const(0x2200)
 81  _SI4713_PROP_TX_ACOMP_THRESHOLD         = const(0x2201)
 82  _SI4713_PROP_TX_ATTACK_TIME             = const(0x2202)
 83  _SI4713_PROP_TX_RELEASE_TIME            = const(0x2203)
 84  _SI4713_PROP_TX_ACOMP_GAIN              = const(0x2204)
 85  _SI4713_PROP_TX_LIMITER_RELEASE_TIME    = const(0x2205)
 86  _SI4713_PROP_TX_ASQ_INTERRUPT_SOURCE    = const(0x2300)
 87  _SI4713_PROP_TX_ASQ_LEVEL_LOW           = const(0x2301)
 88  _SI4713_PROP_TX_ASQ_DURATION_LOW        = const(0x2302)
 89  _SI4713_PROP_TX_AQS_LEVEL_HIGH          = const(0x2303)
 90  _SI4713_PROP_TX_AQS_DURATION_HIGH       = const(0x2304)
 91  _SI4713_PROP_TX_RDS_INTERRUPT_SOURCE    = const(0x2C00)
 92  _SI4713_PROP_TX_RDS_PI                  = const(0x2C01)
 93  _SI4713_PROP_TX_RDS_PS_MIX              = const(0x2C02)
 94  _SI4713_PROP_TX_RDS_PS_MISC             = const(0x2C03)
 95  _SI4713_PROP_TX_RDS_PS_REPEAT_COUNT     = const(0x2C04)
 96  _SI4713_PROP_TX_RDS_MESSAGE_COUNT       = const(0x2C05)
 97  _SI4713_PROP_TX_RDS_PS_AF               = const(0x2C06)
 98  _SI4713_PROP_TX_RDS_FIFO_SIZE           = const(0x2C07)
 99  #pylint: enable=bad-whitespace
100  
101  
102  class SI4713:
103      """SI4713 RDS FM transmitter.  Initialize by specifying:
104       - i2c: The I2C bus connected to the board.
105  
106      Optionally specify:
107       - address: The I2C address if it has been changed.
108       - reset: A DigitalInOut instance connected to the board's reset line,
109                this will be used to perform a soft reset when necessary.
110       - timeout_s: The amount of time (in seconds) to wait for a command to
111                    succeed.  If this timeout is exceed a runtime error is thrown.
112      """
113  
114      # Class-level buffer to reduce allocations and heap fragmentation.
115      # This is not thread-safe or re-entrant by design!
116      _BUFFER = bytearray(10)
117  
118      def __init__(self, i2c, *, address=_SI4710_ADDR1, reset=None, timeout_s=0.1):
119          self._timeout_s = timeout_s
120          self._device = i2c_device.I2CDevice(i2c, address)
121          # Configure reset line if it was provided.
122          self._reset = reset
123          if self._reset is not None:
124              self._reset.switch_to_output(value=True)
125          self.reset()
126          # Check product ID.
127          if self._get_product_number() != 13:
128              raise RuntimeError('Failed to find SI4713, check wiring!')
129  
130      def _read_u8(self, address):
131          # Read an 8-bit unsigned value from the specified 8-bit address.
132          with self._device as i2c:
133              self._BUFFER[0] = address & 0xFF
134              i2c.write(self._BUFFER, end=1, stop=True)
135              i2c.readinto(self._BUFFER, end=1)
136          return self._BUFFER[0]
137  
138      def _read_into(self, buf, count=None):
139          # Read data directly from the I2C bus into the specified buffer.  If
140          # count is not provided the buffer will be filled, otherwise count bytes
141          # will be written to the buffer.
142          if count is None:
143              count = len(buf)
144          with self._device as i2c:
145              i2c.readinto(buf, end=count)
146  
147      def _write_from(self, buf, count=None):
148          # Write a buffer of byte data to the chip.  If count is not specified
149          # then the entire buffer is written, otherwise count bytes are written.
150          # This function will wait to verify the command was successfully
151          # sent/performed and if it fails to see success in the specified
152          # timeout (100ms by default) it will throw an exception.
153          if count is None:
154              count = len(buf)
155          # Send command.
156          with self._device as i2c:
157              i2c.write(buf, end=count, stop=True)
158          # Poll the status bit waiting for success or throwing a timeout error.
159          start = time.monotonic()
160          while True:
161              with self._device as i2c:
162                  i2c.readinto(self._BUFFER, end=1)
163              if self._BUFFER[0] & _SI4710_STATUS_CTS > 0:
164                  return
165              if time.monotonic() - start > self._timeout_s:
166                  raise RuntimeError('Timeout waiting for SI4723 response, check wiring!')
167  
168      def _set_property(self, prop, val):
169          # Set a property of the SI4713 chip.  These are both 16-bit values.
170          self._BUFFER[0] = _SI4710_CMD_SET_PROPERTY
171          self._BUFFER[1] = 0
172          self._BUFFER[2] = prop >> 8
173          self._BUFFER[3] = prop & 0xFF
174          self._BUFFER[4] = val >> 8
175          self._BUFFER[5] = val & 0xFF
176          self._write_from(self._BUFFER, count=6)
177  
178      def _get_product_number(self):
179          # Retrieve the product number/ID value of the chip and return it.
180          # First send a get revision command.
181          self._BUFFER[0] = _SI4710_CMD_GET_REV
182          self._BUFFER[1] = 0
183          self._write_from(self._BUFFER, count=2)
184          # Then read 9 bytes to get the response data and parse out pn.
185          with self._device as i2c:
186              i2c.readinto(self._BUFFER, end=9)
187          return self._BUFFER[1]
188          # Other potentially useful but unused data:
189          #fw = (self._BUFFER[2] << 8) | self._BUFFER[3]
190          #patch = (self._BUFFER[4] << 8) | self._BUFFER[5]
191          #cmp = (self._BUFFER[6] << 8) | self._BUFFER[7]
192          #rev = (self._BUFFER[8])
193  
194      def reset(self):
195          """Perform a reset of the chip using the reset line.  Will also
196          perform necessary chip power up procedures."""
197          # Toggle reset low for a few milliseconds if the line was provided.
198          if self._reset is not None:
199              # Toggle reset line low for a few milliseconds to reset the chip.
200              self._reset.value = True
201              time.sleep(0.01)
202              self._reset.value = False
203              time.sleep(0.01)
204              self._reset.value = True
205          # Next perform all the chip power up procedures.
206          self._BUFFER[0] = _SI4710_CMD_POWER_UP
207          self._BUFFER[1] = 0x12
208          # CTS interrupt disabled
209          # GPO2 output disabled
210          # Boot normally
211          # xtal oscillator ENabled
212          # FM transmit
213          self._BUFFER[2] = 0x50  # analog input mode
214          self._write_from(self._BUFFER, count=3)
215          # configuration! see datasheet page 254
216          # crystal is 32.768
217          self._set_property(_SI4713_PROP_REFCLK_FREQ, 32768)
218          # 74uS pre-emph (USA std)
219          self._set_property(_SI4713_PROP_TX_PREEMPHASIS, 0)
220          # max gain?
221          self._set_property(_SI4713_PROP_TX_ACOMP_GAIN, 10)
222          # turn on limiter and AGC
223          self._set_property(_SI4713_PROP_TX_ACOMP_ENABLE, 0x0)
224  
225      @property
226      def interrupt_status(self):
227          """Read the interrupt bit status of the chip.  This will return a byte
228          value with interrupt status bits as defined by the radio, see page
229          11 of the AN332 programming guide:
230          https://www.silabs.com/documents/public/application-notes/AN332.pdf
231          """
232          return self._read_u8(_SI4710_CMD_GET_INT_STATUS)
233  
234      def _poll_interrupt_status(self, expected):
235          # Poll the interrupt status bit for an expected exact value.
236          # Will throw an exception if the timeout is exceeded before the status
237          # reaches the desired value.
238          start = time.monotonic()
239          while self.interrupt_status != expected:
240              time.sleep(0.01)  # Short delay for other processing.
241              if time.monotonic() - start > self._timeout_s:
242                  raise RuntimeError('Timeout waiting for SI4713 to respond!')
243  
244      def _tune_status(self):
245          # Retrieve the tune status command values from the radio.  Will store
246          # the raw result of the tune status command in self._BUFFER (see page
247          # 22 of AN332).
248          # Construct tune status command and send it.
249          self._BUFFER[0] = _SI4710_CMD_TX_TUNE_STATUS
250          self._BUFFER[1] = 0x01
251          self._write_from(self._BUFFER, count=2)
252          # Now read 8 bytes of response data.
253          self._read_into(self._BUFFER, count=8)
254  
255      def _asq_status(self):
256          # Retrieve the ASQ (audio signal quality) status from the chip.  Will
257          # store the raw result of the ASQ status command in self._BUFFER (see
258          # page 25 of AN332).
259          # Construct ASQ status command and send it.
260          self._BUFFER[0] = _SI4710_CMD_TX_ASQ_STATUS
261          self._BUFFER[1] = 0x01
262          self._write_from(self._BUFFER, count=2)
263          # Now read 5 bytes of response data.
264          self._read_into(self._BUFFER, count=5)
265  
266      @property
267      def tx_frequency_khz(self):
268          """Get and set the transmit frequency of the chip (in kilohertz).  See
269          AN332 page 19 for a discussion of the constraints on this value, in
270          particular only a multiple of 50khz can be specified, and the value
271          must be between 76 and 108mhz.
272          """
273          self._tune_status()
274          # Reconstruct frequency from tune status response.
275          frequency = (self._BUFFER[2] << 8) | self._BUFFER[3]
276          # Return result, scaling back to khz from 10's of khz.
277          return frequency * 10
278  
279      @tx_frequency_khz.setter
280      def tx_frequency_khz(self, val):
281          assert 76000 <= val <= 108000
282          assert (val % 50) == 0
283          # Convert to units of 10khz that chip expects.
284          val = (val // 10) & 0xFFFF
285          # Construct tune command.
286          self._BUFFER[0] = _SI4710_CMD_TX_TUNE_FREQ
287          self._BUFFER[1] = 0
288          self._BUFFER[2] = val >> 8
289          self._BUFFER[3] = val & 0xFF
290          self._write_from(self._BUFFER, count=4)
291          # Wait for the CTS and tune complete bits to be set.
292          self._poll_interrupt_status(0x81)
293  
294      @property
295      def tx_power(self):
296          """Get and set the transmit power in dBuV (decibel microvolts).  Can
297          be a value within the range of 88-115, or 0 to indicate transmission
298          power is disabled.  Setting this value assumes auto-tuning of antenna
299          capacitance, see the set_tx_power_capacitance function for explicit
300          control of setting both transmit power and capacitance if needed.
301          """
302          self._tune_status()
303          # Reconstruct power from tune status response and return it.
304          return self._BUFFER[5]
305  
306      def set_tx_power_capacitance(self, tx_power, capacitance):
307          """Set both the transmit power (in dBuV, from 88-115) and antenna
308          capacitance of the transmitter.  Capacitance is a value specified in
309          pF from 0.25 to 47.75 (in 0.25 steps), or 0 to indicate automatic
310          tuning. You typically don't need to use this function unless you want
311          explicit control of tuning antenna capacitance, instead for simple
312          transmit power changes use the tx_power property (which assumes
313          automatic antenna capacitance).
314          """
315          # Validate tx power and capacitance are in allowed range.
316          assert tx_power == 0 or (88 <= tx_power <= 115)
317          assert capacitance == 0 or (0.25 <= capacitance <= 47.75)
318          # Convert capacitance to 0.25 pF units that chip expects.
319          capacitance = int(capacitance / 0.25)
320          # Construct a tune power command and send it.
321          self._BUFFER[0] = _SI4710_CMD_TX_TUNE_POWER
322          self._BUFFER[1] = 0
323          self._BUFFER[2] = 0
324          self._BUFFER[3] = tx_power & 0xFF
325          self._BUFFER[4] = capacitance & 0xFF
326          self._write_from(self._BUFFER, count=5)
327  
328      @tx_power.setter
329      def tx_power(self, val):
330          # Assume automatic antenna capacitance tuning (0 value).
331          self.set_tx_power_capacitance(val, 0)
332  
333      @property
334      def tx_antenna_capacitance(self):
335          """Read the transmit antenna capacitance in pico-Farads (pF).  Use the
336          set_tx_power_capacitance function to change this value (must also
337          change transmit power at the same time).  It's uncommon to adjust this
338          beyond the automatic tuning option!
339          """
340          self._tune_status()
341          # Reconstruct capacitance from tune status response and return it
342          # (scaled appropriately for pF units).
343          return self._BUFFER[6] * 0.25
344  
345      def received_noise_level(self, frequency_khz, antenna_capacitance=0):
346          """Measure the received noise level for the specified frequency (in
347          kilohertz, 76mhz - 108mhz and must be a multiple of 50) and return its
348          value in units of dBuV (decibel microvolts).  Will use automatic
349          antenna capacitance tuning by default, otherwise specify an antenna
350          capacitance in pF from 0.25 to 47.75 (only steps of 0.25pF are
351          supported).
352          """
353          # Validate frequency and capacitance.
354          assert 76000 <= frequency_khz <= 108000
355          assert (frequency_khz % 50) == 0
356          assert antenna_capacitance == 0 or \
357                 (0.25 <= antenna_capacitance <= 47.75)
358          # Convert frequency and capacitance to units used by the chip.
359          frequency_khz = (frequency_khz // 10) & 0xFFFF
360          antenna_capacitance = int(antenna_capacitance / 0.25)
361          # First send a read tune measure command to kick off the measurement.
362          self._BUFFER[0] = _SI4710_CMD_TX_TUNE_MEASURE
363          self._BUFFER[1] = 0
364          self._BUFFER[2] = frequency_khz >> 8
365          self._BUFFER[3] = frequency_khz & 0xFF
366          self._BUFFER[4] = antenna_capacitance
367          self._write_from(self._BUFFER, count=5)
368          # Wait for CTS and tune measure complete bits to be set.
369          self._poll_interrupt_status(0x81)
370          # Finally make a request for tune status and grab the received noise
371          # level value now that it's up to date.
372          self._tune_status()
373          return self._BUFFER[7]
374  
375      @property
376      def input_level(self):
377          """Read the input level of audio to the chip and return it in dBfs
378          units.
379          """
380          # Perform ASQ request, then parse out 8 bit _signed_ input level value.
381          self._asq_status()
382          return ustruct.unpack('bbbbb', self._BUFFER)[4]
383  
384      @property
385      def audio_signal_status(self):
386          """Retrieve the ASQ or audio signal quality status value from the chip.
387          This is a byte that indicates if the transmitted input audio signal is
388          overmodulating (too high) or above/below input audio level thresholds.
389          See page 25 of AN332 for more discussion of this value:
390          https://www.silabs.com/documents/public/application-notes/AN332.pdf
391          """
392          # Perform ASQ request, the parse out the status byte.
393          self._asq_status()
394          return self._BUFFER[1]
395  
396      def gpio_control(self, gpio1=False, gpio2=False, gpio3=False):
397          """Control the GPIO outputs of the chip.  Each gpio1, gpio2, gpio3
398          parameter is a boolean that indicates if that GPIO channel
399          (corresponding to GPIO1, GPIO2, GPIO3 of the chip respectively) is
400          driven actively (True) or is high-impedence/off (False).  By default
401          any unspecified GPIO is set to high-impedence/off unless otherwise
402          provided.
403          """
404          # Construct GPIO control state and send a GPIO control command.
405          control = 0x00
406          if gpio1:
407              control |= 0b00000010
408          if gpio2:
409              control |= 0b00000100
410          if gpio3:
411              control |= 0b00001000
412          self._BUFFER[0] = _SI4710_CMD_GPO_CTL
413          self._BUFFER[1] = control
414          self._write_from(self._BUFFER, count=2)
415  
416      def gpio_set(self, gpio1=False, gpio2=False, gpio3=False):
417          """Drive the GPIO outputs of the chip that are enabled with active
418          output.  Each gpio1, gpio2, gpio3 parameter is a boolean that indicates
419          if the associated GPIO (corresponding to GPIO1, GPIO2, GPIO3 of the
420          chip respectively) is driven high (True) or low (False).  By default
421          all GPIO are assumed to be set low (False) unless otherwise
422          specified.  Note that you must first set GPIOs to active output with
423          the gpio_control function to see their output physically change.
424          """
425          # Construct GPIO set command and send it.
426          set_command = 0x00
427          if gpio1:
428              set_command |= 0b00000010
429          if gpio2:
430              set_command |= 0b00000100
431          if gpio3:
432              set_command |= 0b00001000
433          self._BUFFER[0] = _SI4710_CMD_GPO_SET
434          self._BUFFER[1] = set_command
435          self._write_from(self._BUFFER, count=2)
436  
437      def _set_rds_station(self, station):
438          # Set the RDS station broadcast value.
439          station_length = len(station)
440          assert 0 <= station_length <= 96
441          self._BUFFER[0] = _SI4710_CMD_TX_RDS_PS
442          # Fire off each 4 byte update of the station value.
443          for i in range(0, station_length, 4):
444              self._BUFFER[1] = i // 4
445              self._BUFFER[2] = station[i] if i < station_length else ' '
446              self._BUFFER[3] = station[i+1] if i+1 < station_length else ' '
447              self._BUFFER[4] = station[i+2] if i+2 < station_length else ' '
448              self._BUFFER[5] = station[i+3] if i+3 < station_length else ' '
449              self._write_from(self._BUFFER, count=6)
450  
451      def _set_rds_buffer(self, rds_buffer):
452          # Set the RDS buffer broadcast value.
453          buf_length = len(rds_buffer)
454          # 53 blocks in the circular buffer, each 2 bytes long.
455          assert 0 <= buf_length <= 106
456          self._BUFFER[0] = _SI4710_CMD_TX_RDS_BUFF
457          self._BUFFER[1] = 0x06  # Clear the buffer and start update with first
458                                  # request, then future requests will turn off
459                                  # the empty bit to continue setting data.
460          self._BUFFER[2] = 0x20
461          # Fire off each 4 byte update of the station value.
462          for i in range(0, buf_length, 4):
463              self._BUFFER[3] = i // 4
464              self._BUFFER[4] = rds_buffer[i] if i < buf_length else ' '
465              self._BUFFER[5] = rds_buffer[i+1] if i+1 < buf_length else ' '
466              self._BUFFER[6] = rds_buffer[i+2] if i+2 < buf_length else ' '
467              self._BUFFER[7] = rds_buffer[i+3] if i+3 < buf_length else ' '
468              self._write_from(self._BUFFER, count=8)
469              # Make sure to turn off empty bit for next requests.
470              self._BUFFER[1] = 0x04
471  
472      rds_station = property(None, _set_rds_station, None,
473                             """Set the RDS broadcast station to the specified
474                             byte string.  Can be at most 96 bytes long and will
475                             be padded with blank spaces if less.
476                             """)
477  
478      rds_buffer = property(None, _set_rds_buffer, None,
479                            """Set the RDS broadcast buffer to the specified byte
480                            string.  Can be at most 106 bytes long and will be
481                            padded with blank spaces if less.
482                            """)
483  
484      def configure_rds(self, program_id, station=None, rds_buffer=None):
485          """Configure and enable the RDS broadcast of the specified program ID.
486          Program ID must be a 16-bit value that will be broacast on the RDS
487          bands of the transmitter.  Specify optional station and RDS buffer
488          strings that will be used to broadcast the station and currently
489          playing song information, or later set the rds_station and
490          rds_buffer to change these values too.  The station value is up to 96
491          bytes long, and the buffer is up to 106 bytes long.  Note this will
492          configure RDS properties of the chip for a typical North American RDS
493          broadcast (deviation, mix, repeat, etc. parameters).
494          """
495          assert 0 <= program_id <= 65535
496          # Set RDS parameters:
497          # 66.25KHz (default is 68.25)
498          self._set_property(_SI4713_PROP_TX_AUDIO_DEVIATION, 6625)
499          # 2KHz (default)
500          self._set_property(_SI4713_PROP_TX_RDS_DEVIATION, 200)
501          # RDS IRQ
502          self._set_property(_SI4713_PROP_TX_RDS_INTERRUPT_SOURCE, 0x0001)
503          # program identifier
504          self._set_property(_SI4713_PROP_TX_RDS_PI, program_id)
505          # 50% mix (default)
506          self._set_property(_SI4713_PROP_TX_RDS_PS_MIX, 0x03)
507          # RDSD0 & RDSMS (default)
508          self._set_property(_SI4713_PROP_TX_RDS_PS_MISC, 0x1808)
509          # 3 repeats (default)
510          self._set_property(_SI4713_PROP_TX_RDS_PS_REPEAT_COUNT, 3)
511          self._set_property(_SI4713_PROP_TX_RDS_MESSAGE_COUNT, 1)
512          # no AF
513          self._set_property(_SI4713_PROP_TX_RDS_PS_AF, 0xE0E0)
514          self._set_property(_SI4713_PROP_TX_RDS_FIFO_SIZE, 0)
515          self._set_property(_SI4713_PROP_TX_COMPONENT_ENABLE, 0x0007)
516          # Set station and buffer to initial values if specified.
517          if station is not None:
518              self._set_rds_station(station)
519          if rds_buffer is not None:
520              self._set_rds_buffer(rds_buffer)