sa828.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #!/usr/bin/python3
  2. import logging
  3. import serial
  4. import argparse
  5. import sys
  6. import os
  7. import time
  8. logging.basicConfig(format='%(name)s: %(levelname)s: %(message)s', level=logging.INFO)
  9. logger = logging.getLogger('SA828')
  10. CTCSSCDCSS_CODES = [
  11. "None", "67.0", "71.9", "74.4", "77.0", "79.7", "82.5", "85.4", "88.5",
  12. "91.5", "94.8", "97.4", "100.0", "103.5", "107.2", "110.9", "114.8", "118.8",
  13. "123.0", "127.3", "131.8", "136.5", "141.3", "146.2", "151.4", "156.7",
  14. "162.2", "167.9", "173.8", "179.9", "186.2", "192.8", "203.5", "210.7",
  15. "218.1", "225.7", "233.6", "241.8", "250.3",
  16. "023I", "025I", "026I", "031I", "032I", "036I", "043I", "047I", "051I", "053I", "054I",
  17. "065I", "071I", "072I", "073I", "074I", "114I", "115I", "116I", "125I", "131I", "132I",
  18. "134I", "143I", "152I", "155I", "156I", "162I", "165I", "172I", "174I", "205I", "223I",
  19. "226I", "243I", "244I", "245I", "251I", "261I", "263I", "265I", "271I", "306I", "311I",
  20. "315I", "331I", "343I", "346I", "351I", "364I", "365I", "371I", "411I", "412I", "413I",
  21. "423I", "431I", "432I", "445I", "464I", "465I", "466I", "503I", "506I", "516I", "532I",
  22. "546I", "565I", "606I", "612I", "624I", "627I", "631I", "632I", "654I", "662I", "664I",
  23. "703I", "712I", "723I", "731I", "732I", "734I", "743I", "754I",
  24. "023I", "025I", "026I", "031I", "032I", "036I", "043I", "047I", "051I", "053I", "054I",
  25. "065I", "071I", "072I", "073I", "074I", "114I", "115I", "116I", "125I", "131I", "132I",
  26. "134I", "143I", "152I", "155I", "156I", "162I", "165I", "172I", "174I", "205I", "223I",
  27. "226I", "243I", "244I", "245I", "251I", "261I", "263I", "265I", "271I", "306I", "311I",
  28. "315I", "331I", "343I", "346I", "351I", "364I", "365I", "371I", "411I", "412I", "413I",
  29. "423I", "431I", "432I", "445I", "464I", "465I", "466I", "503I", "506I", "516I", "532I",
  30. "546I", "565I", "606I", "612I", "624I", "627I", "631I", "632I", "654I", "662I", "664I",
  31. "703I", "712I", "723I", "731I", "732I", "734I", "743I", "754I"
  32. ]
  33. DEFAULT_BAUDRATE = 9600
  34. BAUD_RATES = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]
  35. class SA828:
  36. EOL = "\r\n"
  37. RETOK = "OK\r\n"
  38. RETERROR = "ERROR\r\n"
  39. READ_TIMEOUT = 3
  40. def __init__(self, port=None, baud=DEFAULT_BAUDRATE):
  41. self.serial = None
  42. try:
  43. self.serial = serial.Serial(port=port,
  44. baudrate=baud,
  45. parity=serial.PARITY_NONE,
  46. stopbits=serial.STOPBITS_ONE,
  47. bytesize=serial.EIGHTBITS,
  48. timeout=self.READ_TIMEOUT)
  49. logger.debug(self.serial)
  50. except serial.SerialException as err:
  51. logger.debug(err)
  52. if not isinstance(self.serial, serial.Serial):
  53. raise IOError('Error openning the serial port')
  54. def close(self):
  55. self.serial.close()
  56. def send(self, *args):
  57. data = ','.join(args)
  58. logger.debug('Sending: %s', data)
  59. data = bytes(data + self.EOL, 'ascii')
  60. try:
  61. self.serial.write(data)
  62. except serial.SerialException as err:
  63. logger.error(err)
  64. def readline(self):
  65. try:
  66. line = self.serial.readline()
  67. except serial.SerialException as err:
  68. logger.warning(err)
  69. return None
  70. try:
  71. line = line.decode('ascii')
  72. except UnicodeDecodeError:
  73. logger.debug(line)
  74. logger.error('Character decode error: Check your baudrate')
  75. logger.debug('Received: %s', line.rstrip())
  76. return line.rstrip()
  77. def version(self):
  78. self.send("\x41\x41\x46\x41\x41")
  79. time.sleep(0.5)
  80. reply = self.readline()
  81. return reply
  82. def read_parameter(self):
  83. self.send("AAFA1")
  84. time.sleep(0.5)
  85. reply = self.readline()
  86. return reply
  87. def reset(self):
  88. self.send("AAFA2")
  89. time.sleep(0.5)
  90. reply = self.readline()
  91. return reply
  92. def set_parameter(self, data):
  93. self.send("AAFA3" + data)
  94. time.sleep(3)
  95. reply = self.readline()
  96. return reply
  97. def main():
  98. parser = argparse.ArgumentParser(description="generate configuration for switch port")
  99. parser.add_argument("--debug", action="store_true", default=False)
  100. parser.add_argument("--port", type=str, default="/dev/ttyUSB0", help="Serial port [default: linux console port]")
  101. parser.add_argument('--speed', type=int, choices=BAUD_RATES, default=DEFAULT_BAUDRATE, help="Connection speed")
  102. subparsers = parser.add_subparsers()
  103. p_readversion = subparsers.add_parser("version", help='Read the version of the radio')
  104. p_readversion.set_defaults(func="version")
  105. p_reset = subparsers.add_parser("reset", help='Reset the radio')
  106. p_reset.set_defaults(func="reset")
  107. p_readparameter = subparsers.add_parser("readparameter", help='Read the full configuration of the radio')
  108. p_readparameter.set_defaults(func="readparameter")
  109. p_setparameter = subparsers.add_parser("setparameter", help='Set the configuration of the radio')
  110. p_setparameter.set_defaults(func="setparameter")
  111. p_setparameter.add_argument("--channel", required=True, help="Channel number, [1-16]")
  112. p_setparameter.add_argument("--txfreq",required=True, help="Transmit frequency : format '145.4500'")
  113. p_setparameter.add_argument("--rxfreq",required=True, help="Receive frequency : format '145.4500'")
  114. p_setparameter.add_argument("--txctcss",required=True, help="Transmit CTCSS : format 'None' or '88.5' or 023I or 023N")
  115. p_setparameter.add_argument("--rxctcss",required=True, help="Receive CTCSS : format 'None' or '88.5' or 023I or 023N")
  116. p_setparameter.add_argument("--squelch",required=True, help="Set the Squelch : [0-8]")
  117. opts = parser.parse_args()
  118. if not hasattr(opts, 'func'):
  119. print('sa818: error: the following arguments are required: {version,reset,readparameter,setparameter}\n'
  120. 'use --help for more information',
  121. file=sys.stderr)
  122. sys.exit(os.EX_USAGE)
  123. try:
  124. radio = SA828(opts.port, opts.speed)
  125. except (IOError, SystemError) as err:
  126. logger.error(err)
  127. sys.exit(os.EX_IOERR)
  128. if opts.debug:
  129. logger.setLevel(logging.DEBUG)
  130. logger.debug(opts)
  131. if opts.func == 'version':
  132. logger.info('Version: %s', radio.version())
  133. if opts.func == 'reset':
  134. logger.info('Resetting the radio')
  135. radio.reset()
  136. if opts.func == 'readparameter':
  137. params=radio.read_parameter().split(",")
  138. params[0]=params[0][2:]
  139. print("Configuration:")
  140. print("==============")
  141. for i in range(0, 16):
  142. if i == 0:
  143. print("Channel %s:\tTx : %s\tRx : %s" % (i+1, params[i], params[i+1]))
  144. else:
  145. print("Channel %s:\tTx : %s\tRx : %s" % (i+1, params[i*2], params[i*2+1]))
  146. print("CTCS/CDCS :\tTx : %s\t Rx : %s" % (CTCSSCDCSS_CODES[int(params[32])], CTCSSCDCSS_CODES[int(params[33])]))
  147. print("Squelch :\t%s" % params[34])
  148. if opts.func == 'setparameter':
  149. logger.info('Setting the radio')
  150. params=radio.read_parameter().split(",")
  151. params[0]=params[0][2:]
  152. channel = {}
  153. for i in range(0, 16):
  154. if i == 0:
  155. channel[i+1]=[params[i],params[i+1]]
  156. else:
  157. channel[i+1]=[params[i*2],params[i*2+1]]
  158. channel[int(opts.channel)]=[opts.txfreq,opts.rxfreq]
  159. # Creation de la chaine complete
  160. for i in range(0, 16):
  161. if i == 0:
  162. tosend="%s,%s," % (channel[i+1][0],channel[i+1][1])
  163. else:
  164. tosend=tosend+"%s,%s," % (channel[i+1][0],channel[i+1][1])
  165. #search value in array and return position
  166. for i in range(0,len(CTCSSCDCSS_CODES)):
  167. if CTCSSCDCSS_CODES[i] == opts.txctcss:
  168. txctcss=i
  169. break
  170. else:
  171. txctcss=0
  172. for i in range(0,len(CTCSSCDCSS_CODES)):
  173. if CTCSSCDCSS_CODES[i] == opts.rxctcss:
  174. rxctcss=i
  175. break
  176. else:
  177. rxctcss=0
  178. tosend=tosend+"%03d,%03d,%s" % (txctcss,rxctcss,opts.squelch)
  179. radio.set_parameter(tosend)
  180. if __name__ == "__main__":
  181. main()