From 1778bb6ef90ee194bea2617ff7e75a1e1ef55ba5 Mon Sep 17 00:00:00 2001 From: Jiaming Wang Date: Thu, 26 Oct 2023 16:11:30 +0200 Subject: [PATCH] Modification: 1. Change the testbed circuit. The TX and RX Arduinos now use "Serial1" to synchronize between experimental traces, which is a more robust way than the previous one (the voltage of two digital pins are not stable enough during transmission, and will lead to unexpected save file break). 2. Change the Arduino codes according to the above modification. 3. Fix some bugs in the "plain0" TX Arduino codes. 4. Fix some bugs in the data processing and figure plotting code. Some data processing were missing, and figure plotting used wrong results. 5. Add a main file "main_sim_txrx.m" which calls the simulation functions used in the early stage of the project, which is not fully compatible with the latest decoder. 6. About documentation. - Add detailed description on the two MATLAB toolboxes used in the code, which are highly recommended. The modification of corresponding codes to avoid these toolboxes are also provided. - Fix the description about the symbol interval of the data. --- Arduino/1Tx_gold/1Tx_gold.ino | 340 +++++++------- Arduino/1Tx_goldman/1Tx_goldman.ino | 342 +++++++------- Arduino/1Tx_plain0/1Tx_plain0.ino | 347 +++++++------- Arduino/2Tx_gold/2Tx_gold.ino | 362 +++++++------- Arduino/2Tx_goldman/2Tx_goldman.ino | 364 +++++++-------- Arduino/3Tx_gold/3Tx_gold.ino | 380 ++++++++------- Arduino/3Tx_goldman/3Tx_goldman.ino | 382 ++++++++------- Arduino/4Tx_goldman/4Tx_goldman.ino | 402 ++++++++-------- Arduino/4Tx_goldman0/4Tx_goldman0.ino | 404 ++++++++-------- Arduino/4Tx_ooc0/4Tx_ooc0.ino | 400 ++++++++-------- Arduino/4Tx_plain0/4Tx_plain0.ino | 407 ++++++++-------- Arduino/5Tx_goldman/5Tx_goldman.ino | 422 +++++++++-------- Arduino/6Tx_goldman/6Tx_goldman.ino | 442 +++++++++--------- Arduino/Rx/Rx.ino | 42 +- README.md | 53 ++- code_algo/DecodeSequence_ViterbiBackward.m | 2 +- code_algo/DecodeSequence_ViterbiForward4.m | 32 +- code_algo/EstimateChannelMMoSW.m | 118 ++--- code_algo/GenerateDataChips.m | 4 +- code_algo/GeneratePreambleBits.m | 2 +- code_algo/GeneratePreambleChips.m | 2 +- code_algo/GeneratePreambleDetectionChips.m | 2 +- code_algo/GetCEWeights.m | 12 +- code_algo/PreambleDetectionSW8.m | 32 +- .../decode_mmo_coherent_MMoNTxSW11loop.m | 251 +++++----- code_algo/decode_mmo_noncoherent_MMoNTx.m | 38 +- code_algo/emulates_construct_rxIn.m | 35 +- code_algo/get_gold_code.m | 8 +- code_algo/get_gold_code2.m | 8 +- code_algo/read_txrx.m | 32 +- code_algo/sim_mc_cir.m | 12 +- code_algo/sim_mc_cir3.m | 10 +- code_algo/sim_mmo_tx.m | 54 +-- code_algo/sim_tx.m | 24 +- code_figure/figure_results_ceLoss_nTx_ce.m | 12 +- code_figure/figure_results_codetuple.m | 2 +- code_figure/figure_results_moreMo_Tx_ce.m | 2 +- code_figure/generate_all_fig.m | 6 +- documentation/circuits/circuit.cddx | Bin 3903 -> 3938 bytes documentation/circuits/circuit.png | Bin 54252 -> 58183 bytes documentation/data_process.md | 50 +- documentation/testbed.md | 12 +- loop_emulates_txrx_all.m | 44 +- loop_emulates_txrx_noncoherent.m | 44 +- main_emulate_txrx.m | 101 ++-- main_emulates_txrx_allloop.m | 28 +- main_sim_txrx.m | 89 ++++ 47 files changed, 3138 insertions(+), 3019 deletions(-) create mode 100644 main_sim_txrx.m diff --git a/Arduino/1Tx_gold/1Tx_gold.ino b/Arduino/1Tx_gold/1Tx_gold.ino index dac30ee..ab8deef 100644 --- a/Arduino/1Tx_gold/1Tx_gold.ino +++ b/Arduino/1Tx_gold/1Tx_gold.ino @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -76,53 +74,50 @@ void tx0OnInit (void) { Ticker timerTx0OnInit (tx0OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); - + Serial.println ("Init done"); + // load CDMA settings - sprintf(TxSettingFile, "gold.txt"); + sprintf (TxSettingFile, "gold.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -130,177 +125,176 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -378,7 +372,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/1Tx_goldman/1Tx_goldman.ino b/Arduino/1Tx_goldman/1Tx_goldman.ino index e6c243b..0bb8c04 100644 --- a/Arduino/1Tx_goldman/1Tx_goldman.ino +++ b/Arduino/1Tx_goldman/1Tx_goldman.ino @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -76,53 +74,50 @@ void tx0OnInit (void) { Ticker timerTx0OnInit (tx0OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); - + Serial.println ("Init done"); + // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -130,177 +125,176 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -351,7 +345,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; tempind = (xbit[i] != midx) == cdmacode[cdmaidx[i]][cidx]; @@ -378,7 +372,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/1Tx_plain0/1Tx_plain0.ino b/Arduino/1Tx_plain0/1Tx_plain0.ino index b9e7c25..a379f3d 100644 --- a/Arduino/1Tx_plain0/1Tx_plain0.ino +++ b/Arduino/1Tx_plain0/1Tx_plain0.ino @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -76,53 +74,50 @@ void tx0OnInit (void) { Ticker timerTx0OnInit (tx0OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); - + // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = prelen * cdmalen + 2 * TotalSymbols; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -130,177 +125,176 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); +delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -346,9 +340,8 @@ void tx_transmit_over (int pump) void txi_transmit (int i) { - int tempind; - int cidx; - int midx; + int tempind, cidx, sign, midx; + if (pind[i] == prelen) { if (xind[i] == 0) @@ -383,12 +376,12 @@ void txi_transmit (int i) if (xind[i] == 2) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else { - cidx = floor(pchip[i]/2); + cidx = floor (pchip[i]/2); sign = pchip[i] % 2; if (sign == 0) diff --git a/Arduino/2Tx_gold/2Tx_gold.ino b/Arduino/2Tx_gold/2Tx_gold.ino index 34f3e2c..076518d 100644 --- a/Arduino/2Tx_gold/2Tx_gold.ino +++ b/Arduino/2Tx_gold/2Tx_gold.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 2; const int motors[nTx] = {2, 3}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)}; const float motorCon[nTx] = {10, 10}; const unsigned int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -89,53 +87,50 @@ void tx1OnInit (void) { Ticker timerTx1OnInit (tx1OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); - + Serial.println ("Init done"); + // load CDMA settings - sprintf(TxSettingFile, "gold.txt"); + sprintf (TxSettingFile, "gold.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -143,187 +138,186 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -401,7 +395,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/2Tx_goldman/2Tx_goldman.ino b/Arduino/2Tx_goldman/2Tx_goldman.ino index 88608be..85b8a38 100644 --- a/Arduino/2Tx_goldman/2Tx_goldman.ino +++ b/Arduino/2Tx_goldman/2Tx_goldman.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 2; const int motors[nTx] = {2, 3}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)}; const float motorCon[nTx] = {10, 10}; const unsigned int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -89,53 +87,50 @@ void tx1OnInit (void) { Ticker timerTx1OnInit (tx1OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); - + Serial.println ("Init done"); + // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -143,187 +138,186 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -374,7 +368,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; tempind = (xbit[i] != midx) == cdmacode[cdmaidx[i]][cidx]; @@ -401,7 +395,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/3Tx_gold/3Tx_gold.ino b/Arduino/3Tx_gold/3Tx_gold.ino index 74a18d2..b343798 100644 --- a/Arduino/3Tx_gold/3Tx_gold.ino +++ b/Arduino/3Tx_gold/3Tx_gold.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 3; const int motors[nTx] = {2, 3, 4}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen), 20+14*(1+prelen)*2}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen), 20+14* (1+prelen)*2}; const float motorCon[nTx] = {10, 10, 10}; const unsigned int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -102,53 +100,50 @@ void tx2OnInit (void) { Ticker timerTx2OnInit (tx2OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "gold.txt"); + sprintf (TxSettingFile, "gold.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -156,197 +151,196 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -424,7 +418,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/3Tx_goldman/3Tx_goldman.ino b/Arduino/3Tx_goldman/3Tx_goldman.ino index 4e3b35f..ad64862 100644 --- a/Arduino/3Tx_goldman/3Tx_goldman.ino +++ b/Arduino/3Tx_goldman/3Tx_goldman.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 3; const int motors[nTx] = {2, 3, 4}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen), 20+14*(1+prelen)*2}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen), 20+14* (1+prelen)*2}; const float motorCon[nTx] = {10, 10, 10}; const unsigned int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -102,53 +100,50 @@ void tx2OnInit (void) { Ticker timerTx2OnInit (tx2OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -156,197 +151,196 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -397,7 +391,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; tempind = (xbit[i] != midx) == cdmacode[cdmaidx[i]][cidx]; @@ -424,7 +418,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/4Tx_goldman/4Tx_goldman.ino b/Arduino/4Tx_goldman/4Tx_goldman.ino index 450246c..af282eb 100644 --- a/Arduino/4Tx_goldman/4Tx_goldman.ino +++ b/Arduino/4Tx_goldman/4Tx_goldman.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 4; const int motors[nTx] = {2, 3, 4, 5}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)*1, 20+14*(1+prelen)*2, 20+14*(1+prelen)*3}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)*1, 20+14* (1+prelen)*2, 20+14* (1+prelen)*3}; const float motorCon[nTx] = {10, 10, 10, 10}; const int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -115,53 +113,50 @@ void tx3OnInit (void) { Ticker timerTx3OnInit (tx3OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -169,207 +164,206 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + timerTx3OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + timerTx3OnInit.update (); + if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } + timerTx3On.update (); + if (timerTx3On.counter () >= totalChips) { timerTx3On.stop (); } + timerTx3Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } + if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx3On.state () != status_t::STOPPED) { continue; } + if (timerTx3Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - timerTx3OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - timerTx3OnInit.update (); - if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } - timerTx3On.update (); - if (timerTx3On.counter() >= totalChips) { timerTx3On.stop (); } - timerTx3Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx3On.state () != status_t::STOPPED) { continue; } - if (timerTx3Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -420,7 +414,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; tempind = (xbit[i] != midx) == cdmacode[cdmaidx[i]][cidx]; @@ -447,7 +441,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/4Tx_goldman0/4Tx_goldman0.ino b/Arduino/4Tx_goldman0/4Tx_goldman0.ino index f85cf84..0571062 100644 --- a/Arduino/4Tx_goldman0/4Tx_goldman0.ino +++ b/Arduino/4Tx_goldman0/4Tx_goldman0.ino @@ -24,7 +24,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 4; const int motors[nTx] = {2, 3, 4, 5}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)*1, 20+14*(1+prelen)*2, 20+14*(1+prelen)*3}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)*1, 20+14* (1+prelen)*2, 20+14* (1+prelen)*3}; const float motorCon[nTx] = {10, 10, 10, 10}; const int TotalSymbols = 60; @@ -45,12 +45,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -116,55 +114,52 @@ void tx3OnInit (void) { Ticker timerTx3OnInit (tx3OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); - assert(sizeof(cdmacode) == sizeof(cdmacode[0])*cdmanum); + assert (sizeof (cdmacode) == sizeof (cdmacode[0])*cdmanum); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; - assert(sizeof(cdmacode[0]) == sizeof(cdmacode[0][0])*goldlen); + assert (sizeof (cdmacode[0]) == sizeof (cdmacode[0][0])*goldlen); totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -172,207 +167,206 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - timerTx3OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - timerTx3OnInit.update (); - if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } - timerTx3On.update (); - if (timerTx3On.counter() >= totalChips) { timerTx3On.stop (); } - timerTx3Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx3On.state () != status_t::STOPPED) { continue; } - if (timerTx3Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + timerTx3OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + timerTx3OnInit.update (); + if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } + timerTx3On.update (); + if (timerTx3On.counter () >= totalChips) { timerTx3On.stop (); } + timerTx3Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } + if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx3On.state () != status_t::STOPPED) { continue; } + if (timerTx3Off.state () != status_t::STOPPED) { continue; } - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -423,7 +417,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; if (xbit[i]) { @@ -457,7 +451,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/4Tx_ooc0/4Tx_ooc0.ino b/Arduino/4Tx_ooc0/4Tx_ooc0.ino index 16a2204..c43aa7c 100644 --- a/Arduino/4Tx_ooc0/4Tx_ooc0.ino +++ b/Arduino/4Tx_ooc0/4Tx_ooc0.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 4; const int motors[nTx] = {2, 3, 4, 5}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)*1, 20+14*(1+prelen)*2, 20+14*(1+prelen)*3}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)*1, 20+14* (1+prelen)*2, 20+14* (1+prelen)*3}; const float motorCon[nTx] = {10, 10, 10, 10}; const int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -115,53 +113,50 @@ void tx3OnInit (void) { Ticker timerTx3OnInit (tx3OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "ooc.txt"); + sprintf (TxSettingFile, "ooc.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); ooclen = settingFile.parseInt (); cdmalen = ooclen; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(ooclen); + Serial.println (ooclen); #endif for (int i = 0; i < cdmanum; i++) { @@ -169,207 +164,206 @@ void setup() { ooccode[i][j] = settingFile.parseInt (); #if debug - Serial.print(ooccode[i][j]); + Serial.print (ooccode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (ooclen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + timerTx3OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + timerTx3OnInit.update (); + if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } + timerTx3On.update (); + if (timerTx3On.counter () >= totalChips) { timerTx3On.stop (); } + timerTx3Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } + if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx3On.state () != status_t::STOPPED) { continue; } + if (timerTx3Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(ooclen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - timerTx3OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - timerTx3OnInit.update (); - if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } - timerTx3On.update (); - if (timerTx3On.counter() >= totalChips) { timerTx3On.stop (); } - timerTx3Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx3On.state () != status_t::STOPPED) { continue; } - if (timerTx3Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -453,7 +447,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/4Tx_plain0/4Tx_plain0.ino b/Arduino/4Tx_plain0/4Tx_plain0.ino index cd11e5b..f4f7f1d 100644 --- a/Arduino/4Tx_plain0/4Tx_plain0.ino +++ b/Arduino/4Tx_plain0/4Tx_plain0.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 4; const int motors[nTx] = {2, 3, 4, 5}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)*1, 20+14*(1+prelen)*2, 20+14*(1+prelen)*3}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)*1, 20+14* (1+prelen)*2, 20+14* (1+prelen)*3}; const float motorCon[nTx] = {PulseOnTime, 10, 10, 10}; const unsigned int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -115,53 +113,50 @@ void tx3OnInit (void) { Ticker timerTx3OnInit (tx3OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = prelen * cdmalen + 2 * TotalSymbols; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -169,207 +164,206 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + timerTx3OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + timerTx3OnInit.update (); + if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } + timerTx3On.update (); + if (timerTx3On.counter () >= totalChips) { timerTx3On.stop (); } + timerTx3Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } + if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx3On.state () != status_t::STOPPED) { continue; } + if (timerTx3Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - timerTx3OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - timerTx3OnInit.update (); - if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } - timerTx3On.update (); - if (timerTx3On.counter() >= totalChips) { timerTx3On.stop (); } - timerTx3Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx3On.state () != status_t::STOPPED) { continue; } - if (timerTx3Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -415,9 +409,8 @@ void tx_transmit_over (int pump) void txi_transmit (int i) { - int tempind; - int cidx; - int midx; + int tempind, cidx, sign, midx; + if (pind[i] == prelen) { if (xind[i] == 0) @@ -452,12 +445,12 @@ void txi_transmit (int i) if (xind[i] == 2) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else { - cidx = floor(pchip[i]/2); + cidx = floor (pchip[i]/2); sign = pchip[i] % 2; if (sign == 0) diff --git a/Arduino/5Tx_goldman/5Tx_goldman.ino b/Arduino/5Tx_goldman/5Tx_goldman.ino index d2740ee..6496d3c 100644 --- a/Arduino/5Tx_goldman/5Tx_goldman.ino +++ b/Arduino/5Tx_goldman/5Tx_goldman.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 5; const int motors[nTx] = {2, 3, 4, 5, 6}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)*1, 20+14*(1+prelen)*2, 20+14*(1+prelen)*3, 20+14*(1+prelen)*4}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)*1, 20+14* (1+prelen)*2, 20+14* (1+prelen)*3, 20+14* (1+prelen)*4}; const float motorCon[nTx] = {10, 10, 10, 10, 10}; const int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -128,53 +126,50 @@ void tx4OnInit (void) { Ticker timerTx4OnInit (tx4OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -182,217 +177,216 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; + tx4OnInitCounter = motorOffsets[4]+motorOffsets2[4]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + timerTx3OnInit.start (); + timerTx4OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + timerTx3OnInit.update (); + if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } + timerTx3On.update (); + if (timerTx3On.counter () >= totalChips) { timerTx3On.stop (); } + timerTx3Off.update (); + timerTx4OnInit.update (); + if (timerTx4On.state () == status_t::RUNNING) { timerTx4OnInit.stop (); } + timerTx4On.update (); + if (timerTx4On.counter () >= totalChips) { timerTx4On.stop (); } + timerTx4Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } + if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx3On.state () != status_t::STOPPED) { continue; } + if (timerTx3Off.state () != status_t::STOPPED) { continue; } + if (timerTx4OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx4On.state () != status_t::STOPPED) { continue; } + if (timerTx4Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; - tx4OnInitCounter = motorOffsets[4]+motorOffsets2[4]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - timerTx3OnInit.start (); - timerTx4OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - timerTx3OnInit.update (); - if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } - timerTx3On.update (); - if (timerTx3On.counter() >= totalChips) { timerTx3On.stop (); } - timerTx3Off.update (); - timerTx4OnInit.update (); - if (timerTx4On.state () == status_t::RUNNING) { timerTx4OnInit.stop (); } - timerTx4On.update (); - if (timerTx4On.counter() >= totalChips) { timerTx4On.stop (); } - timerTx4Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx3On.state () != status_t::STOPPED) { continue; } - if (timerTx3Off.state () != status_t::STOPPED) { continue; } - if (timerTx4OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx4On.state () != status_t::STOPPED) { continue; } - if (timerTx4Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -443,7 +437,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; tempind = (xbit[i] != midx) == cdmacode[cdmaidx[i]][cidx]; @@ -470,7 +464,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/6Tx_goldman/6Tx_goldman.ino b/Arduino/6Tx_goldman/6Tx_goldman.ino index c4471e4..9df6977 100644 --- a/Arduino/6Tx_goldman/6Tx_goldman.ino +++ b/Arduino/6Tx_goldman/6Tx_goldman.ino @@ -23,7 +23,7 @@ const unsigned int DisposedTime = 10000; // **** Pump Parameters **** const int nTx = 6; const int motors[nTx] = {2, 3, 4, 5, 6, 7}; -const int motorOffsets[nTx] = {20, 20+14*(1+prelen)*1, 20+14*(1+prelen)*2, 20+14*(1+prelen)*3, 20+14*(1+prelen)*4, 20+14*(1+prelen)*5}; +const int motorOffsets[nTx] = {20, 20+14* (1+prelen)*1, 20+14* (1+prelen)*2, 20+14* (1+prelen)*3, 20+14* (1+prelen)*4, 20+14* (1+prelen)*5}; const float motorCon[nTx] = {10, 10, 10, 10, 10, 10}; const int TotalSymbols = 100; @@ -44,12 +44,10 @@ int randlist[7]; int motorOffsets2[nTx]; // **** TxRx Sync for Record **** -const int sync = 9; -const int ack = 8; const int chipSelect = 53; -/* +/* Arduino Uno * SD card attached to SPI bus as follows: - ** CS - pin 10 (Chip Select) + ** CS - pin 10 (Chip Select) ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 @@ -141,53 +139,50 @@ void tx5OnInit (void) { Ticker timerTx5OnInit (tx5OnInit, ChipInterval, 0, MILLIS); // **** Setup **** -void setup() +void setup () { // initialize serial communication with computer: - Serial.begin(115200); - while (!Serial) ; + Serial.begin (115200); + Serial1.begin (19200); // setup pins for (int i = 0; i < nTx; i++) { pinMode (motors[i], OUTPUT); } - pinMode(sync, OUTPUT); - pinMode(ack, INPUT); - digitalWrite(sync, LOW); // Initialize SD Card - Serial.println("Init SD"); - if (!SD.begin(chipSelect)) + Serial.println ("Init SD"); + if (!SD.begin (chipSelect)) { - Serial.println("Init fail"); - while (1); + Serial.println ("Init fail"); + exit (0); } - Serial.println("Init done"); + Serial.println ("Init done"); // load CDMA settings - sprintf(TxSettingFile, "goldman.txt"); + sprintf (TxSettingFile, "goldman.txt"); if (!SD.exists (TxSettingFile)) { - Serial.println("Setting not exist"); - Serial.println(""); - while(1); + Serial.println ("Setting not exist"); + Serial.println (""); + exit (0); } Serial.print ("Setting file Name: "); Serial.println (TxSettingFile); settingFile = SD.open (TxSettingFile, FILE_READ); if (!settingFile) { - Serial.println("Setting open fail"); - Serial.println(""); - while(1); + Serial.println ("Setting open fail"); + Serial.println (""); + exit (0); } cdmanum = settingFile.parseInt (); goldlen = settingFile.parseInt (); cdmalen = goldlen * 2; totalChips = (prelen + TotalSymbols) * cdmalen; #if debug - Serial.println(goldlen); + Serial.println (goldlen); #endif for (int i = 0; i < cdmanum; i++) { @@ -195,227 +190,226 @@ void setup() { cdmacode[i][j] = settingFile.parseInt (); #if debug - Serial.print(cdmacode[i][j]); + Serial.print (cdmacode[i][j]); #endif } #if debug - Serial.println(""); + Serial.println (""); #endif } settingFile.close (); Serial.println ("Setting loaded"); + delay (500); + + // wait for rx to set up + Serial.println ("Waiting for Rx to set up..."); + while (!Serial1.available ()) + { + delay (1000); + Serial.println ("Waiting..."); + } + if (Serial1.read () != 'a') + { + Serial.println ("Rx sent a wrong message"); + exit (0); + } + Serial.println ("Rx set up confirmed"); // prepare the pumps - Serial.println("Set up"); - Serial.println("Pipe in water"); + Serial.println ("Set up"); + Serial.println ("Pipe in water"); turn_off_all (); - delay(DisposedTime); - Serial.println("Fill the tubes"); + delay (DisposedTime); + Serial.println ("Fill the tubes"); turn_on_all (); - delay(DisposedTime); - Serial.println("Clear the tubes"); + delay (DisposedTime); + Serial.println ("Clear the tubes"); turn_off_all (); - delay(DisposedTime); - Serial.println("Set up done"); - Serial.println(""); - Serial.println(""); + delay (DisposedTime); + Serial.println ("Set up done"); + Serial.println (""); + Serial.println (""); - randomSeed(analogRead(0)); + randomSeed (analogRead (0)); } -void loop() { - delay(5000); +void loop () { + delay (5000); - switch(digitalRead(ack)) + for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) { - case LOW: - // Rx is not synced - turn_off_all(); - delay(ChipInterval); - Serial.println("AMotors stopped"); - Serial.println(""); - Serial.println(""); - break; + // check if write file exists not + sprintf (TxRecordFile, "%02d.txt", fileIndex); + if (SD.exists (TxRecordFile)) + { + SD.remove (TxRecordFile); + #if debug + Serial.println ("File removed"); + #endif + } + + // randomness + for (int i = 0; i < cdmanum; i++) + { + randlist[i] = i; + } + for (int i = 0; i < nTx; i++) + { + int temp = random (cdmanum-i); + cdmaidx[i] = randlist[temp]; + randlist[temp] = randlist[cdmanum-i-1]; + motorOffsets2[i] = random (cdmalen); + } + + // create record file + Serial.print ("File Name: "); + Serial.println (TxRecordFile); + recordFile = SD.open (TxRecordFile, FILE_WRITE); + if (!recordFile) + { + Serial.println ("Record open fail"); + Serial.println (""); + exit (0); + } + + // start collecting data + recordFile.print (ChipInterval); + recordFile.print (" "); + recordFile.print (TotalSymbols); + recordFile.print (" "); + recordFile.print (nTx); + recordFile.print (" "); + recordFile.print (goldlen); + recordFile.print (" "); + recordFile.print (prelen); + recordFile.println (""); + + for (int i = 0; i < nTx; i++) + { + #if debug + Serial.print (" ("); + Serial.print (motors[i]); + Serial.print (", "); + Serial.print (motorOffsets[i]); + Serial.print (", "); + Serial.print (motorOffsets2[i]); + Serial.print (", "); + Serial.print (motorCon[i]); + Serial.print ("), "); + + Serial.print (" (code "); + Serial.print (cdmaidx[i]); + Serial.println (")"); + #endif + + recordFile.print (" ("); + recordFile.print (motors[i]); + recordFile.print (", "); + recordFile.print (motorOffsets[i]); + recordFile.print (", "); + recordFile.print (motorOffsets2[i]); + recordFile.print (", "); + recordFile.print (motorCon[i]); + recordFile.print ("), "); + + recordFile.print (" (code "); + recordFile.print (cdmaidx[i]); + recordFile.println (")"); + } + + Serial.println ("Random bits"); + + Serial.println ("START"); + recordFile.println ("START"); + + // set counter + tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; + tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; + tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; + tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; + tx4OnInitCounter = motorOffsets[4]+motorOffsets2[4]; + tx5OnInitCounter = motorOffsets[5]+motorOffsets2[5]; + + // Notify Rx to start + Serial1.write ('a'); + delay (200); + + timerTx0OnInit.start (); + timerTx1OnInit.start (); + timerTx2OnInit.start (); + timerTx3OnInit.start (); + timerTx4OnInit.start (); + timerTx5OnInit.start (); + while (1) + { + timerTx0OnInit.update (); + if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } + timerTx0On.update (); + if (timerTx0On.counter () >= totalChips) { timerTx0On.stop (); } + timerTx0Off.update (); + timerTx1OnInit.update (); + if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } + timerTx1On.update (); + if (timerTx1On.counter () >= totalChips) { timerTx1On.stop (); } + timerTx1Off.update (); + timerTx2OnInit.update (); + if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } + timerTx2On.update (); + if (timerTx2On.counter () >= totalChips) { timerTx2On.stop (); } + timerTx2Off.update (); + timerTx3OnInit.update (); + if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } + timerTx3On.update (); + if (timerTx3On.counter () >= totalChips) { timerTx3On.stop (); } + timerTx3Off.update (); + timerTx4OnInit.update (); + if (timerTx4On.state () == status_t::RUNNING) { timerTx4OnInit.stop (); } + timerTx4On.update (); + if (timerTx4On.counter () >= totalChips) { timerTx4On.stop (); } + timerTx4Off.update (); + timerTx5OnInit.update (); + if (timerTx5On.state () == status_t::RUNNING) { timerTx5OnInit.stop (); } + timerTx5On.update (); + if (timerTx5On.counter () >= totalChips) { timerTx5On.stop (); } + timerTx5Off.update (); + + if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx0On.state () != status_t::STOPPED) { continue; } + if (timerTx0Off.state () != status_t::STOPPED) { continue; } + if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx1On.state () != status_t::STOPPED) { continue; } + if (timerTx1Off.state () != status_t::STOPPED) { continue; } + if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx2On.state () != status_t::STOPPED) { continue; } + if (timerTx2Off.state () != status_t::STOPPED) { continue; } + if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx3On.state () != status_t::STOPPED) { continue; } + if (timerTx3Off.state () != status_t::STOPPED) { continue; } + if (timerTx4OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx4On.state () != status_t::STOPPED) { continue; } + if (timerTx4Off.state () != status_t::STOPPED) { continue; } + if (timerTx5OnInit.state () != status_t::STOPPED) { continue; } + if (timerTx5On.state () != status_t::STOPPED) { continue; } + if (timerTx5Off.state () != status_t::STOPPED) { continue; } - case HIGH: - // Rx is synced - for (int fileIndex = fileIndexStart; fileIndex <= fileIndexEnd; fileIndex++) - { - // check if write file exists not - sprintf(TxRecordFile, "%02d.txt", fileIndex); - if (SD.exists (TxRecordFile)) - { - SD.remove (TxRecordFile); - #if debug - Serial.println("File removed"); - #endif - } - - // randomness - for (int i = 0; i < cdmanum; i++) - { - randlist[i] = i; - } - for (int i = 0; i < nTx; i++) - { - int temp = random(cdmanum-i); - cdmaidx[i] = randlist[temp]; - randlist[temp] = randlist[cdmanum-i-1]; - motorOffsets2[i] = random(cdmalen); - } - - // create record file - Serial.print("File Name: "); - Serial.println(TxRecordFile); - recordFile = SD.open(TxRecordFile, FILE_WRITE); - if (!recordFile) - { - Serial.println("Record open fail"); - Serial.println(""); - while(1); - } - - // start collecting data - recordFile.print(ChipInterval); - recordFile.print(" "); - recordFile.print(TotalSymbols); - recordFile.print(" "); - recordFile.print(nTx); - recordFile.print(" "); - recordFile.print(goldlen); - recordFile.print(" "); - recordFile.print(prelen); - recordFile.println(""); - - for (int i = 0; i < nTx; i++) - { - #if debug - Serial.print("("); - Serial.print(motors[i]); - Serial.print(", "); - Serial.print(motorOffsets[i]); - Serial.print(", "); - Serial.print(motorOffsets2[i]); - Serial.print(", "); - Serial.print(motorCon[i]); - Serial.print("), "); - - Serial.print("(code "); - Serial.print(cdmaidx[i]); - Serial.println(")"); - #endif - - recordFile.print("("); - recordFile.print(motors[i]); - recordFile.print(", "); - recordFile.print(motorOffsets[i]); - recordFile.print(", "); - recordFile.print(motorOffsets2[i]); - recordFile.print(", "); - recordFile.print(motorCon[i]); - recordFile.print("), "); - - recordFile.print("(code "); - recordFile.print(cdmaidx[i]); - recordFile.println(")"); - } - - Serial.println("Random bits"); - - Serial.println("START"); - recordFile.println("START"); - - // set counter - tx0OnInitCounter = motorOffsets[0]+motorOffsets2[0]; - tx1OnInitCounter = motorOffsets[1]+motorOffsets2[1]; - tx2OnInitCounter = motorOffsets[2]+motorOffsets2[2]; - tx3OnInitCounter = motorOffsets[3]+motorOffsets2[3]; - tx4OnInitCounter = motorOffsets[4]+motorOffsets2[4]; - tx5OnInitCounter = motorOffsets[5]+motorOffsets2[5]; - - // Notify Rx to start - digitalWrite(sync, HIGH); - delay(200); - - timerTx0OnInit.start (); - timerTx1OnInit.start (); - timerTx2OnInit.start (); - timerTx3OnInit.start (); - timerTx4OnInit.start (); - timerTx5OnInit.start (); - while (1) - { - timerTx0OnInit.update (); - if (timerTx0On.state () == status_t::RUNNING) { timerTx0OnInit.stop (); } - timerTx0On.update (); - if (timerTx0On.counter() >= totalChips) { timerTx0On.stop (); } - timerTx0Off.update (); - timerTx1OnInit.update (); - if (timerTx1On.state () == status_t::RUNNING) { timerTx1OnInit.stop (); } - timerTx1On.update (); - if (timerTx1On.counter() >= totalChips) { timerTx1On.stop (); } - timerTx1Off.update (); - timerTx2OnInit.update (); - if (timerTx2On.state () == status_t::RUNNING) { timerTx2OnInit.stop (); } - timerTx2On.update (); - if (timerTx2On.counter() >= totalChips) { timerTx2On.stop (); } - timerTx2Off.update (); - timerTx3OnInit.update (); - if (timerTx3On.state () == status_t::RUNNING) { timerTx3OnInit.stop (); } - timerTx3On.update (); - if (timerTx3On.counter() >= totalChips) { timerTx3On.stop (); } - timerTx3Off.update (); - timerTx4OnInit.update (); - if (timerTx4On.state () == status_t::RUNNING) { timerTx4OnInit.stop (); } - timerTx4On.update (); - if (timerTx4On.counter() >= totalChips) { timerTx4On.stop (); } - timerTx4Off.update (); - timerTx5OnInit.update (); - if (timerTx5On.state () == status_t::RUNNING) { timerTx5OnInit.stop (); } - timerTx5On.update (); - if (timerTx5On.counter() >= totalChips) { timerTx5On.stop (); } - timerTx5Off.update (); - - if (timerTx0OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx0On.state () != status_t::STOPPED) { continue; } - if (timerTx0Off.state () != status_t::STOPPED) { continue; } - if (timerTx1OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx1On.state () != status_t::STOPPED) { continue; } - if (timerTx1Off.state () != status_t::STOPPED) { continue; } - if (timerTx2OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx2On.state () != status_t::STOPPED) { continue; } - if (timerTx2Off.state () != status_t::STOPPED) { continue; } - if (timerTx3OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx3On.state () != status_t::STOPPED) { continue; } - if (timerTx3Off.state () != status_t::STOPPED) { continue; } - if (timerTx4OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx4On.state () != status_t::STOPPED) { continue; } - if (timerTx4Off.state () != status_t::STOPPED) { continue; } - if (timerTx5OnInit.state () != status_t::STOPPED) { continue; } - if (timerTx5On.state () != status_t::STOPPED) { continue; } - if (timerTx5Off.state () != status_t::STOPPED) { continue; } - - break; - } - - delay(DisposedTime); - - // Notify Rx to end - digitalWrite(sync, LOW); - delay(200); - Serial.println("END"); - Serial.println(""); - Serial.println(""); - recordFile.close(); - - } - while(1); break; + } + + delay (DisposedTime); + + // Notify Rx to end + Serial1.write ('z'); + delay (200); + Serial.println ("END"); + Serial.println (""); + Serial.println (""); + recordFile.close (); + } } // Preparing pumps actions -void turn_off_all(void) +void turn_off_all (void) { for (int i = 0; i < nTx; i++) { @@ -466,7 +460,7 @@ void txi_transmit (int i) int midx; if (pchip[i] == goldlen) { - cidx = floor(xind[i]/2); + cidx = floor (xind[i]/2); midx = xind[i] % 2; tempind = (xbit[i] != midx) == cdmacode[cdmaidx[i]][cidx]; @@ -493,7 +487,7 @@ void txi_transmit (int i) if (xind[i] == cdmalen) { xind[i] = 0; - xbit[i] = random(2); + xbit[i] = random (2); } } else diff --git a/Arduino/Rx/Rx.ino b/Arduino/Rx/Rx.ino index 0f0e19d..3945b2f 100644 --- a/Arduino/Rx/Rx.ino +++ b/Arduino/Rx/Rx.ino @@ -10,34 +10,32 @@ const int Ts = 5; //Gap between sampling in ms //******************************************** //*****************Setup Connections********** -const int sync = 9; -const int ack = 8; //acknowledge to tx that rx is ready -const int chipSelect = 10; +int record = LOW; +const int chipSelect = 53; const byte ECsensorPin = A1; // EC Meter analog output,pin on analog 1 //******************************************** //*******************Variables**************** File myFile; -int fileIndex = 15; +int fileIndex = 6; char fileName[50]; int activated = 0; unsigned int AnalogReading; unsigned long AnalogSampleTime; + //********************************************* void rx_receive (void); Ticker timerRx (rx_receive, Ts, 0, MILLIS); -void setup() -{ - pinMode (sync, INPUT); - pinMode (ack, OUTPUT); - digitalWrite (ack,LOW); - +void setup () +{ while (activated == 0) { Serial.begin (115200); + Serial1.begin (19200); + Serial.print ("\n\n\n\n\n"); Serial.print ("Initializing SD card..."); if (!SD.begin (chipSelect)) @@ -54,19 +52,36 @@ void setup() AnalogReading = 0; AnalogSampleTime = millis (); AnalogReading = analogRead (ECsensorPin); - digitalWrite (ack, HIGH); + Serial1.write ('a'); } void loop () { - switch (digitalRead (sync)) + Serial1.write ('a'); + if (Serial1.available ()) + { + switch (Serial1.read ()) + { + case 'a': + record = HIGH; + break; + case 'z': + record = LOW; + break; + default: + Serial.println ("Tx sent a wrong message"); + exit (0); + } + } + + switch (record) { case HIGH: if (!myFile) { Serial.print ("Opening File Number: "); Serial.println (fileIndex); - sprintf(fileName, "%02d.txt", fileIndex); + sprintf (fileName, "%02d.txt", fileIndex); if (SD.exists (fileName)) {SD.remove (fileName);} myFile = SD.open (fileName, FILE_WRITE); if (myFile) {Serial.println ("File Created");} @@ -103,4 +118,5 @@ void rx_receive (void) myFile.print (AnalogSampleTime); myFile.print ('\t'); myFile.println (AnalogReading); + // Serial.println (AnalogReading); } diff --git a/README.md b/README.md index e667b90..0146375 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,46 @@ Use `git clone` to download this repository. This project is composed of the experiment part and the data processing part. -For experiment part, please prepare two Arduinos (one for TXs and one for RX) and install the Arduino IDE. The required `Ticker.h` and `OneWire.h` can be downloaded from the Arduino IDE library. - -For data processing part, please install MATLAB R2019b or any later version. Please install any toolbox that is reported missing when you run the scripts. +### Experiment + +For experiment part, please prepare two Arduinos (one for TXs and one for RX) and install the Arduino IDE. The following two header files required to run the code, which can be found in the Arduino IDE library (In the Arduino IDE, click the `Tools > Manage Libraries...`). + +* `Ticker.h` +* `OneWire.h` + +And of course, you need to prepare pumps, tubes, salt (and/or baking soda) and the [EC reader](https://www.dfrobot.com/product-1123.html). + +### Data processing + +For data processing part, please install MATLAB R2019b or any later version. The following toolboxes are **_highly recommended_**. + +* `Communication Toolbox` + + This toolbox is used in `code_algo/get_gold_code.m` to generate Gold code of certain parameters, and `code_algo/get_gold_code2.m` to generate MoMA code (which is Gold code modulated with Manchester code). + + However, these two functions are only used in simulation for the early stage of the research. Once we go to experiment, we only need to generate the codes of required length and store in a TXT file before hand. Thus, during transmission the TX Arduino will pick the code for each TX from this file. Since MoMA assumes that the RX knows the pre-assigned code to each TX, the decoding will proceed depending on this assignment. + +* `Parallel Computing Toolbox` + + This toolbox is used in `loop_emulates_txrx_all.m` and `loop_emulates_txrx_noncoherent.m` to parallel the decoding of multiple traces, thus accelerate the whole result generating procedure. Note that this toolbox is NOT used to speed up the decoding of ONE trace. Instead, each CPU core is used to process different traces concurrently. + + You can get rid of this toolbox by making the following modifications to the code, **_which will definitely and extremely increase the time to generate the results_**. + - In `main_emulates_txrx_allloop.m`, comment the following code from line 5 to 7. + ``` + if isempty(gcp("nocreate")) + parpool(feature("numcores")); + end + ``` + This code creates the maximum number of workers, which is the same as the number of the CPU cores. But you can also specify a suitable number you want, e.g. 8 workers with the following code + ``` + if isempty(gcp("nocreate")) + parpool(8); + end + ``` + - In `loop_emulates_txrx_all.m` and `loop_emulates_txrx_noncoherent.m`, change the code in line 108 from + ``` + parfor kk = 1:totalruns + ``` + to + ``` + for kk = 1:totalruns + ``` ## How to run this artifact? @@ -28,10 +65,14 @@ The data processing codes are MATLAB scripts and functions. We provide two MATLAB scripts that can directly generate the figures in the paper. -[`main_emulates_txrx_allloop.m`](/main_emulates_txrx_allloop.m) processes the experiment data and save the results as `.mat` files in corresponding folders. The script contains multiple loops, each defining a variable that specifies either the experiment data or the algorithm. Since not all combinations are reported in the paper, we add multiple `if` conditions and mark in comment their corresponding figure indices. Please feel free to modify the `if` conditions if you would like to see other results. +[`main_emulates_txrx_allloop.m`](/main_emulates_txrx_allloop.m) processes the experiment data and save the results as `.mat` files in corresponding folders. The script contains multiple loops, each defining a variable that specifies either the experiment data or the algorithm. Since not all combinations are reported in the paper, we add multiple `if` conditions and mark in comment their corresponding figure indices. Please feel free to modify the `if` conditions if you would like to see other results. The script then calls [`loop_emulates_txrx_all.m`](/loop_emulates_txrx_all.m) and [`loop_emulates_txrx_noncoherent.m`](/loop_emulates_txrx_noncoherent.m) to process the specified data in the specified way. [`code_figure/generate_all_fig.m`](/code_figure/generate_all_fig.m) reads the results in `.mat` files and generate the figures. It calls multiple scripts, each generating one figure, and corresponding indices in the paper are marked in comment. And you can run each of them separately as well. -Simply running these two scripts in sequence will generate all the reported figures, but `main_emulates_txrx_allloop.m` may take a long time. +Simply running these two scripts in sequence will generate all the reported figures, but `main_emulates_txrx_allloop.m` may take a long time. + +## How to reuse this artifact? + +[`main_emulate_txrx.m`](/main_emulate_txrx.m) is a cleaner version of `main_emulates_txrx_allloop.m`, which is used to debug the algorithms with a single experiment trace. -For more details about the scripts, please refer to [Processing the data](/documentation/data_process.md). +For more details about the scripts, please refer to [Processing the data](/documentation/data_process.md). diff --git a/code_algo/DecodeSequence_ViterbiBackward.m b/code_algo/DecodeSequence_ViterbiBackward.m index 82b8380..78f38f5 100644 --- a/code_algo/DecodeSequence_ViterbiBackward.m +++ b/code_algo/DecodeSequence_ViterbiBackward.m @@ -8,7 +8,7 @@ dBit = params.dBit; lags = params.lags; catch - error('decoding missing variables'); + error("decoding missing variables"); end %% diff --git a/code_algo/DecodeSequence_ViterbiForward4.m b/code_algo/DecodeSequence_ViterbiForward4.m index 8c518d0..f7963c7 100644 --- a/code_algo/DecodeSequence_ViterbiForward4.m +++ b/code_algo/DecodeSequence_ViterbiForward4.m @@ -10,7 +10,7 @@ hPres = params.hPres; chan = params.chan; catch - error('decoding missing variables'); + error("decoding missing variables"); end %% @@ -22,9 +22,9 @@ try lags_new = params.lags_new; catch, lags_new = inf(1,nTx); end try cpIdx = params.cpIdx; catch, cpIdx = 0; end try endIdx = params.endIdx; catch, endIdx = length(yd{1}); end -try mode = params.mode; catch, mode = 'de'; end +try mode = params.mode; catch, mode = "dc"; end -try code = params.code; catch, code = 'goldman'; end +try code = params.code; catch, code = "goldman"; end for j = 1:nMo for i = 1:nTx xChip{j,i} = GenerateCodeChips(xChip{j,i},code); @@ -174,14 +174,14 @@ end end end - clear('VStatesTemp2'); + clear("VStatesTemp2"); % delete inf ind = isinf(cell2mat(VStatesTemp(:,end))); VStatesTemp(ind,:) = []; VTraces{j,jy+1}(ind,:) = []; if size(VStatesTemp,1) == 0 - error('viterbi reaches all states with zero probability'); + error("viterbi reaches all states with zero probability"); end % probability of current state @@ -215,12 +215,12 @@ end switch mode - case 'pd' + case "pd" ViterbiLogProbCurrent(k) = -abs(yd{j}(jy) - yh); - case 'ce' + case "ce" ViterbiLogProbCurrent(k) = ComputeViterbiLogProb(yd{j}(jy), yh, ... 10*chan.nn{j}, 10*chan.np{j}); - case 'de' + case "dc" ViterbiLogProbCurrent(k) = ComputeViterbiLogProb(yd{j}(jy), yh, ... chan.nn{j}, chan.np{j}); end @@ -240,7 +240,7 @@ end % sort and truncate lower probability - [~, ind] = sort(cell2mat(VStatesTemp(:,end)), 'descend'); + [~, ind] = sort(cell2mat(VStatesTemp(:,end)), "descend"); if length(ind) > nTracks ind(nTracks+1:end) = []; end @@ -253,19 +253,19 @@ %% values for next window (only once) if jy == cpIdx checkpoint = struct( ... - 'tracedBits', {tracedBits}, ... - 'VTraceLast', {VTraces(:,jy+1)}, ... - 'VStateLast', {VStates}); + "tracedBits", {tracedBits}, ... + "VTraceLast", {VTraces(:,jy+1)}, ... + "VStateLast", {VStates}); end end %% -if exist('checkpoint', 'var') +if exist("checkpoint", "var") rval.checkpoint = checkpoint; end rval.viterbi = struct( ... - 'tracedBits', {tracedBits}, ... - 'VStates', {VStates}, ... - 'VTraces', {VTraces}); + "tracedBits", {tracedBits}, ... + "VStates", {VStates}, ... + "VTraces", {VTraces}); end \ No newline at end of file diff --git a/code_algo/EstimateChannelMMoSW.m b/code_algo/EstimateChannelMMoSW.m index 62c227d..2361ff8 100644 --- a/code_algo/EstimateChannelMMoSW.m +++ b/code_algo/EstimateChannelMMoSW.m @@ -1,16 +1,16 @@ function rval = EstimateChannelMMoSW(params) %% model parameters -try noisemodel = params.noisemodel; catch, noisemodel = 'pois'; end +try noisemodel = params.noisemodel; catch, noisemodel = "pois"; end %% inputs -try yr = params.yr; catch, error('channel estimation missing variables'); end -try xChip = params.xChip; catch, error('channel estimation missing variables'); end -try dBit = params.dBit; catch, error('channel estimation missing variables'); end -try pChip = params.pChip; catch, error('channel estimation missing variables'); end -try lags_ce = params.lags_ce; catch, error('channel estimation missing variables'); end -try algo = params.algo; catch, algo = 'af'; end -try code = params.code; catch, code = 'goldman'; end +try yr = params.yr; catch, error("channel estimation missing variables"); end +try xChip = params.xChip; catch, error("channel estimation missing variables"); end +try dBit = params.dBit; catch, error("channel estimation missing variables"); end +try pChip = params.pChip; catch, error("channel estimation missing variables"); end +try lags_ce = params.lags_ce; catch, error("channel estimation missing variables"); end +try algo = params.algo; catch, algo = "af"; end +try code = params.code; catch, code = "goldman"; end %% nMo = size(xChip,1); @@ -30,7 +30,7 @@ if nMo == 1 moOffset = num2cell(zeros(1,nTx)); else - error('channel estimation missing fields'); + error("channel estimation missing fields"); end end @@ -95,7 +95,7 @@ %% estimate channel idx_ce = find(~isinf(lags_ce)); switch algo - case {'ls'} + case {"ls"} if nbest for j = 1:nMo hpTemp = [cell2mat(Xp(j,idx_ce)), ones(lens_ce(j),1)] \ (yp{j} - yo{j}); @@ -112,13 +112,13 @@ end end end - case {'af'} + case {"af"} % initialization for j = 1:nMo for i = 1:nTx if isinf(lags_ce(i)), continue; end if length(hp{j,i}) == hTotal, continue; end - error('hp should have been initialized'); + error("hp should have been initialized"); end end firstRun = true; @@ -130,13 +130,13 @@ threshold = 1e-4; hps2 = hp; bps2 = nb; - afIn = struct('afloss', afloss, 'nbest', nbest, ... - 'yps', {yp}, 'yos', {yo}, 'Xps', {Xp}, ... - 'hps', {hp}, 'bps', {nb}, ... - 'weight_pos', weight_pos, 'weight_posy', weight_posy, ... - 'weight_simTx', weight_simTx, 'weight_simMo', weight_simMo, ... - 'weight_smth', weight_smth, 'weight_cntr', weight_cntr, ... - 'lags', lags_ce, 'noisemodel', noisemodel, 'sameMo', sameMo); + afIn = struct("afloss", afloss, "nbest", nbest, ... + "yps", {yp}, "yos", {yo}, "Xps", {Xp}, ... + "hps", {hp}, "bps", {nb}, ... + "weight_pos", weight_pos, "weight_posy", weight_posy, ... + "weight_simTx", weight_simTx, "weight_simMo", weight_simMo, ... + "weight_smth", weight_smth, "weight_cntr", weight_cntr, ... + "lags", lags_ce, "noisemodel", noisemodel, "sameMo", sameMo); rvalaf = get_stats_af(afIn); while 1 % update @@ -153,13 +153,13 @@ end % which one is better? - afIn2 = struct('afloss', afloss, 'nbest', nbest, ... - 'yps', {yp}, 'yos', {yo}, 'Xps', {Xp}, ... - 'hps', {hps2}, 'bps', {bps2}, ... - 'weight_pos', weight_pos, 'weight_posy', weight_posy, ... - 'weight_simTx', weight_simTx, 'weight_simMo', weight_simMo, ... - 'weight_smth', weight_smth, 'weight_cntr', weight_cntr, ... - 'lags', lags_ce, 'noisemodel', noisemodel, 'sameMo', sameMo); + afIn2 = struct("afloss", afloss, "nbest", nbest, ... + "yps", {yp}, "yos", {yo}, "Xps", {Xp}, ... + "hps", {hps2}, "bps", {bps2}, ... + "weight_pos", weight_pos, "weight_posy", weight_posy, ... + "weight_simTx", weight_simTx, "weight_simMo", weight_simMo, ... + "weight_smth", weight_smth, "weight_cntr", weight_cntr, ... + "lags", lags_ce, "noisemodel", noisemodel, "sameMo", sameMo); rvalaf2 = get_stats_af(afIn2); if rvalaf2.loss > rvalaf.loss stepsize = stepsize / 2; @@ -168,21 +168,21 @@ % time to stop? if time_to_stop( ... - struct('hps', {hp}, 'bps', {nb}), ... - struct('hps', {hps2}, 'bps', {bps2}), ... + struct("hps", {hp}, "bps", {nb}), ... + struct("hps", {hps2}, "bps", {bps2}), ... threshold) if firstRun firstRun = false; stepsize = stepsize0; nb(:) = {0}; - afIn = struct('afloss', afloss, 'nbest', nbest, ... - 'yps', {yp}, 'yos', {yo}, 'Xps', {Xp}, ... - 'hps', {hp}, 'bps', {nb}, ... - 'weight_pos', weight_pos, 'weight_posy', weight_posy, ... - 'weight_simTx', weight_simTx, 'weight_simMo', weight_simMo, ... - 'weight_smth', weight_smth, 'weight_cntr', weight_cntr, ... - 'lags', lags_ce, 'noisemodel', noisemodel, 'sameMo', sameMo); + afIn = struct("afloss", afloss, "nbest", nbest, ... + "yps", {yp}, "yos", {yo}, "Xps", {Xp}, ... + "hps", {hp}, "bps", {nb}, ... + "weight_pos", weight_pos, "weight_posy", weight_posy, ... + "weight_simTx", weight_simTx, "weight_simMo", weight_simMo, ... + "weight_smth", weight_smth, "weight_cntr", weight_cntr, ... + "lags", lags_ce, "noisemodel", noisemodel, "sameMo", sameMo); rvalaf = get_stats_af(afIn); continue; else @@ -196,7 +196,7 @@ rvalaf = rvalaf2; end otherwise - error('channel estimation algorithm not supported'); + error("channel estimation algorithm not supported"); end %% post process @@ -218,13 +218,13 @@ end idx = yh > max(yh) * 0.1; switch noisemodel - case 'norm' + case "norm" nn{j} = std(yh-yp{j}); np{j} = 0; - case {'pois', 'pois0'} + case {"pois", "pois0"} nn{j} = 0; np{j} = std((yh(idx)-yp{j}(idx))./sqrt(yh(idx))); - case 'mix' + case "mix" matA = [1, mean(yh(idx)); mean(1./yh(idx)), 1]; vecb = [sum((yh(idx)-yp{j}(idx)).^2); ... sum((yh(idx)-yp{j}(idx)).^2./yh(idx))]; @@ -339,10 +339,10 @@ for j = 1:nMo % LS term switch noisemodel - case {'norm', 'pois'} + case {"norm", "pois"} loss = loss + mean(ypsErr{j}.^2) ... + weight_posy * mean(relu(ypsErr{j}).^2); - case 'pois0' + case "pois0" loss = loss + mean(ypsErr{j}(idxs{j}).^2 ./ yhs{j}(idxs{j})) ... + weight_posy * mean(relu(ypsErr{j}(idxs{j})).^2 ./ yhs{j}(idxs{j})); end @@ -384,10 +384,10 @@ %% background % LS term switch noisemodel - case {'norm', 'pois'} + case {"norm", "pois"} grad_b{j} = grad_b{j} + (mean(ypsErr{j})) ... + weight_posy * (mean(relu(ypsErr{j}))); - case 'pois0' + case "pois0" e2yh = ypsErr{j}(idxs{j}) ./ yhs{j}(idxs{j}); grad_b{j} = grad_b{j} + (mean(e2yh.*(e2yh+2))) ... + weight_posy * (mean(relu(e2yh).*(e2yh+2))); @@ -400,14 +400,14 @@ if isinf(lags(i)), continue; end % LS term switch noisemodel - case {'norm', 'pois'} - grad_h{j,i} = grad_h{j,i} + (Xps{j,i}' * ypsErr{j} / hTotal) ... - + weight_posy * (Xps{j,i}' * relu(ypsErr{j}) / hTotal); - case 'pois0' + case {"norm", "pois"} + grad_h{j,i} = grad_h{j,i} + (Xps{j,i}.' * ypsErr{j} / hTotal) ... + + weight_posy * (Xps{j,i}.' * relu(ypsErr{j}) / hTotal); + case "pois0" e2yh = ypsErr{j}(idxs{j}) ./ yhs{j}(idxs{j}); grad_h{j,i} = grad_h{j,i} + ... - (Xps{j,i}(idxs{j},:)' * (e2yh.*(e2yh+2)) / hTotal) ... - + weight_posy * (Xps{j,i}(idxs{j},:)' * (relu(e2yh).*(e2yh+2)) / hTotal); + (Xps{j,i}(idxs{j},:).' * (e2yh.*(e2yh+2)) / hTotal) ... + + weight_posy * (Xps{j,i}(idxs{j},:).' * (relu(e2yh).*(e2yh+2)) / hTotal); end % non-negative term grad_h{j,i} = grad_h{j,i} + (-weight_pos * relu(-hps{j,i}) / hTotal); @@ -511,10 +511,10 @@ for j = 1:nMo % LS term switch noisemodel - case {'norm', 'pois'} + case {"norm", "pois"} loss = loss + sum(ypsErr{j}.^2) ... + weight_posy * sum(relu(ypsErr{j}).^2); - case 'pois0' + case "pois0" loss = loss + sum(ypsErr{j}(idxs{j}).^2 ./ yhs{j}(idxs{j})) ... + weight_posy * sum(relu(ypsErr{j}(idxs{j})).^2 ./ yhs{j}(idxs{j})); end @@ -556,10 +556,10 @@ %% background % LS term switch noisemodel - case {'norm', 'pois'} + case {"norm", "pois"} grad_b{j} = grad_b{j} + (sum(ypsErr{j})) ... + weight_posy * (sum(relu(ypsErr{j}))); - case 'pois0' + case "pois0" e2yh = ypsErr{j}(idxs{j}) ./ yhs{j}(idxs{j}); grad_b{j} = grad_b{j} + (sum(e2yh.*(e2yh+2))) ... + weight_posy * (sum(relu(e2yh).*(e2yh+2))); @@ -572,14 +572,14 @@ if isinf(lags(i)), continue; end % LS term switch noisemodel - case {'norm', 'pois'} - grad_h{j,i} = grad_h{j,i} + (Xps{j,i}' * ypsErr{j}) ... - + weight_posy * (Xps{j,i}' * relu(ypsErr{j})); - case 'pois0' + case {"norm", "pois"} + grad_h{j,i} = grad_h{j,i} + (Xps{j,i}.' * ypsErr{j}) ... + + weight_posy * (Xps{j,i}.' * relu(ypsErr{j})); + case "pois0" e2yh = ypsErr{j}(idxs{j}) ./ yhs{j}(idxs{j}); grad_h{j,i} = grad_h{j,i} + ... - (Xps{j,i}(idxs{j},:)' * (e2yh.*(e2yh+2))) ... - + weight_posy * (Xps{j,i}(idxs{j},:)' * (relu(e2yh).*(e2yh+2))); + (Xps{j,i}(idxs{j},:).' * (e2yh.*(e2yh+2))) ... + + weight_posy * (Xps{j,i}(idxs{j},:).' * (relu(e2yh).*(e2yh+2))); end % non-negative term grad_h{j,i} = grad_h{j,i} + (-weight_pos * relu(-hps{j,i})); diff --git a/code_algo/GenerateDataChips.m b/code_algo/GenerateDataChips.m index 2033fed..af31895 100644 --- a/code_algo/GenerateDataChips.m +++ b/code_algo/GenerateDataChips.m @@ -3,12 +3,12 @@ if size(xbit,1) == 1 xbit = xbit.'; elseif size(xbit,2) ~= 1 - error('input is not a vector'); + error("input is not a vector"); end if size(xchip,1) == 1 xchip = xchip.'; elseif size(xchip,2) ~= 1 - error('input is not a vector'); + error("input is not a vector"); end %% -1 x -1 -> 1 diff --git a/code_algo/GeneratePreambleBits.m b/code_algo/GeneratePreambleBits.m index 6cf2e67..7c839bb 100644 --- a/code_algo/GeneratePreambleBits.m +++ b/code_algo/GeneratePreambleBits.m @@ -1,7 +1,7 @@ function mseq = GeneratePreambleBits(len) %% p = nextpow2(len/2+1); -pr = primpoly(p, 'nodisplay'); +pr = primpoly(p, "nodisplay"); fbconnection = de2bi(pr); n = length(fbconnection); diff --git a/code_algo/GeneratePreambleChips.m b/code_algo/GeneratePreambleChips.m index a084b8c..73fecc1 100644 --- a/code_algo/GeneratePreambleChips.m +++ b/code_algo/GeneratePreambleChips.m @@ -3,7 +3,7 @@ if size(xchip,1) == 1 xchip = xchip.'; elseif size(xchip,2) ~= 1 - error('input is not a vector'); + error("input is not a vector"); end %% diff --git a/code_algo/GeneratePreambleDetectionChips.m b/code_algo/GeneratePreambleDetectionChips.m index 41d1838..d75dc8e 100644 --- a/code_algo/GeneratePreambleDetectionChips.m +++ b/code_algo/GeneratePreambleDetectionChips.m @@ -1,7 +1,7 @@ function rval = GeneratePreambleDetectionChips(pchip, L) %% % oversampling code are composed of {1,-1,0} -% we need to change all '0's to its preceding non-zero value +% we need to change all "0"s to its preceding non-zero value rval = pchip; lastIdx = 1; for k = 2:length(pchip) diff --git a/code_algo/GetCEWeights.m b/code_algo/GetCEWeights.m index 0685969..f97390d 100644 --- a/code_algo/GetCEWeights.m +++ b/code_algo/GetCEWeights.m @@ -1,15 +1,5 @@ function weights = GetCEWeights(note) -if note >= 100 && note < 199 - % backup1, mean - % 1.25s hPre and 1.75s hPost -elseif note >= 200 && note < 299 - % backup1, sum - % 1.25s hPre and 1.75s hPost -elseif note >= 300 && note < 399 - % backup2, mean - % 1.25s hPre and 1.75s hPost -elseif note >= 400 && note < 499 - % backup2, sum +if note >= 100 && note < 499 % 1.25s hPre and 1.75s hPost end diff --git a/code_algo/PreambleDetectionSW8.m b/code_algo/PreambleDetectionSW8.m index 918d123..1e07ebf 100644 --- a/code_algo/PreambleDetectionSW8.m +++ b/code_algo/PreambleDetectionSW8.m @@ -9,13 +9,13 @@ Lp = params.Lp; dBit = params.dBit; catch - error('preamble detection missing inputs'); + error("preamble detection missing inputs"); end try isrepeat = params.isrepeat; catch, isrepeat = false; end -try algo = params.algo; catch, algo = 'sc'; end -try algoCE = params.algoCE; catch, algoCE = 'gt'; end -try code = params.code; catch, code = 'goldman'; end +try algo = params.algo; catch, algo = "sc"; end +try algoCE = params.algoCE; catch, algoCE = "gt"; end +try code = params.code; catch, code = "goldman"; end %% related variables nMo = size(xChip,1); @@ -27,7 +27,7 @@ if nMo == 1 moOffset = num2cell(zeros(1,nTx)); else - error('missing fields'); + error("missing fields"); end end assert(sum(sum(cell2mat(moOffset)==0)<1)==0); @@ -120,7 +120,7 @@ %% search for potential packets lags_detected = inf(1,nTx); switch algo - case 'gt' + case "gt" for i = 1:nTx if isinf(lags_gt(i)), continue; end @@ -129,7 +129,7 @@ end end - case 'sc' + case "sc" pksTemp = -inf(1,nTx); lagsTemp = inf(1,nTx); for i = 1:nTx @@ -139,7 +139,7 @@ lagMin = max(lags_min(i),plen); lagMax = min(lags_max(i),2*plen-1); - [pkTemp, lagTemp] = max(metric_final(lagMin+1:lagMax+1,i),[],'omitnan'); + [pkTemp, lagTemp] = max(metric_final(lagMin+1:lagMax+1,i),[],"omitnan"); lagTemp = lagTemp + lagMin; if pkTemp < max(metric_final(lagTemp+1:min(lagTemp+2*Lp*2,size(metric_final,1)),i)) @@ -150,7 +150,7 @@ lagsTemp(i) = lagTemp; end - [~, maxInd] = sort(pksTemp,'descend'); + [~, maxInd] = sort(pksTemp,"descend"); for i = 1:nTx if isinf(pksTemp(maxInd(i))), continue; end ind = find(lagsTemp>lagsTemp(maxInd(i))-plen ... @@ -168,20 +168,20 @@ || sum(~isinf(lags_gt)) > 0 ... ) && false if isempty(groot().Children) - figure('units','normalized','outerposition',[0 0 1 1]); + figure("units","normalized","outerposition",[0 0 1 1]); end - tiledlayout(3,1,'TileSpacing','compact'); + tiledlayout(3,1,"TileSpacing","compact"); nexttile; - plot(pcorrTx); title('corr'); box on; grid on; + plot(pcorrTx); title("corr"); box on; grid on; xticks(0:plen/2:mlen); xlim([0,mlen]); nexttile; - plot(pcorr2Tx); title('corr2'); box on; grid on; + plot(pcorr2Tx); title("corr2"); box on; grid on; xticks(0:plen/2:mlen); xlim([0,mlen]); nexttile; - plot(metric); title('metric'); box on; grid on; + plot(metric); title("metric"); box on; grid on; xticks(0:plen/2:mlen); xlim([0,mlen]); lg = legend(cellstr(num2str([lags; lags_detected; lags_gt].'))); - lg.Layout.Tile = 'east'; + lg.Layout.Tile = "east"; %% % lags_detected = lags_new; @@ -190,7 +190,7 @@ %% compute related values lags(~isinf(lags_detected)) = lags_detected(~isinf(lags_detected)); - if sum(~isinf(lags_detected)) > 0 && ~isequal(algoCE, 'gt') + if sum(~isinf(lags_detected)) > 0 && ~isequal(algoCE, "gt") for j = 1:nMo temp = mean(cell2mat(hp(j,:)),2); for i = 1:nTx diff --git a/code_algo/decode_mmo_coherent_MMoNTxSW11loop.m b/code_algo/decode_mmo_coherent_MMoNTxSW11loop.m index 6be3b52..8d43496 100644 --- a/code_algo/decode_mmo_coherent_MMoNTxSW11loop.m +++ b/code_algo/decode_mmo_coherent_MMoNTxSW11loop.m @@ -6,6 +6,7 @@ %% rxIn parameters try + T = params.T; yRx = params.yRx; xChannel = params.xChannel; xChip = params.xChip; @@ -14,17 +15,17 @@ pChip = params.pChip; txOffset = params.txOffset; catch - error('missing fields'); + error("missing fields"); end try isrepeat = params.isrepeat; catch, isrepeat = false; end -try noisemodel = params.noisemodel; catch, noisemodel = 'pois'; end -try algoPD = params.algoPD; catch, algoPD = 'sc'; end -try algoCE = params.algoCE; catch, algoCE = 'ls'; end +try noisemodel = params.noisemodel; catch, noisemodel = "pois"; end +try algoPD = params.algoPD; catch, algoPD = "sc"; end +try algoCE = params.algoCE; catch, algoCE = "ls"; end try nTracks = params.nTracks; catch, nTracks = 2^8; end try sync = params.sync; catch, sync = -1; end -try mode = params.mode; catch, mode = 'dc'; end -try code = params.code; catch, code = 'goldman'; end +try mode = params.mode; catch, mode = "dc"; end +try code = params.code; catch, code = "goldman"; end try sameMo = params.sameMo; catch, sameMo = false; end %% related variables @@ -37,10 +38,10 @@ if nMo == 1 moOffset = num2cell(zeros(1,nTx)); else - error('missing fields'); + error("missing fields"); end end -moOffsetMax = max(cell2mat(moOffset),[],'all'); +moOffsetMax = max(cell2mat(moOffset),[],"all"); try weights_ce = params.weights_ce; @@ -100,16 +101,28 @@ hPre = params.hPre; hPost = params.hPost; catch - hPre = ceil(0.6/T); hPost = ceil(1.2/T); + hPre = ceil(1250/T); hPost = ceil(1750/T); end -chan = struct('hp', {cell(nMo,nTx)}, ... - 'hpre', {cell(nMo,nTx)}, ... - 'nb', {cell(nMo,1)}, ... - 'nn', {cell(nMo,1)}, ... - 'np', {cell(nMo,1)}); +chan = struct("hp", {cell(nMo,nTx)}, ... + "hpre", {cell(nMo,nTx)}, ... + "nb", {cell(nMo,1)}, ... + "nn", {cell(nMo,1)}, ... + "np", {cell(nMo,1)}); chan.hpre(:) = {-1}; + +switch algoPD + case "gt" + PDOff = zeros(1,nTx); + case "gt1" + PDOff = min(poissrnd(ceil(200/T),[1 nTx]), ceil(500/T)); + for i = 1:nTx + txOffset{i} = txOffset{i} + PDOff(i); + end + algoPD = "gt"; +end + switch algoCE - case 'gt' + case "gt" for i = 1:nTx chan.hp(:,i) = xChannel.xCIR(:,i); chan.hpre(:,i) = {pkOffset(i)}; @@ -117,7 +130,7 @@ chan.nb = xChannel.noiseb; chan.nn = xChannel.noisen; chan.np = xChannel.noisep; - case 'af0' + case "af0" % "0" assumes known CIR estimation (possibly from previos packet) for i = 1:nTx for j = 1:nMo @@ -135,7 +148,7 @@ chan.nn = xChannel.noisen; chan.np = xChannel.noisep; % remove "0" to perform normal channel estimation - algoCE = 'af'; + algoCE = "af"; end chan_gt = chan; % decoding variables @@ -187,7 +200,7 @@ % lags_min/lags_max, rough arriving range pdDo = true; if sync >= 0 - if ~exist('lags_min', 'var') + if ~exist("lags_min", "var") lags_min = zeros(1,nTx); for i = 1:nTx lags_min(i) = txOffset{i} - (swStart-1) + pkOffset(i) - sync; @@ -216,21 +229,21 @@ lags2 = lags; lags2(~isinf(lags_new)) = lags_new(~isinf(lags_new)); if sum(~isinf(lags2)) > 0 forwardIn = struct( ... - 'yd', {yr2}, 'xBit', {xBit}, ... - 'xChip', {xChip}, 'pChip', {pChip}, ... - 'lags', lags2, ... - 'moOffset', {moOffset}, 'hPres', {chan.hpre}, ... - 'chan', chan, 'mode', 'pd', ... - 'endIdx', min(3.5*plen+moOffsetMax,ylen-swStart+1), ... - 'nTracks', nTracks, 'code', code); - if exist('checkpoint', 'var') + "yd", {yr2}, "xBit", {xBit}, ... + "xChip", {xChip}, "pChip", {pChip}, ... + "lags", lags2, ... + "moOffset", {moOffset}, "hPres", {chan.hpre}, ... + "chan", chan, "mode", "pd", ... + "endIdx", min(3.5*plen+moOffsetMax,ylen-swStart+1), ... + "nTracks", nTracks, "code", code); + if exist("checkpoint", "var") forwardIn.checkpoint = checkpoint; end forward = DecodeSequence_ViterbiForward4(forwardIn); backwardIn = struct( ... - 'viterbi', forward.viterbi, ... - 'dBit', {dBit}, 'lags', lags2); + "viterbi", forward.viterbi, ... + "dBit", {dBit}, "lags", lags2); backward = DecodeSequence_ViterbiBackward(backwardIn); dBit2 = backward.dBit; else @@ -267,20 +280,20 @@ respTemp = reshape(respTemp, plen/2, [], nMo); % decide if potential new packets - if mean(respTemp(:,2,:), 'all') < 0 ... - || mean(respTemp(:,3,:), 'all') < 0.1 + if mean(respTemp(:,2,:), "all") < 0 ... + || mean(respTemp(:,3,:), "all") < 0.1 pdDo = false; end %% packet detection pdIn = struct( ... - 'isrepeat', isrepeat, ... - 'yp', {yr2}, 'xChip', {xChip}, 'moOffset', {moOffset}, ... - 'xBit', {xBit}, 'dBit', {dBit2}, ... - 'Lp', Lp, 'pChip', {pChip}, ... - 'hPre', hPre, 'hPost', hPost, 'hp', {chan.hp}, ... - 'lags', lags2, 'hPres', {chan.hpre}, ... - 'algo', algoPD2, 'algoCE', algoCE, 'code', code); + "isrepeat", isrepeat, ... + "yp", {yr2}, "xChip", {xChip}, "moOffset", {moOffset}, ... + "xBit", {xBit}, "dBit", {dBit2}, ... + "Lp", Lp, "pChip", {pChip}, ... + "hPre", hPre, "hPost", hPost, "hp", {chan.hp}, ... + "lags", lags2, "hPres", {chan.hpre}, ... + "algo", algoPD2, "algoCE", algoCE, "code", code); % other pdIn if sync >=0 @@ -312,7 +325,7 @@ %% check each potential new packet new_tx_confirmed = false; - [~, idx_newp] = sort(lags_newp, 'ascend'); + [~, idx_newp] = sort(lags_newp, "ascend"); for idx_newp2 = idx_newp if isinf(lags_newp(idx_newp2)) break; @@ -323,7 +336,7 @@ lags_ce2(~isinf(lags_new)) = lags_new(~isinf(lags_new)); lags_ce2(idx_newp2) = lags_newp(idx_newp2); - if isequal(algoCE,'gt') + if isequal(algoCE,"gt") moOffset2 = cell2mat(moOffset)-cell2mat(chan.hpre); moOffset2(:,~isinf(lags_ce2)) = ... cell2mat(moOffset(:,~isinf(lags_ce2))) - hPre; @@ -353,33 +366,33 @@ %% estimate channel endIdxCE = max(lags_ce2(~isinf(lags_ce2))) + plen - hPre; estimateIn2 = struct( ... - 'noisemodel', noisemodel, 'isrepeat', isrepeat, ... - 'yr', {yr2}, 'lags_ce', lags_ce2, 'lags_old', lags_old, ... - 'moOffset', {moOffset2}, 'xChip', {xChip}, ... - 'dBit', {dBit3_old}, 'pChip', {pChip}, ... - 'hPre', hPre, 'hPost', hPost, 'hp', {chan.hp}, 'nb', {nbPD}, ... - 'algo', 'af', 'weights', weights_pd, 'code', code, ... - 'sameMo', sameMo, ... - 'endIdx', endIdxCE); + "noisemodel", noisemodel, "isrepeat", isrepeat, ... + "yr", {yr2}, "lags_ce", lags_ce2, "lags_old", lags_old, ... + "moOffset", {moOffset2}, "xChip", {xChip}, ... + "dBit", {dBit3_old}, "pChip", {pChip}, ... + "hPre", hPre, "hPost", hPost, "hp", {chan.hp}, "nb", {nbPD}, ... + "algo", "af", "weights", weights_pd, "code", code, ... + "sameMo", sameMo, ... + "endIdx", endIdxCE); estimate2 = EstimateChannelMMoSW(estimateIn2); %% decode forwardIn = struct( ... - 'yd', {yr2}, 'xBit', {xBit}, ... - 'xChip', {xChip}, 'pChip', {pChip}, ... - 'lags', lags3, ... - 'moOffset', {moOffset}, 'hPres', {chan.hpre}, ... - 'chan', estimate2, 'mode', 'pd', ... - 'endIdx', max(lags_ce2(~isinf(lags_ce2))) + plen, ... - 'nTracks', nTracks, 'code', code); - if exist('checkpoint', 'var') + "yd", {yr2}, "xBit", {xBit}, ... + "xChip", {xChip}, "pChip", {pChip}, ... + "lags", lags3, ... + "moOffset", {moOffset}, "hPres", {chan.hpre}, ... + "chan", estimate2, "mode", "pd", ... + "endIdx", max(lags_ce2(~isinf(lags_ce2))) + plen, ... + "nTracks", nTracks, "code", code); + if exist("checkpoint", "var") forwardIn.checkpoint = checkpoint; end forward3 = DecodeSequence_ViterbiForward4(forwardIn); backwardIn = struct( ... - 'viterbi', forward3.viterbi, ... - 'dBit', {dBit}, 'lags', lags3); + "viterbi", forward3.viterbi, ... + "dBit", {dBit}, "lags", lags3); backward3 = DecodeSequence_ViterbiBackward(backwardIn); dBit3 = backward3.dBit; @@ -421,20 +434,20 @@ stem(xBit{j,i}); stem(dBit2{j,i}); if temp1 > 0 && temp1 <= nBit - stem(temp1,xBit{j,i}(temp1),'k','LineWidth',2); + stem(temp1,xBit{j,i}(temp1),"k","LineWidth",2); end if temp2 > 0 && temp2 <= nBit - stem(temp2,xBit{j,i}(temp2),'--k','LineWidth',2); + stem(temp2,xBit{j,i}(temp2),"--k","LineWidth",2); end % CIR subplot(nTx2+2,2*nMo,(i2-1)*2*nMo+j*2); hold on; box on; grid on; title("Tx"+num2str(i)+", Mo"+num2str(j)+", pd"); - plot((1:length(chan_gt.hp{j,i}))-chan_gt.hpre{j,i}-1, chan_gt.hp{j,i}, '-o'); - plot((1:length(chan.hp{j,i}))-chan.hpre{j,i}-1, chan.hp{j,i}, '-+'); + plot((1:length(chan_gt.hp{j,i}))-chan_gt.hpre{j,i}-1, chan_gt.hp{j,i}, "-o"); + plot((1:length(chan.hp{j,i}))-chan.hpre{j,i}-1, chan.hp{j,i}, "-+"); if isinf(lags_ce2(i)), continue; end - plot((1:length(estimate2.hp{j,i}))-hPre-1, estimate2.hp{j,i}, 'LineWidth', 2); + plot((1:length(estimate2.hp{j,i}))-hPre-1, estimate2.hp{j,i}, "LineWidth", 2); end i2 = i2 + 1; end @@ -477,10 +490,10 @@ subplot(nTx2+2,nMo,nTx2*nMo+j); hold on; box on; grid on; plot(yr2{j}); plot(yo3{j}); - plot([estimate2.sttIdx,estimate2.sttIdx],ylim,'-k','LineWidth',2); - plot([estimate2.endIdx,estimate2.endIdx],ylim,'--k','LineWidth',2); + plot([estimate2.sttIdx,estimate2.sttIdx],ylim,"-k","LineWidth",2); + plot([estimate2.endIdx,estimate2.endIdx],ylim,"--k","LineWidth",2); xticks(0:plen/2:3.5*plen); xlim([0,3.5*plen]); - legend('rx signal', 'temp signal'); + legend("rx signal", "temp signal"); title("Mol "+string(j)+" total"); % subtract @@ -488,10 +501,10 @@ plot(yr2{j}-yo0{j}); plot(yr2{j}-yo{j}); plot(yr2{j}-yo2{j}); - plot([estimate2.sttIdx,estimate2.sttIdx],ylim,'-k','LineWidth',2); - plot([estimate2.endIdx,estimate2.endIdx],ylim,'--k','LineWidth',2); + plot([estimate2.sttIdx,estimate2.sttIdx],ylim,"-k","LineWidth",2); + plot([estimate2.endIdx,estimate2.endIdx],ylim,"--k","LineWidth",2); xticks(0:plen/2:3.5*plen); xlim([0,3.5*plen]); - legend('gt residual', 'expected residual', 'actual residual'); + legend("gt residual", "expected residual", "actual residual"); title("Mol "+string(j)+" SIC"); end @@ -502,14 +515,14 @@ % compare channel of two consecutive blocks estimateInHalf = struct( ... - 'noisemodel', noisemodel, 'isrepeat', isrepeat, ... - 'yr', {yr2}, 'lags_ce', lags_ce2, 'lags_old', lags_old, ... - 'moOffset', {moOffset2}, 'xChip', {xChip}, ... - 'dBit', {dBit3_old}, 'pChip', {pChip}, ... - 'hPre', hPre, 'hPost', hPost, 'hp', {estimate2.hp}, 'nb', {estimate2.nb}, ... - 'algo', 'af', 'weights', weights_pd, 'code', code, ... - 'sameMo', sameMo, ... - 'endIdx', max(lags_ce2(~isinf(lags_ce2))) + round((plen-hPre)/2)); + "noisemodel", noisemodel, "isrepeat", isrepeat, ... + "yr", {yr2}, "lags_ce", lags_ce2, "lags_old", lags_old, ... + "moOffset", {moOffset2}, "xChip", {xChip}, ... + "dBit", {dBit3_old}, "pChip", {pChip}, ... + "hPre", hPre, "hPost", hPost, "hp", {estimate2.hp}, "nb", {estimate2.nb}, ... + "algo", "af", "weights", weights_pd, "code", code, ... + "sameMo", sameMo, ... + "endIdx", max(lags_ce2(~isinf(lags_ce2))) + round((plen-hPre)/2)); estimateHalf1 = EstimateChannelMMoSW(estimateInHalf); estimateInHalf.sttIdx = max(lags_ce2(~isinf(lags_ce2))) + round((plen-hPre)/2); @@ -576,8 +589,8 @@ labels_pd = [labels_pd; -1, isequal(algoPD2,"gt"), ... sum(isinf(lags_new(~isinf(lags_gt)))), nan]; % - errors_pd = [errors_pd; mean(respTemp(:,2,:), 'all'), ... - mean(respTemp(:,3,:), 'all')]; + errors_pd = [errors_pd; mean(respTemp(:,2,:), "all"), ... + mean(respTemp(:,3,:), "all")]; % corr_temp = nan([1,size(corr_pd,[2,3])]); corr_pd = [corr_pd; corr_temp]; @@ -593,19 +606,19 @@ end - if isequal(algoPD,'gt') || ~debug_pd || isequal(lags_new,lags_gt) + if isequal(algoPD,"gt") || ~debug_pd || isequal(lags_new,lags_gt) break; end end - if ~isequal(mode,'dc') && (sum(isinf(lags)) == 0 || max(lags_gt) < 0) + if ~isequal(mode,"dc") && (sum(isinf(lags)) == 0 || max(lags_gt) < 0) break; end %% final channel estimation lags_ce = lags; lags_ce(lags 0 + if ~isequal(algoCE,"gt") && sum(~isinf(lags_ce)) > 0 lags_ce_last = max(lags_ce(~isinf(lags_ce))); lags_ce_next = min(lags_new(lags_new>lags_ce_last)); if numel(lags_ce_next) == 0, lags_ce_next = inf; end @@ -615,14 +628,14 @@ moOffset2 = num2cell(cell2mat(moOffset)-cell2mat(chan.hpre)); estimateIn = struct( ... - 'noisemodel', noisemodel, 'isrepeat', isrepeat, ... - 'yr', {yr2}, 'lags_ce', lags_ce, 'lags_old', lags_old, ... - 'moOffset', {moOffset2}, 'xChip', {xChip}, ... - 'dBit', {dBit2}, 'pChip', {pChip}, ... - 'hPre', hPre, 'hPost', hPost, 'hp', {chan.hp}, 'nb', {chan.nb}, ... - 'algo', algoCE, 'weights', weights_ce, 'code', code, ... - 'sameMo', sameMo, ... - 'endIdx', endIdxCE); + "noisemodel", noisemodel, "isrepeat", isrepeat, ... + "yr", {yr2}, "lags_ce", lags_ce, "lags_old", lags_old, ... + "moOffset", {moOffset2}, "xChip", {xChip}, ... + "dBit", {dBit2}, "pChip", {pChip}, ... + "hPre", hPre, "hPost", hPost, "hp", {chan.hp}, "nb", {chan.nb}, ... + "algo", algoCE, "weights", weights_ce, "code", code, ... + "sameMo", sameMo, ... + "endIdx", endIdxCE); estimateIn.hp = chan.hp; estimateIn.algo = algoCE; @@ -647,20 +660,20 @@ stem(xBit{j,i}); stem(dBit2{j,i}); if temp1 > 0 && temp1 <= nBit - stem(temp1,xBit{j,i}(temp1),'k','LineWidth',2); + stem(temp1,xBit{j,i}(temp1),"k","LineWidth",2); end if temp2 > 0 && temp2 <= nBit - stem(temp2,xBit{j,i}(temp2),'--k','LineWidth',2); + stem(temp2,xBit{j,i}(temp2),"--k","LineWidth",2); end % CIR subplot(nTx2+2,2*nMo,(i2-1)*2*nMo+j*2); hold on; box on; grid on; title("Tx"+num2str(i)+", Mo"+num2str(j)+", ce"); - plot((1:length(chan_gt.hp{j,i}))-chan_gt.hpre{j,i}-1, chan_gt.hp{j,i}, '-o'); - plot((1:length(chan.hp{j,i}))-chan.hpre{j,i}-1, chan.hp{j,i}, '-+'); + plot((1:length(chan_gt.hp{j,i}))-chan_gt.hpre{j,i}-1, chan_gt.hp{j,i}, "-o"); + plot((1:length(chan.hp{j,i}))-chan.hpre{j,i}-1, chan.hp{j,i}, "-+"); if isinf(lags_ce(i)), continue; end - plot((1:length(estimate.hp{j,i}))-hPre-1, estimate.hp{j,i}, 'LineWidth', 2); + plot((1:length(estimate.hp{j,i}))-hPre-1, estimate.hp{j,i}, "LineWidth", 2); end i2 = i2 + 1; end @@ -699,12 +712,12 @@ plot(yo{j}); plot(yo1{j}); plot(yo2{j}); - plot([estimate.sttIdx,estimate.sttIdx],ylim,'-k','LineWidth',2); - plot([estimate.endIdx,estimate.endIdx],ylim,'--k','LineWidth',2); - xlabel('sample index'); + plot([estimate.sttIdx,estimate.sttIdx],ylim,"-k","LineWidth",2); + plot([estimate.endIdx,estimate.endIdx],ylim,"--k","LineWidth",2); + xlabel("sample index"); xticks(0:plen/2:swSize); - ylabel('signal'); - legend('rx', 'gt', 'ce old', 'ce'); + ylabel("signal"); + legend("rx", "gt", "ce old", "ce"); end %% @@ -722,32 +735,32 @@ if min(lags_all) < plen if swEnd == ylen endIdxDC = swEnd-swStart+1; - elseif ~isequal(algoCE,'gt') && sum(~isinf(lags_ce)) > 0 + elseif ~isequal(algoCE,"gt") && sum(~isinf(lags_ce)) > 0 endIdxDC = endIdxCE; else endIdxDC = min(plen + 2*swAdv+moOffsetMax,swEnd-swStart+1); end forwardIn = struct( ... - 'yd', {yr2}, 'xBit', {xBit}, ... - 'xChip', {xChip}, 'pChip', {pChip}, ... - 'lags', lags_all, ... - 'moOffset', {moOffset}, 'hPres', {chan.hpre}, ... - 'chan', chan, 'mode', 'de', ... - 'cpIdx', swAdv, 'nTracks', nTracks, ... - 'endIdx', endIdxDC, 'code', code); - if exist('checkpoint', 'var') + "yd", {yr2}, "xBit", {xBit}, ... + "xChip", {xChip}, "pChip", {pChip}, ... + "lags", lags_all, ... + "moOffset", {moOffset}, "hPres", {chan.hpre}, ... + "chan", chan, "mode", "dc", ... + "cpIdx", swAdv, "nTracks", nTracks, ... + "endIdx", endIdxDC, "code", code); + if exist("checkpoint", "var") forwardIn.checkpoint = checkpoint; end forward = DecodeSequence_ViterbiForward4(forwardIn); checkpoint = forward.checkpoint; backwardIn = struct( ... - 'viterbi', forward.viterbi, ... - 'dBit', {dBit}, 'lags', lags_all); + "viterbi", forward.viterbi, ... + "dBit", {dBit}, "lags", lags_all); backward = DecodeSequence_ViterbiBackward(backwardIn); dBit = backward.dBit; else - clear('checkpoint'); + clear("checkpoint"); end %% next window @@ -764,12 +777,14 @@ end %% return -PDOff = lags + swStart-1 - cell2mat(txOffset) - pkOffset; +if algoPD ~= "gt" + PDOff = lags + swStart-1 - cell2mat(txOffset) - pkOffset; +end rxOut.PDOff = PDOff; rxOut.chan = chan; - rxOut.dBit = dBit; + BER = cell(size(xBit)); for j = 1:size(xBit,1) for i = 1:nTx @@ -780,11 +795,11 @@ if debug_pd rxOut.debug_pd = struct( ... - 'labels', labels_pd, ... - 'errors', errors_pd, ... - 'corr', corr_pd, ... - 'ratio', ratio_pd, ... - 'ratio2', ratio2_pd); + "labels", labels_pd, ... + "errors", errors_pd, ... + "corr", corr_pd, ... + "ratio", ratio_pd, ... + "ratio2", ratio2_pd); end end diff --git a/code_algo/decode_mmo_noncoherent_MMoNTx.m b/code_algo/decode_mmo_noncoherent_MMoNTx.m index 11d1f02..b5d6bda 100644 --- a/code_algo/decode_mmo_noncoherent_MMoNTx.m +++ b/code_algo/decode_mmo_noncoherent_MMoNTx.m @@ -14,17 +14,17 @@ pChip = params.pChip; txOffset = params.txOffset; catch - error('missing fields'); + error("missing fields"); end try isrepeat = params.isrepeat; catch, isrepeat = false; end -try noisemodel = params.noisemodel; catch, noisemodel = 'pois'; end -try algoPD = params.algoPD; catch, algoPD = 'sc'; end -try algoCE = params.algoCE; catch, algoCE = 'ls'; end +try noisemodel = params.noisemodel; catch, noisemodel = "pois"; end +try algoPD = params.algoPD; catch, algoPD = "sc"; end +try algoCE = params.algoCE; catch, algoCE = "ls"; end try nTracks = params.nTracks; catch, nTracks = 2^8; end try sync = params.sync; catch, sync = -1; end -try mode = params.mode; catch, mode = 'dc'; end -try code = params.code; catch, code = 'goldman'; end +try mode = params.mode; catch, mode = "dc"; end +try code = params.code; catch, code = "goldman"; end %% related variables nMo = size(xChip,1); @@ -36,10 +36,10 @@ if nMo == 1 moOffset = num2cell(zeros(1,nTx)); else - error('missing fields'); + error("missing fields"); end end -moOffsetMax = max(cell2mat(moOffset),[],'all'); +moOffsetMax = max(cell2mat(moOffset),[],"all"); debug_pd = debug_pd & ~isequal(algoPD,"gt"); @@ -78,14 +78,14 @@ % hPre = ceil(0.6/T); hPost = ceil(1.2/T); hPre = 7; hPost = 10; end -chan = struct('hp', {cell(nMo,nTx)}, ... - 'hpre', {cell(nMo,nTx)}, ... - 'nb', {cell(nMo,1)}, ... - 'nn', {cell(nMo,1)}, ... - 'np', {cell(nMo,1)}); +chan = struct("hp", {cell(nMo,nTx)}, ... + "hpre", {cell(nMo,nTx)}, ... + "nb", {cell(nMo,1)}, ... + "nn", {cell(nMo,1)}, ... + "np", {cell(nMo,1)}); chan.hpre(:) = {-1}; switch algoCE - case 'gt' + case "gt" for i = 1:nTx chan.hp(:,i) = xChannel.xCIR(:,i); chan.hpre(:,i) = {pkOffset(i)}; @@ -93,7 +93,7 @@ chan.nb = xChannel.noiseb; chan.nn = xChannel.noisen; chan.np = xChannel.noisep; - case 'af0' + case "af0" % "0" assumes known CIR estimation (possibly from previos packet) for i = 1:nTx for j = 1:nMo @@ -111,7 +111,7 @@ chan.nn = xChannel.noisen; chan.np = xChannel.noisep; % remove "0" to perform normal channel estimation - algoCE = 'af'; + algoCE = "af"; end chan_gt = chan; % decoding variables @@ -160,9 +160,9 @@ if debug_pd rxOut.debug_pd = struct( ... - 'labels_pd', labels_pd, ... - 'errors_pd', errors_pd, ... - 'metrics_pd', metrics_pd); + "labels_pd", labels_pd, ... + "errors_pd", errors_pd, ... + "metrics_pd", metrics_pd); end end diff --git a/code_algo/emulates_construct_rxIn.m b/code_algo/emulates_construct_rxIn.m index c52dcd0..df7ad17 100644 --- a/code_algo/emulates_construct_rxIn.m +++ b/code_algo/emulates_construct_rxIn.m @@ -42,10 +42,10 @@ %% compute ground truth CIR xChannel = struct( ... - 'xCIR', {cell(nMo,nTx)}, ... - 'noiseb', {cell(nMo,1)}, ... - 'noisen', {cell(nMo,1)}, ... - 'noisep', {cell(nMo,1)}); + "xCIR", {cell(nMo,nTx)}, ... + "noiseb", {cell(nMo,1)}, ... + "noisen", {cell(nMo,1)}, ... + "noisep", {cell(nMo,1)}); % CIR long/short range for lenCIR = ["long", "short"] @@ -57,11 +57,11 @@ end if isfigureCIR && lenCIR=="long" - f1 = figure('units','normalized','outerposition',[0 0 1 1]); + f1 = figure("units","normalized","outerposition",[0 0 1 1]); tiledlayout(2,nTx); end if isfigureRx && lenCIR=="long" - f2 = figure('units','normalized','outerposition',[0 0 1 1]); + f2 = figure("units","normalized","outerposition",[0 0 1 1]); tiledlayout(2,1); end @@ -123,17 +123,18 @@ %% construct rxIn rxIn = struct( ... -'yRx', {cell(nMo,1)}, ... -'xChannel', {xChannel}, ... -'xChip', {cell(nMo,nTx)}, ... -'xBit', {cell(nMo,nTx)}, ... -'pChip', {cell(nMo,nTx)}, ... -'txOffset', {cell(1,nTx)}, ... -'moOffset', {cell(nMo,nTx)}, ... -'Lp', Lp2, ... -'code', code, ... -'hPre', hPre, ... -'hPost', hPost); +"T", T2, ... +"yRx", {cell(nMo,1)}, ... +"xChannel", {xChannel}, ... +"xChip", {cell(nMo,nTx)}, ... +"xBit", {cell(nMo,nTx)}, ... +"pChip", {cell(nMo,nTx)}, ... +"txOffset", {cell(1,nTx)}, ... +"moOffset", {cell(nMo,nTx)}, ... +"Lp", Lp2, ... +"code", code, ... +"hPre", hPre, ... +"hPost", hPost); for j = 1:nMo foldername = datanote(j,1)+"/"+string(T)+"ms_"+pumpstr+"_"+string(Lp)+"_"+code; diff --git a/code_algo/get_gold_code.m b/code_algo/get_gold_code.m index e431c6b..5fdc7d9 100644 --- a/code_algo/get_gold_code.m +++ b/code_algo/get_gold_code.m @@ -5,12 +5,12 @@ nTx = nMax; end if nTx > nMax - error('Please select higher order polynomials'); + error("Please select higher order polynomials"); end %% switch degree - % primpoly(8,'all'); + % primpoly(8,"all"); case 3 polynomial1 = "x^3+x^2+1"; polynomial2 = "x^3+x+1"; @@ -36,7 +36,7 @@ polynomial1 = "x^11+x^2+1"; polynomial2 = "x^11+x^8+x^5+x^2+1"; otherwise - error('not supported degree'); + error("not supported degree"); end nChip = 2^degree-1; @@ -66,7 +66,7 @@ end end -assert(sum(isnan(rChip), 'all') == 0); +assert(sum(isnan(rChip), "all") == 0); end diff --git a/code_algo/get_gold_code2.m b/code_algo/get_gold_code2.m index f00da9a..7f81383 100644 --- a/code_algo/get_gold_code2.m +++ b/code_algo/get_gold_code2.m @@ -5,12 +5,12 @@ nTx = nMax; end if nTx > nMax - error('Please select higher order polynomials'); + error("Please select higher order polynomials"); end %% switch degree - % primpoly(8,'all'); + % primpoly(8,"all"); case 3 polynomial1 = "x^3+x^2+1"; polynomial2 = "x^3+x+1"; @@ -36,7 +36,7 @@ polynomial1 = "x^11+x^2+1"; polynomial2 = "x^11+x^8+x^5+x^2+1"; otherwise - error('not supported degree'); + error("not supported degree"); end nChip = 2^degree-1; @@ -70,7 +70,7 @@ rChip2(2:2:end,:) = -rChip; rChip = rChip2; -assert(sum(isnan(rChip), 'all') == 0); +assert(sum(isnan(rChip), "all") == 0); end diff --git a/code_algo/read_txrx.m b/code_algo/read_txrx.m index da2e174..dbadd5e 100644 --- a/code_algo/read_txrx.m +++ b/code_algo/read_txrx.m @@ -1,4 +1,4 @@ -function [tx, rx] = readtxrx(folder, note, code) +function [tx, rx] = read_txrx(folder, note, code) %% read tx if contains(code, "goldman") || contains(code, "plain") settingFile = folder+"/../goldman.txt"; @@ -27,9 +27,9 @@ end %% CDMA code -codeId = fopen(settingFile, 'r'); +codeId = fopen(settingFile, "r"); l = fgetl(codeId); -A = sscanf(l, '%f %f'); +A = sscanf(l, "%f %f"); nCode = A(1); nChip = A(2); codes = zeros(nChip,nCode); @@ -41,7 +41,7 @@ fclose(codeId); %% tx -txId = fopen(txFile, 'r'); +txId = fopen(txFile, "r"); if txId == -1 disp(txFile); end @@ -49,10 +49,10 @@ % first line l = fgetl(txId); try - A = sscanf(l, '%f %f %f %f %f'); + A = sscanf(l, "%f %f %f %f %f"); Lp = A(5); catch - A = sscanf(l, '%f %f %f %f'); + A = sscanf(l, "%f %f %f %f"); Lp = 4; end T = A(1) * 1e-3; @@ -72,7 +72,7 @@ moOffset = zeros(1,nTx); for i = 1:nTx l = fgetl(txId); - A = sscanf(l, '(%f, %f, %f, %f), (code %f)'); + A = sscanf(l, "(%f, %f, %f, %f), (code %f)"); txPumps(i,:) = A(1:4).'; txPumps(i,2) = A(2) * T; txOffset(1,i) = A(2); @@ -88,7 +88,7 @@ fclose(txId); % tx bits -txId = fopen(txFile, 'r'); +txId = fopen(txFile, "r"); txBits = zeros(nBit,nTx); nBits = ones(1,nTx); txChips = zeros((nBit+Lp)*nChip*2,nTx); @@ -96,7 +96,7 @@ while true l = fgetl(txId); - if contains(l,'START') + if contains(l,"START") break; end end @@ -107,17 +107,17 @@ if l == -1 break; end - if contains(l,'bit') - A = sscanf(l, '%f, %f, bit %f'); + if contains(l,"bit") + A = sscanf(l, "%f, %f, bit %f"); txBits(nBits(A(2)+1),A(2)+1) = A(3); nBits(A(2)+1) = nBits(A(2)+1) + 1; - elseif contains(l,'prem') - A = sscanf(l, '%f, %f, prem %f'); + elseif contains(l,"prem") + A = sscanf(l, "%f, %f, prem %f"); if nBits(A(2)+1) == 1 startTime(A(2)+1) = A(1); end else - A = sscanf(l, '%f, %f, %f'); + A = sscanf(l, "%f, %f, %f"); txChips(nChips(A(2)+1),A(2)+1) = A(3); nChips(A(2)+1) = nChips(A(2)+1) + 1; end @@ -140,8 +140,8 @@ function rxSignal = readrx(rxFile) res = 1e-3; -rxId = fopen(rxFile, 'r'); -A = textscan(rxId, '%f\t%f'); +rxId = fopen(rxFile, "r"); +A = textscan(rxId, "%f\t%f"); fclose(rxId); A = cell2mat(A); diff --git a/code_algo/sim_mc_cir.m b/code_algo/sim_mc_cir.m index a7311f7..955fa48 100644 --- a/code_algo/sim_mc_cir.m +++ b/code_algo/sim_mc_cir.m @@ -4,11 +4,11 @@ try betas = cirParam.betas; catch betas = cell(nMo,1); betas(:) = {[10;0.1;1.5;0.1]}; % [50;0.4;1;0] - % W', D/v2, d/v, tau + % W", D/v2, d/v, tau end try T = cirParam.T; catch T = 1e-1; end try Tmax = cirParam.Tmax; catch Tmax = 10; end -try mode = cirParam.mode; catch mode = 'max'; end +try mode = cirParam.mode; catch mode = "max"; end %% assert(numel(betas) == nMo); @@ -26,12 +26,12 @@ end switch mode - case 'rand' + case "rand" idx = randi([1,10]); - case 'max' + case "max" [~, idx] = max(cirs{1}); idx = mod(idx-1,10)+1; otherwise - error('wrong sim_mc_cir mode'); + error("wrong sim_mc_cir mode"); end istart = inf; for j = 1:nMo @@ -39,7 +39,7 @@ istart = min(istart, find(cirs{j}>1e-3,1)); end for j = 1:nMo - iend = find(cirs{j}>1e-3,1,'last'); + iend = find(cirs{j}>1e-3,1,"last"); cirs{j}(1:istart-1) = []; cirs{j}(iend+1:end) = []; end diff --git a/code_algo/sim_mc_cir3.m b/code_algo/sim_mc_cir3.m index 18469df..1574596 100644 --- a/code_algo/sim_mc_cir3.m +++ b/code_algo/sim_mc_cir3.m @@ -7,7 +7,7 @@ end try T = cirParam.T; catch T = 1e-1; end try Tmax = cirParam.Tmax; catch Tmax = 10; end -try mode = cirParam.mode; catch mode = 'max'; end +try mode = cirParam.mode; catch mode = "max"; end %% assert(numel(betas) == nMo); @@ -25,12 +25,12 @@ end switch mode - case 'rand' + case "rand" idx = randi([1,10]); - case 'max' + case "max" [~, idx] = max(cirs{1}); idx = mod(idx-1,10)+1; otherwise - error('wrong sim_mc_cir mode'); + error("wrong sim_mc_cir mode"); end istart = inf; for j = 1:nMo @@ -38,7 +38,7 @@ istart = min(istart, find(cirs{j}>1e-3,1)); end for j = 1:nMo - iend = find(cirs{j}>1e-3,1,'last'); + iend = find(cirs{j}>1e-3,1,"last"); cirs{j}(1:istart-1) = []; cirs{j}(iend+1:end) = []; end diff --git a/code_algo/sim_mmo_tx.m b/code_algo/sim_mmo_tx.m index e1f3a5d..291f733 100644 --- a/code_algo/sim_mmo_tx.m +++ b/code_algo/sim_mmo_tx.m @@ -28,7 +28,7 @@ try xCIR = params.xChannel.xCIR; assert(iscell(xCIR)); catch xCIR = cell(nMo,nTx); for i = 1:nTx - xCIR(:,i) = sim_mc_cir3(struct('T', T, 'nMo', nMo)); + xCIR(:,i) = sim_mc_cir3(struct("T", T, "nMo", nMo)); end end try xChip = params.xChip; catch @@ -68,24 +68,24 @@ SINR = cell(nMo,nTx); for j = 1:nMo xChannel2 = struct( ... - 'noiseb', noiseb{j}, ... - 'noisen', noisen{j}, ... - 'noisep', noisep{j}, ... - 'xCIR', {xCIR(j,:)}); + "noiseb", noiseb{j}, ... + "noisen", noisen{j}, ... + "noisep", noisep{j}, ... + "xCIR", {xCIR(j,:)}); txOffsetMo = txOffset; for i = 1:nTx txOffsetMo{1,i} = txOffset{1,i} + moOffset{j,i}; end txIn2 = struct( ... - 'T', T, ... - 'nDegree', nDegree, ... - 'nTx', nTx, ... - 'nBit', nBit, ... - 'Lp', Lp, ... - 'txOffset', {txOffsetMo}, ... - 'xChannel', {xChannel2}, ... - 'xChip', {xChip(j,:)}); + "T", T, ... + "nDegree", nDegree, ... + "nTx", nTx, ... + "nBit", nBit, ... + "Lp", Lp, ... + "txOffset", {txOffsetMo}, ... + "xChannel", {xChannel2}, ... + "xChip", {xChip(j,:)}); if isrepeat assert(size(xBit,1) == 1); @@ -120,18 +120,20 @@ %% return txOut xChannel = struct( ... - 'noiseb', {noiseb}, ... - 'noisen', {noisen}, ... - 'noisep', {noisep}, ... - 'xCIR', {xCIR}); + "noiseb", {noiseb}, ... + "noisen", {noisen}, ... + "noisep", {noisep}, ... + "xCIR", {xCIR}); txOut = struct( ... - 'yRx', {yRx}, ... - 'txOffset', {txOffset2}, ... - 'moOffset', {moOffset}, ... - 'xChannel', xChannel, ... - 'xChip', {xChip}, ... - 'xBit', {xBit}, ... - 'pChip', {pChip}, ... - 'yTx', {yTx}, ... - 'SINR', {SINR}); + "T", T, ... + "yRx", {yRx}, ... + "txOffset", {txOffset2}, ... + "moOffset", {moOffset}, ... + "xChannel", xChannel, ... + "xChip", {xChip}, ... + "xBit", {xBit}, ... + "Lp", Lp, ... + "pChip", {pChip}, ... + "yTx", {yTx}, ... + "SINR", {SINR}); end \ No newline at end of file diff --git a/code_algo/sim_tx.m b/code_algo/sim_tx.m index ca2535b..01857eb 100644 --- a/code_algo/sim_tx.m +++ b/code_algo/sim_tx.m @@ -13,7 +13,7 @@ txOffset = cell(1,nTx); txOffset(:) = {0}; end try xCIR = params.xChannel.xCIR; catch - xCIR = cell(1,nTx); xCIR(:) = sim_mc_cir3(struct('T', T)); + xCIR = cell(1,nTx); xCIR(:) = sim_mc_cir3(struct("T", T)); end try xChip = params.xChip; catch xChip = get_gold_code(nTx, nDegree); @@ -83,16 +83,18 @@ end %% return txOut -xChannel = struct('xCIR', xCIR, 'noiseb', noiseb, ... - 'noisen', noisen, 'noisep', noisep); +xChannel = struct("xCIR", xCIR, "noiseb", noiseb, ... + "noisen", noisen, "noisep", noisep); txOut = struct( ... - 'yRx', {yRx}, ... - 'txOffset', {txOffset}, ... - 'xChannel', {xChannel}, ... - 'xChip', {xChip}, ... - 'xBit', {xBit}, ... - 'pChip', {pChip}, ... - 'yTx', {yTx}, ... - 'SINR', {SINR}); + "T", T, ... + "yRx", {yRx}, ... + "txOffset", {txOffset}, ... + "xChannel", {xChannel}, ... + "xChip", {xChip}, ... + "xBit", {xBit}, ... + "Lp", Lp, ... + "pChip", {pChip}, ... + "yTx", {yTx}, ... + "SINR", {SINR}); end \ No newline at end of file diff --git a/code_figure/figure_results_ceLoss_nTx_ce.m b/code_figure/figure_results_ceLoss_nTx_ce.m index ba58651..9356643 100644 --- a/code_figure/figure_results_ceLoss_nTx_ce.m +++ b/code_figure/figure_results_ceLoss_nTx_ce.m @@ -3,7 +3,7 @@ if ~exist("matversion","var"), matversion = "author"; end %% -ceRange = [1,5,12]+300; +ceRange = [1,12,5]+cenoteFinal-1; %% mol = "salt"; @@ -49,12 +49,12 @@ % L3 similarity (not applicable for 1 molecule) % L4 smoothness switch mod(ceRange(ceIdx),100) - case 1 + case {1,21} legendName(ceIdx) = "L0+L1+L2"; - case 5 - legendName(ceIdx) = "L0+L1"; - case 12 + case {5,25} legendName(ceIdx) = "L0+L2"; + case {12,32} + legendName(ceIdx) = "L0+L1"; end T = 125; @@ -62,7 +62,7 @@ Lp2Name = ""; nMo = 1; codeName = "goldman"; - algoName = "gt-af0"; + algoName = "gt1-af0"; for idx = 1:length(txNameRange) txName = txNameRange(idx); diff --git a/code_figure/figure_results_codetuple.m b/code_figure/figure_results_codetuple.m index 42fa2fb..be063a0 100644 --- a/code_figure/figure_results_codetuple.m +++ b/code_figure/figure_results_codetuple.m @@ -12,7 +12,7 @@ dataName = dataRange(codeIdx); nMo = numel(strfind(dataName,"-"))+1; - matName = "../mat_"+matversion+"/mat"+dataName+"_11/"+ceName+"/emulates_125ms_2-7_16_goldman_"+string(nMo)+"_gt-af0.mat"; + matName = "../mat_"+matversion+"/mat"+dataName+"_11/"+ceName+"/emulates_125ms_2-7_16_goldman_"+string(nMo)+"_gt1-af0.mat"; disp(matName); if isfile(matName) diff --git a/code_figure/figure_results_moreMo_Tx_ce.m b/code_figure/figure_results_moreMo_Tx_ce.m index 27080de..51c0331 100644 --- a/code_figure/figure_results_moreMo_Tx_ce.m +++ b/code_figure/figure_results_moreMo_Tx_ce.m @@ -11,7 +11,7 @@ Lp2Name = ""; osName = ""; codeName = "goldman"; codelength = 14; -algoName = "gt-af0"; +algoName = "gt1-af0"; algover = "11"; topo = "line"; diff --git a/code_figure/generate_all_fig.m b/code_figure/generate_all_fig.m index e684605..723ece9 100644 --- a/code_figure/generate_all_fig.m +++ b/code_figure/generate_all_fig.m @@ -15,11 +15,11 @@ % figure 10 (include reference index) figure_results_codecomplement % figure 11 -figure_results_ceLoss_nTx_ce % DIFFERENT +figure_results_ceLoss_nTx_ce % % figure 12a,12b -figure_results_moreMo_Tx_ce % DIFFERENT +figure_results_moreMo_Tx_ce % % figure 13 -figure_results_codetuple % DIFFERENT +figure_results_codetuple % % figure 14 figure_results_moreMo_rate_pd % figure 15 diff --git a/documentation/circuits/circuit.cddx b/documentation/circuits/circuit.cddx index e72528a94577c22af2c9f7b1b7a927a421c0f492..d5d737411929cc385a7f332469acd7d290290f35 100644 GIT binary patch delta 2817 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 delta 2735 zcmY*b2{aT67apZy491$7EECFZ79&e}p7bmuOZHw_CL~)$lie60mGx<4?8+Wtj4it% zAtq~C!q3zwhR8qm?brMN&iUUx=bn4N?|kRnbMHC#oR1;ZCI!86`8cNt0Km-+i18D! zU-v5y5d;AMx_=}?&kz$oy%ueqA@FpDmS5c?9~)4K_h{m7#KxKmES0MaN#)mlnascj zh!Lnx?BpGuXXTf=N2Os6ShVL{CM>ht=*woZ4je+*t0eB>cRqL1L)ayOyS9$g@x9eS zI>WY>p3NTq$>?!2AEwK;#*`KqwuLZ?@ywj+skp`+pU@v$NMU7f0n^ty`4`rXbxGe^m{ z)4IPS`-V!1&9Hjjic>8Ou)B6$6^O;si4UyWnFOQj2VvX3TimK9IjN4s0d4&UI<_Q`yt3e+S;>naNEY%k0aOI5}rQw8lkPyGlp&u>my&W%J_ zS{Xb{+q~Fi!4nI+LPu0?e5gC`?Bl>xO7OQSX?FB^)st`2Z7}+tw^(1)&oZxTqsC*) zGj_2^H#e=p{Ofk4iVXJt1+)WIp%*J8cuwqm(HZm6ju`!1Xe{aqPs@Sxl;#LlT;uTi z9KL#zDAqEo$mh%os~7+&Tid0oSxkEL+Fn?{J!N_|-7tgMAmeH7|HEtvdujZ>3v0lp za-G&Rl6UjbPUkmQu=Z`E?dwf8f9|&3d8941Ol*UdZA`ngw7`&S>rS~RQ%9Ipcxi0U zBY)kjtJ6JThv(uaeJzhUyqR?pI)-4cIU%4>qJIaRp`Y>r%oN+w)0852NI)|_vW@mO ztGdqDOIi~L?!fB%n#zHA#vSu%`k9RyVT&d%?t80KA3grIAWy)#Fo2ii2^CxnCV!6zmm1;~ME{JoIsmr%l z7!*1tq{mz}&Wr@EjS{~rj9N&1dLg z%cynlZ(bS?+Es?#RDss$!%(Vg|45UNqu^27ARh@GZz5<( zTxS~OBmU^%EakSu4jBOE-@hCIJc2nk@(C?!sgZ+?@2|h?J^Ca$_d0+^@JWNk-cT!x z8Bz$R#(H)w;$7%$+Fs{2I}>b%pTIgA;-@`NSh%W-<=73LGbDMTjlBLa?@g60l#!c$ zipa;lPPqp)dspa?e-3NU=vgH`mR0~2xV>k`ep+}IS(#_NYWJRG6Be~Cnt(K>L}%j^ zU!N~zfHAM?$9P#8Hykmqykq?7l#rsMa;MS#>Mypqhhlotz+oY=mn7kQ!IEt&aA5c@ znd^yOz}e%T={#TX*wcb;53>5daKB$#^S(orb@#=+nYc{B8BU(S6f<#{Ak{QKxcn4-EHb@%@07(vEpA?i z@OJr-e1eE6?z`GO#2itr=zP0(4pSWJI%fG~tIXY8^FuB&^TzyFsTPv~nk|NhQ5NkC zP3w$u1S$d_ju{e{5Ki@w%<`l_;Tcuvv6@j`Dkrg~<1zEz@3Sc*c$ja>tZxb>E(vQa z2CH@GkB+`y^>uA)yOSJk&G8}%B4jm*NIDLbBla&+Erg`EA`iYII>A{6i}yhXsd80q z?OMOd@Rb7xOb$=BwV#e^ZKr&bby2V%9n(s*_9}9d%y`NWVJB(5PKfFe2)!urJIiFnuIq}*bGHU~PZ)rA zm>6}jO&S`e#cOS*q$f5FCOJr`4iYBE;!aM)f$eV>lKG!TwB2rj>j|@N>+UT3=<4GB ztxYU)`|bcVQdLrg$Oh9s2`?<35izq~g@#Y-KV+gkuDbZjOZt!W7Q>Mi^t%Oj5BdIS zkS?D$fm8tifFDOmiUElcFY-sC1PsPPc!mkEYYAvh-_INlP2TUg&3rBwMg6#g(0OVv zFq-yl<8W{DU!Mai1MG?K5yrh;SHRE2WIA!KEPea9WQ%*s0dy)gc~#F(&*hO~rlItQ zyjxNx5pJPuIp)V?Ny)yC>51_Jvi_^R8!I00kI8Ox<_$e`>UB~6AZN|8TCgfl6z%`qdn&v(Q~uXFfWQ6T<6*n0YG)oT z4gs{Ax|RmYRb4~fO~60&ntEO$R}0@@y>AY z|I;v%-1slRe^UQ{BEh3G{fYt(6H+Vx>Ay8U=dN=A06!D_OK?;RIFv~H{HK1-Af1AU Qa~P8}AaX!?{-2Wn09)k`=>Px# diff --git a/documentation/circuits/circuit.png b/documentation/circuits/circuit.png index bd78d029082dc51ce4378e8ecfcd8232f9398fca..0277e7604a4058ad86863e41f36f76b8e6f6640b 100644 GIT binary patch literal 58183 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 z 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 diff --git a/documentation/data_process.md b/documentation/data_process.md index f50a7d2..051632a 100644 --- a/documentation/data_process.md +++ b/documentation/data_process.md @@ -8,29 +8,47 @@ As said, `main_emulates_txrx_allloop.m` processes the data in the way that are r | Variable | Meaning | Potential value | |-----------|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------| -| datanote0 | The single-molecule channel in experiment or the multi-molecule channel to be emulated. | n-by-1 string array. Only the combinations of all fork or line channels are meaningful. | -| nMo | The number of molecule in the experiment data or the emulated data. | Any positive integer when $datanote0$ is a single string; or equals to the length of $datanote0$. | -| code | The code used by the TXs. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | -| T | The chip interval in milliseconds. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | -| pumpstr | The active TX pins on TX Arduino. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | -| Lp | The length of the preamble as multiples of CDMA code length. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | +| `datanote0` | The single-molecule channel in experiment or the multi-molecule channel to be emulated. | n-by-1 string array. Only the combinations of all fork or line channels are meaningful. | +| `nMo` | The number of molecule in the experiment data or the emulated data. | Any positive integer when $datanote0$ is a single string; or equals to the length of $datanote0$. | +| `code` | The code used by the TXs. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | +| `T` | The chip interval in milliseconds. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | +| `pumpstr` | The active TX pins on TX Arduino. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | +| `Lp` | The length of the preamble as multiples of CDMA code length. | Check [dataset](/dataset) and [documentation](/documentation/testbed.md) | -### Algorithm related variables +### Decoder + +`main_emualtes_tx_allloop.m` calls two different scripts which actually process the raw data and output the results. + +* [`loop_emulates_txrx_noncoherent.m`](/loop_emulates_txrx_noncoherent.m) uses the [noncoherent decoder](/code_algo/decode_mmo_noncoherent_MMoNTx.m) (constant thresholder), which serves as a baseline. The only result assumes that the RX knows the arriving time of each TX. +* [`loop_emulates_txrx_all.m`](/loop_emulates_txrx_all.m) uses the [proposed decoder](/code_algo/decode_mmo_coherent_MMoNTxSW11loop.m). All results in the paper (except one bar) are processed by this decoder. + +### Decoder related variables | Variable | Meaning | Potential value | |----------|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------| -| cenote | Preset adaptive filtering loss weights for channel estimation. | Check [GetCEWeights()](/code_algo/GetCEWeights.m). | -| pdnote | Preset adaptive filtering loss weights for packet detection. | Check [GetCEWeights()](/code_algo/GetCEWeights.m). | -| T2 | $T/T2$ is the oversampling rate. | No lower than the actual Arduino sampling interval. | -| Lp2 | Conceptual preamble length, which treats some data bits as preamble. | No lower than $Lp$. | -| mode_pd | Packet detection only. The decoder stops as long as all TXs are detected, whether correct or wrong. | True or false. | -| debug_pd | Debugging packet detection. It corrects false packet detection and records all metrics. | True (only if mode_pd is true) or false. | -| algoPD | Packet detection algorithm. | "gt" for ground truth or "sc" for detection. | -| algoCE | Channel estimation algorithm. | "gt" for ground truth or "af0" for estimation. | +| `cenote` | Preset adaptive filtering loss weights for channel estimation. | Check [GetCEWeights()](/code_algo/GetCEWeights.m). | +| `pdnote` | Preset adaptive filtering loss weights for packet detection. | Check [GetCEWeights()](/code_algo/GetCEWeights.m). | +| `T2` | $T/T2$ is the oversampling rate. | No lower than the actual Arduino sampling interval. | +| `Lp2` | Conceptual preamble length, which treats some data bits as preamble. | No lower than $Lp$. | +| `algoPD` | Packet detection algorithm. | "gt", "gt1" or "sc". | +| `algoCE` | Channel estimation algorithm. | "gt" (only if `algoPD` is "gt") or "af0". | +| `mode_pd` | Packet detection only. The decoder stops as long as all TXs are detected, whether correct or wrong. | True (only if `algoPD` is "sc") or false. | +| `debug_pd` | Debugging packet detection. It corrects false packet detection and records all metrics. | True (only if `algoPD` is "sc") or false. | + +The `algoPD` has three options + +* "gt" means ground truth, which assumes the RX knows exactly when each packet arrvies. The ground truth is based on the peak of the CIR of each TX. +* "gt1" also assumes the RX knows exactly when each packet arrvies, but with a few bits lag. This mode is meant to test the decoding performance after correct packet detection, because we observe that the packet detection is usually a few bits behind the peak of the CIR and such lag is larger at higher data rate (i.e. higher ISI). +* "sc" performs packet detection normally. + +The `algoCE` has two options + +* "gt" mean ground truth. But this is not the real ground truth, which is estimated from the whole packet. It is only legitimate when `algoPD` is "gt". +* "af0" performs channel estimation normally, where the CIR is estimated from the preamble only. It is legitimate with any `algoPD` option. ## Results format -If you run `main_emulates_txrx_allloop.m` yourself, the results can be found in folder [`mat_temp`](/mat_temp). We also provide our processed results in folder [`mat_author`](/mat_author). +If you run `main_emulates_txrx_allloop.m` yourself, the results can be found in folder [`mat_temp`](/mat_temp). The results are saved in the same naming manner for directories and `.mat` files. diff --git a/documentation/testbed.md b/documentation/testbed.md index 5174a1c..ad373e6 100644 --- a/documentation/testbed.md +++ b/documentation/testbed.md @@ -34,7 +34,7 @@ The RX Arduino setting is much more easier, which only requires connecting the [ ### Synchronizing TX and RX -The TX Arduino and the RX Arduino are synchronized by two pins. The TX sends a HIGH voltage to the RX through the SYNC pin (pin 9) when a new trace starts. Then, the RX replies a HIGH votalge to the TX through the ACK pin (pin 8). +The TX Arduino and the RX Arduino are synchronized through "Serial1", which requires connection of TX Arduino Pin18 to RX Arduino Pin19, and TX Arduino Pin19 to RX Arduino Pin18. ## Coding @@ -112,7 +112,7 @@ The experiment is conducted as the following steps. ### TX data -Take [this](/dataset/data5/125ms_2-3-4-5_16_goldman/tx/00.TXT) as an example of TX data. +Take [/dataset/data5/125ms_2-3-4-5_16_goldman/tx/00.TXT](/dataset/data5/125ms_2-3-4-5_16_goldman/tx/00.TXT) as an example of TX data. #### Experiment setup @@ -122,10 +122,10 @@ The first line shows the basic setup of the experiments. 125 100 4 7 16 ``` -* `125` is the chip interval in milliseconds. +* `125` is the chip interval in milliseconds. But coding `plain0` is an exception, where the chip interval is double the value (e.g. `875ms_2_2_plain0` has 1.75s symbol interval). * `100` is the number of data bits in each packet. * `4` is the number of active TX. -* `7` is the length of the gold code. It is either used directly as CDMA or to generate the MoMA codebook, depending on the coding scheme explained [here](#coding). +* `7` is the length of the gold code. It is either used directly as CDMA or to generate the MoMA codebook, depending on the coding scheme explained [in this part](#coding). * `16` is the length of the preamble as multiples of the code length. #### Transmitter setup @@ -163,7 +163,7 @@ The rest of the file after the line `START` records the behavior of each TX, and ### RX data -Take [this](/dataset/data5/125ms_2-3-4-5_16_goldman/rx/00.TXT) as an example of RX data. +Take [/dataset/data5/125ms_2-3-4-5_16_goldman/rx/00.TXT](/dataset/data5/125ms_2-3-4-5_16_goldman/rx/00.TXT) as an example of RX data. ``` 24087 21 @@ -237,7 +237,7 @@ Take `125ms_2-3-4-5_16_goldman` as an example. | T | 125 | Chip interval in milliseconds. | | pumpstr | 2-3-4-5 | Indices of active pumps (TXs) separated by '-'. | | Lp | 16 | Length of preamble as multiples of code length. | -| code | goldman | Coding type. More details are explained [here](#coding). | +| code | goldman | Coding type. More details are explained [in this part](#coding). | ### Fourth layer diff --git a/loop_emulates_txrx_all.m b/loop_emulates_txrx_all.m index 381c3d6..81ae9fb 100644 --- a/loop_emulates_txrx_all.m +++ b/loop_emulates_txrx_all.m @@ -1,4 +1,4 @@ -warning('off'); +warning("off"); subruns = 1e2; maxIdx = 5; if ~exist("debug_pd","var"), debug_pd = false; end @@ -43,10 +43,10 @@ dothisloop = true; if isfile(matname) %|| isfile(matname0) - fprintf('Existing %s\n', matname); + fprintf("Existing %s\n", matname); dothisloop = false; elseif isfile(submatname) - fprintf('Existing %s\n', submatname); + fprintf("Existing %s\n", submatname); dothisloop = false; elseif isfile(subclockname) todo = false; @@ -54,7 +54,7 @@ try load(subclockname); if endtime > datetime - fprintf('Someone else is working on %s ......\n', matname); + fprintf("Someone else is working on %s ......\n", matname); todo = false; break; else @@ -80,14 +80,14 @@ endtime = starttime + expdur; remdur = endtime - datetime; if remdur < predur - fprintf('Remaining time may not finish next data.'); + fprintf("Remaining time may not finish next data"); return; end - save(subclockname, 'endtime'); + save(subclockname, "endtime"); %% - disp(datetime('now')); - fprintf('Working on %s ......\n', submatname); + disp(datetime("now")); + fprintf("Working on %s ......\n", submatname); tic; @@ -96,8 +96,8 @@ else totalruns = subruns; end - disp(['working on nMo=' num2str(nMo) ', Lp=' num2str(Lp) ... - ', runs=' num2str(totalruns) ', subIdx=' num2str(subIdx)]); + disp("working on nMo="+string(nMo)+", Lp="+string(Lp) ... + +", runs="+string(totalruns)+", subIdx="+string(subIdx)); notes_temp = nan(totalruns,nMo); if ~mode_pd, ber_temp = nan(totalruns,nMo,nTx); end @@ -107,7 +107,7 @@ % for kk = 1:totalruns parfor kk = 1:totalruns addpath("code_algo"); - warning('off'); + warning("off"); if prod(ntxt) <= maxIdx * subruns note_idx = kk+(subIdx-1)*subruns-1; @@ -125,14 +125,14 @@ notes = arrayfun(@(a)sprintf("%02d",a), note_temp); constructIn = struct(... - 'datanote', datanote, ... - 'T', T, ... - 'pumpstr', pumpstr, ... - 'Lp', Lp, ... - 'code', code, ... - 'notes', notes, ... - 'T2', T2, ... - 'Lp2', Lp2); + "datanote", datanote, ... + "T", T, ... + "pumpstr", pumpstr, ... + "Lp", Lp, ... + "code", code, ... + "notes", notes, ... + "T2", T2, ... + "Lp2", Lp2); try constructIn.hPre = hPre; catch, constructIn.hPre = ceil(1250/T2); end try constructIn.hPost = hPost; catch, constructIn.hPost = ceil(1750/T2); end try constructIn.hlen = hlen; catch ; end @@ -149,7 +149,7 @@ rxOut = decode_mmo_coherent_MMoNTxSW11loop(rxIn); - [~,idx] = sort(cell2mat(rxIn.txOffset),'ascend'); + [~,idx] = sort(cell2mat(rxIn.txOffset),"ascend"); if ~mode_pd, ber_temp(kk,:,:) = cell2mat(rxOut.BER(:,idx)); end if ~debug_pd pdoff_temp(kk,1,:) = rxOut.PDOff(idx); @@ -166,8 +166,8 @@ toc; - disp(datetime('now')); - fprintf('Finished with %s ......\n', submatname); + disp(datetime("now")); + fprintf("Finished with %s ......\n", submatname); %% delete(subclockname); diff --git a/loop_emulates_txrx_noncoherent.m b/loop_emulates_txrx_noncoherent.m index 4bd978c..1800748 100644 --- a/loop_emulates_txrx_noncoherent.m +++ b/loop_emulates_txrx_noncoherent.m @@ -1,4 +1,4 @@ -warning('off'); +warning("off"); subruns = 1e2; maxIdx = 5; if ~exist("debug_pd","var"), debug_pd = false; end @@ -43,10 +43,10 @@ dothisloop = true; if isfile(matname) %|| isfile(matname0) - fprintf('Existing %s\n', matname); + fprintf("Existing %s\n", matname); dothisloop = false; elseif isfile(submatname) - fprintf('Existing %s\n', submatname); + fprintf("Existing %s\n", submatname); dothisloop = false; elseif isfile(subclockname) todo = false; @@ -54,7 +54,7 @@ try load(subclockname); if endtime > datetime - fprintf('Someone else is working on %s ......\n', matname); + fprintf("Someone else is working on %s ......\n", matname); todo = false; break; else @@ -80,14 +80,14 @@ endtime = starttime + expdur; remdur = endtime - datetime; if remdur < predur - fprintf('Remaining time may not finish next data.'); + fprintf("Remaining time may not finish next data"); return; end - save(subclockname, 'endtime'); + save(subclockname, "endtime"); %% - disp(datetime('now')); - fprintf('Working on %s ......\n', submatname); + disp(datetime("now")); + fprintf("Working on %s ......\n", submatname); tic; @@ -96,8 +96,8 @@ else totalruns = subruns; end - disp(['working on nMo=' num2str(nMo) ', Lp=' num2str(Lp) ... - ', runs=' num2str(totalruns) ', subIdx=' num2str(subIdx)]); + disp("working on nMo="+string(nMo)+", Lp="+string(Lp) ... + +", runs="+string(totalruns)+", subIdx="+string(subIdx)); notes_temp = nan(totalruns,nMo); if ~mode_pd, ber_temp = nan(totalruns,nMo,nTx); end @@ -107,7 +107,7 @@ % for kk = 1:totalruns parfor kk = 1:totalruns addpath("code_algo"); - warning('off'); + warning("off"); if prod(ntxt) <= maxIdx * subruns note_idx = kk+(subIdx-1)*subruns-1; @@ -125,14 +125,14 @@ notes = arrayfun(@(a)sprintf("%02d",a), note_temp); constructIn = struct(... - 'datanote', datanote, ... - 'T', T, ... - 'pumpstr', pumpstr, ... - 'Lp', Lp, ... - 'code', code, ... - 'notes', notes, ... - 'T2', T2, ... - 'Lp2', Lp2); + "datanote", datanote, ... + "T", T, ... + "pumpstr", pumpstr, ... + "Lp", Lp, ... + "code", code, ... + "notes", notes, ... + "T2", T2, ... + "Lp2", Lp2); try constructIn.hPre = hPre; catch, constructIn.hPre = ceil(1250/T2); end try constructIn.hPost = hPost; catch, constructIn.hPost = ceil(1750/T2); end try constructIn.hlen = hlen; catch ; end @@ -149,7 +149,7 @@ rxOut = decode_mmo_noncoherent_MMoNTx(rxIn); - [~,idx] = sort(cell2mat(rxIn.txOffset),'ascend'); + [~,idx] = sort(cell2mat(rxIn.txOffset),"ascend"); if ~mode_pd, ber_temp(kk,:,:) = cell2mat(rxOut.BER(:,idx)); end if ~debug_pd pdoff_temp(kk,1,:) = rxOut.PDOff(idx); @@ -166,8 +166,8 @@ toc; - disp(datetime('now')); - fprintf('Finished with %s ......\n', submatname); + disp(datetime("now")); + fprintf("Finished with %s ......\n", submatname); %% delete(subclockname); diff --git a/main_emulate_txrx.m b/main_emulate_txrx.m index 9645406..287dd72 100644 --- a/main_emulate_txrx.m +++ b/main_emulate_txrx.m @@ -8,79 +8,80 @@ %% addpath("code_algo"); -warning('off'); +warning("off"); ismc = true; isrepeat = false; -noisemodel = 'pois'; +noisemodel = "pois"; isfigure = false; %% datanote = "dataset/data1"; T = 125; -Lp = 16; Lp2 = Lp; +T2 = T; +Lp = 16; +Lp2 = Lp; pumpstr = "2-3-4-5"; code = "goldman"; +if ~exist("notes", "var") notes = arrayfun(@(a)(string(sprintf("%02d",a))),randi([1 40],[2 1])); +end disp(notes); -% T = 437; -% Lp = 2; Lp2 = Lp; -% pumpstr = "2"; -% code = "plain0"; -% notes = "01"; - %% get ground truth CIR -rxIn = emulates_construct_rxIn(struct(... - 'datanote', datanote, ... - 'T', T, ... - 'pumpstr', pumpstr, ... - 'Lp', Lp, ... - 'code', code, ... - 'notes', notes, ... - 'T2', T, ... - 'Lp2', Lp2, ... - 'hPre', ceil(1250/T), ... - 'hPost', ceil(1750/T))); -try rxIn.hlen = hlen; catch; end +constructIn = struct(... + "datanote", datanote, ... + "T", T, ... + "pumpstr", pumpstr, ... + "Lp", Lp, ... + "code", code, ... + "notes", notes, ... + "T2", T2, ... + "Lp2", Lp2); +try constructIn.hPre = hPre; catch, constructIn.hPre = ceil(1250/T2); end +try constructIn.hPost = hPost; catch, constructIn.hPost = ceil(1750/T2); end +try constructIn.hlen = hlen; catch ; end +rxIn = emulates_construct_rxIn(constructIn); -%% rxOut -rxIn.noisemodel = 'pois'; +rxIn.weights_ce = GetCEWeights(221); +rxIn.sameMo = length(datanote)==1; -% rxIn.algoPD = 'gt'; -% rxIn.algoCE = 'gt'; -% rxIn.mode = 'dc'; -% rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +%% rxOut +% rxIn.algoPD = "gt"; +% rxIn.algoCE = "gt"; +% rxIn.mode = "dc"; +% rxOut = decode_mmo_coherent_MMoNTxSW11loop(rxIn); % disp(cell2mat(rxOut.BER)); -% disp('----'); +% disp("----"); -% rxIn.algoPD = 'gt'; -% rxIn.algoCE = 'af0'; -% rxIn.mode = 'dc'; -% rxIn.sameMo = true; -% rxIn.weight_ce = GetCEWeights(301); -% for i = 1:length(rxIn.txOffset) -% rxIn.txOffset{i} = rxIn.txOffset{i} + pdoff_temp(ijk,1,i); -% end -% % rxIn.debug_ce = true; -% rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +% rxIn.algoPD = "gt"; +% rxIn.algoCE = "af0"; +% rxIn.mode = "dc"; +% rxOut = decode_mmo_coherent_MMoNTxSW11loop(rxIn); % disp(cell2mat(rxOut.BER)); -% disp('----'); +% disp("----"); -rxIn.debug_pd = false; -rxIn.algoPD = 'sc'; -rxIn.algoCE = 'af0'; -rxIn.mode = 'dc'; -rxIn.sameMo = true; -rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +rxIn.algoPD = "gt1"; +rxIn.algoCE = "af0"; +rxIn.mode = "dc"; +rxOut = decode_mmo_coherent_MMoNTxSW11loop(rxIn); +disp(rxOut.PDOff); disp(cell2mat(rxOut.BER)); -disp('----'); -disp('------------------------------'); +disp("----"); + +% rxIn.algoPD = "sc"; +% rxIn.algoCE = "af0"; +% rxIn.debug_pd = false; +% rxIn.mode = "dc"; +% rxOut = decode_mmo_coherent_MMoNTxSW11loop(rxIn); +% disp(cell2mat(rxOut.BER)); +% disp("----"); +disp("------------------------------"); %% if false %% - figure('units','normalized','outerposition',[0 0 1 1]); + figure("units","normalized","outerposition",[0 0 1 1]); for j = 1:size(txOut.xBit,1) for i = 1:nTx subplot(size(txOut.xBit,1), nTx, (j-1)*size(txOut.xBit,1)+i); @@ -88,8 +89,8 @@ stem(txOut.xBit{j,i}); stem(rxOut.dBit{j,i}); % yyaxis right; - % plot(txOut.xChannel.xCIR{j,i}, 'k'); - % plot(rxOut.chan.hp{j,i}, 'k'); + % plot(txOut.xChannel.xCIR{j,i}, "k"); + % plot(rxOut.chan.hp{j,i}, "k"); end end end \ No newline at end of file diff --git a/main_emulates_txrx_allloop.m b/main_emulates_txrx_allloop.m index 4066052..0649b0a 100644 --- a/main_emulates_txrx_allloop.m +++ b/main_emulates_txrx_allloop.m @@ -2,8 +2,8 @@ % expdur = hours(24); starttime = datetime; predur = hours(0); -if isempty(gcp('nocreate')) - parpool(feature('numcores')); +if isempty(gcp("nocreate")) + parpool(feature("numcores")); end addpath("code_algo"); @@ -94,6 +94,30 @@ loop_emulates_txrx_all end +% +if (ismember(cenote,cenote0) && nMo==2 && isequal(datanote0,"1") && code=="goldman" ... + && ismember(pumpstr,["2","3-4","2-3-4","2-3-4-5"]) && T==125 && Lp==16) ... % figure 6 debug + || (ismember(cenote,cenote0) && nMo==1 && isequal(datanote0,"1") && code=="gold" ... + && ismember(pumpstr,["2","2-3"]) && T==125 && Lp==16) ... % figure 6 debug + || (ismember(cenote,cenote0) && nMo==1 && isequal(datanote0,"1") && code=="plain0" ... + && pumpstr=="2" && T==437 && Lp==2) ... % figure 6 debug + || (ismember(mod(cenote,cenoteOffset),[1,5,12]) && nMo==1 && isequal(datanote0,"1") ... + && ismember(pumpstr,["2","3-4","2-3-4","2-3-4-5"]) && T==125 && Lp==16) ... % figure 11 + || (ismember(cenote,cenote0) && ismember(nMo,[1,2]) && (numel(datanote0)==1&&ismember(datanote0,["1","3","4","5"])) && code=="goldman" ... + && ismember(pumpstr,["2","3-4","2-3-4","2-3-4-5"]) && T==125 && Lp==16) ... % figure 12 + || (ismember(cenote,cenote0) && nMo==2 && (numel(datanote0)==2&&ismember(datanote0.',[["1";"3"],["4";"5"]].',"rows")) && code=="goldman" ... + && ismember(pumpstr,["2","3-4","2-3-4","2-3-4-5"]) && T==125 && Lp==16) ... % figure 12 + || (ismember(cenote,cenote0) && nMo==1 && (numel(datanote0)==1&&ismember(datanote0,["1","2"])) && code=="goldman" ... + && pumpstr=="2-7" && T==125 && Lp==16) ... % figure 13 + || (ismember(cenote,cenote0) && nMo==2 && isequal(datanote0,["1";"2"]) && code=="goldman" ... + && pumpstr=="2-7" && T==125 && Lp==16) % figure 13 + debug_pd = false; + mode_pd = false; + algoPD = "gt1"; + algoCE = "af0"; + loop_emulates_txrx_all +end + % if (ismember(cenote,cenote0) && ismember(nMo,[1,2]) && isequal(datanote0,"1") && code=="goldman" ... && ismember(pumpstr,["2","3-4","2-3-4","2-3-4-5"]) && T==125 && Lp==16) ... % debug diff --git a/main_sim_txrx.m b/main_sim_txrx.m new file mode 100644 index 0000000..4cceb40 --- /dev/null +++ b/main_sim_txrx.m @@ -0,0 +1,89 @@ +% DEBUG CODE 1 +% for ijk = 1:size(notes_temp,1) +% disp(ijk); +% disp(squeeze(ber_temp(ijk,:,:))); +% notes = arrayfun(@(a)(string(sprintf("%02d",a))),notes_temp(ijk,:).'); +% main_emulate_txrx +% end + +%% +addpath("code_algo"); +warning("off"); + +ismc = true; +isrepeat = false; +noisemodel = "pois"; +isfigure = false; + +%% +T = 125; +T2 = T; +Lp = 16; +Lp2 = Lp; +code = "goldman"; +nDegree = 3; + +%% get ground truth CIR +constructIn = struct(... + "T", T, ... + "Lp", Lp, ... + "code", code, ... + "nDegree", nDegree, ... + "T2", T2, ... + "Lp2", Lp2); +try constructIn.hPre = hPre; catch, constructIn.hPre = ceil(1250/T2); end +try constructIn.hPost = hPost; catch, constructIn.hPost = ceil(1750/T2); end +try constructIn.hlen = hlen; catch ; end +rxIn = sim_mmo_tx(constructIn); + +rxIn.weights_ce = GetCEWeights(221); +rxIn.sameMo = true; + +%% rxOut +% rxIn.algoPD = "gt"; +% rxIn.algoCE = "gt"; +% rxIn.mode = "dc"; +% rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +% disp(cell2mat(rxOut.BER)); +% disp("----"); + +% rxIn.algoPD = "gt"; +% rxIn.algoCE = "af0"; +% rxIn.mode = "dc"; +% rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +% disp(cell2mat(rxOut.BER)); +% disp("----"); + +rxIn.algoPD = "gt1"; +rxIn.algoCE = "af0"; +rxIn.mode = "dc"; +rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +disp(rxOut.PDOff); +disp(cell2mat(rxOut.BER)); +disp("----"); + +% rxIn.algoPD = "sc"; +% rxIn.algoCE = "af0"; +% rxIn.debug_pd = false; +% rxIn.mode = "dc"; +% rxOut = decode_mmo_coherent_MMoNTxSW11ce(rxIn); +% disp(cell2mat(rxOut.BER)); +% disp("----"); +disp("------------------------------"); + +%% +if false + %% + figure("units","normalized","outerposition",[0 0 1 1]); + for j = 1:size(txOut.xBit,1) + for i = 1:nTx + subplot(size(txOut.xBit,1), nTx, (j-1)*size(txOut.xBit,1)+i); + hold on; box on; grid on; + stem(txOut.xBit{j,i}); + stem(rxOut.dBit{j,i}); + % yyaxis right; + % plot(txOut.xChannel.xCIR{j,i}, "k"); + % plot(rxOut.chan.hp{j,i}, "k"); + end + end +end \ No newline at end of file