pseudo_jjy.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. from machine import RTC
  2. from machine import Pin
  3. import rp2
  4. import utime as time
  5. import ntptime
  6. import network
  7. from config import WIFI_CONFIG
  8. # 40kHzキャリア発振ステートマシン
  9. @rp2.asm_pio(
  10. set_init=(rp2.PIO.OUT_LOW),
  11. autopull=False,
  12. )
  13. def _40kHz_osc():
  14. wrap_target()
  15. wait(0, irq, 4) # IRQ4 = on/off: キャリア発振/停止
  16. set(pins, 1) # High
  17. nop() # 1Clock
  18. set(pins, 0) # Low
  19. wrap()
  20. # JJY変調ステートマシン
  21. @rp2.asm_pio(
  22. autopull=False,
  23. sideset_init=(rp2.PIO.OUT_LOW),
  24. fifo_join=rp2.PIO.JOIN_TX
  25. )
  26. def _JJY_Pulse():
  27. irq(4).side(0) # IRQ4 Set / Carrier stop
  28. wrap_target()
  29. pull(block) # 1
  30. mov(x, osr) # 2
  31. pull(block) # 3
  32. mov(y, osr) # 4
  33. irq(clear,4).side(1) # IRQ4 clear/ Carrier start
  34. label("send_loop")
  35. jmp(x_dec, "send_loop")
  36. irq(4).side(0) # IRQ4 Set / Carrier stop
  37. label("wait_loop")
  38. jmp(y_dec, "wait_loop")
  39. wrap()
  40. # JJY変調コード
  41. JJY_PULSE_CODE = ((800*1000-5,200*1000-1), # 0 = 800ms carrier + 200ms blank
  42. (500*1000-5,500*1000-1), # 1 = 500ms carrier + 500ms blank
  43. (200*1000-5,800*1000-1)) # Position Marker = 200ms carrier + 800ms blank
  44. JJY_MARKER = 2
  45. # JJYタイムコードエンコーダー
  46. def jjy_encode(minute, hour, yearday, year, weekday):
  47. code = [0] * 60
  48. pa1 = 0 # parity
  49. pa2 = 0
  50. # marker
  51. for i in (0, 9, 19, 29, 39, 49, 59):
  52. code[i] = JJY_MARKER
  53. # minute
  54. m10, m1 = minute // 10, minute % 10
  55. code[1], code[2], code[3] = (m10 >> 2) & 1, (m10 >> 1) & 1, m10 & 1
  56. code[5], code[6], code[7], code[8] = (m1 >> 3) & 1, (m1 >> 2) & 1, (m1 >> 1) & 1, m1 & 1
  57. pa2 = (code[1]+code[2]+code[3]+code[5]+code[6]+code[7]+code[8]) % 2
  58. # hour
  59. h10, h1 = hour // 10, hour % 10
  60. code[12], code[13] = (h10 >> 1) & 1, h10 & 1
  61. code[15], code[16], code[17], code[18] = (h1 >> 3) & 1, (h1 >> 2) & 1, (h1 >> 1) & 1, h1 & 1
  62. pa1 = (code[12]+code[13]+code[15]+code[16]+code[17]+code[18]) % 2
  63. # yearday
  64. yd100, yd10, yd1 = yearday // 100, (yearday % 100) // 10, yearday % 10
  65. code[22], code[23] = (yd100 >> 1) & 1, yd100 & 1
  66. code[25], code[26], code[27], code[28] = (yd10 >> 3) & 1, (yd10 >> 2) & 1, (yd10 >> 1) & 1, yd10 & 1
  67. code[30], code[31], code[32], code[33] = (yd1 >> 3) & 1, (yd1 >> 2) & 1, (yd1 >> 1) & 1, yd1 & 1
  68. # parity
  69. code[36] = pa1
  70. code[37] = pa2
  71. # year下2桁
  72. year %= 100
  73. y10, y1 = year // 10, year % 10
  74. code[41], code[42], code[43], code[44] = (y10 >> 3) & 1, (y10 >> 2) & 1, (y10 >> 1) & 1, y10 & 1
  75. code[45], code[46], code[47], code[48] = (y1 >> 3) & 1, (y1 >> 2) & 1, (y1 >> 1) & 1, y1 & 1
  76. # weekday
  77. jjy_weekday = (weekday + 1) % 7
  78. code[50], code[51], code[52] = (jjy_weekday >> 2) & 1, (jjy_weekday >> 1) & 1, jjy_weekday & 1
  79. return code
  80. # Wi-Fi接続
  81. def wifi_connect(ssid, passkey, timeout=20):
  82. conn = network.WLAN(network.STA_IF)
  83. if conn.isconnected():
  84. return conn
  85. conn.active(True)
  86. conn.connect(ssid, passkey)
  87. while not conn.isconnected() and timeout > 0:
  88. time.sleep(1)
  89. timeout -= 1
  90. if conn.isconnected():
  91. return conn
  92. else:
  93. return None
  94. # RTCをNTPで設定
  95. def setup_rtc_from_ntp():
  96. conn = wifi_connect(WIFI_CONFIG["ssid"], WIFI_CONFIG["pass"])
  97. if conn is not None:
  98. conn.ifconfig()
  99. time.sleep(1)
  100. rtc = RTC()
  101. ntptime.host = 'ntp.nict.jp' # 日本用
  102. now = time.localtime(ntptime.time() + 9 * 60 * 60)
  103. rtc.datetime((now[0], now[1], now[2], now[6], now[3], now[4], now[5], 0))
  104. print("Set RTC to %d/%d/%d, %d:%d:%d" % (now[0], now[1], now[2], now[3], now[4], now[5]))
  105. conn.active(False)
  106. conn = None
  107. else:
  108. print("Cant connect to %s" % WIFI_CONFIG["ssid"])
  109. # RTCをあわせる間隔(分)
  110. EXPIRE = 60 * 6 # 6時間に1回RTCを補正する
  111. # 疑似JJY出力ポート
  112. OSC_OUT = 16
  113. INDICATOR_PIN = 15
  114. if __name__ == "__main__":
  115. # 疑似JJY送信機の準備
  116. osc = Pin(OSC_OUT, Pin.OUT)
  117. osc.value(0)
  118. # 160kHz / 4 = 40kHz
  119. sm_osc = rp2.StateMachine(0, _40kHz_osc, freq=160000, set_base=osc)
  120. # 1MHz = 1us
  121. sm_jjy = rp2.StateMachine(1, _JJY_Pulse, freq=1000000,sideset_base=Pin(INDICATOR_PIN))
  122. # 先にsm_jjyを起動してIRQ4を立てておく
  123. sm_jjy.active(1)
  124. sm_osc.active(1)
  125. # 時間計測カウンタ(分)
  126. counter = EXPIRE + 1
  127. try:
  128. while(True):
  129. counter += 1
  130. # 初回及びEXPIRE分おきにRTCを更新して時間を調整
  131. if counter > EXPIRE:
  132. counter = 0
  133. # FIFOが空になるまで待つ
  134. while sm_jjy.tx_fifo() != 0:
  135. time.sleep(1)
  136. # 更に待つ
  137. time.sleep(2)
  138. # これで疑似JJYの送信は停止しているはず
  139. # sm.active(0)からのリスタートが何故か機能しないのでこの方法で止めるしかない
  140. # NTPでRTCを更新
  141. try:
  142. setup_rtc_from_ntp()
  143. except Exception:
  144. # なにか問題が起きても無視する
  145. pass
  146. # 次の0秒を待つ
  147. while True:
  148. now = time.localtime()
  149. if now[5] == 0:
  150. break
  151. jjy_code = jjy_encode(now[4], now[3], now[7], now[0], now[6])
  152. if sm_jjy.tx_fifo() != 0:
  153. # 送信し終わってない場合、RTCに調整が入ってズレた可能性があるので1分飛ばす
  154. time.sleep(1)
  155. continue
  156. # JJYタイムコード送信
  157. for code in jjy_code:
  158. # ブロックしても構わない
  159. sm_jjy.put(JJY_PULSE_CODE[code][0])
  160. sm_jjy.put(JJY_PULSE_CODE[code][1])
  161. except KeyboardInterrupt:
  162. print("keyboard interrupt")
  163. finally:
  164. sm_osc.active(0)
  165. sm_jjy.active(0)
  166. # PIO0からすべてのPIOコードを削除して終了する
  167. pio0 = rp2.PIO(0)
  168. pio0.remove_program()
  169. Pin(OSC_OUT, Pin.OUT).low()