2023/8/9

add one rotation axis

mmwave 的 python tool 有 azimuth, elevation tilt,缺一個 (不知道名子)。
所以加上去。

code:
elevAziRotMatrix = np.matrix([  [  math.cos(aziTilt),  math.cos(elevTilt)*math.sin(aziTilt), math.sin(elevTilt)*math.sin(aziTilt)],
                                [ -math.sin(aziTilt),  math.cos(elevTilt)*math.cos(aziTilt), math.sin(elevTilt)*math.cos(aziTilt)],
                                [                  0,                   -math.sin(elevTilt),                   math.cos(elevTilt)],
                             ])
對照 [https://en.wikipedia.org/wiki/Rotation_matrix wiki]:


用 0 度來猜對應關係..
所以看起來缺的應該是 β

另外 eleTilt, aziTilt 的方向和座標定義方向相反 α β γ
  • β : rotation
  • α : elevation
  • γ : azimuth
所以code :
 cos(γ),  cos(α)sin(γ), sin(α)sin(γ)
 sin(γ),  cos(α)cos(γ), sin(α)cos(γ)
      0, -sin(α)      , cos(α)
和 公式比較, α γ 的角度方向大概是相反,所以 sin 都要 * -1。

加入 rotTilt : β

原來 sin(0) = 0 的部份,要加回去 sin(β),也就是 sin(rotTilt)

2023/8/7

筆記:3D people counting, point cloud detection algorithm

根據 [https://dev.ti.com/tirex/explore/node?a=1AslXXD__1.10.00.13&node=A__AIQPG9x7K34A8l4ZELgznA__radar_toolbox__1AslXXD__1.10.00.13&r=1AslXXD__1.20.00.11 3D people tracking detection layer tuning guide],
這個3D point cloud 的 processing 的順序是:
  • 每個chirp 都做 range FFT
  • 同一TX反射的,每個水平ANT 的 range FFT,做 capon beamformer,找出方位角
  • 這樣就類似 R-V map 一樣,變成2D 的 heat map 圖,不過現在是 Range-Azimuth map,因為原來的 doppler FFT 改成 capon beamformer
  • 在 2D heat map (R-A map) 上,用 2D CFar (2-pass CFar)找出 signal point (detection point)
  • 找出 detection point (2D heat map 的 bin),再用垂直向的 ANT 做 capon beamformer 找出垂直角(elevation)
  • 針對detection point,同一 ant,連續chirp 的 bin,做 doppler FFT,算出速度

範例有兩種 ANT 排列,一個是安裝在天花板,一個安裝在牆上。
天花板的ANT 排列,X-Z 向是對稱的。
牆壁的ANT排例,X向的天線數量比較多。

所以兩個的 processing algorithm 不一樣。

牆壁的ANT,X 向比較多,所以先做 azimuth capon beamformer,再把結果的 bin 做 elevation beam former,
天花板 X-Z 向一樣多,所以都做....

2023/8/4

go-lang small http file server with upload function

ChatGPT 寫的,還沒看能不能動...
package main

import (
	"html/template"
	"io"
	"net/http"
	"os"
	"path/filepath"
)

func main() {
	http.HandleFunc("/", handleFileServer)
	http.HandleFunc("/upload", handleUpload)

	// Start the server on port 8080
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		panic(err)
	}
}

func handleFileServer(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	dirPath := "." // Current directory
	filePath := filepath.Join(dirPath, r.URL.Path)
	file, err := os.Open(filePath)
	if err != nil {
		http.Error(w, "File not found", http.StatusNotFound)
		return
	}
	defer file.Close()

	// Check if the path is a directory
	fileInfo, err := file.Stat()
	if err != nil {
		http.Error(w, "Failed to get file info", http.StatusInternalServerError)
		return
	}
	if fileInfo.IsDir() {
		renderDirectory(w, filePath)
		return
	}

	// Serve the file if it's not a directory
	http.ServeFile(w, r, filePath)
}

func renderDirectory(w http.ResponseWriter, dirPath string) {
	tmpl, err := template.New("directory").Parse(directoryTemplate)
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	// Read the directory content
	dir, err := os.ReadDir(dirPath)
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	data := struct {
		DirectoryName string
		Files         []string
	}{
		DirectoryName: dirPath,
		Files:         make([]string, 0),
	}

	for _, entry := range dir {
		data.Files = append(data.Files, entry.Name())
	}

	// Render the template
	err = tmpl.Execute(w, data)
	if err != nil {
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
	}
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Limit the maximum file size to 10 MB
	r.ParseMultipartForm(10 << 20)

	// Get the uploaded file
	file, handler, err := r.FormFile("file")
	if err != nil {
		http.Error(w, "Failed to get file from request", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// Create the file in the current directory
	f, err := os.Create(handler.Filename)
	if err != nil {
		http.Error(w, "Failed to create file on server", http.StatusInternalServerError)
		return
	}
	defer f.Close()

	// Copy the uploaded file to the server
	_, err = io.Copy(f, file)
	if err != nil {
		http.Error(w, "Failed to save file on server", http.StatusInternalServerError)
		return
	}

	// Redirect back to the directory listing
	http.Redirect(w, r, "/", http.StatusFound)
}

const directoryTemplate = `
<!DOCTYPE html>
<html>
<head>
	<title>Directory Listing: {{ .DirectoryName }}</title>
</head>
<body>
	<h1>Directory Listing: {{ .DirectoryName }}</h1>
	<ul>
		{{ range .Files }}
		<li><a href="{{ . }}">{{ . }}</a></li>
		{{ end }}
	</ul>
	<form action="/upload" method="post" enctype="multipart/form-data">
		<input type="file" name="file">
		<input type="submit" value="Upload">
	</form>
</body>
</html>
`

2023/7/10

mmwave profilecfg

ref: profilecfg 參數就是設定 chirp profile: start freq, slop, adc idle, start time, ramp stop time 參數的command.
每個欄位的定義要看 sdk 安裝目錄文件: mmwave_sdk_user_guide.pdf。
profileCfg 欄位是:
* profile id
* start freq
* idle time
* adc valid start time
* ramp end time  : 至少要大於 adc_samples/adc_sample_freq
* tx power : 0
* tx phase : 0
* freq slop
* tx start time
* adc samples
* adc sample freq
* high pass filter 1 corner freq
* high pass filter 2 corner freq
* rxGain
這些參數,在 mmwave studio 的 RampTimingCalculator 中,填好左邊的 slop, samples, sample freq, 其他參數就會在右邊顯示出來。
根據說明,右邊的參數是 minimum value,在 profileCfg 中,只要確定數值比他大就可以。


max range 跟 sample rate 有關系,因為距離越遠的object,delta F 越大。
range resulution 代表在 range FFT 上,鄰近兩個 peak 的對應頻率,轉換成距離。
就是 range FFT 的頻率解析度,就是 1/T, T 就是 整 sample points * sampling period .


要降低bandwidth..
* 減少 sample points * 減少 slope

2023/6/27

bookmark : python struct

struct 是python 用來處理 C 結構資料的module.

用 pack 來把一堆變數排在一起。
用 unpack 把 byte data 變成一堆變數。

pack, unpack 轉換的時候,就要靠 format string 來描述。
例如:
  • 'iif' : 兩個integer之後是一個float,所以總長度是 4+4+4,也可以用 '2i f'
  • 'B?l' : unsigned char, boolean 跟 long,所以總長度是 1 + 1 + 4 = 6
example:
packed = struct.pack('2if',1,2,1.3)
a, b, c = struct.unpack('2if',packed)
print( a,b,c )

1 2 1.29999999526

2023/6/21

TI mmwave, occupancy detection uart frame format.

mss_main.c :
static void OccupancyDetection3D_handleObjectDetResult
//copy to the format for output, and to future tracker
    gMmwMssMCB.pointCloudToUart.header.length               =   sizeof(mmwLab_output_message_tl) 
                                                              + sizeof(mmwLab_output_message_point_unit) 
                                                              + sizeof(mmwLab_output_message_UARTpoint) * outputFromDSP->pointCloudOut.object_count;
    if ( outputFromDSP->pointCloudOut.object_count == 0)
        gMmwMssMCB.pointCloudToUart.header.length           =   0;
    gMmwMssMCB.pointCloudToUart.header.type                 =   MMWDEMO_OUTPUT_MSG_POINT_CLOUD;
    gMmwMssMCB.pointCloudToUart.pointUint.azimuthUnit       =   0.01f;
    gMmwMssMCB.pointCloudToUart.pointUint.elevationUnit     =   0.01f;
    gMmwMssMCB.pointCloudToUart.pointUint.rangeUnit         =   0.00025f;
    gMmwMssMCB.pointCloudToUart.pointUint.dopplerUnit       =   0.00028f;
    gMmwMssMCB.pointCloudToUart.pointUint.snrUint           =   0.04f;
    gMmwMssMCB.numDetectedPoints                            =   outputFromDSP->pointCloudOut.object_count;
    for (pntIdx = 0; pntIdx < (int32_t)outputFromDSP->pointCloudOut.object_count; pntIdx++ )
    {
        gMmwMssMCB.pointCloudToUart.point[pntIdx].azimuth   =   (int8_t)round(outputFromDSP->pointCloudOut.pointCloud[pntIdx].azimuthAngle / gMmwMssMCB.pointCloudToUart.pointUint.azimuthUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].elevation =   (int8_t)round((outputFromDSP->pointCloudOut.pointCloud[pntIdx].elevAngle)/ gMmwMssMCB.pointCloudToUart.pointUint.elevationUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].range     =   (uint16_t)round(outputFromDSP->pointCloudOut.pointCloud[pntIdx].range / gMmwMssMCB.pointCloudToUart.pointUint.rangeUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].doppler   =   (int16_t)round(outputFromDSP->pointCloudOut.pointCloud[pntIdx].velocity / gMmwMssMCB.pointCloudToUart.pointUint.dopplerUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].snr       =   (uint16_t)round((float)outputFromDSP->pointCloudOut.snr[pntIdx].snr * 0.125f / gMmwMssMCB.pointCloudToUart.pointUint.snrUint);

        gMmwMssMCB.pointCloudFromDSP[pntIdx].elevAngle      =   outputFromDSP->pointCloudOut.pointCloud[pntIdx].elevAngle;
        gMmwMssMCB.pointCloudFromDSP[pntIdx].range          =   outputFromDSP->pointCloudOut.pointCloud[pntIdx].range;
        gMmwMssMCB.pointCloudFromDSP[pntIdx].velocity        =   outputFromDSP->pointCloudOut.pointCloud[pntIdx].velocity;
        gMmwMssMCB.pointCloudSideInfoFromDSP[pntIdx].snr            =   (float)outputFromDSP->pointCloudOut.snr[pntIdx].snr * 0.125f;
    }
message 定義:mmwLab_output.h:
typedef enum mmwLab_output_message_type_e
{
    /*! @brief   List of detected points */
    MMWDEMO_OUTPUT_MSG_DETECTED_POINTS = 1,

    /*! @brief   Range profile */
    MMWDEMO_OUTPUT_MSG_RANGE_PROFILE,

    /*! @brief   Noise floor profile */
    MMWDEMO_OUTPUT_MSG_NOISE_PROFILE,

    /*! @brief   Samples to calculate static azimuth  heatmap */
    MMWDEMO_OUTPUT_MSG_AZIMUT_STATIC_HEAT_MAP,

    /*! @brief   Range/Doppler detection matrix */
    MMWDEMO_OUTPUT_MSG_RANGE_DOPPLER_HEAT_MAP,

    /*! @brief   Point Cloud - Array of detected points (range/angle/doppler) */
    MMWDEMO_OUTPUT_MSG_POINT_CLOUD,

    /*! @brief   Target List - Array of detected targets (position, velocity, error covariance) */
    MMWDEMO_OUTPUT_MSG_TARGET_LIST,

    /*! @brief   Target List - Array of target indices */
    MMWDEMO_OUTPUT_MSG_TARGET_INDEX,

    /*! @brief   Classifier Output -- Array of target indices and tags */
    MMWDEMO_OUTPUT_MSG_CLASSIFIER_OUTPUT,

    /*! @brief   Stats information */
    MMWDEMO_OUTPUT_MSG_STATS,

    /*! @brief   Presence information */
    MMWDEMO_OUTPUT_PRESENCE_IND,

    MMWDEMO_OUTPUT_MSG_MAX
} mmwLab_output_message_type;
另外在 sdk 的 packages\ti\demo\xwr64xx\mmw\mmw_output.h 有:
typedef enum MmwDemo_output_message_type_e
{
    /*! @brief   List of detected points */
    MMWDEMO_OUTPUT_MSG_DETECTED_POINTS = 1,

    /*! @brief   Range profile */
    MMWDEMO_OUTPUT_MSG_RANGE_PROFILE,

    /*! @brief   Noise floor profile */
    MMWDEMO_OUTPUT_MSG_NOISE_PROFILE,

    /*! @brief   Samples to calculate static azimuth  heatmap */
    MMWDEMO_OUTPUT_MSG_AZIMUT_STATIC_HEAT_MAP,

    /*! @brief   Range/Doppler detection matrix */
    MMWDEMO_OUTPUT_MSG_RANGE_DOPPLER_HEAT_MAP,

    /*! @brief   Stats information */
    MMWDEMO_OUTPUT_MSG_STATS,

    /*! @brief   List of detected points */
    MMWDEMO_OUTPUT_MSG_DETECTED_POINTS_SIDE_INFO,

    /*! @brief   Samples to calculate static azimuth/elevation heatmap, (all virtual antennas exported) */
    MMWDEMO_OUTPUT_MSG_AZIMUT_ELEVATION_STATIC_HEAT_MAP,

    /*! @brief   temperature stats from Radar front end */
    MMWDEMO_OUTPUT_MSG_TEMPERATURE_STATS,

    MMWDEMO_OUTPUT_MSG_MAX
} MmwDemo_output_message_type;
前面一樣,6 開始不同,所以這個 example 的 tlv type 是自己用的。



mss_main.c 從 UART 送出 TLV 的地方 MmwDemo_uartTxTask:
        /* Send packet header */
        UART_write (uartHandle,
                           (uint8_t*)&header,
                           sizeof(mmwLab_output_message_header));

        /* Send detected Objects */
        if (objOut->header.length > 0)
        {
            UART_write (uartHandle,
                               (uint8_t*)objOut,
                               objOut->header.length);
        }
所以就是送 header,再送 objOut.
header 歸 header,只是在說接下來的 object detection result tlv 有幾個 tlv。
length, checksum 都是 header 自己,沒有包含接下來的 detection result tlv

先看 header..
typedef struct mmwLab_output_message_header_t
{
    /*! @brief   Output buffer magic word (sync word). It is initialized to  {0x0102,0x0304,0x0506,0x0708} */
    uint16_t    magicWord[4];

    /*! brief   Version: : MajorNum * 2^24 + MinorNum * 2^16 + BugfixNum * 2^8 + BuildNum   */
    uint32_t     version;

    /*! @brief   Total packet length including header in Bytes */
    uint32_t    totalPacketLen;

    /*! @brief   platform type */
    uint32_t    platform;

    /*! @brief   Frame number */
    uint32_t    frameNumber;

    /*! @brief   For Advanced Frame config, this is the sub-frame number in the range
     * 0 to (number of subframes - 1). For frame config (not advanced), this is always
     * set to 0. */
    uint32_t    subFrameNumber;

    /*! @brief Detection Layer timing */
    uint32_t    chirpProcessingMargin;
    uint32_t    frameProcessingTimeInUsec;

    /*! @brief Localization Layer Timing */
    uint32_t    trackingProcessingTimeInUsec;
    uint32_t    uartSendingTimeInUsec;


    /*! @brief   Number of TLVs */
    uint16_t    numTLVs;

    /*! @brief   check sum of the header */
    uint16_t    checkSum;

} mmwLab_output_message_header;
設定值是...
    header.platform =  0xA6843;
    header.magicWord[0] = 0x0102;
    header.magicWord[1] = 0x0304;
    header.magicWord[2] = 0x0506;
    header.magicWord[3] = 0x0708;
    header.version =    MMWAVE_SDK_VERSION_BUILD |
                        (MMWAVE_SDK_VERSION_BUGFIX << 8) |
                        (MMWAVE_SDK_VERSION_MINOR << 16) |
                        (MMWAVE_SDK_VERSION_MAJOR << 24);

  ...
  ...
        packetLen = sizeof(mmwLab_output_message_header);
        header.chirpProcessingMargin        =   timingInfo->interChirpProcessingMargin;
        header.frameProcessingTimeInUsec    =   timingInfo->frameProcessingTimeInUsec;
        header.uartSendingTimeInUsec        =   gMmwMssMCB.uartProcessingTimeInUsec; 
		
        if (objOut->header.length > 0)
        {
            packetLen += objOut->header.length;
            tlvIdx++;
        }

        header.numTLVs = tlvIdx;
        header.totalPacketLen   =   packetLen;
        header.frameNumber      =   frameIdx;
        header.subFrameNumber   =   subFrameIdx;
        header.checkSum         =   0;


        headerPtr               =   (uint16_t *)&header;
        for(n=0, sum = 0; n < sizeof(mmwLab_output_message_header)/sizeof(uint16_t); n++)
                                sum += *headerPtr++;
        header.checkSum         =   ~((sum >> 16) + (sum & 0xFFFF));

payload 的 objOut 是...
       objOut          =   &(gMmwMssMCB.pointCloudToUart);
其中,pointCloudToUart 是:
typedef struct MmwDemo_output_message_UARTpointCloud_t
{
    mmwLab_output_message_tl           header;
    mmwLab_output_message_point_unit   pointUint;
    mmwLab_output_message_UARTpoint    point[MAX_RESOLVED_OBJECTS_PER_FRAME];
} mmwLab_output_message_UARTpointCloud;
又分別是:
typedef struct mmwLab_output_message_tl_t
{
    /*! @brief   TLV type */
    uint32_t    type;

    /*! @brief   Length in bytes */
    uint32_t    length;

} mmwLab_output_message_tl;

typedef struct mmwLab_output_message_point_uint_t
{
    /*! @brief elevation  reporting unit, in radians */
    float       elevationUnit;
    /*! @brief azimuth  reporting unit, in radians */
    float       azimuthUnit;
    /*! @brief Doppler  reporting unit, in m/s */
    float       dopplerUnit;
    /*! @brief range reporting unit, in m */
    float       rangeUnit;
    /*! @brief SNR  reporting unit, linear */
    float       snrUint;

} mmwLab_output_message_point_unit;

typedef struct mmwLab_output_message_UARTpoint_t
{
    /*! @brief Detected point elevation, in number of azimuthUnit */
    int8_t      elevation;
    /*! @brief Detected point azimuth, in number of azimuthUnit */
    int8_t      azimuth;
    /*! @brief Detected point doppler, in number of dopplerUnit */
    int16_t      doppler;
    /*! @brief Detected point range, in number of rangeUnit */
    uint16_t        range;
    /*! @brief Range detection SNR, in number of snrUnit */
    uint16_t       snr;

} mmwLab_output_message_UARTpoint;



TI 有提供一個 python code, 做 pc 端UI : industrial visualizer
其中的 gui_parser.py 中 readAndParseUart( ) 就是 read uart 的進入點。
依序 sync magicword 之後,交給 parseFrame.py ,找出 tlvtype 之後,對應的 tlv parsing funtion 在 parseTLVs.py

occupancy detection 的 tlv frame 雖然是自己的,不是用 sdk 中 demo 的 標準 frame structure,
但是最後 parse point cloud 的格式,和 parseTLVs.py 的 parseCompressedSphericalPointCloudTLV( ) 一樣。

一個沒有 0 points 的 frame length 是 48,有 point 資料的frame 會是 48 + tlv length.

tlvlength 的資料中,在 CompressedSphericalCloudTLV 中,包含以下:
    tlvHeaderStruct = struct(...
        'type',             {'uint32', 4}, ... % TLV object Type
        'length',           {'uint32', 4});    % TLV object Length, in bytes, including TLV header

    % Point Cloud TLV reporting unit for all reported points
    pointUintStruct = struct(...
        'elevUnit',             {'float', 4}, ... % elevation, in rad
        'azimUnit',             {'float', 4}, ... % azimuth, in rad
        'dopplerUnit',          {'float', 4}, ... % Doplper, in m/s
        'rangeUnit',            {'float', 4}, ... % Range, in m
        'snrUnit',              {'float', 4});    % SNR, ratio
之後是一堆points 的資料,每個 point 的內容是:
    % Point Cloud TLV object consists of an array of points.
    % Each point has a structure defined below
    pointStruct = struct(...
        'elevation',        {'int8', 1}, ... % elevation, in rad
        'azimuth',          {'int8', 1}, ... % azimuth, in rad
        'doppler',          {'int16', 2}, ... % Doplper, in m/s
        'range',            {'uint16', 2}, ... % Range, in m
        'snr',              {'uint16', 2});    % SNR, ratio
以 frameLength 5098 的 frame 來看。
扣掉 frame Header 48 bytes,剩下 460
460 扣掉 4+4+5x*4 = 28 之後,剩下 432,
這 432 bytes 都是 point data,每個 point data 是 8 bytes, 所以有.. 432/8 = 54 points


結果這個 unit 的內容是固定的...
ref: mss_main.c - OccupancyDetection3D_handleObjectDetResult()
    gMmwMssMCB.pointCloudToUart.header.type                 =   MMWDEMO_OUTPUT_MSG_POINT_CLOUD;
    gMmwMssMCB.pointCloudToUart.pointUint.azimuthUnit       =   0.01f;
    gMmwMssMCB.pointCloudToUart.pointUint.elevationUnit     =   0.01f;
    gMmwMssMCB.pointCloudToUart.pointUint.rangeUnit         =   0.00025f;
    gMmwMssMCB.pointCloudToUart.pointUint.dopplerUnit       =   0.00028f;
    gMmwMssMCB.pointCloudToUart.pointUint.snrUint           =   0.04f;
    gMmwMssMCB.numDetectedPoints                            =   outputFromDSP->pointCloudOut.object_count;
    for (pntIdx = 0; pntIdx < (int32_t)outputFromDSP->pointCloudOut.object_count; pntIdx++ )
    {
        gMmwMssMCB.pointCloudToUart.point[pntIdx].azimuth   =   (int8_t)round(outputFromDSP->pointCloudOut.pointCloud[pntIdx].azimuthAngle / gMmwMssMCB.pointCloudToUart.pointUint.azimuthUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].elevation =   (int8_t)round((outputFromDSP->pointCloudOut.pointCloud[pntIdx].elevAngle)/ gMmwMssMCB.pointCloudToUart.pointUint.elevationUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].range     =   (uint16_t)round(outputFromDSP->pointCloudOut.pointCloud[pntIdx].range / gMmwMssMCB.pointCloudToUart.pointUint.rangeUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].doppler   =   (int16_t)round(outputFromDSP->pointCloudOut.pointCloud[pntIdx].velocity / gMmwMssMCB.pointCloudToUart.pointUint.dopplerUnit);
        gMmwMssMCB.pointCloudToUart.point[pntIdx].snr       =   (uint16_t)round((float)outputFromDSP->pointCloudOut.snr[pntIdx].snr * 0.125f / gMmwMssMCB.pointCloudToUart.pointUint.snrUint);

        gMmwMssMCB.pointCloudFromDSP[pntIdx].elevAngle      =   outputFromDSP->pointCloudOut.pointCloud[pntIdx].elevAngle;
        gMmwMssMCB.pointCloudFromDSP[pntIdx].range          =   outputFromDSP->pointCloudOut.pointCloud[pntIdx].range;
        gMmwMssMCB.pointCloudFromDSP[pntIdx].velocity        =   outputFromDSP->pointCloudOut.pointCloud[pntIdx].velocity;
        gMmwMssMCB.pointCloudSideInfoFromDSP[pntIdx].snr            =   (float)outputFromDSP->pointCloudOut.snr[pntIdx].snr * 0.125f;
    }

2023/6/16

TI mmWave python : send config file

send config file 抄這篇:
import serial
import time

serialControl = serial.Serial("/dev/ttyUSB1",115200,timeout=0.01)

with open(configFilePath, "r") as configFile:
    for configLine in configFile.readlines():
        # Send config value to the control port
        serialControl.write(configLine.encode())
        # Wait for response from control port
        time.sleep(0.01)
        echo = serialControl.readline()
        done = serialControl.readline()
        prompt = serialControl.read(11)
        print(echo.decode('utf-8'), end='')
        if verbose:
            print(done.decode('utf-8'))
            print(prompt.decode('utf-8'))
time.sleep(0.01) 很重要,不然會送得太快,chip 後來會掉字。
另外, cfg file 是 dos 還是 unix 格式都沒關係。 (\r\n or \n)。

encode, decode 是要做 字串 對 byte 之間的轉換。

open serial port 時,加上 timeout=0.01 是為了之後 readline( ) N 次的關係。
如果不加 timeout,readline 為卡住,一直到有資料輸出。
在 command error 的時候,就只有 Error 跟 prompt,少一次 Done。

TI Example, Lab 中的code,雖然都是用 cfg file, cfg command。
但是每個 Example. Lab support 的 command 不一樣。


dataport 沒有資料。
查一下是不是command 有錯。

sdk 的 util/cli.c 的 CLI_task 負責 control console
task 從 configurPort 讀取後,先和 gCLI.cfg.tableEntry[] 找 match cmd.
如果沒有,再交給 CLI_MMWaveExtensionHandler( ) 處理。
再沒有,就是 "not recognized as CLI command"

其中 * gCLI.cfg.tableEntry[] 是每個 example 自己定義的 command * CLI_MMWaveExtensionHandler 裡面使 mmwave SDK 內建的 command 錯誤的command 好像不會對程式造成影響...

結果:

送出 cfg command 時,要 delay,即使已經有收到 Done, 跟 prompt,還是要 delay 一下。
使用 matlab 的 script 在 windows 7 上 run,可能每個 step run 起來比較慢。所以 OK
在 linux 用 python 跑就會太快了,要再每個 command 送出後做 time.sleep(0.01),另外做 sensorStart 之前也要 delay 久一點。
實際有沒有成功,可以用 sensorStar 的回應來看,如果 OK, sensorStart 後,一樣會回答 Done,然後出現 prompt等待command。
這種狀況下 Dataport 才會有輸出。