Phone Calls on Trains
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
WHAT IS IT?
This is a model of phone calls on trains and their impact on the situated experience of callers and other passengers. By varying the number of passengers, their initial activities, and how considerate they are about others, it produces different "soundscapes" that promote or inhibit certain activities - such as reading, listening, or making phone calls.
The model also includes technological interventions to influence the soundscapes: Noise-canceling headphones, quiet zones, and "silent speech devices" (that make a phone call silent).
HOW IT WORKS
Passengers sit in a random seat and start performing an activity: Reading, calling, listening, or something unrelated (e.g., playing a game on the phone). Whether they can perform their activity depends on their surroundings. For example, a reader will be disturbed if someone else nearby makes a phone call. Conversely, a "listener" can only listen if their is a nearby phone call. Callers disturb other nearby callers, and "unrelated" performers are never disturbed.
Passengers' affect level becomes more positive if they can successfully perform their activity. Otherwise it becomes more negative. At a certain threshold, they will consider either switching the activity or to wait until the nearby disturbance stops.
Once every passenger has found an activity they can perform in their location, the model run stops. Depending on the number of passengers, their initial activities, and their considerateness, the emerging soundscape typically supports certain activities and inhibits others.
HOW TO USE IT
This model currently does not work in the web viewer. It requires the seats.csv file with the coordinates of each seat and the train.png file with the background image placed in the same folder.
The interface has sliders to vary the main variables: Number of passengers, percentage of callers (other activities are randomized), and percentage of considerate/inconsiderate passengers.
You can vary these sliders to see how they influence the number of performers for each activity.
There are three potential interventions: Noise-canceling headphones, silent speech devices, and quiet zones. Noise-canceling headphones make it possible to read even if a caller is nearby. Silent speech devices reduce the noise of a phone call so that it doesn't disturb anyone. The quiet zone is an environmental intervention: In the right area of the train compartment, phone calls will be forbidden (and in the model, everybody complies with that rule).
The remaining two sliders (mood change rate and interactive were added for a sensitivity analysis and to run larger experiments with the model.
EXTENDING THE MODEL
In the publication about that model, we only studied each intervention individually. It would be interesting to study how they work in conjunction. Other interventions, such as a way to extend the hearing range for listeners could also be included.
HOW TO CITE
Our analysis of this model was published as follows (currently "accepted"):
Uhde, A., Sadeghian, S., and Hassenzahl, M. (2026): Noise-Canceling, Silent Speech, or Quiet Zones? Technology and Social Soundscape Negotiation on Trains. In Proceedings of the ACM on Human-Computer Interaction (MHCI). 23 pages.
COPYRIGHT AND LICENSE
CC BY-SA 4.0
Comments and Questions
extensions [csv] ;extensions [csv profiler] globals [ seats countdown radius ;; Outcome variables | CHECK: everything here should be group-level variables total_listeners total_callers total_readers total_unrelated ;; count total passengers of each group active_listeners active_callers active_readers active_unrelated ;; count active passengers of each group active_callers_silent_speech_device active_readers_headphone active_readers_quiet_zone user_mood_reader user_mood_caller user_mood_listener user_mood_unrelated active_user_mood_reader active_user_mood_caller active_user_mood_listener active_user_mood_unrelated avg_others_listeners avg_others_callers avg_others_readers avg_others_unrelated other_mood_readers other_mood_callers other_mood_listeners other_mood_unrelated ] breed [ passengers passenger ] passengers-own [ plan activity considerateness headphone silent_speech_device positive_affect_limit negative_affect_limit mood surrounding_mood total_surrounding_passengers ] to setup clear-all ;; make individual runs replicable random-seed new-seed ;; load background image if interactive [import-drawing "train.png"] ;; "interactive" is a performance switch for automated model runs. It turns off background drawing and color changes. ;; ENVIRONMENT VARIABLES ;; The distance between two seats (horizontally) is around 94 cm on the real train and around 64 pixels in the model. Thus, 1m on the real train ;; are 68 px in the model. ;; The critical distance measured in the pilot study is at 2.05m or 140 px in the simulation environment. set radius 140 set countdown 3 ;; set countdown for stop condition (run stops if all agents perform their plan for this many rounds in a row) ;; IMPORT SEAT LAYOUT ;; ;; The seat coordinates are saved in the seats.csv file to increase readability ;; ;; Read seat coordinates: set seats [] let raw_csv csv:from-file "seats.csv" ;; create a list of strings from the csv file (each string includes both coordinates, e.g., ["270 24" ... ]) let raw_strings [] foreach raw_csv [ row -> (foreach (remove "" row) [ entry -> (set raw_strings insert-item 0 raw_strings entry) ]) ] ;; split the strings to get a list of lists with the two coordinates as strings for each seat (e.g. [["270" "24"] ... ] let string_list [] foreach raw_strings [ coordinates -> set string_list lput (split-string coordinates) string_list ] ;; convert the coordinates from string format and add a patch for each to the seats set. ;; The y coordinates are converted (y -> -y). The coordinates in the csv file follow the format in GIMP and need to be converted to the Netlogo format. foreach string_list [ coordinates -> set seats (patch-set seats patch (read-from-string (first coordinates)) (- read-from-string (last coordinates))) ] ;; CREATE PASSENGERS ;; ;; Create passengers and randomly assign some initial values. The plan, considerateness, headphone, and silent speech device ;; variables are randomized later, depending on the experimental condition. ;; ;; The individual affect limits for the activites are randomized and roughly based on Turner et al. (2008), assuming a normal ;; distribution. They are bounded by the PANAS scale limits when calculating the affect balance as: ;; ;; positive affect - negative affect = [-4, 4] ;; ;; Additionally, if positive_affect_limit happens to be negative or negative_affect_limit happens to be positive, they are ;; adjusted to their respective mean +/- two standard deviations. ask n-of population seats [ sprout-passengers 1 [ set shape "circle" set size 15 set color black set plan one-of (list "read" "listen" "unrelated") set activity "none" if (plan = "unrelated") [ set activity "unrelated" ] set considerateness "considerate" set headphone false set silent_speech_device false set positive_affect_limit (random-normal 1.713 0.656) if (positive_affect_limit <= 0) [set positive_affect_limit 0.401] ;; mean positive_affect_limit - 2 standard deviations if (positive_affect_limit > 4) [set positive_affect_limit 3.025] ;; mean positive_affect_limit + 2 standard deviations set negative_affect_limit (random-normal -1.324 0.618) if (negative_affect_limit >= 0) [set negative_affect_limit -0.088] ;; mean negative_affect_limit + 2 standard deviations if (negative_affect_limit < -4) [set negative_affect_limit -2.56] ;; mean negative_affect_limit - 2 standard deviations set mood (random-normal 0 1) ;; set mood to random initial levels (normal curve, mean = 0, standard deviation = 1) if (mood < negative_affect_limit) [set mood negative_affect_limit] if (mood > positive_affect_limit) [set mood positive_affect_limit] set surrounding_mood 0 set total_surrounding_passengers 0 ] ] ;; randomly assign callers let abs_callers int (callers * population) ; (cut-off rounding, e.g., 1.7 -> 1) ask n-of abs_callers passengers [ set plan "call" ] ;; randomly distribute headphones among all passengers let abs_headphones int (headphones * population) ask n-of abs_headphones passengers [ set headphone true ] ;; randomly distribute silent speech devices among all passengers let abs_silent_speech_devices int (silent_speech_devices * population) ask n-of abs_silent_speech_devices passengers [ set silent_speech_device true ] ;; randomly assign passengers to be considerate or inconsiderate let abs_inconsiderates int (inconsiderates * population) ask n-of abs_inconsiderates passengers [ set considerateness "inconsiderate" ] ask passengers [ sense-surroundings ] ;; basic output for headless mode to keep track of progress if (behaviorspace-run-number mod 500 = 0) [ print "" print date-and-time type "run: " print behaviorspace-run-number type "population: " print population type "callers: " print callers type "headphones: " print headphones type "silent speech devices: " print silent_speech_devices type "Quiet Zone: " print QuietZone type "Inconsiderates: " print inconsiderates print "" ] check-setup reset-ticks end to go ;; STOP CONDITION ;; ;; Every passenger can perform their plan in their current seat. Because of ;; the asynchronous process, this has a countdown timer for a few rounds. if all-good? [ type "Run " type behaviorspace-run-number print " finished." stop ] ;; PASSENGER BEHAVIOR ;; ;; Every passenger goes through the same set of subprocedures. Differences based ;; on the different plans are detailed in the subprocedures. ask passengers [ ifelse disturbed? [ ;; Can the activity be performed here? Returns true if disturbed/impossible, false if not. set activity "none" ] [ ifelse disturbing? [ ;; Does the activity disturb anyone? Returns true if it does, false if not. set activity "none" ] [ ;; If the activity neither disturbs nor is disturbed, perform it. set activity plan ] ] ifelse (activity = "none") [ ;; Slightly negative impact on mood for passengers who cannot perform their activity. get-frustrated if had-enough? [ give-up ] ] [ ;; Slightly positive impact on mood for passengers who can perform their activity. enjoy ] if interactive [set-color] ;; Updates the color of the agent to match the plan and activity. ] ;; DATA COLLECTION ;; ;; Group level data are prepared here before plotting data-collection tick end to-report disturbed? ;; Unrelated practices are never disturbed by definition if (plan = "unrelated") [report false] ;; Readers if (plan = "read") [ ;; Readers are not disturbed if they wear headphones if (headphone) [report false] ;; Readers without headphones in a normal compartment are disturbed ;; if anyone in the radius (in the same compartment) is calling without a silent speech device if not in-quiet-zone? [ if any? other passengers in-radius radius with [ activity = "call" and not silent_speech_device and not in-quiet-zone? ] [ report true ] ] ;; All other readers are undisturbed: ;; - not wearing headphones, but in quiet zone ;; - not wearing headphones but no nearby passenger in ;; the same compartment is calling report false ] ;; Callers if (plan = "call") [ ;; Callers are disturbed if in the quiet zone. if in-quiet-zone? [ report true ] ;; Callers are disturbed if they are in a normal compartment ;; but anyone nearby is calling without a silent speech device (and in the ;; same compartment) if any? other passengers in-radius radius with [ activity = "call" and not silent_speech_device and not in-quiet-zone? ] [ report true ] ;; All other callers are undisturbed: ;; - not in the quiet zone but no nearby passenger in ;; the same compartment is calling report false ] ;; Listeners if (plan = "listen") [ ;; No callers in the quiet zone, so listeners cannot ;; listen here. if in-quiet-zone? [ report true ] if any? other passengers in-radius radius with [ (activity = "call") and not silent_speech_device and not in-quiet-zone? ] [ report false ] ;; if they find one, they are NOT "disturbed" ;; If listeners finds no nearby caller within their radius, ;; who has no silent speech device, and if both are not in the quiet zone, ;; they are "disturbed" report true ] error "Error: The disturbed? procedure did not catch this passenger" end to-report disturbing? ;; This checks for (other people's) *activities*, not *plans*, assuming that ;; the passengers can only observe active performances. ;; Inconsiderate passengers do not check whether they disturb anyone. if (considerateness = "inconsiderate") [report false] ;; Listening, reading, and unrelated practices are never disturbing in the model. if (plan != "call") [report false] ;; Callers with silent_speech_devices do not disturb anyone. if (silent_speech_device) [report false] ;; Callers in quiet zone always disturb. if (in-quiet-zone?) [report true] ;; Callers disturb nearby readers and callers. ;; Note: This does not check for the other person's equipment (e.g., headphones) if any? other passengers in-radius radius with [ (activity = "read" or activity = "call") and not in-quiet-zone? ] [ report true ] ;; If passengers have: ;; 1. considerateness = considerate ;; 2. a plan to call ;; 3. no silent speech device ;; 4. are not in the quiet zone ;; 5. but no other nearby passenger is reading or calling ;; then they are not disturbing anyone. report false end to enjoy ;; Slightly increase mood if a passenger can perform their activity. Passengers ;; with an "unrelated" practice do not change their mood anymore. if (plan != "unrelated") [ set mood min (list (mood + mood_change_rate) positive_affect_limit) ;; increase mood up to the individual maximum positive affect ] end to get-frustrated ;; Slightly decrease mood if a passenger cannot perform their activity. Passengers ;; with an "unrelated" practice do not change their mood anymore. if (plan != "unrelated") [ set mood max (list (mood - mood_change_rate) negative_affect_limit) ;; reduce mood down to the individual maximum negative affect ] end ;; Based on the pilot study, the chance for passengers to actually change their seats is quite low (modeled now at 0%). ;; The probability to stay put and wait for the disturbance to end is now set to 39%. ;; The probability to switch activities is now set to 61%. to-report had-enough? ;; If if (plan != "unrelated") [ ;; Passengers have not had enough if the mood is still positive if (mood > 0) [report false] ifelse (random 100 + 1 < 61) [ report true ] ;; 61% chance that a passenger has had enough (change activity) [ report false ] ;; 39% chance that a passenger has not had enough (do nothing) ] end to give-up let choose_alternative (random 75 + 1) if (choose_alternative <= 50) ;; 66% chance to switch to a different related plan (because this comprises two alternative activities) [ change-plan stop ] if (choose_alternative > 50) ;; 33% chance to switch to an unrelated plan [ set plan "unrelated" set activity "unrelated" stop ] end to change-plan let new-plan "" if (plan = "listen") [ set new-plan one-of (list "read" "call") ] if (plan = "read") [ set new-plan one-of (list "listen" "call") ] if (plan = "call") [ set new-plan one-of (list "read" "listen") ] if (plan = "unrelated") [ error "Error: Passenger with unrelated plan tried to change plan (change-plan)" ] set plan new-plan end ;; This procedure is not used anymore but left here for reference. to switch-seat ;; randomly select a seat, within or outside of a possible quiet zone and ;; independent of the passenger's current plan let selected-seat NOBODY let free-seats patch-set (seats with [not any? passengers in-radius 5]) set selected-seat (one-of free-seats) if (selected-seat != NOBODY) [ move-to selected-seat ] end to data-collection ask passengers [ sense-surroundings ] get-user-data get-other-data end to sense-surroundings let surrounding_passengers (other passengers in-radius radius) ifelse any? surrounding_passengers [ set surrounding_mood (mean [mood] of surrounding_passengers) set total_surrounding_passengers (count surrounding_passengers) ] [ set surrounding_mood 0 set total_surrounding_passengers 0 ] end to get-user-data ;; USER MOOD AND NUMBERS ;; ;; listeners let listener_set (passengers with [plan = "listen"]) set total_listeners (count listener_set) ifelse (total_listeners > 0) [ set user_mood_listener (mean [mood] of listener_set) carefully [set active_listeners (count listener_set with [activity = "listen"])] [set active_listeners 0] carefully [set active_user_mood_listener (mean [mood] of listener_set with [activity = "listen"])] [set active_user_mood_listener 0] ] [ set user_mood_listener 0 set active_listeners 0 set active_user_mood_listener 0 ] ;; callers let caller_set (passengers with [plan = "call"]) set total_callers (count caller_set) ifelse (total_callers > 0) [ set user_mood_caller (mean [mood] of caller_set) carefully [set active_callers (count caller_set with [activity = "call"])] [set active_callers 0] carefully [set active_callers_silent_speech_device (count caller_set with [activity = "call" and silent_speech_device])] [set active_callers_silent_speech_device 0] carefully [set active_user_mood_caller (mean [mood] of caller_set with [activity = "call"])] [set active_user_mood_caller 0] ] [ set user_mood_caller 0 set active_callers 0 set active_callers_silent_speech_device 0 set active_user_mood_caller 0 ] ;; readers let reader_set (passengers with [plan = "read"]) set total_readers (count reader_set) ifelse (total_readers > 0) [ set user_mood_reader (mean [mood] of reader_set) carefully [set active_readers (count reader_set with [activity = "read"])] [set active_readers 0] carefully [set active_readers_headphone (count reader_set with [activity = "read" and headphone])] [set active_readers_headphone 0] carefully [set active_readers_quiet_zone (count reader_set with [activity = "read" and in-quiet-zone?])] [set active_readers_quiet_zone 0] carefully [set active_user_mood_reader (mean [mood] of reader_set with [activity = "read"])] [set active_user_mood_reader 0] ] [ set user_mood_reader 0 set active_readers 0 set active_readers_headphone 0 set active_readers_quiet_zone 0 set active_user_mood_reader 0 ] ;; unrelated let unrelated_set (passengers with [plan = "unrelated"]) set total_unrelated (count unrelated_set) ifelse (total_unrelated > 0) [ set user_mood_unrelated (mean [mood] of unrelated_set) carefully [set active_unrelated (count unrelated_set with [activity = "unrelated"])] [set active_unrelated 0] carefully [set active_user_mood_unrelated (mean [mood] of unrelated_set with [activity = "unrelated"])] [set active_user_mood_unrelated 0] ] [ set user_mood_unrelated 0 set active_unrelated 0 set active_user_mood_unrelated 0 ] end to get-other-data ;; OTHER MOOD AND NUMBERS (in surroundings) ;; ;; This procedure aggregates the data about surrounding passengers on a group level ;; listeners ifelse active_listeners > 0 [ let active_listener_set passengers with [activity = "listen"] set avg_others_listeners (mean [total_surrounding_passengers] of active_listener_set) set other_mood_listeners (mean [surrounding_mood] of active_listener_set) ] [ set avg_others_listeners 0 set other_mood_listeners 0 ] ;; callers ifelse active_callers > 0 [ let active_caller_set passengers with [activity = "call"] set avg_others_callers (mean [total_surrounding_passengers] of active_caller_set) set other_mood_callers (mean [surrounding_mood] of active_caller_set) ] [ set avg_others_callers 0 set other_mood_callers 0 ] ;; readers ifelse active_readers > 0 [ let active_reader_set passengers with [activity = "read"] set avg_others_readers (mean [total_surrounding_passengers] of active_reader_set) set other_mood_readers (mean [surrounding_mood] of active_reader_set) ] [ set avg_others_readers 0 set other_mood_readers 0 ] ;; unrelated ifelse active_unrelated > 0 [ let active_unrelated_set passengers with [activity = "unrelated"] set avg_others_unrelated (mean [total_surrounding_passengers] of active_unrelated_set) set other_mood_unrelated (mean [surrounding_mood] of active_unrelated_set) ] [ set avg_others_unrelated 0 set other_mood_unrelated 0 ] end to-report in-quiet-zone? ifelse (QuietZone and xcor > 1090) [ report true ] [ report false ] end to-report all-good? if (countdown = 0) [report true] ifelse (any? passengers with [activity != plan]) [ set countdown 3 report false ] [ set countdown countdown - 1 report false ] end to set-color ;; readers are violet ;; active readers: #762a83 (118,42,131 -> 113.9) ;; inactive readers: #c5b2d7 (197,178,215 -> 117.8) if (plan = "read") [ set shape "square" ifelse (activity = "read") ; violet [ set color 113.9 ] [ set color 117.8 ] ] ;; callers are orange ;; active callers: #f1a340 (241,163,64 -> 26.5) ;; inactive callers: #fac9ab ()250,201,171 -> 28.2) if (plan = "call") [ set shape "circle" ifelse (activity = "call") ; orange [ set color 26.5 ] [ set color 28.2 ] ] ;; listeners are green ;; active listeners: #1b7837 (27,120,55 -> 63) ;; inactive listeners: #98ce86 (152,206,134 -> 56.9) if (plan = "listen") [ set shape "triangle" ifelse (activity = "listen") ; green [ set color 63 ] [ set color 56.9 ] ] ;; unrelated are black if (plan = "none" or plan = "unrelated") [ set shape "pentagon" set color 0 ] end to-report split-string [a] let len (length a) let break (position " " a) report list (substring a 0 break) (substring a (break + 1) len) end ;; Validation checks to check-setup ;; Check Rounding Bugs ;; Are the population counts correct? if (count passengers != population) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers) type " passengers, although there should be " print population ] ;; Are the caller counts correct? if (count passengers with [plan = "call"] != int (population * callers)) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [plan = "call"]) type " callers, although there should be " print (population * callers) ] ;; Are the headphone counts correct? if (count passengers with [headphone = true] != int (population * headphones)) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [headphone = true]) type " passengers with headphones, although there should be " print int (population * headphones) ] ;; Are the silent speech device counts correct? if (count passengers with [silent_speech_device = true] != int (population * silent_speech_devices)) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [silent_speech_device = true]) type " passengers with silent speech devices, although there should be " print int (population * silent_speech_devices) ] ;; Are the social norm counts correct? if (count passengers with [considerateness = "inconsiderate"] != int (population * inconsiderates)) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [considerateness = "inconsiderate"]) type " inconsiderate passengers , although there should be " print int (population * inconsiderates) ] ;; Validate positive affect limits if (count passengers with [positive_affect_limit <= 0 or positive_affect_limit > 4] > 0) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [positive_affect_limit <= 0 or positive_affect_limit > 4]) print " passengers with a positive affect limit outside the allowed range of (0, 4]." ] ;; Validate negative affect limits if (count passengers with [negative_affect_limit >= 0 or negative_affect_limit < -4] > 0) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [negative_affect_limit >= 0 or negative_affect_limit < -4]) print " passengers with a negative affect limit outside the allowed range of [-4, 0)." ] ;; Validate that mood is in PANAS range if (count passengers with [mood < -4 or mood > 4] > 0) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [mood < -4 or mood > 4]) print " passengers with a mood outside the PANAS range of [-4, 4]." ] ;; Validate that mood is in individual limits if (count passengers with [mood < negative_affect_limit or mood > positive_affect_limit] > 0) [ type "[WARNING] Run: " type behaviorspace-run-number type " There are " type (count passengers with [mood < negative_affect_limit or mood > positive_affect_limit]) print " passengers with a mood outside their individual range." ] end
There are 2 versions of this model.
Attached files
| File | Type | Description | Last updated | |
|---|---|---|---|---|
| Phone Calls on Trains.png | preview | The model environment | 11 days ago, by Alarith Uhde | Download |
| seats.csv | data | Seat Layout | 11 days ago, by Alarith Uhde | Download |
| train.png | background | Train Environment | 11 days ago, by Alarith Uhde | Download |
This model does not have any ancestors.
This model does not have any descendants.
Download this model