import argparse
import time
import traceback
import numpy as np
from ocs import ocs_agent, site_config
from ocs.ocs_twisted import TimeoutLock
from socs.agents.wiregrid_encoder.drivers import EncoderParser
NUM_ENCODER_TO_PUBLISH = 1000
SEC_ENCODER_TO_PUBLISH = 1
COUNTER_INFO_LENGTH = 100
COUNTS_ON_BELT = 52000
REFERENCE_COUNT_MAX = 2 << 15 # > that of belt on wiregrid (=nominal 52000)
def count2time(counts, t_offset=0.):
t_array = np.array(counts, dtype=float) - counts[0]
t_array *= 5.e-9
t_array += t_offset
return t_array.tolist()
[docs]
class WiregridEncoderAgent:
""" Agent to record the wiregrid rotary-encoder data.
The encoder signal and IRIG timing signal is read
by a BeagleBoneBlack (BBB).
The BBB sends the data to this PC via Ethernet.
Args:
bbport(int): Port number of the PC
determined in the script running in the BBB.
"""
def __init__(self, agent_obj, bbport=50007):
self.agent: ocs_agent.OCSAgent = agent_obj
self.log = agent_obj.log
self.lock = TimeoutLock()
self.take_data = False
self.bbport = bbport
# Stored previous rising_edge_count and irig_time
self.rising_edge_count = 0
self.irig_time = 0
agg_params = {'frame_length': 60}
self.agent.register_feed(
'wgencoder_rough', record=True,
agg_params=agg_params, buffer_time=0.5)
agg_params = {'frame_length': 60, 'exclude_influx': True}
self.agent.register_feed(
'wgencoder_full', record=True, agg_params=agg_params)
self.parser = EncoderParser(beaglebone_port=self.bbport)
[docs]
def acq(self, session, params=None):
"""acq()
**Process** - Run data acquisition.
Notes:
The most recent data collected is stored in session.data in the
structure::
>>> response.session['data']
{'fields': {
'irig_data': {
'last_updated': timestamp,
'irig_time': computer unix time
in receiving the IRIG packet,
'rising_edge_count': PRU clock (BBB clock) count,
'edge_diff': Difference of PRU clock
from the previous IRIG data,
'irig_sec': IRIG second,
'irig_min': IRIG minuite,
'irig_hour': IRIG hour,
'irig_day': IRIG Day,
'irig_year': IRIG Year
},
'encoder_data': {
'last_updated': timestamp,
'quadrature' (list): quadrature encoder signals,
'pru_clock' (list): PRU clock (Beaglebone clock) ,
'reference_degree' (list):
Encoder rotation position [deg.],
'error' (list): Encoder error flags
},
},
'timestamp':
timestamp when it updates the irig or encoder data
}
"""
time_encoder_published = 0
quad_data = []
pru_clock = []
ref_count = []
error_flag = []
received_time_list = []
dclock = []
dcount = []
rot_speed = []
with self.lock.acquire_timeout(timeout=0, job='acq') as acquired:
if not acquired:
self.log.warn(
'Could not start acq because {} is already running'
.format(self.lock.job))
return False, 'Could not acquire lock.'
self.take_data = True
# Initialize data containers
session.data = {'fields': {'irig_data': {}, 'encoder_data': {},
'timestamp': 0}}
irig_rdata = {'timestamp': 0,
'block_name': 'wgencoder_irig',
'data': {
'irig_time': 0,
'rising_edge_count': 0,
'edge_diff': 0,
'irig_sec': 0,
'irig_min': 0,
'irig_hour': 0,
'irig_day': 0,
'irig_year': 0,
}
}
irig_last_updated = 0
irig_fdata = {'timestamps': [],
'block_name': 'wgencoder_irig_raw',
'data': {
'quadrature': [],
'pru_clock': [],
'reference_degree': [],
'error': []
}
}
enc_rdata = {
'timestamps': [],
'block_name': 'wgencoder_rough',
'data': {}
}
enc_last_updated = 0
enc_fdata = {
'timestamps': [],
'block_name': 'wgencoder_full',
'data': {}
}
# Loop
while self.take_data:
try:
self.parser.grab_and_parse_data()
except Exception as e:
self.log.warn('ERROR in grab_and_parse_data() : error={}'
.format(e))
with open('parse_error.log', 'a') as f:
traceback.print_exc(file=f)
# Check current time
current_time = time.time()
# IRIG part mainly takes over CHWP scripts by H.Nishino
if len(self.parser.irig_queue):
irig_last_updated = current_time
irig_data = self.parser.irig_queue.popleft()
rising_edge_count = irig_data[0]
irig_time = irig_data[1]
irig_info = irig_data[2]
synch_pulse_clock_counts = irig_data[3]
sys_time = irig_data[4]
irig_rdata['data']['irig_time'] = irig_time
irig_rdata['data']['rising_edge_count'] = rising_edge_count
irig_rdata['data']['edge_diff']\
= rising_edge_count - self.rising_edge_count
irig_rdata['data']['irig_sec']\
= self.parser.de_irig(irig_info[0], 1)
irig_rdata['data']['irig_min']\
= self.parser.de_irig(irig_info[1], 0)
irig_rdata['data']['irig_hour']\
= self.parser.de_irig(irig_info[2], 0)
irig_rdata['data']['irig_day']\
= self.parser.de_irig(irig_info[3], 0)\
+ self.parser.de_irig(irig_info[4], 0) * 100
irig_rdata['data']['irig_year']\
= self.parser.de_irig(irig_info[5], 0)
# Beagleboneblack clock frequency measured by IRIG
if self.rising_edge_count > 0 and irig_time > 0:
bbb_clock_freq =\
float(rising_edge_count - self.rising_edge_count)\
/ (irig_time - self.irig_time)
else:
bbb_clock_freq = 0.
irig_rdata['data']['bbb_clock_freq'] = bbb_clock_freq
self.agent.publish_to_feed('wgencoder_rough', irig_rdata)
self.rising_edge_count = rising_edge_count
self.irig_time = irig_time
# saving clock counts for every refernce edge
# and every irig bit info
# 0.09: time difference in seconds b/w reference marker and
# the first index marker
irig_fdata['timestamps'] =\
sys_time + 0.09 + np.arange(10) * 0.1
irig_fdata['data']['irig_synch_pulse_clock_time'] =\
list(irig_time + 0.09 + np.arange(10) * 0.1)
irig_fdata['data']['irig_synch_pulse_clock_counts'] =\
synch_pulse_clock_counts
irig_fdata['data']['irig_info'] = list(irig_info)
self.agent.publish_to_feed('wgencoder_full', irig_fdata)
# End of IRIG case
if len(self.parser.encoder_queue):
enc_last_updated = current_time
encoder_data = self.parser.encoder_queue.popleft()
quad_data += encoder_data[0].tolist()
pru_clock += encoder_data[1].tolist()
ref_count +=\
(encoder_data[2] % REFERENCE_COUNT_MAX).tolist()
error_flag += encoder_data[3].tolist()
received_time_list.append(encoder_data[4])
dclock.append(
(encoder_data[1][-1] - encoder_data[1][0]) * 5e-9)
if (dclock[-1] > 0.)\
and (ref_count[-COUNTER_INFO_LENGTH] > ref_count[-1]):
dcount.append(
(ref_count[-1]
+ COUNTS_ON_BELT
- ref_count[-COUNTER_INFO_LENGTH])
/ COUNTS_ON_BELT)
else:
dcount.append(
(ref_count[-1]
- ref_count[-COUNTER_INFO_LENGTH])
/ COUNTS_ON_BELT)
rot_speed.append(dcount[-1] / dclock[-1])
if len(pru_clock) > NUM_ENCODER_TO_PUBLISH \
or (len(pru_clock)
and (current_time - time_encoder_published)
> SEC_ENCODER_TO_PUBLISH):
enc_rdata['timestamps'] = received_time_list
enc_rdata['data']['quadrature'] =\
quad_data[::COUNTER_INFO_LENGTH]
enc_rdata['data']['pru_clock'] =\
pru_clock[::COUNTER_INFO_LENGTH]
enc_rdata['data']['reference_degree'] =\
(np.array(ref_count)[::COUNTER_INFO_LENGTH]
* 360 / COUNTS_ON_BELT).tolist()
enc_rdata['data']['error'] =\
error_flag[::COUNTER_INFO_LENGTH]
enc_rdata['data']['rotation_speed'] = rot_speed # Hz
self.agent.publish_to_feed(
'wgencoder_rough', enc_rdata)
enc_fdata['timestamps'] =\
count2time(pru_clock, received_time_list[0])
enc_fdata['data']['quadrature'] = quad_data
enc_fdata['data']['pru_clock'] = pru_clock
enc_fdata['data']['reference_count'] = ref_count
enc_fdata['data']['error'] = error_flag
self.agent.publish_to_feed('wgencoder_full', enc_fdata)
quad_data = []
pru_clock = []
ref_count = []
error_flag = []
received_time_list = []
dclock = []
dcount = []
rot_speed = []
time_encoder_published = current_time
# End of filling encoder data
# End of encoder case
# store session.data
irig_field_dict = {
'last_updated': irig_last_updated,
'irig_time': irig_rdata['data']['irig_time'],
'rising_edge_count':
irig_rdata['data']['rising_edge_count'],
'edge_diff': irig_rdata['data']['edge_diff'],
'irig_sec': irig_rdata['data']['irig_sec'],
'irig_min': irig_rdata['data']['irig_min'],
'irig_hour': irig_rdata['data']['irig_hour'],
'irig_day': irig_rdata['data']['irig_day'],
'irig_year': irig_rdata['data']['irig_year']
}
enc_field_dict = {
'last_updated': enc_last_updated,
'quadrature': enc_rdata['data']['quadrature'],
'pru_clock': enc_rdata['data']['pru_clock'],
'reference_degree':
enc_rdata['data']['reference_degree'],
'error': enc_rdata['data']['error']
}
session.data['timestamp'] = current_time
session.data['fields']['irig_data'] = irig_field_dict
session.data['fields']['encoder_data'] = enc_field_dict
# End of loop
# End of lock acquiring
self.agent.feeds['wgencoder_rough'].flush_buffer()
# This buffer (full data) has huge data size.
self.agent.feeds['wgencoder_full'].flush_buffer()
return True, 'Acquisition exited cleanly.'
def stop_acq(self, session, params=None):
if self.take_data:
self.take_data = False
return True, 'requested to stop takeing data.'
else:
return False, 'acq is not currently running.'
def make_parser(parser=None):
if parser is None:
parser = argparse.ArgumentParser()
pgroup = parser.add_argument_group('Agent Options')
pgroup.add_argument('--port', dest='port',
type=int, default=50007,
help='Port of the beaglebone '
'running wiregrid encoder DAQ')
return parser
def main(args=None):
parser = make_parser()
args = site_config.parse_args(agent_class='WiregridEncoderAgent',
parser=parser,
args=args)
agent, runner = ocs_agent.init_site_agent(args)
wg_encoder_agent = WiregridEncoderAgent(agent, bbport=args.port)
agent.register_process('acq',
wg_encoder_agent.acq,
wg_encoder_agent.stop_acq,
startup=True)
runner.run(agent, auto_reconnect=True)
if __name__ == '__main__':
main()