minor changes in functionality
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# This is a compatible server for the TEF6686 firmware, but instead using the i2c control mode
|
||||
# This fully works with FM-DX-Webserver (no plugins)
|
||||
# Released under the Unlicense (however both FM-DX-Webserver and the firmware are under the tyranny of GPL-3)
|
||||
|
||||
import socket
|
||||
import hashlib
|
||||
@@ -9,28 +10,31 @@ import libtimer
|
||||
import time
|
||||
from tef import TEF6686
|
||||
from protocol import I2CPCClient
|
||||
import os
|
||||
|
||||
HOST = '0.0.0.0'
|
||||
PORT = 7373
|
||||
PASSWORD = "test"
|
||||
HOST = os.getenv("HOST") or '0.0.0.0'
|
||||
PORT = int(os.getenv("PORT") or 0) or 7373
|
||||
DEVICE = os.getenv("DEV") or "COM6"
|
||||
|
||||
PASSWORD = os.getenv("PW") or "test"
|
||||
CLOCK = 12000000 # DP-666
|
||||
|
||||
SALT_LENGTH = 16
|
||||
SS_UPDATE_INTERVAL = 0.125
|
||||
RDS_UPDATE_INTERVAL = 0.086
|
||||
|
||||
def generate_salt(length=SALT_LENGTH):
|
||||
def generate_salt(length=SALT_LENGTH) -> str:
|
||||
return "".join(secrets.choice(string.ascii_lowercase) for _ in range(length))
|
||||
|
||||
def compute_hash(salt, password):
|
||||
def compute_hash(salt: str, password: str):
|
||||
return hashlib.sha1((salt + password).encode()).hexdigest().encode()
|
||||
|
||||
def init_tef():
|
||||
p = I2CPCClient("COM6")
|
||||
p = I2CPCClient(DEVICE)
|
||||
tef = TEF6686(p)
|
||||
tef.init(clock=CLOCK)
|
||||
tef.AUDIO_Set_Mute(False)
|
||||
tef.AUDIO_Set_Volume(60)
|
||||
tef.AUDIO_Set_Volume(40)
|
||||
tef.FM_Tune_To(1, 9500)
|
||||
tef.FM_Set_RDS(1)
|
||||
tef.FM_Set_ChannelEqualizer(True)
|
||||
@@ -38,7 +42,7 @@ def init_tef():
|
||||
tef.APPL_Set_OperationMode(True)
|
||||
return tef
|
||||
|
||||
def authenticate(conn):
|
||||
def authenticate(conn: socket.socket):
|
||||
salt = generate_salt()
|
||||
conn.sendall(salt.encode() + b"\n")
|
||||
expected_hash = compute_hash(salt, PASSWORD)
|
||||
@@ -53,6 +57,8 @@ def process_command(tef: TEF6686, data: bytes, state: dict, conn: socket.socket)
|
||||
for cmd in data.splitlines():
|
||||
if cmd.startswith(b"T"):
|
||||
freq = int(cmd.decode().removeprefix("T").strip()) // 10
|
||||
if freq < 6500: continue
|
||||
if freq > 10800: continue
|
||||
state['last_tune'] = freq
|
||||
tef.FM_Tune_To(1, freq)
|
||||
out += f"T{freq*10}\n".encode()
|
||||
@@ -76,6 +82,7 @@ def process_command(tef: TEF6686, data: bytes, state: dict, conn: socket.socket)
|
||||
elif cmd.startswith(b"x"):
|
||||
out += b"OK\n"
|
||||
tef.APPL_Set_OperationMode(False)
|
||||
tef.FM_Tune_To(1, state["last_tune"])
|
||||
elif cmd.startswith(b"X"):
|
||||
tef.APPL_Set_OperationMode(True)
|
||||
elif cmd.startswith(b"W"):
|
||||
@@ -99,14 +106,11 @@ def process_command(tef: TEF6686, data: bytes, state: dict, conn: socket.socket)
|
||||
tef.FM_Set_Bandwidth((state["scan_bw"] == 0), state["scan_bw"])
|
||||
conn.sendall(b"U")
|
||||
for freq in range(state["scan_start"], state["scan_stop"] + state["scan_step"], state["scan_step"]):
|
||||
tef.FM_Tune_To(2, freq)
|
||||
time.sleep(0.0055)
|
||||
conn.sendall(str(freq * 10).encode())
|
||||
conn.sendall(b" = ")
|
||||
tef.FM_Tune_To(2, freq) # Auto mutes, less commands sent
|
||||
time.sleep(0.0064)
|
||||
_, level, *_ = tef.FM_Get_Quality_Data()
|
||||
if level is None: continue
|
||||
conn.sendall(str((level // 10) + 10).encode())
|
||||
conn.sendall(b", ")
|
||||
conn.sendall(str(freq * 10).encode() + b" = " + str(level // 10).encode() + b", ")
|
||||
conn.sendall(b"\n")
|
||||
|
||||
tef.FM_Tune_To(1, state["last_tune"])
|
||||
@@ -114,9 +118,9 @@ def process_command(tef: TEF6686, data: bytes, state: dict, conn: socket.socket)
|
||||
|
||||
return out
|
||||
|
||||
def send_signal_status(tef: TEF6686, conn: socket.socket, state):
|
||||
def send_signal_status(tef: TEF6686, conn: socket.socket, state: dict):
|
||||
_, _, stereo, _ = tef.FM_Get_Processing_Status()
|
||||
stereo = stereo if stereo is not None else 1000
|
||||
stereo = stereo if stereo is not None else 1000 # Default to Mono
|
||||
|
||||
status, level, usn, wam, _, bandwidth, _ = tef.FM_Get_Quality_Data()
|
||||
if status is None or level is None or wam is None or usn is None or bandwidth is None: return
|
||||
@@ -125,54 +129,70 @@ def send_signal_status(tef: TEF6686, conn: socket.socket, state):
|
||||
|
||||
data = b"S"
|
||||
if state['forced_mono']: data += b"M"
|
||||
elif stereo < 500: data += b"s"
|
||||
elif stereo < 350: data += b"s"
|
||||
else: data += b"m"
|
||||
|
||||
data += f"{level},{wam//10},{usn//10},{bandwidth}\n\n".encode()
|
||||
conn.sendall(data)
|
||||
|
||||
def send_rds_data(tef, conn):
|
||||
def send_rds_data(tef: TEF6686, conn: socket.socket):
|
||||
status, A, B, C, D, dec_error = tef.FM_Get_RDS_Data__decoder()
|
||||
if None in (status, A, B, C, D, dec_error): return
|
||||
if status is None or A is None or B is None or C is None or D is None or dec_error is None: return # Fucking hate pyright
|
||||
|
||||
if (status & (1 << 9) == 0) or (status & (1 << 15) == 0): return
|
||||
|
||||
err = 0
|
||||
err |= ((dec_error >> 8) & 0x30) >> 4
|
||||
err |= ((dec_error >> 8) & 0xC)
|
||||
err |= ((dec_error >> 8) & 3) << 4
|
||||
data = b""
|
||||
if (status & (1 << 13) == 0):
|
||||
err = 0
|
||||
err |= ((dec_error >> 8) & 0x30) >> 4
|
||||
err |= ((dec_error >> 8) & 0xC)
|
||||
err |= ((dec_error >> 8) & 3) << 4
|
||||
|
||||
data = b"R"
|
||||
data += f"{B:04X}{C:04X}{D:04X}{err:02X}".encode()
|
||||
data = b"R"
|
||||
data += f"{B:04X}{C:04X}{D:04X}{err:02X}\n".encode()
|
||||
|
||||
pi_error = dec_error >> 14
|
||||
pi_error = (dec_error >> 14) & 0b11
|
||||
if pi_error < 3:
|
||||
data += b"\nP" + f"{A:04X}".encode()
|
||||
data += b"P" + f"{A:04X}".encode()
|
||||
data += b"?" * pi_error + b"\n"
|
||||
elif status & (1 << 12):
|
||||
pi_error = (dec_error >> 10) & 0b11
|
||||
if pi_error < 3:
|
||||
data += b"\nP" + f"{C:04X}".encode()
|
||||
data += b"P" + f"{C:04X}".encode()
|
||||
data += b"?" * pi_error + b"\n"
|
||||
|
||||
conn.sendall(data)
|
||||
|
||||
def run_server():
|
||||
tef = init_tef()
|
||||
state = {
|
||||
'last_tune': 9500,
|
||||
'last_eqims': 0b11,
|
||||
'forced_mono': False,
|
||||
'deemp': 0,
|
||||
'bw': 0,
|
||||
'scan_start': 87500,
|
||||
'scan_stop': 108000,
|
||||
'scan_step': 100,
|
||||
'scan_bw': 0,
|
||||
}
|
||||
with init_tef() as tef:
|
||||
device, hw_version, sw_version = d if (d := tef.APPL_Get_Identification()) else (None, None, None)
|
||||
if device and hw_version and sw_version:
|
||||
variant = device & 127
|
||||
hw_major = hw_version >> 8
|
||||
hw_minor = hw_version & 127
|
||||
sw_major = sw_version >> 8
|
||||
|
||||
variant_str = "TEF6686"
|
||||
if variant == 1: variant_str = "TEF6687"
|
||||
if variant == 9: variant_str = "TEF6688"
|
||||
if variant == 3: variant_str = "TEF6689"
|
||||
|
||||
print(f"Got {variant_str} (V{hw_major}{hw_minor}{sw_major})")
|
||||
|
||||
state = {
|
||||
'last_tune': 9500,
|
||||
'last_eqims': 0b11,
|
||||
'forced_mono': False,
|
||||
'deemp': 0,
|
||||
'bw': 0,
|
||||
'scan_start': 87500,
|
||||
'scan_stop': 108000,
|
||||
'scan_step': 100,
|
||||
'scan_bw': 0,
|
||||
}
|
||||
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind((HOST, PORT))
|
||||
s.listen()
|
||||
print(f"Server listening on {HOST}:{PORT}")
|
||||
@@ -191,6 +211,7 @@ def run_server():
|
||||
conn.sendall(f"B{int(state['forced_mono'])}\n".encode())
|
||||
conn.sendall(f"D{state['deemp']}\n".encode())
|
||||
conn.sendall(f"W{state['bw']}\n".encode())
|
||||
conn.sendall(f"M0\n".encode())
|
||||
conn.setblocking(False)
|
||||
|
||||
ss_timer = libtimer.Timer()
|
||||
@@ -211,7 +232,6 @@ def run_server():
|
||||
if rds_timer.get_time() > RDS_UPDATE_INTERVAL:
|
||||
send_rds_data(tef, conn)
|
||||
rds_timer.reset()
|
||||
time.sleep(0.01)
|
||||
finally: tef.close()
|
||||
time.sleep(0.015)
|
||||
|
||||
if __name__ == "__main__": run_server()
|
||||
Reference in New Issue
Block a user