你好哈布爾。
可能許多購買手錶或氣象站的人都在包裝上看到過無線電控制時鐘甚至原子鐘標誌。 這很方便,因為把時鐘放在桌子上就足夠了,過一會兒它就會自動調整到準確的時間。
讓我們弄清楚它是如何工作的並用 Python 編寫一個解碼器。
有不同的時間同步系統。 歐洲最流行的是德國製度
下面寫的所有內容都是關於 DCF77 的。
信號接收
DCF77 是一個長波電台,工作頻率為 77.5KHz,傳輸 AM 信號。 該站容量為50千瓦,距離法蘭克福25公里,於1959年開始工作,1973年在準確時間中添加了日期信息。 77 kHz頻率下的波長非常大,因此天線場的尺寸也非常不錯(圖片來自維基百科):
有了這樣的天線和輸入功率,接收區域幾乎覆蓋整個歐洲、白俄羅斯、烏克蘭和俄羅斯部分地區。
任何人都可以錄製。 為此,只需轉到在線接收器
在同一個地方,我們按下下載按鈕並錄製一個幾分鐘長的片段。 當然,如果您有一個能夠記錄 77.5 kHz 頻率的“真實”接收器,則可以使用它。
當然,當通過互聯網接收準確的時間無線電信號時,我們不會得到真正準確的時間——信號的傳輸存在延遲。 但我們的目標只是了解信號的結構,為此互聯網記錄已經足夠了。 當然,在現實生活中,需要使用專門的設備來進行接收和解碼,下面將對此進行討論。
那麼,我們收到了記錄,讓我們開始處理它。
信號解碼
讓我們用 Python 加載該文件並查看其結構:
from scipy.io import wavfile
from scipy import signal
import matplotlib.pyplot as plt
import numpy as np
sample_rate, data = wavfile.read("dcf_websdr_2019-03-26T20_25_34Z_76.6kHz.wav")
plt.plot(data[:100000])
plt.show()
我們看到一個典型的幅度調製:
為了簡化解碼,我們使用希爾伯特變換獲取信號包絡:
analytic_signal = signal.hilbert(data)
A = np.abs(analytic_signal)
plt.plot(A[:100000])
擴展結果:
讓我們使用低通濾波器平滑噪聲發射,同時計算平均值,稍後解析時會派上用場。
b, a = signal.butter(2, 20.0/sample_rate)
zi = signal.lfilter_zi(b, a)
A, _ = signal.lfilter(b, a, A, zi=zi*A[0])
avg = (np.amax(A) + np.amin(A))/2
結果(黃線):幾乎是矩形信號,相當容易分析。
解析
首先您需要獲取位序列。 信號結構本身非常簡單。
脈衝被分成第二個間隔。 如果脈沖之間的距離為0.1s(即脈衝本身的長度為0.9s),我們在位序列中添加“0”,如果距離為0.2s(即長度為0.8s),我們在位序列中添加“1” “2”。 每分鐘的結束由一個 XNUMX 秒長的“長”脈衝指示,位序列重置為零,然後再次開始填充。
上面的代碼很容易用Python編寫。
sig_start, sig_stop = 0, 0
pos = 0
bits_str = ""
while pos < cnt - 4:
if A[pos] < avg and A[pos+1] > avg:
# Signal begin
sig_start = pos
if A[pos] > avg and A[pos+1] < avg:
# Signal end
sig_stop = pos
diff = sig_stop - sig_start
if diff < 0.85*sample_rate:
bits_str += "1"
if diff > 0.85*sample_rate and diff < 1.25*sample_rate:
bits_str += "0"
if diff > 1.5*sample_rate:
print(bits_str)
bits_str = ""
pos += 1
結果,我們得到了一個位序列,在我們的示例中,兩秒鐘的時間如下所示:
0011110110111000001011000001010000100110010101100010011000
0001111100110110001010100001010000100110010101100010011000
順便說一句,有趣的是信號中還有“第二層”數據。 位序列也被編碼為
我們的最後一步:獲取實際數據。 位每秒傳輸一次,因此我們只有 59 位,其中編碼了相當多的信息:
這些位的描述見
對於那些想要自己嘗試的人,解碼代碼在劇透下給出。
源代碼
def decode(bits):
if bits[0] != '0' or bits[20] != '1':
return
minutes, hours, day_of_month, weekday, month, year = map(convert_block,
(bits[21:28], bits[29:35], bits[36:42], bits[42:45],
bits[45:50], bits[50:58]))
days = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
print('{dow}, {dom:02}.{mon:02}.{y}, {h:02}:{m:02}'.format(h=hours, m=minutes, dow=days[weekday],
dom=day_of_month, mon=month, y=year))
def convert_ones(bits):
return sum(2**i for i, bit in enumerate(bits) if bit == '1')
def convert_tens(bits):
return 10*convert_ones(bits)
def right_parity(bits, parity_bit):
num_of_ones = sum(int(bit) for bit in bits)
return num_of_ones % 2 == int(parity_bit)
def convert_block(bits, parity=False):
if parity and not right_parity(bits[:-1], bits[-1]):
return -1
ones = bits[:4]
tens = bits[4:]
return convert_tens(tens) + convert_ones(ones)
運行該程序,我們將看到類似以下的輸出:
0011110110111000001011000001010000100110010101100010011000
Tuesday, 26.03.19, 21:41
0001111100110110001010100001010000100110010101100010011000
Tuesday, 26.03.19, 21:42
事實上,這就是所有的魔法。 這種系統的優點是解碼極其簡單,並且可以在任何最簡單的微控制器上完成。 我們只需計算脈衝的長度,累加 60 位,然後在每分鐘結束時我們就得到準確的時間。 與其他時間同步方法(例如 GPS,或者上帝保佑,互聯網:)相比,這種無線電同步實際上不需要電力 - 例如,一個普通的家庭氣象站只需 2 節 AA 電池即可工作大約一年。 因此,連手錶都是用無線電同步製造的,當然更不用說掛鐘或街頭車站時鐘了。
DCF的方便和簡單也吸引了DIY愛好者。 只需 10-20 美元,您就可以購買帶有現成接收器和 TTL 輸出的現成天線模塊,可連接到 Arduino 或其他控制器。
對於 Arduino 已經編寫並且
那些願意的人甚至可以通過安裝具有無線電同步功能的新機制來升級老祖母的手錶:
您可以使用關鍵字“無線電控制運動”在 eBay 上找到一個。
最後,為那些讀過本文的人提供一個生活竅門。 即使接下來的幾千公里內沒有任何無線電信號發射器,這樣的信號也很容易自己產生。 Google Play 上有一個名為“DCF77 Emulator”的程序,可以向耳機輸出信號。 據作者介紹,如果你把耳機線繞在時鐘上,它們就會捕捉到信號(我不知道如何捕捉到,因為普通耳機不會發出77KHz信號,但接收可能是由於諧波造成的)。 該程序在 Android 9 上對我來說根本不起作用 - 根本沒有聲音(或者也許我沒有聽到它 - 畢竟 77KHz :),但也許有人會更幸運。 然而,有些將自己打造為成熟的 DCF 信號發生器,這在同一個 Arduino 或 ESP32 上很容易實現:
(資源
結論
事實證明 DCF 系統確實非常簡單和方便。 借助簡單而便宜的接收器,您可以隨時隨地獲得準確的時間,當然是在接待區。 看來,儘管數字化和“物聯網”已經廣泛普及,但在未來很長一段時間內仍將需要這種簡單的解決方案。
來源: www.habr.com