This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

IWR6843AOPEVM: I have written Python code for fall detection, but I am encountering some issues.

Part Number: IWR6843AOPEVM

Hello Ti,

we have written python code to check fall detection in IWR6843AOPEVM radar module.
Code is running but only for once after that it get crashed.

Code is attached here                      

# Standard Imports
import sys
import numpy as np
import time
import math
import struct
import os
import string
import serial
import serial.tools.list_ports
import statistics
import warnings
import random
import copy
import collections

from collections import deque
from gl_classes import GLTextItem

# Local File Imports
from gui_threads import *
from gui_parser import uartParser
from graphUtilities import *
from gui_common import *
from cachedData import *
from fall_detection import FallDetection, fallDetectionSliderClass


# from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QDateTimeEdit,
#         QDial, QDialog, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
#         QProgressBar, QPushButton, QRadioButton, QScrollBar, QSizePolicy,
#         QSlider, QSpinBox, QStyleFactory, QTableWidget, QTableWidgetItem, QTabWidget, QTextEdit,
#         QVBoxLayout, QWidget, QFileDialog, QButtonGroup, QFormLayout, QFrame, QSpacerItem)
cachedData = cachedDataType()



class Window():
    def __init__(self, parent=None, size=[]):
        # TODO bypass serial read function to also log to a file

        self.frameTime = 50
        self.graphFin = 1
        self.hGraphFin = 1
        self.threeD = 1
        self.lastFramePoints = np.zeros((5,1))
        self.plotTargets = 1
        self.frameNum = 0
        self.profile = {'startFreq': 60.25, 'numLoops': 64, 'numTx': 3, 'sensorHeight':2.0, 'maxRange':10, 'az_tilt':0, 'elev_tilt':15.0, 'enabled':0}
        self.chirpComnCfg = {'DigOutputSampRate':23, 'DigOutputBitsSel':0, 'DfeFirSel':0, 'NumOfAdcSamples':128, 'ChirpTxMimoPatSel':4, 'ChirpRampEndTime':36.1, 'ChirpRxHpfSel':1}
        self.chirpTimingCfg = {'ChirpIdleTime':8, 'ChirpAdcSkipSamples':24, 'ChirpTxStartTime':0, 'ChirpRfFreqSlope':47.95, 'ChirpRfFreqStart':60}
        self.guiMonitor = {'pointCloud':1, 'rangeProfile':0, 'NoiseProfile':0, 'rangeAzimuthHeatMap':0, 'rangeDopplerHeatMap':0, 'statsInfo':0}
        self.rangeRes = 0
        self.rangeAxisVals = np.zeros(int(self.chirpComnCfg['NumOfAdcSamples']/2))
        self.sensorHeight = 1.5
        self.numFrameAvg = 10
        self.configSent = 0
        self.previousFirstZ = -1
        self.yzFlip = 0

        self.trackColorMap = None
        self.prevConfig = DEMO_NAME_OOB
        self.vitalsPatientData = []
        self.pointBounds = {'enabled':False, 'minX':0, 'maxX':0, 'minY':0, 'maxY':0, 'minZ':0, 'maxZ':0}
     
        # Flag to indicate if the last frame was parsed incorrectly to ignore the error on the subsequent frame when 
        # the number of points won't be consistent with the tracker data
        self.lastFrameErrorFlag = False
        #color gradients
#         # TODO Simplify color gradients
#         self.Gradients = OrderedDict([
#     ('bw', {'ticks': [(0.0, (0, 0, 0, 255)), (1, (255, 255, 255, 255))], 'mode': 'rgb'}),
#     ('hot', {'ticks': [(0.3333, (185, 0, 0, 255)), (0.6666, (255, 220, 0, 255)), (1, (255, 255, 255, 255)), (0, (0, 0, 0, 255))], 'mode': 'rgb'}),
#     ('jet', {'ticks': [(1, (166, 0, 0, 255)), (0.32247191011235954, (0, 255, 255, 255)), (0.11348314606741573, (0, 68, 255, 255)), (0.6797752808988764, (255, 255, 0, 255)), (0.902247191011236, (255, 0, 0, 255)), (0.0, (0, 0, 166, 255)), (0.5022471910112359, (0, 255, 0, 255))], 'mode': 'rgb'}),
#     ('summer', {'ticks': [(1, (255, 255, 0, 255)), (0.0, (0, 170, 127, 255))], 'mode': 'rgb'} ),
#     ('space', {'ticks': [(0.562, (75, 215, 227, 255)), (0.087, (255, 170, 0, 254)), (0.332, (0, 255, 0, 255)), (0.77, (85, 0, 255, 255)), (0.0, (255, 0, 0, 255)), (1.0, (255, 0, 127, 255))], 'mode': 'rgb'}),
#     ('winter', {'ticks': [(1, (0, 255, 127, 255)), (0.0, (0, 0, 255, 255))], 'mode': 'rgb'}),
#     ('spectrum2', {'ticks': [(1.0, (255, 0, 0, 255)), (0.0, (255, 0, 255, 255))], 'mode': 'hsv'}),
#     ('heatmap', {'ticks': [ (1, (255, 0, 0, 255)), (0, (131, 238, 255, 255))], 'mode': 'hsv'})
# ])
#         cmap = 'heatmap'
#         if (cmap in self.Gradients):
#             self.gradientMode = self.Gradients[cmap]
#         self.zRange = [-3, 3]
#         self.plotHeights = 1
#         # Gui size
#         if (size):
#             left = 50
#             top = 50
#             width = math.ceil(size.width()*0.9)
#             height = math.ceil(size.height()*0.9)
#             self.setGeometry(left, top, width, height)
        # # Persistent point cloud
        # self.previousClouds = []

        # self.hearPlotData = []
        # self.breathPlotData = []

        # # Set up graph pyqtgraph
        # self.init3dGraph()
        # self.initColorGradient()
        # self.init1dGraph()

        # # Add connect options
        # self.initConnectionPane()
        # self.initStatsPane()
        # self.initPlotControlPane()
        # self.initFallDetectPane()
        # self.initConfigPane()
        # self.initSensorPositionPane()
        # self.initBoundaryBoxPane()
        # self.initVitalsPlots()

        # # Set the layout
        # # Create tab for different graphing options
        # self.graphTabs = QTabWidget()
        # self.graphTabs.addTab(self.pcplot, '3D Plot')
        # self.graphTabs.addTab(self.rangePlot, 'Range Plot')
        # self.graphTabs.currentChanged.connect(self.whoVisible)

        # self.gridlay = QGridLayout()
        # self.gridlay.addWidget(self.comBox, 0,0,1,1)
        # self.gridlay.addWidget(self.statBox, 1,0,1,1)
        # self.gridlay.addWidget(self.configBox,2,0,1,1)
        # self.gridlay.addWidget(self.plotControlBox,3,0,1,1)
        # self.gridlay.addWidget(self.fallDetectionOptionsBox,4,0,1,1)
        # self.gridlay.addWidget(self.spBox,5,0,1,1)
        # self.gridlay.addWidget(self.boxTab,6,0,1,1)
        # self.gridlay.setRowStretch(7,1) # Added to preserve spacing
        # self.gridlay.addWidget(self.graphTabs,0,1,8,1)
        # self.gridlay.addWidget(self.colorGradient, 0, 2, 8, 1)
        # self.gridlay.addWidget(self.vitalsPane, 0, 3, 8, 1)

        # self.vitalsPane.setVisible(False)
        # self.gridlay.setColumnStretch(0,1)
        # self.gridlay.setColumnStretch(1,3)
        # self.setLayout(self.gridlay)

        # Set up parser
        self.parser = uartParser(type="3D People Tracking")

        # # Check cached data for previously used demo and device to set as default options
        # deviceName = cachedData.getCachedDeviceName()
        # if (deviceName != ""):
        #     try:
        #         self.deviceType.setCurrentIndex(DEVICE_LIST.index(deviceName))
        #         if (deviceName == "IWR6843" or deviceName == "IWR1843"):
        #             self.parserType = "DoubleCOMPort"
        #         elif (deviceName == "IWRL6432"):
        #             self.parserType = "SingleCOMPort"
        #         elif (deviceName == "IWRL1432"):
        #             self.parserType = "SingleCOMPort"
        #     except:
        #         print("Device not found. Using default option")
        #         self.deviceType.setCurrentIndex(0)
        # demoName = cachedData.getCachedDemoName()
        # if (demoName != ""):
        #     try:
        #         if (self.deviceType.currentText() in DEVICE_LIST[0:2]):
        #             self.configType.setCurrentIndex(x843_DEMO_TYPES.index(demoName))
        #         if (self.deviceType.currentText() in DEVICE_LIST[2:3]):
        #             self.configType.setCurrentIndex(x432_DEMO_TYPES.index(demoName))
        #         if (self.deviceType.currentText() in DEVICE_LIST[3:]):
        #             self.configType.setCurrentIndex(xWRL1432_DEMO_TYPES.index(demoName))

        #     except:
        #         print("Demo not found. Using default option")
        #         self.configType.setCurrentIndex(0)


    def initConnectionPane(self):
        # Find all Com Ports
        serialPorts = list(serial.tools.list_ports.comports())

        # Find default CLI Port and Data Port
        for port in serialPorts:
            print(port.description)
            if (CLI_XDS_SERIAL_PORT_NAME in port.description or CLI_SIL_SERIAL_PORT_NAME in port.description):
                print(f'\tCLI COM Port found: {port.device}')
                comText = port.device
                comText = comText.replace("COM", "")

            elif (DATA_XDS_SERIAL_PORT_NAME in port.description or DATA_SIL_SERIAL_PORT_NAME in port.description):
                print(f'\tData COM Port found: {port.device}')
                comText = port.device
                comText = comText.replace("COM", "")

    def startApp(self):
        self.configSent = 1
        self.parseTimer.start(int(self.frameTime)) # need this line 

    def parseData(self):
        self.uart_thread.start(priority=QThread.HighestPriority)

    def connectCom(self):
        self.parser.frameTime = self.frameTime
        # init threads and timers
        self.uart_thread = parseUartThread(self.parser)
        self.uart_thread.fin.connect(self.parseData)
        self.parseTimer = QTimer()
        self.parseTimer.setSingleShot(False)
        self.parseTimer.timeout.connect(self.parseData)
        uart = "COM8"
        data = "COM7"
        self.parser.connectComPorts(uart, data)
    # Gets called whenever the sensor position box is modified
    def onChangeSensorPosition(self):
        newHeight = float(2.0)
        newAzTilt = float(0.0)
        newElevTilt = float(15.0)
        # self.cThread = sendCommandThread(self.parser,command)
        # self.cThread.start(priority=QThread.HighestPriority-2)

        # Update Profile info
        self.profile['sensorHeight'] = newHeight

        # # Move evmBox to new position
        # self.evmBox.resetTransform()
        # self.evmBox.rotate(-1*newElevTilt,1,0,0)
        # self.evmBox.rotate(-1*newAzTilt,0,0,1)
        # self.evmBox.translate(0,0,newHeight)
   

    def parseCfg(self, fname):
        with open(fname, 'r') as cfg_file:
            self.cfg = cfg_file.readlines()
        counter = 0
        chirpCount = 0
        for line in self.cfg:
            args = line.split()
            if (len(args) > 0):
                # cfarCfg
                if (args[0] == 'cfarCfg'):
                    pass
                    #self.cfarConfig = {args[10], args[11], '1'}
                # trackingCfg
                elif (args[0] == 'trackingCfg'):
                    if (len(args) < 5):
                        print ("Error: trackingCfg had fewer arguments than expected")
                        continue
                    self.profile['maxTracks'] = int(args[4])
                    # Update the maximum number of tracks based off the cfg file
                    # self.trackColorMap = get_trackColors(self.profile['maxTracks'])
                    for m in range(self.profile['maxTracks']):
                        # Add track gui object
                        mesh = gl.GLLinePlotItem()
                        mesh.setVisible(False)
                        # self.pcplot.addItem(mesh)
                        # self.ellipsoids.append(mesh)
                        # Add track coordinate string
                        # text = GLTextItem()
                        # text.setGLViewWidget(self.pcplot)
                        # text.setVisible(False)
                        # self.pcplot.addItem(text)
                        # self.coordStr.append(text)
                        # Add track classifier label string
                        # classifierText = GLTextItem()
                        # classifierText.setGLViewWidget(self.pcplot)
                        # classifierText.setVisible(False)
                        # self.pcplot.addItem(classifierText)
                        # self.classifierStr.append(classifierText)
                    # If we only support 1 patient, hide the other patient window
                    # if (self.profile['maxTracks'] == 1):
                    #     self.vitals[1]['pane'].setVisible(False)
                    # Initialize Vitals output dictionaries for each potential patient
                    # for i in range (min(self.profile['maxTracks'], MAX_VITALS_PATIENTS)):
                    #     # Initialize 
                    #     patientDict = {}
                    #     patientDict ['id'] = i
                    #     patientDict ['rangeBin'] = 0
                    #     patientDict ['breathDeviation'] = 0
                    #     patientDict ['heartRate'] = []
                    #     patientDict ['breathRate'] = 0
                    #     patientDict ['heartWaveform'] = []
                    #     patientDict ['breathWaveform'] = []
                    #     self.vitalsPatientData.append(patientDict)

                    #     # Make each patient's pane visible
                    #     self.vitals[i]['pane'].setVisible(True)

                elif (args[0] == 'AllocationParam'):
                    pass
                    #self.allocConfig = tuple(args[1:6])
                elif (args[0] == 'GatingParam'):
                    pass
                    #self.gatingConfig = tuple(args[1:4])
                elif (args[0] == 'SceneryParam' or args[0] == 'boundaryBox'):
                    if (len(args) < 7):
                        print ("Error: SceneryParam/boundaryBox had fewer arguments than expected")
                        continue
                    self.boundaryLine = counter
                    leftX = float(args[1])
                    rightX = float(args[2])
                    nearY = float(args[3])
                    farY = float(args[4])
                    bottomZ = float(args[5])
                    topZ = float(args[6])
                    # self.addBoundBox('trackerBounds', leftX, rightX, nearY, farY, bottomZ, topZ)
                    # Default pointBounds box to have the same values as the last boundaryBox in the config
                    # These can be changed by the user
                    # self.boundaryBoxes[0]['boundList'][0].setText(str(leftX))
                    # self.boundaryBoxes[0]['boundList'][1].setText(str(rightX))
                    # self.boundaryBoxes[0]['boundList'][2].setText(str(nearY))
                    # self.boundaryBoxes[0]['boundList'][3].setText(str(farY))
                    # self.boundaryBoxes[0]['boundList'][4].setText(str(bottomZ))
                    # self.boundaryBoxes[0]['boundList'][5].setText(str(topZ))
                elif (args[0] == 'staticBoundaryBox'):
                    self.staticLine = counter
                elif (args[0] == 'profileCfg'):
                    if (len(args) < 12):
                        print ("Error: profileCfg had fewer arguments than expected")
                        continue
                    self.profile['startFreq'] = float(args[2])
                    self.profile['idle'] = float(args[3])
                    self.profile['adcStart'] = float(args[4])
                    self.profile['rampEnd'] = float(args[5])
                    self.profile['slope'] = float(args[8])
                    self.profile['samples'] = float(args[10])
                    self.profile['sampleRate'] = float(args[11])
                    print(self.profile)
                elif (args[0] == 'frameCfg'):
                    if (len(args) < 4):
                        print ("Error: frameCfg had fewer arguments than expected")
                        continue
                    self.frameTime = float(args[5])
                    self.profile['numLoops'] = float(args[3])
                    self.profile['numTx'] = float(args[2])+1
                elif (args[0] == 'chirpCfg'):
                    chirpCount += 1
                elif (args[0] == 'sensorPosition'):
                    # sensorPosition for x843 family has 3 args
                    self.profile['sensorHeight'] = float(args[1])
                    self.profile['az_tilt'] = float(args[2])
                    self.profile['elev_tilt'] = float(args[3])
                    print("Juber")

                    # # sensorPosition for x432 family has 5 args
                    # if (self.deviceType.currentText() in DEVICE_LIST[2] or self.deviceType.currentText() in DEVICE_LIST[3]):
                    #     if (len(args) < 6):
                    #         print ("Error: sensorPosition had fewer arguments than expected")
                    #         continue
                    #     #xOffset and yOffset are not implemented in the python code yet.
                    #     self.profile['xOffset'] = float(args[1])
                    #     self.profile['yOffset'] = float(args[2])
                    #     self.profile['sensorHeight'] = float(args[3])
                    #     self.profile['az_tilt'] = float(args[4])
                    #     self.profile['elev_tilt'] = float(args[5])
                # Only used for Small Obstacle Detection
                elif (args[0] == 'occStateMach'):
                    numZones = int(args[1])
                    if (numZones > 2):
                        print('ERROR: More zones specified by cfg than are supported in this GUI')
                # Only used for Small Obstacle Detection
                elif (args[0] == 'zoneDef'):
                    if (len(args) < 8):
                        print ("Error: zoneDef had fewer arguments than expected")
                        continue
                    zoneIdx = int(args[1])
                    minX = float(args[2])
                    maxX = float(args[3])
                    minY = float(args[4])
                    maxY = float(args[5])
                    # Offset by 3 so it is in center of screen
                    minZ = float(args[6]) + self.profile['sensorHeight']
                    maxZ = float(args[7]) + self.profile['sensorHeight']

                    name = 'occZone' + str(zoneIdx)

                    # self.addBoundBox(name, minX, maxX, minY, maxY, minZ, maxZ)
                elif (args[0] == 'mpdBoundaryBox'):
                    if (len(args) < 8):
                        print ("Error: mpdBoundaryBox had fewer arguments than expected")
                        continue
                    zoneIdx = int(args[1])
                    minX = float(args[2])
                    maxX = float(args[3])
                    minY = float(args[4])
                    maxY = float(args[5])
                    minZ = float(args[6])
                    maxZ = float(args[7])
                    name = 'mpdBox' + str(zoneIdx)
                    # self.addBoundBox(name, minX, maxX, minY, maxY, minZ, maxZ)

                elif (args[0] == 'chirpComnCfg'):
                    if (len(args) < 8):
                        print ("Error: chirpComnCfg had fewer arguments than expected")
                        continue
                    try:
                        self.chirpComnCfg['DigOutputSampRate'] = int(args[1])
                        self.chirpComnCfg['DigOutputBitsSel'] = int(args[2])
                        self.chirpComnCfg['DfeFirSel'] = int(args[3])
                        self.chirpComnCfg['NumOfAdcSamples'] = int(args[4])
                        self.chirpComnCfg['ChirpTxMimoPatSel'] = int(args[5])
                        self.chirpComnCfg['ChirpRampEndTime'] = 10 * float(args[6])
                        self.chirpComnCfg['ChirpRxHpfSel'] = int(args[7])
                    except Exception as e:
                        print (e)

                elif (args[0] == 'chirpTimingCfg'):
                    if (len(args) < 6):
                        print ("Error: chirpTimingCfg had fewer arguments than expected")
                        continue
                    self.chirpTimingCfg['ChirpIdleTime'] = 10.0 * float(args[1])
                    self.chirpTimingCfg['ChirpAdcSkipSamples'] = int(args[2]) << 10
                    self.chirpTimingCfg['ChirpTxStartTime'] = 10.0 * float(args[3])
                    self.chirpTimingCfg['ChirpRfFreqSlope'] = float(args[4])
                    self.chirpTimingCfg['ChirpRfFreqStart'] = float(args[5])
                elif (args[0] == 'clusterCfg'):
                    if (len(args) < 4):
                        print ("Error: clusterCfg had fewer arguments than expected")
                        continue
                    self.profile['enabled'] = float(args[1])
                    self.profile['maxDistance'] = float(args[2])
                    self.profile['minPoints'] = float(args[3])

                # This is specifically guiMonitor for 60Lo, this parsing will break the gui when an SDK 3 config is sent
                elif (args[0] == 'guiMonitor'):
                    if(self.deviceType.currentText() in DEVICE_LIST[2] or self.deviceType.currentText() in DEVICE_LIST[3] ):
                        if (len(args) < 12):
                            print ("Error: guiMonitor had fewer arguments than expected")
                            continue
                    self.guiMonitor['pointCloud'] = int(args[1])
                    self.guiMonitor['rangeProfile'] = int(args[2])
                    self.guiMonitor['NoiseProfile'] = int(args[3])
                    self.guiMonitor['rangeAzimuthHeatMap'] = int(args[4])
                    self.guiMonitor['rangeDopplerHeatMap'] = int(args[5])
                    self.guiMonitor['statsInfo'] = int(args[6])
                
            counter += 1

        # self.rangeRes = (3e8*(100/self.chirpComnCfg['DigOutputSampRate']))/(2*self.chirpTimingCfg['ChirpRfFreqSlope']*self.chirpComnCfg['NumOfAdcSamples'])
        self.rangeRes = (3e8*(100/self.chirpComnCfg['DigOutputSampRate'])*1e6)/(2*self.chirpTimingCfg['ChirpRfFreqSlope']*1e12*self.chirpComnCfg['NumOfAdcSamples'])
        # self.rangePlot.setXRange(0,(self.chirpComnCfg['NumOfAdcSamples']/2)*self.rangeRes,padding=0.01)
        
        self.rangeAxisVals = np.arange(0, self.chirpComnCfg['NumOfAdcSamples']/2*self.rangeRes, self.rangeRes)
        print(self.guiMonitor['rangeProfile'])

        # if (self.guiMonitor['rangeProfile'] == 0):
        #     self.rangePlot.getPlotItem().setLabel('top','range profile disabled')
        # elif (self.guiMonitor['rangeProfile'] == 1):
        #     self.rangePlot.getPlotItem().setLabel('top','Major Range Profile')
        # elif (self.guiMonitor['rangeProfile'] == 2):
        #     self.rangePlot.getPlotItem().setLabel('top','Minor Range Profile')
        # elif (self.guiMonitor['rangeProfile'] == 3):
        #     self.rangePlot.getPlotItem().setLabel('top','Major & Minor Range Profile Mode Not Supported')
        # else:
        #     self.rangePlot.getPlotItem().setLabel('top','INVALID gui monitor range profile input')
        

        # self.profile['maxRange'] = self.profile['sampleRate']*1e3*0.9*3e8/(2*self.profile['slope']*1e12)
        # bw = self.profile['samples']/(self.profile['sampleRate']*1e3)*self.profile['slope']*1e12
        # rangeRes = 3e8/(2*bw)
        # Tc = (self.profile['idle']*1e-6 + self.profile['rampEnd']*1e-6)*chirpCount
        # lda = 3e8/(self.profile['startFreq']*1e9)
        # maxVelocity = lda/(4*Tc)
        # velocityRes = lda/(2*Tc*self.profile['numLoops']*self.profile['numTx'])
        # self.configTable.setItem(1,1,QTableWidgetItem(str(self.profile['maxRange'])[:5]))
        # self.configTable.setItem(2,1,QTableWidgetItem(str(rangeRes)[:5]))
        # self.configTable.setItem(3,1,QTableWidgetItem(str(maxVelocity)[:5]))
        # self.configTable.setItem(4,1,QTableWidgetItem(str(velocityRes)[:5]))
   
        # Update sensor position
        # self.az_tilt.setText(str(0.0))
        # self.elev_tilt.setText(str(15.0))
        # self.s_height.setText(str(2.0))
        self.onChangeSensorPosition()

    def parseData(self):
        self.uart_thread.start(priority=QThread.HighestPriority)

    def selectCfg(self):
        try:
            file = "C:/ti/radar_toolbox_1_20_00_11/tools/visualizers/Industrial_Visualizer/AOP_6m_default.cfg"
            self.parseCfg(file)
            print("Config Loaded")
            # if 'maxTracks' in self.profile:
            #     print('maxTracks')
                # self.updateNumTracksBuffer() # Update the max number of tracks based off the config file
        except Exception as e:
            print(e)
            print('No cfg file selected!')

    def sendCfg(self):
        try:
            self.parser.sendCfg(self.cfg)
            self.configSent = 1
            print("frameTime", self.frameTime)
            self.parseTimer.start(int(self.frameTime)) # need this line 
                
        except Exception as e:
            print(e)
            print ('No cfg file selected!')

if __name__ == '__main__':
    radar= Window()
    radar.initConnectionPane()
    radar.connectCom()
    radar.selectCfg()
    radar.sendCfg()
    radar.startApp()

  • Hi,

    We will not review 500 lines of code for you on this forum. If you have specific questions about specific lines of code, we can help assist. Additionally, if the problem is that python code that you wrote is crashing, then we cannot help you. I would recommend debugging the python yourself to find the error. If the code that we wrote for the visualizer is crashing, then we can. Is the visualizer working without modifications?

    Best,

    Nate