Merge pull request #588 from fillods/prm80_refining

Prm80 refining
This commit is contained in:
Michael Black 2021-03-06 10:52:39 -06:00 committed by GitHub
commit 175092a99c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 593 additions and 61 deletions

View File

@ -52,7 +52,7 @@
// The rig's PLL only deals with freq in Hz divided by this value
#define FREQ_DIV 12500.
/* V4 commands
/* V5 based on V4 commands
* retrieved from https://github.com/f4fez/prm80
* and https://github.com/f4fez/prm80/blob/master/doc/Computer_commands_V4.md
* It used to be from https://sourceforge.net/projects/prm80/
@ -139,16 +139,11 @@ MessageAide: DB "H",0Dh,0Ah
*/
/*
* TODO make read_colon_prompt_and_send() more generic to read
* a prompt terminated by "$" (without space afterwards)
* Read a prompt terminated by delimiter, then write an optional string s.
*/
#define read_dollar_prompt_and_send read_colon_prompt_and_send
/*
* Read a prompt terminated by ": ", then write an optional string s.
*/
static int read_colon_prompt_and_send(hamlib_port_t *rigport,
char *data, int *data_len, const char *s)
static int read_prompt_and_send(hamlib_port_t *rigport,
char *data, int *data_len, const char *s, const char *delimiter,
int space_after_delim)
{
char buf[BUFSZ];
char spacebuf[4];
@ -162,7 +157,7 @@ static int read_colon_prompt_and_send(hamlib_port_t *rigport,
buflen = (data_len == NULL) ? sizeof(buf) : *data_len;
retval = read_string(rigport, data, buflen, ":", 1);
retval = read_string(rigport, data, buflen, delimiter, 1);
if (retval < 0)
{
@ -178,11 +173,14 @@ static int read_colon_prompt_and_send(hamlib_port_t *rigport,
}
// Read one (dummy) space character after the colon
retval = read_block(rigport, spacebuf, 1);
if (retval < 0 && retval != -RIG_ETIMEOUT)
if (space_after_delim)
{
return retval;
retval = read_block(rigport, spacebuf, 1);
if (retval < 0 && retval != -RIG_ETIMEOUT)
{
return retval;
}
}
// Here is the answer to the prompt
@ -191,6 +189,25 @@ static int read_colon_prompt_and_send(hamlib_port_t *rigport,
return retval;
}
/*
* Read a prompt terminated by ": ", then write an optional string s.
*/
static int read_colon_prompt_and_send(hamlib_port_t *rigport,
char *data, int *data_len, const char *s)
{
return read_prompt_and_send(rigport, data, data_len, s, ":", 1);
}
/*
* Read a prompt terminated by "$" (without space afterwards),
* then write an optional string s.
*/
static int read_dollar_prompt_and_send(hamlib_port_t *rigport,
char *data, int *data_len, const char *s)
{
return read_prompt_and_send(rigport, data, data_len, s, "$", 0);
}
/*
* After each executed command, the rig generally sends "\r\n>"
*/
@ -213,7 +230,7 @@ static int prm80_wait_for_prompt(hamlib_port_t *rigport)
/*
*
* \param cmd is string of generally one letter (or digit)
* \param arg1 is an optional string string sent
* \param arg1 is an optional string to send afterwards
* \param wait_prompt boolean when non-nul, will wait for "\r\n>" afterwards
*/
static int prm80_transaction(RIG *rig, const char *cmd,
@ -551,33 +568,19 @@ static int hhtoi(const char *p)
return (int)strtol(buf, NULL, 16);
}
/*
* prm80_get_channel
* Assumes rig!=NULL
/**
* Get system state [E] from rig into \a statebuf
*/
int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
static int prm80_read_system_state(hamlib_port_t *rigport, char *statebuf)
{
struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
struct rig_state *rs = &rig->state;
char statebuf[BUFSZ];
char *p;
int ret, chanstate, mode_byte, lock_byte;
if (chan->vfo == RIG_VFO_MEM)
{
ret = prm80_set_mem(rig, RIG_VFO_CURR, chan->channel_num);
if (ret != RIG_OK)
{
return ret;
}
}
int ret;
// Get rid of possible prompt sent by the rig
rig_flush(&rs->rigport);
rig_flush(rigport);
/* [E] = Show system state */
ret = write_block(&rs->rigport, "E", 1);
ret = write_block(rigport, "E", 1);
if (ret < 0)
{
@ -585,7 +588,7 @@ int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
}
// The response length is fixed
ret = read_block(&rs->rigport, statebuf, 20);
ret = read_block(rigport, statebuf, 20);
if (ret < 0)
{
@ -610,9 +613,13 @@ int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
{
int left_to_read = (p - statebuf) + 1;
memmove(statebuf, p + 1, 20 - left_to_read);
ret = read_block(&rs->rigport, statebuf + 20 - left_to_read, left_to_read);
ret = read_block(rigport, statebuf + 20 - left_to_read, left_to_read);
if (ret >= 0)
if (ret < 0)
{
return ret;
}
else
{
statebuf[20] = '\0';
}
@ -621,6 +628,38 @@ int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
statebuf);
}
return RIG_OK;
}
/*
* prm80_get_channel
* Assumes rig!=NULL
*/
int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
{
struct prm80_priv_data *priv = (struct prm80_priv_data *)rig->state.priv;
struct rig_state *rs = &rig->state;
char statebuf[BUFSZ];
int ret, chanstate, mode_byte, lock_byte;
if (chan->vfo == RIG_VFO_MEM)
{
ret = prm80_set_mem(rig, RIG_VFO_CURR, chan->channel_num);
if (ret != RIG_OK)
{
return ret;
}
}
ret = prm80_read_system_state(&rs->rigport, statebuf);
if (ret != RIG_OK)
{
return ret;
}
/* (Mode-Chan-Chanstate-Sql-Vol-Lock-RX freq-TX freq). */
/* Examples: 1240080AFF0033F02D40 or 14000C00FD0079708020 */
@ -654,10 +693,12 @@ int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only)
chan->levels[LVL_AF].f = ((float)(hhtoi(statebuf + 8) >> 4)) / 15.;
chan->levels[LVL_RFPOWER].f = (mode_byte & 0x02) ? 1.0 : 0.0;
chan->funcs = 0;
chan->funcs |= (chanstate & 0x02) ? RIG_FUNC_REV : 0;
lock_byte = hhtoi(statebuf + 10) & 0x0f;
chan->funcs = (lock_byte != 0) ? RIG_FUNC_LOCK : 0;
chan->funcs |= (lock_byte & 0x05) ? RIG_FUNC_LOCK : 0;
chan->funcs |= (lock_byte & 0x08) ? RIG_FUNC_MUTE : 0;
chan->freq = ((hhtoi(statebuf + 12) << 8) + hhtoi(statebuf + 14)) * FREQ_DIV +
RX_IF_OFFSET;
@ -715,9 +756,8 @@ int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
PLL value to load : $8020
Channel state : $00
TODO: handle the possible query from the rig:
Possibly:
"This channel number doesn't exist. Add new channel (Y/N) ? "
TODO implement correctly read_dollar_prompt_and_send (dollar prompt)
*/
sprintf(buf, "%02u", (unsigned)chan->channel_num);
@ -729,8 +769,10 @@ int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
return ret;
}
// Set the RX frequency as PLL word
// Set the RX frequency as PLL word.
sprintf(buf, "%04X", (unsigned)((chan->freq - RX_IF_OFFSET) / FREQ_DIV));
// "PLL value to load : $"
ret = read_dollar_prompt_and_send(&rs->rigport, NULL, NULL, buf);
if (ret != RIG_OK)
@ -753,6 +795,8 @@ int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
chanstate |= (chan->flags & RIG_CHFLAG_SKIP) ? 0x08 : 0;
sprintf(buf, "%02X", chanstate);
// "Channel state : $"
ret = read_dollar_prompt_and_send(&rs->rigport, NULL, NULL, buf);
if (ret != RIG_OK)
@ -760,6 +804,43 @@ int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
return ret;
}
// Determine if prompt came back (CRLF'>') or have to
// handle the possible query from the rig:
// "This channel number doesn't exist. Add new channel (Y/N) ? "
ret = read_block(&rs->rigport, buf, 3);
if (ret < 0)
{
RETURNFUNC(ret);
}
if (ret == 3 && buf[2] == 'T')
{
// Read the question
ret = read_string(&rs->rigport, buf, sizeof(buf), "?", 1);
if (ret < 0)
{
RETURNFUNC(ret);
}
// Read extra space
ret = read_block(&rs->rigport, buf, 1);
if (ret < 0)
{
RETURNFUNC(ret);
}
// Send confirmation
ret = write_block(&rs->rigport, "Y", 1);
if (ret < 0)
{
RETURNFUNC(ret);
}
}
prm80_wait_for_prompt(&rs->rigport);
}
else
@ -805,7 +886,8 @@ int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
#endif
ret = prm80_set_func(rig, vfo, RIG_FUNC_LOCK, chan->funcs & RIG_FUNC_LOCK);
ret = prm80_set_func(rig, vfo, RIG_FUNC_LOCK,
!!(chan->funcs & RIG_FUNC_LOCK));
if (ret != RIG_OK)
{
@ -817,15 +899,21 @@ int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan)
}
// TODO FUNC_REV ?
// TODO FUNC_REV through Channel state byte ?
// TODO "Read-Modify-Write" (or shadowing in priv area) of the lock bits
int prm80_set_func(RIG *rig, vfo_t vfo, setting_t func, int status)
{
int ret;
if (func & RIG_FUNC_LOCK)
{
/* Lock keys/TX/Vol */
ret = prm80_transaction(rig, "K", (status != 0) ? "03" : "00", 1);
/* Lock keys(b0)/Vol(b2) */
ret = prm80_transaction(rig, "K", (status != 0) ? "05" : "00", 1);
}
else if (func & RIG_FUNC_MUTE)
{
/* Lock RX(b3) */
ret = prm80_transaction(rig, "K", (status != 0) ? "08" : "00", 1);
}
else
{
@ -850,7 +938,7 @@ int prm80_get_func(RIG *rig, vfo_t vfo, setting_t func, int *status)
return ret;
}
*status = (chan.funcs & func);
*status = !!(chan.funcs & func);
return RIG_OK;
}
@ -862,6 +950,7 @@ int prm80_get_func(RIG *rig, vfo_t vfo, setting_t func, int *status)
int prm80_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
{
char buf[BUFSZ];
int ret, mode_byte;
// do some clamping, all levels are float values.
if (val.f < 0.0)
@ -886,19 +975,28 @@ int prm80_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
return prm80_transaction(rig, "F", buf, 1);
case RIG_LEVEL_RFPOWER:
// TODO : modify the Mode byte b1 ?
#if 0
/* Current mode:
; b0: Squelch b1: power
; b2: Squelch open b3: TX
; b4: PLL locked b5: Long press memorize
; b6: Debouncing in effect b7: LCD refresh
*/
// TODO perform a "Read-Modify-Write" of the mode_byte
mode_byte = 0x10;
mode_byte |= (chan->levels[LVL_RFPOWER].f == 0.) ? 0 : 0x02;
#endif
return -RIG_ENIMPL;
// Perform a "Read-Modify-Write" of the mode_byte
ret = prm80_read_system_state(&rig->state.rigport, buf);
if (ret != RIG_OK)
{
return ret;
}
prm80_wait_for_prompt(&rig->state.rigport);
mode_byte = hhtoi(buf);
mode_byte &= ~0x02;
mode_byte |= (val.f == 0.) ? 0 : 0x02;
sprintf(buf, "%02X", (unsigned)mode_byte);
return prm80_transaction(rig, "D", buf, 1);
default:
rig_debug(RIG_DEBUG_ERR, "%s: unsupported set_level %s\n", __func__,
@ -909,6 +1007,110 @@ int prm80_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val)
return RIG_OK;
}
#ifdef V4_ONLY
/*
* get_level RIG_LEVEL_RAWSTR
*/
static int prm80_get_rawstr_RAM(RIG *rig, value_t *val)
{
char buf[BUFSZ];
struct rig_state *rs = &rig->state;
int ret, i;
/* [U] = Print 80c552 internal RAM. */
// Send cmd, Wait for colon prompt, but then send nothing
ret = prm80_transaction(rig, "U", "", 0);
if (ret < 0)
{
return ret;
}
// Read CRLF
ret = read_string(&rs->rigport, buf, BUFSZ, "\n", 1);
if (ret < 0)
{
return ret;
}
// (16 lines of 16 bytes each)
// According to prm.a51, the rssi_hold variable is in RAM at RAM+35.
// The RAM base is at 030h.
#define RSSI_HOLD_ADDR (0x30 + 35) // = 0x53
for (i = 0; i < (RSSI_HOLD_ADDR / 16) + 1; i++)
{
ret = read_string(&rs->rigport, buf, BUFSZ, "\n", 1);
if (ret < 0)
{
return ret;
}
}
// A line looks like this
// "$50 : 00 01 02 53 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\r\n"
val->i = hhtoi(buf + 6 + 3 * (RSSI_HOLD_ADDR % 16));
// discard the remaining content of RAM print
for (i = 0; i < (16 - RSSI_HOLD_ADDR / 16) - 1; i++)
{
read_string(&rs->rigport, buf, BUFSZ, "\n", 1);
}
prm80_wait_for_prompt(&rs->rigport);
return RIG_OK;
}
#endif
/*
* get_level RIG_LEVEL_RAWSTR
*
* NB : requires a V5 firmware!
*/
static int prm80_get_rawstr(RIG *rig, value_t *val)
{
char buf[BUFSZ];
struct rig_state *rs = &rig->state;
int ret;
// Get rid of possible prompt sent by the rig
rig_flush(&rs->rigport);
/* [A] = RSSI */
ret = write_block(&rs->rigport, "A", 1);
if (ret < 0)
{
RETURNFUNC(ret);
}
// The response length is fixed
ret = read_block(&rs->rigport, buf, 4);
if (ret < 0)
{
return ret;
}
if (ret >= 0)
{
buf[ret] = '\0';
}
val->i = hhtoi(buf);
prm80_wait_for_prompt(&rs->rigport);
return RIG_OK;
}
/*
* prm80_get_level
* Assumes rig!=NULL
@ -918,6 +1120,12 @@ int prm80_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
int ret;
channel_t chan;
// Get rawstr apart, it is not read from system state
if (level == RIG_LEVEL_RAWSTR)
{
return prm80_get_rawstr(rig, val);
}
memset(&chan, 0, sizeof(chan));
chan.vfo = RIG_VFO_CURR;
@ -954,6 +1162,52 @@ int prm80_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val)
return RIG_OK;
}
int prm80_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt)
{
char statebuf[BUFSZ];
int ret, mode_byte;
// TODO use command 'A' which is faster, but not in V4
ret = prm80_read_system_state(&rig->state.rigport, statebuf);
if (ret != RIG_OK)
{
return ret;
}
mode_byte = hhtoi(statebuf);
// TX mode on?
*ptt = (mode_byte & 0x08) ? RIG_PTT_ON : RIG_PTT_OFF;
prm80_wait_for_prompt(&rig->state.rigport);
return RIG_OK;
}
int prm80_get_dcd(RIG *rig, vfo_t vfo, dcd_t *dcd)
{
char statebuf[BUFSZ];
int ret, mode_byte;
// TODO use command 'A' which is faster, but not in V4
ret = prm80_read_system_state(&rig->state.rigport, statebuf);
if (ret != RIG_OK)
{
return ret;
}
mode_byte = hhtoi(statebuf);
// Squelch open?
*dcd = (mode_byte & 0x04) ? RIG_DCD_ON : RIG_DCD_OFF;
prm80_wait_for_prompt(&rig->state.rigport);
return RIG_OK;
}
// TODO vfo_op : MCL FROM_VFO ..
/*

View File

@ -24,7 +24,7 @@
#include <hamlib/rig.h>
#define BACKEND_VER "20210217"
#define BACKEND_VER "20210306"
#define PRM80_MEM_CAP { \
.freq = 1, \
@ -57,6 +57,8 @@ int prm80_set_mem(RIG *rig, vfo_t vfo, int ch);
int prm80_get_mem(RIG *rig, vfo_t vfo, int *ch);
int prm80_set_channel(RIG *rig, vfo_t vfo, const channel_t *chan);
int prm80_get_channel(RIG *rig, vfo_t vfo, channel_t *chan, int read_only);
int prm80_get_dcd(RIG *rig, vfo_t vfo, dcd_t *dcd);
int prm80_get_ptt(RIG *rig, vfo_t vfo, ptt_t *ptt);
const char *prm80_get_info(RIG *rig);

View File

@ -33,9 +33,9 @@
#define PRM8060_ALL_MODES (RIG_MODE_FM)
#define PRM8060_FUNC (RIG_FUNC_REV|RIG_FUNC_LOCK)
#define PRM8060_FUNC (RIG_FUNC_REV|RIG_FUNC_LOCK|RIG_FUNC_MUTE)
#define PRM8060_LEVEL_ALL (RIG_LEVEL_AF|RIG_LEVEL_SQL /* |RIG_LEVEL_RFPOWER */)
#define PRM8060_LEVEL_ALL (RIG_LEVEL_AF|RIG_LEVEL_SQL|RIG_LEVEL_RFPOWER)
#define PRM8060_PARM_ALL (RIG_PARM_NONE)
@ -44,6 +44,26 @@
#define PRM8060_VFO (RIG_VFO_MEM)
// Calibration done on PRM8070
#define PRM8060_STR_CAL { 15, \
{ \
{ 0x14, -54 }, /* S0 */ \
{ 0x1D, -48 }, /* S1 */ \
{ 0x26, -42 }, /* S2 */ \
{ 0x33, -36 }, /* S3 */ \
{ 0x3F, -30 }, /* S4 */ \
{ 0x4D, -24 }, /* S5 */ \
{ 0x55, -18 }, /* S6 */ \
{ 0x61, -12 }, /* S7 */ \
{ 0x68, -6 }, /* S8 */ \
{ 0x6C, 0 }, /* S9 */ \
{ 0x81, 10 }, /* +10 */ \
{ 0x8B, 20 }, /* +20 */ \
{ 0x8C, 40 }, /* +40 */ \
{ 0x8C, 50 }, /* +50 */ \
{ 0xFF, 60 } /* +60 */ \
} }
/*
* PRM 8060 rig capabilities.
* http://prm80.sourceforge.net/
@ -58,8 +78,8 @@ const struct rig_caps prm8060_caps =
.copyright = "LGPL",
.status = RIG_STATUS_BETA,
.rig_type = RIG_TYPE_TRANSCEIVER,
.ptt_type = RIG_PTT_NONE,
.dcd_type = RIG_DCD_NONE,
.ptt_type = RIG_PTT_RIG,
.dcd_type = RIG_DCD_RIG,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 4800,
.serial_rate_max = 4800,
@ -74,7 +94,7 @@ const struct rig_caps prm8060_caps =
.has_get_func = PRM8060_FUNC,
.has_set_func = PRM8060_FUNC,
.has_get_level = PRM8060_LEVEL_ALL | RIG_LEVEL_RFPOWER,
.has_get_level = PRM8060_LEVEL_ALL | RIG_LEVEL_RAWSTR,
.has_set_level = RIG_LEVEL_SET(PRM8060_LEVEL_ALL),
.has_get_parm = PRM8060_PARM_ALL,
.has_set_parm = RIG_PARM_SET(PRM8060_PARM_ALL),
@ -120,6 +140,8 @@ const struct rig_caps prm8060_caps =
RIG_FLT_END,
},
.str_cal = PRM8060_STR_CAL,
.rig_init = prm80_init,
.rig_cleanup = prm80_cleanup,
.get_mode = prm80_get_mode,
@ -138,6 +160,8 @@ const struct rig_caps prm8060_caps =
.set_level = prm80_set_level,
.get_level = prm80_get_level,
.reset = prm80_reset,
.get_dcd = prm80_get_dcd,
.get_ptt = prm80_get_ptt,
.get_info = prm80_get_info,
};

252
rigs/prm80/pysimulprm80.py Executable file
View File

@ -0,0 +1,252 @@
#!/usr/bin/env python3
# This file is part of Hamlib
# (C) 2021 Stephane Fillod
#
# SPDX-License-Identifier: GPL-3
"""
Protocol simulator of PRM80xx running firmware V5.
This script needs a POSIX system to run.
On one terminal, start this script:
$ ./pysimulprm80.py -l /tmp/prm80simul
Slave name /dev/pts/5
Name symlinked /tmp/prm80simul
...
On another terminal, test the prm80 backend:
$ rigctl -vvvvvv -r /tmp/prm80simul -m 28001
"""
# Implementation from
# - https://github.com/f4fez/prm80/blob/master/doc/Computer_commands_V4.md
# - https://github.com/f4fez/prm80/blob/master/doc/Computer_control.md
# - https://sourceforge.net/p/hamlib/discussion/25919/thread/93afa09f52/
# - https://github.com/f4fez/prm80/blob/master/src/inc_ser.a51
import argparse
import time
import os, pty, tty, termios
class Prm80Simul:
def __init__(self, pty_r, pty_w):
self.pty_r = pty_r
self.pty_w = pty_w
self.SetupPseudoSerial(self.pty_r)
self.SetupPseudoSerial(self.pty_w)
self.msg_version = "PRM8060 V5.0 430"
self.mdict = {
'V' : self.tch_Version,
'N' : self.tch_Nmem,
'K' : self.tch_Klock,
'F' : self.tch_Fsquelch,
'O' : self.tch_Ovolume,
'D' : self.tch_Dmode,
'T' : self.tch_Tstate,
'U' : self.tch_UprintRAM,
'P' : self.tch_Peditchannel,
'Q' : self.tch_Qmaxchan,
'C' : self.tch_Chanlist,
'R' : self.tch_Rfreq,
'E' : self.tch_Estate,
'A' : self.tch_Astatus,
'#' : self.tch_diese,
'*' : self.tch_autre,
}
# Some live data simulated inside the rig
self.ChanNum = 0
self.LockByte = 0
self.Squelch = 0
self.Volume = 0xff
self.ModeByte = 0x16
self.ChanState = 0x0c
self.MaxChan = 80
self.RxPLL = 0x7970 # 410 MHz (w/ IF offset)
self.TxPLL = 0x8020 # 410 MHz
def SetupPseudoSerial(self, fd):
""" make it raw, at 4800 bauds (defaults to 8N1) """
tty.setraw(fd)
term_settings = termios.tcgetattr(fd)
term_settings[4] = termios.B4800 # ispeed
term_settings[5] = termios.B4800 # ospeed
termios.tcsetattr(fd, termios.TCSANOW, term_settings)
def pty_write(self, a):
""" helper write+flush """
self.pty_w.write(a)
self.flush_w()
def tch_autre(self):
""" Unknown command """
self.pty_write(b'* ?')
def tch_Version(self):
""" Print firmware version """
self.pty_write(self.msg_version.encode())
def tch_Nmem(self):
""" Set current channel """
self.pty_write(b'Channel : ')
self.ChanNum = int(pty_r.read(2).decode('ascii'))
def tch_Klock(self):
""" Set lock byte """
self.pty_write(b'Lock : ')
self.LockByte = int(pty_r.read(2).decode('ascii'), 16)
def tch_Fsquelch(self):
""" Set squelch. """
self.pty_write(b'Squelch : ')
self.Squelch = int(pty_r.read(2).decode('ascii'))
def tch_Ovolume(self):
""" Set volume. """
self.pty_write(b'Volume : ')
self.Volume = int(pty_r.read(2).decode('ascii'))
def tch_Dmode(self):
""" Set "Mode" byte. """
self.pty_write(b'Mode : ')
self.ModeByte = int(pty_r.read(2).decode('ascii'), 16)
def tch_Tstate(self):
""" Set Channel State. """
self.pty_write(b'Channel state : $')
self.ChanState = int(pty_r.read(2).decode('ascii'))
def tch_UprintRAM(self):
""" Print 80c552 internal RAM. """
self.pty_w.write(b'Display the 256 bytes from internal RAM : \r\n')
# 16 lines of 16 bytes each. The command needs about 3 sec.
# In V4, rssi_hold is at 0x53
self.pty_w.write(b'$00 : 00 00 00 D5 09 00 00 0F 7F 99 D2 7D B2 93 8F C5\r\n')
self.pty_w.write(b'$10 : 60 8B 43 CA 31 43 BB BA F9 27 87 47 06 52 BE 55\r\n')
self.pty_w.write(b'$20 : 81 2F FD 17 01 12 A3 00 13 02 34 96 0B 04 54 7A\r\n')
self.pty_w.write(b'$30 : 13 03 00 30 B4 00 03 00 FC 58 71 00 0F 08 08 82\r\n')
self.pty_w.write(b'$40 : 9E 86 EE 60 1F 1F 00 FB FF FF 44 55 FF 73 5A F5\r\n')
self.pty_w.write(b'$50 : 7E 02 06 72 B7 FF 8D B1 92 79 93 8D 3E 4E 8E 06\r\n')
self.pty_w.write(b'$60 : DB C7 3D 11 AE 5A 4F 3D 80 82 D1 8A 88 6C 7B E5\r\n')
self.pty_w.write(b'$70 : 2F 98 4C 72 5B A4 78 5C 7D 45 46 8C 25 23 BB 82\r\n')
self.pty_w.write(b'$80 : 60 1E 2F 42 28 38 E3 7D 94 BC CA B4 B1 43 AE 84\r\n')
self.pty_w.write(b'$90 : 81 88 75 1A 2D 93 7E E8 CF 79 B6 E0 0F 31 AD AA\r\n')
self.pty_w.write(b'$A0 : 4F 1C 0F 12 55 00 24 13 08 D2 0E C0 00 20 AF 0E\r\n')
self.pty_w.write(b'$B0 : 40 FF 40 00 B8 00 0F 0F B8 0F 10 B8 0F 14 10 FC\r\n')
self.pty_w.write(b'$C0 : 08 E8 D8 3F 33 20 D9 50 8F 1A A2 FF CC D4 2E 3A\r\n')
self.pty_w.write(b'$D0 : 12 22 B7 23 2F 8F 64 47 99 21 6C D8 B4 B0 C3 51\r\n')
self.pty_w.write(b'$E0 : 90 E8 67 5C 18 5E AB 2D BD 71 52 0F 96 6F DA 56\r\n')
self.pty_w.write(b'$F0 : 1D E1 08 42 C2 2D 3B C7 87 23 88 E3 1A 95 F1 EE\r\n')
self.flush_w()
def tch_Qmaxchan(self):
""" Set channels number. """
self.pty_write(b'Channels number (00 to 99) : ')
self.MaxChan = int(pty_r.read(2).decode('ascii'))
# This command gets an extra CRLF ??!
self.pty_write(b'\r\n')
def tch_Peditchannel(self):
""" Edit/Add channel """
# TODO keep edited channel content in the class
self.pty_write(b'Channel to set : ')
chan_num = int(pty_r.read(2).decode('ascii'))
self.pty_write(b'\r\n')
# NB: here prompts end with '$'
self.pty_write(b'PLL value to load : $')
pll_freq_value = int(pty_r.read(4).decode('ascii'), 16)
self.pty_write(b'\r\n')
self.pty_write(b'Channel state : $')
chan_state = int(pty_r.read(2).decode('ascii'), 16)
self.pty_write(b'\r\n')
# for higher channels, allow to test this scenario
if chan_num > self.MaxChan and chan_num != 99:
self.pty_write(b'This channel number doesn\'t exist. Add new channel (Y/N) ? ')
# response "Y/N"
yesno = pty_r.read(1).decode('ascii')
self.pty_write(b'\r\n')
def tch_Estate(self):
""" Show system state (Mode-Chan-Chanstate-Sql-Vol-Lock-RX freq-TX freq) """
#self.pty_write(b'16000C00FF0079708020')
self.pty_write('{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:04X}{:04X}'.format(
self.ModeByte,
self.ChanNum,
self.ChanState,
self.Squelch,
self.Volume,
self.LockByte,
self.RxPLL,
self.TxPLL
).encode())
def tch_Astatus(self):
""" Print value of RSSI, squelch and transmit status. V5. """
self.pty_write(b'1801')
def tch_Chanlist(self):
""" Print channels list """
self.pty_write(b'Channels list :\r\n00 : 8464 84\r\n01 : 81B0 00\r\n02 : 8464 0C')
def tch_Rfreq(self):
""" Set synthetiser frequencies """
self.pty_write(b'RX frequency : ')
self.RxPLL = int(pty_r.read(4).decode('ascii'), 16)
self.pty_write(b'\r\nTX frequency : ')
self.TxPLL = int(pty_r.read(4).decode('ascii'), 16)
def tch_diese(self):
""" Ping """
self.pty_write(b'!')
def exec_cmd(self,cmd):
default='*'
self.mdict.get(cmd.upper(),default)() # get() method returns the function matching the argument
self.pty_write(b'\r\n>')
def flush_w(self):
self.pty_w.flush()
if __name__ == '__main__':
master, slave = pty.openpty()
s_name = os.ttyname(slave)
m_name = os.ttyname(master)
pty_r = os.fdopen(master, "rb")
pty_w = os.fdopen(master, "wb")
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--symlink",
help="make a symlink with the opened pty",
type=str,
dest='symlink')
options = parser.parse_args()
print ("Slave name ", s_name)
# Create a symlink
if options.symlink:
if os.access(options.symlink, os.F_OK, follow_symlinks=False):
os.remove(options.symlink)
os.symlink(s_name, options.symlink)
print ("Name symlinked ", options.symlink)
prm80 = Prm80Simul(pty_r, pty_w)
while True:
cmd = pty_r.read(1).decode('ascii')
print ("Received command ["+cmd+"]")
prm80.exec_cmd(cmd)
print ("Restart mainloop")